From 4187fddcbc0b2668e9ff1a84dd4238fdc43acda8 Mon Sep 17 00:00:00 2001 From: Vova Ignatov Date: Thu, 30 Oct 2025 12:09:05 +0000 Subject: [PATCH 1/4] IOS-5255 Update description behaviour --- .../ObjectSettings/Model/ObjectSetting.swift | 4 ++-- .../Loc/Sources/Loc/Generated/Strings.swift | 2 ++ .../Loc/Sources/Loc/Resources/UI.xcstrings | 22 +++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/Model/ObjectSetting.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/Model/ObjectSetting.swift index f79430398d..8f8d29b090 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/Model/ObjectSetting.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/Model/ObjectSetting.swift @@ -52,8 +52,8 @@ extension ObjectSetting { Loc.icon case .cover: Loc.cover - case .description: - Loc.description + case .description(let isVisible): + isVisible ? Loc.hideDescription : Loc.showDescription case .relations: Loc.fields case .history: diff --git a/Modules/Loc/Sources/Loc/Generated/Strings.swift b/Modules/Loc/Sources/Loc/Generated/Strings.swift index 7fe9773213..5103b0a5db 100644 --- a/Modules/Loc/Sources/Loc/Generated/Strings.swift +++ b/Modules/Loc/Sources/Loc/Generated/Strings.swift @@ -369,6 +369,7 @@ public enum Loc { public static let header = Loc.tr("UI", "Header", fallback: "Header") public static let hidden = Loc.tr("UI", "Hidden", fallback: "Hidden") public static let hide = Loc.tr("UI", "Hide", fallback: "Hide") + public static let hideDescription = Loc.tr("UI", "Hide Description", fallback: "Hide Description") public static let hideTypes = Loc.tr("UI", "Hide types", fallback: "Hide types") public static let highlight = Loc.tr("UI", "Highlight", fallback: "Highlight") public static let history = Loc.tr("UI", "History", fallback: "History") @@ -579,6 +580,7 @@ public enum Loc { public static let settings = Loc.tr("UI", "Settings", fallback: "Settings") public static let share = Loc.tr("UI", "Share", fallback: "Share") public static let show = Loc.tr("UI", "Show", fallback: "Show") + public static let showDescription = Loc.tr("UI", "Show Description", fallback: "Show Description") public static let showTypes = Loc.tr("UI", "Show types", fallback: "Show types") public static let skip = Loc.tr("UI", "Skip", fallback: "Skip") public static let sky = Loc.tr("UI", "Sky", fallback: "Sky") diff --git a/Modules/Loc/Sources/Loc/Resources/UI.xcstrings b/Modules/Loc/Sources/Loc/Resources/UI.xcstrings index 1c9282c191..bdfc09f64d 100644 --- a/Modules/Loc/Sources/Loc/Resources/UI.xcstrings +++ b/Modules/Loc/Sources/Loc/Resources/UI.xcstrings @@ -44547,6 +44547,17 @@ } } }, + "Hide Description" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hide Description" + } + } + } + }, "Hide types" : { "extractionState" : "manual", "localizations" : { @@ -83950,6 +83961,17 @@ } } }, + "Show Description" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Description" + } + } + } + }, "Show types" : { "extractionState" : "manual", "localizations" : { From 19d389ecab7f3152815769c8096d67e151d38d4a Mon Sep 17 00:00:00 2001 From: Vova Ignatov Date: Thu, 30 Oct 2025 13:49:07 +0000 Subject: [PATCH 2/4] IOS-5255 Migrate object settings menu to Observation framework Replaced Combine publishers with withObservationTracking for manual observation of nested @Observable objects. Changes include: - Removed Combine dependency and cancellables - Updated @StateObject to @State in views - Converted @Published properties to regular properties - Added @ObservationIgnored to injected dependencies and non-observed properties - Implemented manual observation pattern using withObservationTracking for settings and actions --- .../ObjectActions/ObjectActionsView.swift | 8 ++-- .../ObjectActionsViewModel.swift | 42 +++++++++++-------- .../ObjectSettings/ObjectSettingsView.swift | 8 ++-- .../ObjectSettingsViewModel.swift | 25 ++++++----- .../ObjectSettingsMenuView.swift | 4 +- .../ObjectSettingsMenuViewModel.swift | 34 ++++++++++----- 6 files changed, 73 insertions(+), 48 deletions(-) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsView.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsView.swift index c274baf275..dbc7c7aef1 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsView.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsView.swift @@ -2,12 +2,12 @@ import SwiftUI import AnytypeCore struct ObjectActionsView: View { - - @StateObject private var viewModel: ObjectActionsViewModel + + @State private var viewModel: ObjectActionsViewModel @Environment(\.dismiss) private var dismiss - + init(objectId: String, spaceId: String, output: (any ObjectActionsOutput)?) { - self._viewModel = StateObject(wrappedValue: ObjectActionsViewModel(objectId: objectId, spaceId: spaceId, output: output)) + self._viewModel = State(wrappedValue: ObjectActionsViewModel(objectId: objectId, spaceId: spaceId, output: output)) } var body: some View { diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift index 59da81a05e..305eaf0fff 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift @@ -6,16 +6,22 @@ import UIKit import DeepLinks @MainActor -final class ObjectActionsViewModel: ObservableObject { +@Observable +final class ObjectActionsViewModel { + @ObservationIgnored private let objectId: String + @ObservationIgnored private let spaceId: String + @ObservationIgnored private weak var output: (any ObjectActionsOutput)? - + + @ObservationIgnored private lazy var document: any BaseDocumentProtocol = { openDocumentsProvider.document(objectId: objectId, spaceId: spaceId) }() - + + @ObservationIgnored private lazy var widgetObject: (any BaseDocumentProtocol)? = { guard let info = workspaceStorage.spaceInfo(spaceId: spaceId) else { anytypeAssertionFailure("info not found") @@ -23,31 +29,31 @@ final class ObjectActionsViewModel: ObservableObject { } return openDocumentsProvider.document(objectId: info.widgetsId, spaceId: spaceId) }() - - @Injected(\.objectActionsService) + + @Injected(\.objectActionsService) @ObservationIgnored private var service: any ObjectActionsServiceProtocol - @Injected(\.blockService) + @Injected(\.blockService) @ObservationIgnored private var blockService: any BlockServiceProtocol - @Injected(\.templatesService) + @Injected(\.templatesService) @ObservationIgnored private var templatesService: any TemplatesServiceProtocol - @Injected(\.documentsProvider) + @Injected(\.documentsProvider) @ObservationIgnored private var documentsProvider: any DocumentsProviderProtocol - @Injected(\.blockWidgetService) + @Injected(\.blockWidgetService) @ObservationIgnored private var blockWidgetService: any BlockWidgetServiceProtocol - @Injected(\.spaceViewsStorage) + @Injected(\.spaceViewsStorage) @ObservationIgnored private var workspaceStorage: any SpaceViewsStorageProtocol - @Injected(\.deepLinkParser) + @Injected(\.deepLinkParser) @ObservationIgnored private var deepLinkParser: any DeepLinkParserProtocol - @Injected(\.universalLinkParser) + @Injected(\.universalLinkParser) @ObservationIgnored private var universalLinkParser: any UniversalLinkParserProtocol - @Injected(\.openedDocumentProvider) + @Injected(\.openedDocumentProvider) @ObservationIgnored private var openDocumentsProvider: any OpenedDocumentsProviderProtocol - @Injected(\.workspaceService) + @Injected(\.workspaceService) @ObservationIgnored private var workspaceService: any WorkspaceServiceProtocol - - @Published var objectActions: [ObjectAction] = [] - @Published var toastData: ToastBarData? - @Published var dismiss = false + + var objectActions: [ObjectAction] = [] + var toastData: ToastBarData? + var dismiss = false init(objectId: String, spaceId: String, output: (any ObjectActionsOutput)?) { self.objectId = objectId diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsView.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsView.swift index 2584c9a502..061de8b8a7 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsView.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsView.swift @@ -3,15 +3,15 @@ import Services import AnytypeCore struct ObjectSettingsView: View { - - @StateObject private var viewModel: ObjectSettingsViewModel - + + @State private var viewModel: ObjectSettingsViewModel + init( objectId: String, spaceId: String, output: some ObjectSettingsModelOutput ) { - self._viewModel = StateObject(wrappedValue: ObjectSettingsViewModel(objectId: objectId, spaceId: spaceId, output: output)) + self._viewModel = State(wrappedValue: ObjectSettingsViewModel(objectId: objectId, spaceId: spaceId, output: output)) } var body: some View { diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift index 5b5a476431..23fcfa9e3f 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift @@ -26,27 +26,32 @@ protocol ObjectSettingsModelOutput: AnyObject, ObjectHeaderRouterProtocol, Objec } @MainActor -final class ObjectSettingsViewModel: ObservableObject, ObjectActionsOutput { +@Observable +final class ObjectSettingsViewModel: ObjectActionsOutput { - @Injected(\.openedDocumentProvider) + @Injected(\.openedDocumentProvider) @ObservationIgnored private var openDocumentsProvider: any OpenedDocumentsProviderProtocol - @Injected(\.propertiesService) + @Injected(\.propertiesService) @ObservationIgnored private var propertiesService: any PropertiesServiceProtocol - @Injected(\.objectSettingsBuilder) + @Injected(\.objectSettingsBuilder) @ObservationIgnored private var settingsBuilder: any ObjectSettingsBuilderProtocol - @Injected(\.objectSettingsConflictManager) + @Injected(\.objectSettingsConflictManager) @ObservationIgnored private var conflictManager: any ObjectSettingsPrimitivesConflictManagerProtocol - + + @ObservationIgnored private weak var output: (any ObjectSettingsModelOutput)? - + + @ObservationIgnored private lazy var document: any BaseDocumentProtocol = { openDocumentsProvider.document(objectId: objectId, spaceId: spaceId) }() - + + @ObservationIgnored let objectId: String + @ObservationIgnored let spaceId: String - @Published var settings: [ObjectSetting] = [] - @Published var showConflictAlert = false + var settings: [ObjectSetting] = [] + var showConflictAlert = false init( objectId: String, diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettingsMenu/ObjectSettingsMenuView.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettingsMenu/ObjectSettingsMenuView.swift index d921920b18..febf22d49b 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettingsMenu/ObjectSettingsMenuView.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettingsMenu/ObjectSettingsMenuView.swift @@ -3,7 +3,7 @@ import AnytypeCore struct ObjectSettingsMenuView: View { - @StateObject private var viewModel: ObjectSettingsMenuViewModel + @State private var viewModel: ObjectSettingsMenuViewModel init( objectId: String, @@ -12,7 +12,7 @@ struct ObjectSettingsMenuView: View { ) { let settingsVM = ObjectSettingsViewModel(objectId: objectId, spaceId: spaceId, output: output) let actionsVM = ObjectActionsViewModel(objectId: objectId, spaceId: spaceId, output: settingsVM) - self._viewModel = StateObject(wrappedValue: ObjectSettingsMenuViewModel(settingsViewModel: settingsVM, actionsViewModel: actionsVM)) + self._viewModel = State(wrappedValue: ObjectSettingsMenuViewModel(settingsViewModel: settingsVM, actionsViewModel: actionsVM)) } var body: some View { diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettingsMenu/ObjectSettingsMenuViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettingsMenu/ObjectSettingsMenuViewModel.swift index 60799451ec..f70385b4dd 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettingsMenu/ObjectSettingsMenuViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettingsMenu/ObjectSettingsMenuViewModel.swift @@ -1,15 +1,16 @@ import Foundation -import Combine import SwiftUI @MainActor -final class ObjectSettingsMenuViewModel: ObservableObject { +@Observable +final class ObjectSettingsMenuViewModel { - @Published var menuConfig = ObjectMenuConfiguration(sections: []) + var menuConfig = ObjectMenuConfiguration(sections: []) + @ObservationIgnored let settingsViewModel: ObjectSettingsViewModel + @ObservationIgnored let actionsViewModel: ObjectActionsViewModel - private var cancellables = Set() var showConflictAlert: Binding { Binding( @@ -42,17 +43,30 @@ final class ObjectSettingsMenuViewModel: ObservableObject { } private func setupSubscriptions() { - settingsViewModel.$settings - .sink { [weak self] _ in + observeSettings() + observeActions() + } + + private func observeSettings() { + withObservationTracking { + _ = settingsViewModel.settings + } onChange: { [weak self] in + Task { @MainActor [weak self] in self?.rebuildMenu() + self?.observeSettings() } - .store(in: &cancellables) + } + } - actionsViewModel.$objectActions - .sink { [weak self] _ in + private func observeActions() { + withObservationTracking { + _ = actionsViewModel.objectActions + } onChange: { [weak self] in + Task { @MainActor [weak self] in self?.rebuildMenu() + self?.observeActions() } - .store(in: &cancellables) + } } private func rebuildMenu() { From e3366418fe0e1e765f2016f232f6f961a63f219c Mon Sep 17 00:00:00 2001 From: Vova Ignatov Date: Thu, 30 Oct 2025 13:54:36 +0000 Subject: [PATCH 3/4] IOS-5255 Remove unused Combine imports --- .../Views/Settings/ObjectActions/ObjectActionsViewModel.swift | 1 - .../Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift index 305eaf0fff..5727c2f7bc 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift @@ -1,5 +1,4 @@ import Foundation -import Combine import Services import AnytypeCore import UIKit diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift index 23fcfa9e3f..ab5edc87a6 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift @@ -1,5 +1,4 @@ import Foundation -import Combine import Services import UIKit import FloatingPanel From c0a43089559c1c7824ff31196704a904e7196d56 Mon Sep 17 00:00:00 2001 From: Vova Ignatov Date: Thu, 30 Oct 2025 13:57:45 +0000 Subject: [PATCH 4/4] IOS-5255 Add toast notifications for pin/unpin actions --- .../ObjectActions/ObjectActionsViewModel.swift | 1 + Modules/Loc/Sources/Loc/Generated/Strings.swift | 1 + Modules/Loc/Sources/Loc/Resources/UI.xcstrings | 11 +++++++++++ 3 files changed, 13 insertions(+) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift index 5727c2f7bc..8b7ec20843 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift @@ -115,6 +115,7 @@ final class ObjectActionsViewModel { position: first.map { .above(widgetId: $0.id) } ?? .end ) } + toastData = ToastBarData(pinned ? Loc.unpinned : Loc.pinned) dismiss.toggle() } diff --git a/Modules/Loc/Sources/Loc/Generated/Strings.swift b/Modules/Loc/Sources/Loc/Generated/Strings.swift index 5103b0a5db..03070ffa4c 100644 --- a/Modules/Loc/Sources/Loc/Generated/Strings.swift +++ b/Modules/Loc/Sources/Loc/Generated/Strings.swift @@ -635,6 +635,7 @@ public enum Loc { public static let unlocked = Loc.tr("UI", "Unlocked", fallback: "Unlocked") public static let unmute = Loc.tr("UI", "Unmute", fallback: "Unmute") public static let unpin = Loc.tr("UI", "Unpin", fallback: "Unpin") + public static let unpinned = Loc.tr("UI", "Unpinned", fallback: "Unpinned") public static let unpublish = Loc.tr("UI", "Unpublish", fallback: "Unpublish") public static let unread = Loc.tr("UI", "Unread", fallback: "Unread") public static let unselectAll = Loc.tr("UI", "Unselect all", fallback: "Unselect all") diff --git a/Modules/Loc/Sources/Loc/Resources/UI.xcstrings b/Modules/Loc/Sources/Loc/Resources/UI.xcstrings index bdfc09f64d..e1f0e09b03 100644 --- a/Modules/Loc/Sources/Loc/Resources/UI.xcstrings +++ b/Modules/Loc/Sources/Loc/Resources/UI.xcstrings @@ -69146,6 +69146,17 @@ } } }, + "Unpinned" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unpinned" + } + } + } + }, "Preferences" : { "extractionState" : "manual", "localizations" : {