Last active
February 12, 2026 03:44
-
-
Save adityadaniel/35d5643db2f84f571c0573f820da117d to your computer and use it in GitHub Desktop.
NewPostView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| enum CategoryType: String, Codable, CaseIterable { | |
| case post | |
| case event | |
| } | |
| enum EmotionType: String, Codable, CaseIterable { | |
| case happy | |
| case lovely | |
| case neutral | |
| case sad | |
| case angry | |
| case drowning | |
| var emoji: String { | |
| switch self { | |
| case .happy: return "😄" | |
| case .sad: return "😢" | |
| case .angry: return "😠" | |
| case .drowning: return "🌊" | |
| case .lovely: return "💖" | |
| case .neutral: return "🤔" | |
| } | |
| } | |
| var title: String { | |
| switch self { | |
| case .happy: return "Happy" | |
| case .sad: return "Sad" | |
| case .angry: return "Angry" | |
| case .drowning: return "Drowning" | |
| case .lovely: return "Lovely" | |
| case .neutral: return "Neutral" | |
| } | |
| } | |
| } | |
| struct PostModel { | |
| var id = UUID() | |
| var category: CategoryType | |
| var title: String | |
| var image: UIImage? | |
| var emotion: EmotionType? | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // NewPostView.swift | |
| // Thunderosaurus | |
| // | |
| // Created by Ivan on 11/02/26. | |
| // | |
| import SwiftUI | |
| import PhotosUI | |
| struct PostInputView: View { | |
| @Binding var content: String | |
| var body: some View { | |
| Section("What's this post about") { | |
| TextEditor(text: $content) | |
| .frame(height: 150) | |
| } | |
| } | |
| } | |
| struct NewPostView: View { | |
| @EnvironmentObject private var viewModel: ViewModel | |
| @Environment(\.dismiss) private var dismiss | |
| @State private var selectedCategory: CategoryType = .post | |
| @State private var content: String = "" | |
| @State private var photosItem: PhotosPickerItem? = nil | |
| @State private var selectedImage: UIImage? = nil | |
| @State private var selectedEmotion: EmotionType = .happy | |
| @State private var isSending: Bool = false | |
| @EnvironmentObject private var viewmodel: ViewModel | |
| var body: some View { | |
| Form { | |
| Section { | |
| Picker("Category", selection: self.$selectedCategory) { | |
| ForEach(CategoryType.allCases, id: \.rawValue) { type in | |
| Text(type.rawValue.capitalized) | |
| .tag(type) | |
| } | |
| } | |
| .pickerStyle(.menu) | |
| } | |
| PostInputView(content: $content) | |
| Section { | |
| if let image = self.selectedImage { | |
| Image(uiImage: image) | |
| .resizable() | |
| .aspectRatio(contentMode: .fill) | |
| .frame(maxHeight: 200) | |
| .padding(.vertical) | |
| .clipShape(RoundedRectangle(cornerRadius: 12)) | |
| } else { | |
| PhotosPicker(selection: self.$photosItem) { | |
| Label("Add new photo", systemImage: "photo.badge.plus") | |
| } | |
| } | |
| } | |
| Section("Emotion") { | |
| let columns = Array(repeating: GridItem(.flexible(), spacing: 8), count: 3) | |
| LazyVGrid(columns: columns, spacing: 8) { | |
| ForEach(EmotionType.allCases, id: \.self) { emotionType in | |
| Button { | |
| withAnimation { self.selectedEmotion = emotionType } | |
| } label: { | |
| HStack(spacing: 6) { | |
| Text(emotionType.emoji) | |
| Text(emotionType.title) | |
| .font(.subheadline) | |
| .fontWeight(.semibold) | |
| } | |
| .frame(maxWidth: .infinity) | |
| .padding(.vertical, 8) | |
| .background( | |
| self.selectedEmotion == emotionType | |
| ? Color.accentColor.opacity(0.18) | |
| : Color.clear | |
| ) | |
| .overlay( | |
| Capsule() | |
| .stroke( | |
| self.selectedEmotion == emotionType | |
| ? Color.accentColor | |
| : Color.secondary.opacity(0.4), | |
| lineWidth: 1 | |
| ) | |
| ) | |
| .clipShape(Capsule()) | |
| } | |
| .buttonStyle(.plain) | |
| .accessibilityLabel(emotionType.title) | |
| .accessibilityValue( | |
| self.selectedEmotion == emotionType ? "Selected" : "" | |
| ) | |
| } | |
| } | |
| .padding(.vertical, 4) | |
| } | |
| Section { | |
| Button { | |
| self.sendPost() | |
| } label: { | |
| HStack(spacing: 8) { | |
| if self.isSending { | |
| ProgressView() | |
| .progressViewStyle(.circular) | |
| } | |
| Text(self.isSending ? "Sending..." : "Add") | |
| } | |
| } | |
| .buttonStyle(.glassProminent) | |
| .controlSize(.large) | |
| .buttonSizing(.flexible) | |
| .tint(self.isSending ? .gray : .accentColor) | |
| .disabled(self.isSending) | |
| } | |
| } | |
| .onChange(of: self.photosItem, { oldValue, newValue in | |
| if let item = newValue { | |
| item.loadTransferable(type: Data.self) { photoData in | |
| switch photoData { | |
| case .success(let data): | |
| if let data, let uiImage = UIImage(data: data) { | |
| self.selectedImage = uiImage | |
| } else { | |
| print("error loading photo") | |
| } | |
| case .failure(let error): | |
| print("error loading photo, \(error)") | |
| } | |
| } | |
| } | |
| }) | |
| .navigationTitle("Add new post") | |
| .toolbarTitleDisplayMode(.inlineLarge) | |
| .toolbar { | |
| ToolbarItem(placement: .topBarTrailing) { | |
| Button("Cancel") { | |
| self.dismiss() | |
| } | |
| } | |
| } | |
| } | |
| private func sendPost() { | |
| if self.isSending { | |
| return | |
| } | |
| self.isSending = true | |
| Task { | |
| let delay = UInt64.random(in: 800_000_000...2_200_000_000) | |
| try? await Task.sleep(nanoseconds: delay) | |
| let post = PostModel( | |
| category: self.selectedCategory, | |
| title: self.content, | |
| image: self.selectedImage, | |
| emotion: self.selectedEmotion | |
| ) | |
| self.viewModel.addPost(post) | |
| self.isSending = false | |
| self.dismiss() | |
| } | |
| } | |
| } | |
| #Preview { | |
| NavigationStack { | |
| NewPostView() | |
| } | |
| .environmentObject(ViewModel()) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment