Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Write Form Feature #40

Merged
merged 5 commits into from
Apr 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
503 changes: 426 additions & 77 deletions ForPDA.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions Modules/Sources/APIClient/APIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ public struct APIClient: Sendable {
public var markReadForum: @Sendable (_ id: Int, _ isTopic: Bool) async throws -> Bool
public var getAnnouncement: @Sendable (_ id: Int) async throws -> Announcement
public var getTopic: @Sendable (_ id: Int, _ page: Int, _ perPage: Int) async throws -> Topic
public var getTemplate: @Sendable (_ request: ForumTemplateRequest, _ isTopic: Bool) async throws -> [WriteFormFieldType]
public var getHistory: @Sendable (_ offset: Int, _ perPage: Int) async throws -> History
public var previewPost: @Sendable (_ request: PostPreviewRequest) async throws -> PostPreview
public var sendPost: @Sendable (_ request: PostRequest) async throws -> PostSend

// Favorites
public var getFavorites: @Sendable (_ request: FavoritesRequest, _ policy: CachePolicy) async throws -> AsyncThrowingStream<Favorite, any Error>
Expand Down Expand Up @@ -224,11 +227,39 @@ extension APIClient: DependencyKey {
let request = TopicRequest(id: id, offset: offset, itemsPerPage: perPage, showPostMode: 1)
let response = try await api.get(ForumCommand.Topic.view(data: request))
return try await parser.parseTopic(response)
},
getTemplate: { request, isTopic in
let command = ForumCommand.template(
type: isTopic ? .topic(forumId: request.id) : .post(topicId: request.id),
action: request.action.transferType
)
let response = try await api.get(command)
return try await parser.parseWriteForm(response)
},
getHistory: { offset, perPage in
let response = try await api.get(MemberCommand.history(page: offset, perPage: perPage))
return try await parser.parseHistory(response)
},
previewPost: { request in
let command = ForumCommand.Post.preview(data: PostSendRequest(
topicId: request.post.topicId,
content: request.post.content,
attaches: request.post.attachments,
flag: request.post.flag
), postId: request.id)
let response = try await api.get(command)
return try await parser.parsePostPreview(response)
},
sendPost: { request in
let command = ForumCommand.Post.send(data: PostSendRequest(
topicId: request.topicId,
content: request.content,
attaches: request.attachments,
flag: request.flag
))
let response = try await api.get(command)
return try await parser.parsePostSend(response)
},

// MARK: - Favorites

Expand Down Expand Up @@ -368,10 +399,19 @@ extension APIClient: DependencyKey {
},
getTopic: { _, _, _ in
return .mock
},
getTemplate: { _, _ in
return [.mockTitle, .mockText, .mockEditor]
},
getHistory: { _, _ in
return .mock
},
previewPost: { _ in
return PostPreview(content: "Post Content...", attachmentIds: [])
},
sendPost: { _ in
return PostSend(id: 0, topicId: 1, offset: 2)
},
getFavorites: { _, _ in
.finished()
},
Expand Down
34 changes: 34 additions & 0 deletions Modules/Sources/APIClient/Requests/ForumTemplateRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// ForumTemplateRequest.swift
// ForPDA
//
// Created by Xialtal on 15.03.25.
//

import PDAPI

public struct ForumTemplateRequest {
public let id: Int
public let action: TemplateAction

public enum TemplateAction {
case get
case send([Any])
case preview([Any])
}

public init(id: Int, action: TemplateAction) {
self.id = id
self.action = action
}
}

extension ForumTemplateRequest.TemplateAction {
var transferType: ForumCommand.TemplateAction {
switch self {
case .get: return .get
case .preview(let data): return .preview(data)
case .send(let data): return .send(data)
}
}
}
21 changes: 21 additions & 0 deletions Modules/Sources/APIClient/Requests/PostPreviewRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// PostPreviewRequest.swift
// ForPDA
//
// Created by Xialtal on 15.03.25.
//

import PDAPI

public struct PostPreviewRequest: Sendable {
public let id: Int
public let post: PostRequest

public init(
id: Int,
post: PostRequest
) {
self.id = id
self.post = post
}
}
25 changes: 25 additions & 0 deletions Modules/Sources/APIClient/Requests/PostRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// PostRequest.swift
// ForPDA
//
// Created by Xialtal on 18.03.25.
//

public struct PostRequest: Sendable {
public let topicId: Int
public let content: String
public let flag: Int
public let attachments: [Int]

public init(
topicId: Int,
content: String,
flag: Int,
attachments: [Int]
) {
self.topicId = topicId
self.content = content
self.flag = flag
self.attachments = attachments
}
}
6 changes: 5 additions & 1 deletion Modules/Sources/AnalyticsClient/Events/TopicEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public enum TopicEvent: Event {
case menuOpenInBrowser
case menuGoToEnd
case menuSetFavorite
case menuWritePost

case menuPostReply(Int)

case loadingStart(Int)
case loadingSuccess
Expand All @@ -28,7 +31,8 @@ public enum TopicEvent: Event {

public var properties: [String: String]? {
switch self {
case let .userAvatarTapped(id):
case let .userAvatarTapped(id),
let .menuPostReply(id):
return ["userId": String(id)]

case let .urlTapped(url):
Expand Down
12 changes: 12 additions & 0 deletions Modules/Sources/Models/Common/ReportType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// ReportType.swift
// ForPDA
//
// Created by Xialtal on 26.03.25.
//

public enum ReportType: Sendable, Equatable {
case post
case comment
case reputation
}
80 changes: 80 additions & 0 deletions Modules/Sources/Models/Common/WriteFormFieldType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// WriteFormFieldType.swift
// ForPDA
//
// Created by Xialtal on 14.03.25.
//

public enum WriteFormFieldType: Sendable, Equatable, Hashable {
case title(String)
case text(FormField)
case editor(FormField)
case dropdown(FormField, _ options: [String])
case uploadbox(FormField, _ extensions: [String])
case checkboxList(FormField, _ options: [String])

public struct FormField: Sendable, Equatable, Hashable {
public let name: String
public let description: String
public let example: String
public let flag: Int
public let defaultValue: String

public var isRequired: Bool {
return flag & 1 != 0
}

public var isVisible: Bool {
return flag & 2 != 0
}

public init(
name: String,
description: String,
example: String,
flag: Int,
defaultValue: String
) {
self.name = name
self.description = description
self.example = example
self.flag = flag
self.defaultValue = defaultValue
}
}
}

public extension WriteFormFieldType {
static let mockTitle: WriteFormFieldType =
.title("[b]This is absolute simple title[/b]")

static let mockText: WriteFormFieldType = .text(
FormField(
name: "Topic name",
description: "Enter topic name.",
example: "Starting from For, ends with PDA",
flag: 1,
defaultValue: ""
)
)

static let mockEditor: WriteFormFieldType = .editor(
FormField(
name: "Topic content",
description: "This field contains topic [color=red]hat[/color] content.",
example: "ForPDA Forever!",
flag: 1,
defaultValue: ""
)
)

static let mockEditorSimple: WriteFormFieldType = .editor(
FormField(
name: "",
description: "",
example: "Post text...",
flag: 0,
defaultValue: ""
)
)
}
19 changes: 19 additions & 0 deletions Modules/Sources/Models/Common/WriteFormForType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// WriteFormForType.swift
// ForPDA
//
// Created by Xialtal on 14.03.25.
//

import Foundation

public enum WriteFormForType: Sendable, Equatable {
case report(id: Int, type: ReportType)
case topic(forumId: Int, content: [String])
case post(topicId: Int, content: PostContentType)

public enum PostContentType: Sendable, Equatable {
case template([String])
case simple(String, [Int])
}
}
10 changes: 10 additions & 0 deletions Modules/Sources/Models/Common/WriteFormSend.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// WriteFormSend.swift
// ForPDA
//
// Created by Xialtal on 18.03.25.
//

public enum WriteFormSend: Sendable {
case post(PostSend)
}
19 changes: 19 additions & 0 deletions Modules/Sources/Models/Forum/PostPreview.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// PostPreview.swift
// ForPDA
//
// Created by Xialtal on 15.03.25.
//

public struct PostPreview: Sendable {
public let content: String
public let attachmentIds: [Int]

public init(
content: String,
attachmentIds: [Int]
) {
self.content = content
self.attachmentIds = attachmentIds
}
}
23 changes: 23 additions & 0 deletions Modules/Sources/Models/Forum/PostSend.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// PostSend.swift
// ForPDA
//
// Created by Xialtal on 18.03.25.
//

public struct PostSend: Sendable {
public let id: Int
public let topicId: Int
public let offset: Int

public init(
id: Int,
topicId: Int,
offset: Int
) {
self.id = id
self.topicId = topicId
self.offset = offset
}
}

4 changes: 4 additions & 0 deletions Modules/Sources/Models/Forum/Topic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public struct Topic: Codable, Sendable, Identifiable, Hashable {
public let posts: [Post]
public let navigation: [ForumInfo]

public var canPost: Bool {
return (flag & 64) != 0 && (flag & 16) == 0
}

public var isFavorite: Bool

public struct Poll: Sendable, Codable, Hashable {
Expand Down
35 changes: 35 additions & 0 deletions Modules/Sources/ParsingClient/Parsers/TopicParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,41 @@ public struct TopicParser {
)
}

public static func parsePostPreview(from string: String) throws(ParsingError) -> PostPreview {
guard let data = string.data(using: .utf8) else {
throw ParsingError.failedToCreateDataFromString
}

guard let array = try? JSONSerialization.jsonObject(with: data, options: []) as? [Any] else {
throw ParsingError.failedToCastDataToAny
}

guard let content = array[safe: 2] as? String,
let attachmentIds = array[safe: 3] as? [Int] else {
throw ParsingError.failedToCastFields
}

return PostPreview(content: content, attachmentIds: attachmentIds)
}

public static func parsePostSend(from string: String) throws(ParsingError) -> PostSend {
guard let data = string.data(using: .utf8) else {
throw ParsingError.failedToCreateDataFromString
}

guard let array = try? JSONSerialization.jsonObject(with: data, options: []) as? [Any] else {
throw ParsingError.failedToCastDataToAny
}

guard let id = array[safe: 4] as? Int,
let topicId = array[safe: 2] as? Int,
let offset = array[safe: 3] as? Int else {
throw ParsingError.failedToCastFields
}

return PostSend(id: id, topicId: topicId, offset: offset)
}

// MARK: - Poll

private static func parsePoll(_ array: [Any]) throws(ParsingError) -> Topic.Poll? {
Expand Down
Loading