Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
bd18a73
Init TopicEditFeature
Xialtal Mar 29, 2026
4f4b014
Add topic edit endpoint
Xialtal Mar 29, 2026
0674ded
[WIP] Topic Edit
Xialtal Mar 29, 2026
9744768
Add edit option to topic context menu
Xialtal Mar 31, 2026
76b42b0
Add canEdit permission to Topic model
Xialtal Mar 31, 2026
9384849
[WIP] Topic Edit
Xialtal Mar 31, 2026
a8b9ead
Improve Topic model mock
Xialtal Mar 31, 2026
96c7b85
Fix topic edit analytics
Xialtal Mar 31, 2026
de99413
Merge remote-tracking branch 'origin/develop' into feature/topic-edit
Xialtal Apr 22, 2026
205c8b4
Post-merge fixes
Xialtal Apr 22, 2026
6a51ec9
Improve localizable
Xialtal Apr 22, 2026
efd4eb8
Add auto-focus on new poll option & question fields
Xialtal Apr 22, 2026
c923b02
Extract RemovePollElement button
Xialtal Apr 22, 2026
3b98ade
Fix topic poll answer removing on iOS 17+
Xialtal May 1, 2026
b2cdc1b
Merge remote-tracking branch 'origin/develop' into feature/topic-edit
Xialtal May 1, 2026
7e4d535
Add SingleLineField
Xialtal May 1, 2026
4c1cd45
Add more error reasons to TopicEditResponse
Xialtal May 1, 2026
6aa5c40
[WIP] Topic Edit
Xialtal May 1, 2026
ad2193e
Extract navigations to NavigationModifier in ForumScreen
Xialtal May 1, 2026
4ddd920
Add topic edit action to context menu in ForumFeature
Xialtal May 1, 2026
a5b4924
Add input fields text limit
Xialtal May 5, 2026
8e82d7e
[WIP] Topic Poll
Xialtal May 5, 2026
bd3d424
Add toast on success topic edit for TopicFeature
Xialtal May 5, 2026
44b22c6
Add toast on success topic edit for ForumFeature
Xialtal May 5, 2026
3bb386f
Merge remote-tracking branch 'origin/develop' into feature/topic-edit
Xialtal May 5, 2026
27e2d99
Add missing import
Xialtal May 5, 2026
24ded53
Merge remote-tracking branch 'origin/develop' into feature/topic-edit
Xialtal May 5, 2026
7f8962c
Fix build
Xialtal May 6, 2026
2c23158
Fix topic poll delete
Xialtal May 6, 2026
44e9bd2
Merge remote-tracking branch 'origin/develop' into feature/topic-edit
Xialtal May 6, 2026
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
15 changes: 15 additions & 0 deletions Modules/Sources/APIClient/APIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public struct APIClient: Sendable {
public var getTopic: @Sendable (_ id: Int, _ page: Int, _ perPage: Int, _ postsFilter: TopicPostsFilter) async throws -> Topic
public var modifyForum: @Sendable (_ ids: [Int], _ type: ForumModifyType, _ isUndo: Bool) async throws -> Bool
public var moveTopic: @Sendable (_ id: Int, _ toForumId: Int, _ saveLink: Bool) async throws -> Bool
public var editTopic: @Sendable (_ data: TopicEditRequest) async throws -> TopicEditResponse
public var getTopicViewers: @Sendable (_ id: Int) async throws -> TopicViewers
public var setTopicCurator: @Sendable (_ topicId: Int, _ userId: Int, _ reason: String) async throws -> Bool
public var getTemplate: @Sendable (_ request: ForumTemplateRequest, _ isTopic: Bool) async throws -> [FormFieldType]
Expand Down Expand Up @@ -384,6 +385,17 @@ extension APIClient: DependencyKey {
let status = Int(response.getResponseStatus())!
return status == 0
},
editTopic: { data in
let request = PDAPI.TopicEditRequest(
id: data.id,
title: data.title,
description: data.description,
poll: data.poll
)
let response = try await api.send(ForumCommand.Topic.edit(data: request))
let status = Int(response.getResponseStatus())!
return TopicEditResponse(rawValue: status)
},
getTopicViewers: { topicId in
let command = MemberCommand.sessions(
pageType: .topic,
Expand Down Expand Up @@ -742,6 +754,9 @@ extension APIClient: DependencyKey {
moveTopic: { _, _, _ in
return true
},
editTopic: { _ in
return .success
},
getTopicViewers: { _ in
return .mock
},
Expand Down
27 changes: 27 additions & 0 deletions Modules/Sources/APIClient/Requests/TopicEditRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// TopicEditRequest.swift
// ForPDA
//
// Created by Xialtal on 29.03.26.
//

import Models

public struct TopicEditRequest {
public let id: Int
public let title: String
public let description: String
public let poll: PDAPIDocument?

public init(
id: Int,
title: String,
description: String,
poll: PDAPIDocument?
) {
self.id = id
self.title = title
self.description = description
self.poll = poll
}
}
4 changes: 4 additions & 0 deletions Modules/Sources/AnalyticsClient/Events/ForumEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public enum ForumEvent: Event {

case menuOpen(Int)
case menuGoToEnd(Int)
case menuEdit(Int)

case menuStat(Int, Bool)
case menuMarkRead(Int, Bool)
Expand Down Expand Up @@ -60,6 +61,9 @@ public enum ForumEvent: Event {
case let .menuGoToEnd(id):
return ["id": String(id)]

case let .menuEdit(id):
return ["id": String(id)]

case let .menuStat(id, isForum):
return ["id": String(id), "isForum": String(isForum)]

Expand Down
1 change: 1 addition & 0 deletions Modules/Sources/AnalyticsClient/Events/TopicEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public enum TopicEvent: Event {
case menuGoToEnd
case menuSetFavorite
case menuAboutTopic
case menuEditTopic
case menuWritePost
case menuWritePostWithTemplate

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ extension ForumFeature {
analytics.log(ForumEvent.menuOpen(topic.id))
case .goToEnd:
analytics.log(ForumEvent.menuGoToEnd(topic.id))
case .edit:
analytics.log(ForumEvent.menuEdit(topic.id))
}

case let .view(.contextCommonMenu(option, id, isForum)):
Expand Down
18 changes: 18 additions & 0 deletions Modules/Sources/ForumFeature/ForumFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ToastClient
import FormFeature
import ForumStatFeature
import ForumMoveFeature
import TopicEditFeature

@Reducer
public struct ForumFeature: Reducer, Sendable {
Expand All @@ -28,6 +29,7 @@ public struct ForumFeature: Reducer, Sendable {

public enum Localization {
static let linkCopied = LocalizedStringResource("Link copied", bundle: .module)
static let topicEdited = LocalizedStringResource("The topic has been edited", bundle: .module)
static let markAsReadSuccess = LocalizedStringResource("Marked as read", bundle: .module)
}

Expand Down Expand Up @@ -59,6 +61,7 @@ public struct ForumFeature: Reducer, Sendable {
case form(FormFeature)
case move(ForumMoveFeature)
case stat(ForumStatFeature)
case edit(TopicEditFeature)
}

// MARK: - State
Expand Down Expand Up @@ -170,6 +173,11 @@ public struct ForumFeature: Reducer, Sendable {
case let .destination(.presented(.stat(.delegate(.userTapped(id))))):
return .send(.delegate(.openUser(id: id)))

case .destination(.presented(.edit(.delegate(.topicEdited)))):
return .run { _ in
await toastClient.showToast(ToastMessage(text: Localization.topicEdited, haptic: .success))
}

case .destination, .pageNavigation:
return .none

Expand Down Expand Up @@ -245,6 +253,16 @@ public struct ForumFeature: Reducer, Sendable {
await send(.delegate(.openTopic(id: topic.id, name: topic.name, goTo: .unread)))
await send(.internal(.refresh))
}

case .edit:
state.destination = .edit(TopicEditFeature.State(
id: topic.id,
flag: topic.flag,
title: topic.name,
description: topic.description,
supportsPoll: false
))
return .none
}

case let .view(.contextTopicToolsMenu(action)):
Expand Down
113 changes: 91 additions & 22 deletions Modules/Sources/ForumFeature/ForumScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import BBBuilder
import FormFeature
import ForumStatFeature
import ForumMoveFeature
import TopicEditFeature

@ViewAction(for: ForumFeature.self)
public struct ForumScreen: View {
Expand All @@ -25,10 +26,6 @@ public struct ForumScreen: View {
@Environment(\.tintColor) private var tintColor
@State private var navigationMinimized = false

private var title: String {
return store.forumName ?? String(localized: "Loading...", bundle: .module)
}

private var shouldShowInlineNavigation: Bool {
let isAnyFloatingNavigationEnabled = store.appSettings.floatingNavigation || store.appSettings.experimentalFloatingNavigation
return store.pageNavigation.shouldShow && (!isLiquidGlass || !isAnyFloatingNavigationEnabled)
Expand Down Expand Up @@ -87,13 +84,7 @@ public struct ForumScreen: View {
}
.animation(.default, value: store.forum)
.animation(.default, value: store.sectionsExpandState)
.navigationTitle(Text(title))
._toolbarTitleDisplayMode(.large)
.fullScreenCover(item: $store.scope(state: \.$destination, action: \.destination).form) { store in
NavigationStack {
FormScreen(store: store)
}
}
.navigations(store: store)
.safeAreaInset(edge: .bottom) {
if shouldShowFloatingNavigation {
PageNavigation(
Expand All @@ -104,17 +95,6 @@ public struct ForumScreen: View {
.padding(.bottom, 8)
}
}
.sheet(item: $store.scope(state: \.$destination, action: \.destination).stat) { store in
NavigationStack {
ForumStatView(store: store)
}
}
.fittedSheet(
item: $store.scope(state: \.$destination, action: \.destination).move,
embedIntoNavStack: true
) { store in
ForumMoveView(store: store)
}
.toolbar {
ToolbarItem {
Button {
Expand Down Expand Up @@ -267,6 +247,12 @@ public struct ForumScreen: View {
ContextButton(text: LocalizedStringResource("Go To End", bundle: .module), symbol: .chevronRight2) {
send(.contextTopicMenu(.goToEnd, topic))
}

if topic.canEdit {
ContextButton(text: LocalizedStringResource("Edit", bundle: .module), symbol: .squareAndPencil) {
send(.contextTopicMenu(.edit, topic))
}
}
}
}
}
Expand Down Expand Up @@ -451,6 +437,83 @@ public struct ForumScreen: View {
}
}

// MARK: - Navigation Modifier

struct NavigationModifier: ViewModifier {

@Perception.Bindable private var store: StoreOf<ForumFeature>
@Environment(\.tintColor) private var tintColor

private var title: String {
return store.forumName ?? String(localized: "Loading...", bundle: .module)
}

init(store: StoreOf<ForumFeature>) {
self.store = store
}

func body(content: Content) -> some View {
WithPerceptionTracking {
content
.navigationTitle(Text(title))
._toolbarTitleDisplayMode(.large)
.modifier(FullScreenCoverModifier(store: store))
.modifier(SheetModifier(store: store))
}
}

struct FullScreenCoverModifier: ViewModifier {
@Perception.Bindable private var store: StoreOf<ForumFeature>
@Environment(\.tintColor) private var tintColor

init(store: StoreOf<ForumFeature>) {
self.store = store
}

func body(content: Content) -> some View {
WithPerceptionTracking {
content
.fullScreenCover(item: $store.scope(state: \.$destination, action: \.destination).form) { store in
NavigationStack {
FormScreen(store: store)
}
}
}
}
}

struct SheetModifier: ViewModifier {
@Perception.Bindable private var store: StoreOf<ForumFeature>
@Environment(\.tintColor) private var tintColor

init(store: StoreOf<ForumFeature>) {
self.store = store
}

func body(content: Content) -> some View {
WithPerceptionTracking {
content
.sheet(item: $store.scope(state: \.$destination, action: \.destination).stat) { store in
NavigationStack {
ForumStatView(store: store)
}
}
.sheet(item: $store.scope(state: \.$destination, action: \.destination).edit) { store in
NavigationStack {
TopicEditView(store: store)
}
}
.fittedSheet(
item: $store.scope(state: \.$destination, action: \.destination).move,
embedIntoNavStack: true
) { store in
ForumMoveView(store: store)
}
}
}
}
}

// MARK: - Extensions

extension Bundle {
Expand All @@ -459,6 +522,12 @@ extension Bundle {
}
}

extension View {
func navigations(store: StoreOf<ForumFeature>) -> some View {
self.modifier(NavigationModifier(store: store))
}
}

extension Forum {
var globalAnnouncementAttributed: NSAttributedString? {
guard !globalAnnouncement.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return nil }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
public enum ForumTopicContextMenuAction {
case open
case goToEnd
case edit
}
20 changes: 20 additions & 0 deletions Modules/Sources/ForumFeature/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@
}
}
},
"Edit" : {
"localizations" : {
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Редактировать"
}
}
}
},
"Go To End" : {
"localizations" : {
"ru" : {
Expand Down Expand Up @@ -211,6 +221,16 @@
}
}
},
"The topic has been edited" : {
"localizations" : {
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Тема отредактирована"
}
}
}
},
"Tools" : {
"localizations" : {
"ru" : {
Expand Down
Loading
Loading