import SwiftUI struct PostList: View { @EnvironmentObject private var content: Content @Environment(\.language) private var language: ContentLanguage @State private var selected: Post? = nil @State private var showNewPostView = false @State private var newPostId = "" @State private var newPostIdIsValid = false private let allowedCharactersInPostId = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-")).inverted private var cleanPostId: String { newPostId.trimmingCharacters(in: .whitespacesAndNewlines) } var body: some View { NavigationSplitView { List(content.posts, selection: $selected) { post in Text(post.localized(in: language).title) .tag(post) } .toolbar { ToolbarItem(placement: .primaryAction) { Button(action: { showNewPostView = true }) { Label("New post", systemSymbol: .plus) } } } .navigationSplitViewColumnWidth(min: 250, ideal: 250, max: 250) } content: { if let selected { PostContentView(post: selected) .layoutPriority(1) } else { HStack { Spacer() Text("Select a post to show the content") .font(.largeTitle) .foregroundColor(.secondary) Spacer() }.layoutPriority(1) } } detail: { if let selected { PostDetailView(post: selected) .frame(minWidth: 280) } else { Text("No post selected") .frame(minWidth: 280) } } .sheet(isPresented: $showNewPostView, onDismiss: addNewPost) { TextEntrySheet( title: "Enter the id for the new post", text: $newPostId, isValid: $newPostIdIsValid) } .onChange(of: newPostId) { _, newValue in newPostIdIsValid = isValid(id: newValue) } .onAppear { if selected == nil { selected = content.posts.first } } } private func isValid(id: String) -> Bool { let id = cleanPostId guard id != "" else { return false } guard !content.posts.contains(where: { $0.id == id }) else { return false } // Only allow alphanumeric characters and hyphens return id.rangeOfCharacter(from: allowedCharactersInPostId) == nil } private func addNewPost() { let id = cleanPostId guard isValid(id: id) else { return } let post = Post( id: id, isDraft: true, createdDate: .now, startDate: .now, endDate: nil, tags: [], german: .init(title: "Titel", content: "Text"), english: .init(title: "Title", content: "Text")) content.posts.insert(post, at: 0) selected = post } } #Preview { PostList() .environmentObject(Content()) }