Publish Weblog entry with the specified status

This commit is contained in:
Otavio Cordeiro 2025-12-21 14:53:15 +01:00 committed by Otávio
parent 52d540f789
commit 28474844fe
8 changed files with 74 additions and 20 deletions

View file

@ -5,25 +5,33 @@ public extension String {
/// Creates a weblog entry body with frontmatter from the string content.
///
/// This method formats the string as a weblog entry by adding frontmatter
/// with the specified publication date. The resulting format follows the
/// with the specified publication date and status. The resulting format follows the
/// OMG.LOL weblog API requirements with ISO 8601 date formatting.
///
/// The output format is:
/// ```
/// ---
/// Date: YYYY-MM-DD HH:MM
/// Status: [status value]
/// ---
///
/// [string content]
/// ```
///
/// - Parameter date: The publication date to include in the frontmatter
/// - Parameters:
/// - date: The publication date to include in the frontmatter
/// - status: The publication status to include in the frontmatter (e.g., "Draft", "Live", "Feed Only", "Web
/// Only", "Unlisted")
/// - Returns: UTF-8 encoded data containing the formatted weblog entry body
func weblogEntryBody(with date: Date) -> Data {
func weblogEntryBody(
date: Date,
status: String
) -> Data {
let formattedString = DateFormatter.iso8601WithShortTime.string(from: date)
let body = """
---
Date: \(formattedString)
Status: \(status)
---
\(self)

View file

@ -83,13 +83,14 @@ public enum WeblogRequestFactory {
/// Creates a request to create a new weblog entry.
///
/// This method builds a POST request to create a new weblog entry with the
/// specified content and publication date. The request formats the content
/// with frontmatter containing the date and sends it as raw data to the API.
/// specified content, publication date, and status. The request formats the content
/// with frontmatter containing the date and status, then sends it as raw data to the API.
///
/// The content is formatted as:
/// ```
/// ---
/// Date: YYYY-MM-DD HH:MM
/// Status: [status value]
/// ---
///
/// [content goes here]
@ -97,19 +98,25 @@ public enum WeblogRequestFactory {
///
/// This request requires authentication as it creates content on behalf
/// of the authenticated user. The API will generate a unique entry ID
/// and URL slug based on the content title.
/// and URL slug based on the content title. The entry's visibility and
/// distribution will be controlled by the specified status.
///
/// - Parameters:
/// - address: The user address (username) to create the entry for
/// - content: The markdown content body of the weblog entry
/// - status: The publication status of the entry (e.g., "Draft", "Live", "Feed Only", "Web Only", "Unlisted")
/// - date: The publication date for the entry
/// - Returns: A configured network request for creating a weblog entry
public static func makeCreateWeblogEntryRequest(
address: String,
content: String,
status: String,
date: Date
) -> NetworkRequest<Data, CreateOrUpdateWeblogEntryResponse> {
let body = content.weblogEntryBody(with: date)
let body = content.weblogEntryBody(
date: date,
status: status
)
return .init(
path: "/address/\(address)/weblog/entry",
@ -122,12 +129,13 @@ public enum WeblogRequestFactory {
///
/// This method builds a POST request to update an existing weblog entry
/// identified by its entry ID. The request formats the updated content
/// with frontmatter containing the new date and sends it as raw data.
/// with frontmatter containing the new date and status, then sends it as raw data.
///
/// The content is formatted as:
/// ```
/// ---
/// Date: YYYY-MM-DD HH:MM
/// Status: [status value]
/// ---
///
/// [updated content goes here]
@ -135,21 +143,28 @@ public enum WeblogRequestFactory {
///
/// This request requires authentication and the user must own the entry
/// being updated. The entry's URL and slug will remain unchanged, but
/// the content and metadata will be updated with the new values.
/// the content, publication date, status, and metadata will be updated
/// with the new values.
///
/// - Parameters:
/// - address: The user address (username) who owns the entry
/// - entryID: The unique identifier of the entry to update
/// - content: The updated markdown content body of the weblog entry
/// - status: The updated publication status of the entry (e.g., "Draft", "Live", "Feed Only", "Web Only",
/// "Unlisted")
/// - date: The updated publication date for the entry
/// - Returns: A configured network request for updating the weblog entry
public static func makeUpdateWeblogEntryRequest(
address: String,
entryID: String,
content: String,
status: String,
date: Date
) -> NetworkRequest<Data, CreateOrUpdateWeblogEntryResponse> {
let body = content.weblogEntryBody(with: date)
let body = content.weblogEntryBody(
date: date,
status: status
)
return .init(
path: "/address/\(address)/weblog/entry/\(entryID)",

View file

@ -20,7 +20,12 @@
EntryResponseMother.makeEntryResponses(count: 2)
}
func createWeblogEntry(address: String, content: String, date: Date) async throws -> EntryResponse {
func createWeblogEntry(
address: String,
content: String,
status: String,
date: Date
) async throws -> EntryResponse {
EntryResponseMother.makeEntryResponse()
}
@ -28,6 +33,7 @@
address: String,
entryID: String,
content: String,
status: String,
date: Date
) async throws -> EntryResponse {
EntryResponseMother.makeEntryResponse()

View file

@ -39,7 +39,13 @@
// MARK: - Public
func fetchEntries() async throws {}
func createOrUpdateEntry(address: String, entryID: String?, body: String, date: Date) async throws {}
func createOrUpdateEntry(
address: String,
entryID: String?,
body: String,
status: String,
date: Date
) async throws {}
func deleteEntry(address: String, entryID: String) async throws {}
}

View file

@ -76,7 +76,7 @@ struct EditorView: View {
.gridColumnAlignment(.trailing)
Picker(
selection: $viewModel.selectedStatus,
selection: $viewModel.status,
content: {
ForEach(WeblogEntryStatus.allCases) { status in
Text(status.displayName)

View file

@ -11,7 +11,7 @@ final class EditorViewModel {
var body: String
var entryID: String?
var date: Date
var selectedStatus: WeblogEntryStatus
var status: WeblogEntryStatus
var shouldDismiss = false
private(set) var isSubmitting = false
@ -46,7 +46,7 @@ final class EditorViewModel {
self.body = body
self.date = date
self.entryID = entryID
selectedStatus = status
self.status = status
self.repository = repository
}
@ -63,6 +63,7 @@ final class EditorViewModel {
address: address,
entryID: entryID,
body: body,
status: status.rawValue,
date: date
)
shouldDismiss = true

View file

@ -41,22 +41,24 @@ public protocol WeblogNetworkServiceProtocol: AnyObject, Sendable {
/// Creates a new weblog entry for the specified address.
///
/// This method sends a POST request to the OMG.LOL API to create a new weblog entry.
/// The content is formatted with frontmatter including the publication date and
/// sent as raw data to the API endpoint.
/// The content is formatted with frontmatter including the publication date, status,
/// and sent as raw data to the API endpoint.
///
/// The request requires authentication and will create a new entry with a
/// system-generated unique identifier. The entry will be immediately available
/// at the user's weblog URL.
/// at the user's weblog URL according to the specified status.
///
/// - Parameters:
/// - address: The user address (username) to create the entry for
/// - content: The markdown content body of the weblog entry
/// - status: The publication status of the entry (e.g., "Draft", "Live", "Feed Only", "Web Only", "Unlisted")
/// - date: The publication date for the entry
/// - Returns: The created weblog entry with metadata and generated ID
/// - Throws: Network errors, authentication errors, or API-specific errors.
func createWeblogEntry(
address: String,
content: String,
status: String,
date: Date
) async throws -> EntryResponse
@ -64,16 +66,18 @@ public protocol WeblogNetworkServiceProtocol: AnyObject, Sendable {
///
/// This method sends a POST request to the OMG.LOL API to update an existing
/// weblog entry identified by its entry ID. The content is formatted with
/// frontmatter including the updated publication date and sent as raw data.
/// frontmatter including the updated publication date, status, and sent as raw data.
///
/// The request requires authentication and the user must own the entry being
/// updated. The entry's URL and slug will remain unchanged, but the content,
/// publication date, and metadata will be updated.
/// publication date, status, and metadata will be updated.
///
/// - Parameters:
/// - address: The user address (username) who owns the entry
/// - entryID: The unique identifier of the entry to update
/// - content: The updated markdown content body of the weblog entry
/// - status: The updated publication status of the entry (e.g., "Draft", "Live", "Feed Only", "Web Only",
/// "Unlisted")
/// - date: The updated publication date for the entry
/// - Returns: The updated weblog entry with new content and metadata
/// - Throws: Network errors, authentication errors, or API-specific errors if the entry is not found.
@ -81,6 +85,7 @@ public protocol WeblogNetworkServiceProtocol: AnyObject, Sendable {
address: String,
entryID: String,
content: String,
status: String,
date: Date
) async throws -> EntryResponse
@ -143,12 +148,14 @@ actor WeblogNetworkService: WeblogNetworkServiceProtocol {
func createWeblogEntry(
address: String,
content: String,
status: String,
date: Date
) async throws -> EntryResponse {
let response = try await networkClient.run(
WeblogRequestFactory.makeCreateWeblogEntryRequest(
address: address,
content: content,
status: status,
date: date
)
)
@ -163,6 +170,7 @@ actor WeblogNetworkService: WeblogNetworkServiceProtocol {
address: String,
entryID: String,
content: String,
status: String,
date: Date
) async throws -> EntryResponse {
let response = try await networkClient.run(
@ -170,6 +178,7 @@ actor WeblogNetworkService: WeblogNetworkServiceProtocol {
address: address,
entryID: entryID,
content: content,
status: status,
date: date
)
)

View file

@ -60,6 +60,7 @@ public protocol WeblogRepositoryProtocol: Sendable {
/// - address: The address for which to create or update the weblog entry.
/// - entryID: The unique identifier of the entry to update. If nil, creates a new entry.
/// - body: The content body of the weblog entry in markdown format
/// - status: The publication status of the entry (e.g., "Draft", "Live", "Feed Only", "Web Only", "Unlisted")
/// - date: The publication date for the entry
/// - Throws: Network errors from the remote operation or persistence errors
/// from the local storage operations.
@ -67,6 +68,7 @@ public protocol WeblogRepositoryProtocol: Sendable {
address: String,
entryID: String?,
body: String,
status: String,
date: Date
) async throws
@ -139,6 +141,7 @@ actor WeblogRepository: WeblogRepositoryProtocol {
address: String,
entryID: String? = nil,
body: String,
status: String,
date: Date
) async throws {
if let entryID {
@ -146,12 +149,14 @@ actor WeblogRepository: WeblogRepositoryProtocol {
address: address,
entryID: entryID,
body: body,
status: status,
date: date
)
} else {
try await createEntry(
address: address,
body: body,
status: status,
date: date
)
}
@ -162,6 +167,7 @@ actor WeblogRepository: WeblogRepositoryProtocol {
private func createEntry(
address: String,
body: String,
status: String,
date: Date
) async throws {
guard await authSessionService.isLoggedIn else {
@ -171,6 +177,7 @@ actor WeblogRepository: WeblogRepositoryProtocol {
_ = try await networkService.createWeblogEntry(
address: address,
content: body,
status: status,
date: date
)
@ -181,6 +188,7 @@ actor WeblogRepository: WeblogRepositoryProtocol {
address: String,
entryID: String,
body: String,
status: String,
date: Date
) async throws {
guard await authSessionService.isLoggedIn else {
@ -191,6 +199,7 @@ actor WeblogRepository: WeblogRepositoryProtocol {
address: address,
entryID: entryID,
content: body,
status: status,
date: date
)