diff --git a/Packages/Route/Sources/Route/Windows/EditWeblogEntryWindow.swift b/Packages/Route/Sources/Route/Windows/EditWeblogEntryWindow.swift index 948ff47..634754b 100644 --- a/Packages/Route/Sources/Route/Windows/EditWeblogEntryWindow.swift +++ b/Packages/Route/Sources/Route/Windows/EditWeblogEntryWindow.swift @@ -30,17 +30,22 @@ public struct EditWeblogEntry: Codable, Hashable { /// The unique identifier for the weblog entry, if it already exists. public let entryID: String? + /// The publication status of the weblog entry. + public let status: String? + // MARK: - Lifecycle public init( address: String, body: String, date: Date, - entryID: String? + entryID: String?, + status: String? ) { self.address = address self.body = body self.date = date self.entryID = entryID + self.status = status } } diff --git a/Packages/Weblog/Sources/Weblog/Factories/ViewModelFactory.swift b/Packages/Weblog/Sources/Weblog/Factories/ViewModelFactory.swift index 57d399c..de299ad 100644 --- a/Packages/Weblog/Sources/Weblog/Factories/ViewModelFactory.swift +++ b/Packages/Weblog/Sources/Weblog/Factories/ViewModelFactory.swift @@ -63,13 +63,15 @@ final class ViewModelFactory: Sendable { address: String, body: String, date: Date, - entryID: String? + entryID: String?, + status: WeblogEntryStatus ) -> EditorViewModel { .init( address: address, body: body, date: date, entryID: entryID, + status: status, repository: container.resolve() ) } diff --git a/Packages/Weblog/Sources/Weblog/Fixtures/EditorViewModelMother.swift b/Packages/Weblog/Sources/Weblog/Fixtures/EditorViewModelMother.swift index 6dfb36b..aa11e58 100644 --- a/Packages/Weblog/Sources/Weblog/Fixtures/EditorViewModelMother.swift +++ b/Packages/Weblog/Sources/Weblog/Fixtures/EditorViewModelMother.swift @@ -13,6 +13,7 @@ body: "# This is the title\n\nThis is the body...", date: .init(), entryID: nil, + status: .draft, repository: WeblogRepositoryMother.makeWeblogRepository() ) } diff --git a/Packages/Weblog/Sources/Weblog/Models/WeblogEntryStatus.swift b/Packages/Weblog/Sources/Weblog/Models/WeblogEntryStatus.swift new file mode 100644 index 0000000..e8a9219 --- /dev/null +++ b/Packages/Weblog/Sources/Weblog/Models/WeblogEntryStatus.swift @@ -0,0 +1,48 @@ +import Foundation + +/// Represents the publication status of a weblog entry. +/// +/// Status values control how entries are displayed and distributed across +/// the weblog and its feeds. Each status provides different visibility +/// and accessibility options for entries. +enum WeblogEntryStatus: String, CaseIterable, Hashable, Identifiable { + + /// Draft status marks an entry as unpublished. + /// + /// Entries with this status are not publicly accessible and are + /// typically used for work-in-progress content. + case draft = "Draft" + + /// Live status makes an entry fully published and accessible. + /// + /// Entries with this status appear on the weblog, in post lists, + /// on tag pages, and are included in RSS, Atom, and JSON feeds. + case live + + /// Feed Only status hides the entry from the web but includes it in feeds. + /// + /// Entries with this status are treated as unlisted on the web (not + /// appearing in post lists, tag pages, or as landing pages) but are + /// included in RSS, Atom, and JSON feeds. + case liveRSSOnly = "Feed Only" + + /// Web Only status hides the entry from feeds but keeps it on the web. + /// + /// Entries with this status are accessible on the weblog and appear + /// in post lists and tag pages, but are completely hidden from RSS, + /// Atom, and JSON feeds. + case liveWebOnly = "Web Only" + + /// Unlisted status makes an entry accessible only via direct URL. + /// + /// Entries with this status are not served as default/landing pages, + /// do not appear in post lists or recent posts, and are not included + /// on tag pages. They are only accessible to people who know the + /// entry's direct URL. + case unlisted = "Unlisted" + + // MARK: - Properties + + var id: Self { self } + var displayName: String { rawValue.capitalized } +} diff --git a/Packages/Weblog/Sources/Weblog/Scenes/EditWeblogEntryScene.swift b/Packages/Weblog/Sources/Weblog/Scenes/EditWeblogEntryScene.swift index 67f54c4..dfcb011 100644 --- a/Packages/Weblog/Sources/Weblog/Scenes/EditWeblogEntryScene.swift +++ b/Packages/Weblog/Sources/Weblog/Scenes/EditWeblogEntryScene.swift @@ -27,6 +27,7 @@ struct EditWeblogEntryScene: Scene { body: entry?.body ?? "", date: entry?.date ?? .init(), entryID: entry?.entryID, + status: entry?.status.flatMap(WeblogEntryStatus.init) ?? .draft, address: entry?.address ?? "" ) } @@ -44,6 +45,7 @@ struct EditWeblogEntryScene: Scene { body: String, date: Date, entryID: String?, + status: WeblogEntryStatus, address: String ) -> some View { let viewModel = environment.viewModelFactory @@ -51,7 +53,8 @@ struct EditWeblogEntryScene: Scene { address: address, body: body, date: date, - entryID: entryID + entryID: entryID, + status: status ) EditorView( diff --git a/Packages/Weblog/Sources/Weblog/Views/App/WeblogApp.swift b/Packages/Weblog/Sources/Weblog/Views/App/WeblogApp.swift index 8b21b47..91443de 100644 --- a/Packages/Weblog/Sources/Weblog/Views/App/WeblogApp.swift +++ b/Packages/Weblog/Sources/Weblog/Views/App/WeblogApp.swift @@ -88,7 +88,8 @@ struct WeblogApp: View { address: address, body: "# Title of your post\n\nThis is the body of your post...", date: .init(), - entryID: nil + entryID: nil, + status: nil ) ) } label: { diff --git a/Packages/Weblog/Sources/Weblog/Views/Editor/EditorView.swift b/Packages/Weblog/Sources/Weblog/Views/Editor/EditorView.swift index 6bc1050..6d3c635 100644 --- a/Packages/Weblog/Sources/Weblog/Views/Editor/EditorView.swift +++ b/Packages/Weblog/Sources/Weblog/Views/Editor/EditorView.swift @@ -70,6 +70,23 @@ struct EditorView: View { ) {} .help("Select publication time") } + + GridRow { + Text("Status") + .gridColumnAlignment(.trailing) + + Picker( + selection: $viewModel.selectedStatus, + content: { + ForEach(WeblogEntryStatus.allCases) { status in + Text(status.displayName) + } + }, + label: { EmptyView() } + ) + .pickerStyle(.radioGroup) + .labelsHidden() + } } .padding() } diff --git a/Packages/Weblog/Sources/Weblog/Views/Editor/EditorViewModel.swift b/Packages/Weblog/Sources/Weblog/Views/Editor/EditorViewModel.swift index f0764f6..8c7d099 100644 --- a/Packages/Weblog/Sources/Weblog/Views/Editor/EditorViewModel.swift +++ b/Packages/Weblog/Sources/Weblog/Views/Editor/EditorViewModel.swift @@ -11,6 +11,7 @@ final class EditorViewModel { var body: String var entryID: String? var date: Date + var selectedStatus: WeblogEntryStatus var shouldDismiss = false private(set) var isSubmitting = false @@ -38,12 +39,14 @@ final class EditorViewModel { body: String, date: Date, entryID: String?, + status: WeblogEntryStatus, repository: any WeblogRepositoryProtocol ) { self.address = address self.body = body self.date = date self.entryID = entryID + selectedStatus = status self.repository = repository } diff --git a/Packages/Weblog/Sources/Weblog/Views/Weblog Entry/WeblogEntryView.swift b/Packages/Weblog/Sources/Weblog/Views/Weblog Entry/WeblogEntryView.swift index 9ac7c4b..a077a10 100644 --- a/Packages/Weblog/Sources/Weblog/Views/Weblog Entry/WeblogEntryView.swift +++ b/Packages/Weblog/Sources/Weblog/Views/Weblog Entry/WeblogEntryView.swift @@ -171,7 +171,8 @@ struct WeblogEntryView: View { address: viewModel.address, body: viewModel.body, date: viewModel.publishedDate, - entryID: viewModel.id + entryID: viewModel.id, + status: viewModel.status ) ) }