From bf351e3907d38c834a3c78605341a5d278746491 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Wed, 29 May 2024 13:33:50 +0300 Subject: [PATCH 01/17] IOS-2936 Delete SynchronizedDictionary from object details storage --- .../Documents/Document/BaseDocument.swift | 17 ++++- .../Document/BaseDocumentProtocol.swift | 13 ++++ .../Bookmark/BlockBookmarkViewModel.swift | 26 +++----- .../DataView/DataViewBlockViewModel.swift | 65 +++++++------------ .../Blocks/Link/BlockLinkViewModel.swift | 29 +++++---- .../Models/BlockViewModelBuilder.swift | 16 +---- .../Storage/ObjectDetailsStorage.swift | 10 +-- 7 files changed, 83 insertions(+), 93 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index d1f559804b..a9b96f5a8e 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -39,6 +39,8 @@ final class BaseDocument: BaseDocumentProtocol { return $sync.compactMap { $0 }.eraseToAnyPublisher() } + private var syncSubject = PassthroughSubject<[DocumentUpdate], Never>() + // MARK: - State private var parsedRelationsSubject = CurrentValueSubject(.empty) var parsedRelationsPublisher: AnyPublisher { @@ -235,10 +237,23 @@ final class BaseDocument: BaseDocumentProtocol { } parsedRelationsSubject.send(parsedRelations) - + syncSubject.send(updates) sync = () } + func subscibeFor(update: [DocumentUpdate]) -> AnyPublisher<[DocumentUpdate], Never> { + return syncSubject + .merge(with: Just(update)) + .map { syncUpdate in + if syncUpdate.contains(.general) { + return update + } + return update.filter { syncUpdate.contains($0) } + } + .filter { $0.isNotEmpty } + .eraseToAnyPublisher() + } + private func setupView(_ model: ObjectViewModel) async { viewModelSetter.objectViewUpdate(model) isOpened = true diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index c7f3507f56..fd77450bbf 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -35,4 +35,17 @@ protocol BaseDocumentProtocol: AnyObject, BaseDocumentGeneralProtocol { var syncPublisher: AnyPublisher { get } var resetBlocksSubject: PassthroughSubject, Never> { get } var permissionsPublisher: AnyPublisher { get } + + func subscibeFor(update: [DocumentUpdate]) -> AnyPublisher<[DocumentUpdate], Never> +} + +extension BaseDocumentProtocol { + func subscibeForDetails(objectId: String) -> AnyPublisher { + subscibeFor(update: [.details(id: objectId)]) + .compactMap { [weak self] _ in + self?.detailsStorage.get(id: objectId) + } + .removeDuplicates() + .eraseToAnyPublisher() + } } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift index baa4cbe132..1cdf9aea21 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift @@ -19,34 +19,33 @@ final class BlockBookmarkViewModel: BlockViewModelProtocol { } private let editorCollectionController: EditorCollectionReloadable - private var objectDetailsProvider: ObjectDetailsInfomationProvider? private let infoProvider: BlockModelInfomationProvider - private let detailsStorage: ObjectDetailsStorage + private let document: BaseDocumentProtocol private let showBookmarkBar: (BlockInformation) -> () private let openUrl: (AnytypeURL) -> () private var subscriptions = [AnyCancellable]() + private var targetDetails: ObjectDetails? init( editorCollectionController: EditorCollectionReloadable, infoProvider: BlockModelInfomationProvider, - detailsStorage: ObjectDetailsStorage, + document: BaseDocumentProtocol, showBookmarkBar: @escaping (BlockInformation) -> (), openUrl: @escaping (AnytypeURL) -> () ) { self.editorCollectionController = editorCollectionController self.infoProvider = infoProvider - self.detailsStorage = detailsStorage + self.document = document self.showBookmarkBar = showBookmarkBar self.openUrl = openUrl } private func setupSubscription() { - objectDetailsProvider? - .$details - .receiveOnMain() - .sink { [weak editorCollectionController, weak self] _ in + document.subscibeForDetails(objectId: bookmarkData.targetObjectID) + .sinkOnMain { [weak editorCollectionController, weak self] details in guard let self else { return } + self.targetDetails = details editorCollectionController?.reconfigure(items: [.block(self)]) }.store(in: &subscriptions) } @@ -60,7 +59,7 @@ final class BlockBookmarkViewModel: BlockViewModelProtocol { case .fetching: return emptyViewConfiguration(text: Loc.Content.Bookmark.loading, state: .uploading) case .done: - guard let objectDetails = objectDetailsProvider?.details else { + guard let objectDetails = targetDetails else { anytypeAssertionFailure("Coudn't find object details for bookmark") return UnsupportedBlockViewModel(info: info).makeContentConfiguration(maxWidth: width) } @@ -89,7 +88,7 @@ final class BlockBookmarkViewModel: BlockViewModelProtocol { case .fetching: break case .done: - guard let objectDetails = objectDetailsProvider?.details else { + guard let objectDetails = targetDetails else { return } let payload = BlockBookmarkPayload(bookmarkData: bookmarkData, objectDetails: objectDetails) @@ -110,14 +109,9 @@ final class BlockBookmarkViewModel: BlockViewModelProtocol { } private func setupSubscriptionIfNeeded() { - guard objectDetailsProvider.isNil, bookmarkData.targetObjectID.isNotEmpty else { + guard subscriptions.isEmpty, bookmarkData.targetObjectID.isNotEmpty else { return } - objectDetailsProvider = ObjectDetailsInfomationProvider( - detailsStorage: detailsStorage, - targetObjectId: bookmarkData.targetObjectID, - details: detailsStorage.get(id: bookmarkData.targetObjectID) - ) setupSubscription() } } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/DataView/DataViewBlockViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/DataView/DataViewBlockViewModel.swift index 70760a8a07..6aff18e905 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/DataView/DataViewBlockViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/DataView/DataViewBlockViewModel.swift @@ -3,73 +3,56 @@ import Combine import UIKit import AnytypeCore -final class ObjectDetailsInfomationProvider { - @Published private(set) var details: ObjectDetails? - - private let detailsStorage: ObjectDetailsStorage - private let targetObjectId: String - private var subscription: AnyCancellable? - - init( - detailsStorage: ObjectDetailsStorage, - targetObjectId: String, - details: ObjectDetails? - ) { - self.detailsStorage = detailsStorage - self.targetObjectId = targetObjectId - self.details = details - - setupPublisher() - } - - private func setupPublisher() { - subscription = detailsStorage - .publisherFor(id: targetObjectId) - .sink { [weak self] in $0.map { self?.details = $0 } } - } -} - final class DataViewBlockViewModel: BlockViewModelProtocol { let blockInformationProvider: BlockModelInfomationProvider - let objectDetailsProvider: ObjectDetailsInfomationProvider + let document: BaseDocumentProtocol private let showFailureToast: (_ message: String) -> () private let openSet: (EditorScreenData) -> () private weak var reloadable: EditorCollectionReloadable? + private var targetDetails: ObjectDetails? var info: BlockInformation { blockInformationProvider.info } var hashable: AnyHashable { info.id } var detailsSubscription: AnyCancellable? + private var blockData: BlockDataview { + guard case let .dataView(data) = info.content else { + anytypeAssertionFailure("DataViewBlockViewModel's blockInformation has wrong content type") + return .empty + } + + return data + } + init( blockInformationProvider: BlockModelInfomationProvider, - objectDetailsProvider: ObjectDetailsInfomationProvider, + document: BaseDocumentProtocol, reloadable: EditorCollectionReloadable?, showFailureToast: @escaping (_ message: String) -> (), openSet: @escaping (EditorScreenData) -> () ) { self.blockInformationProvider = blockInformationProvider - self.objectDetailsProvider = objectDetailsProvider + self.document = document self.reloadable = reloadable self.showFailureToast = showFailureToast self.openSet = openSet - detailsSubscription = objectDetailsProvider - .$details - .removeDuplicates() - .receiveOnMain() - .sink { [weak self] _ in - guard let self = self else { return } - let selfItem = EditorItem.block(self) - self.reloadable?.reconfigure(items: [selfItem]) - } + detailsSubscription = document + .subscibeForDetails(objectId: blockData.targetObjectID) + .sinkOnMain { [weak self] _ in + guard let self = self else { return } + self.targetDetails = targetDetails + let selfItem = EditorItem.block(self) + self.reloadable?.reconfigure(items: [selfItem]) + } } func makeContentConfiguration(maxWidth: CGFloat) -> UIContentConfiguration { guard case let .dataView(data) = info.content, - !(objectDetailsProvider.details?.isDeleted ?? false) else { + !(targetDetails?.isDeleted ?? false) else { return NonExistentBlockViewModel(info: info) .makeContentConfiguration(maxWidth: maxWidth) } @@ -79,7 +62,7 @@ final class DataViewBlockViewModel: BlockViewModelProtocol { let content: DataViewBlockContent let subtitle = isCollection ? Loc.Content.DataView.InlineCollection.subtitle : Loc.Content.DataView.InlineSet.subtitle let placeholder = isCollection ? Loc.Content.DataView.InlineCollection.untitled : Loc.Content.DataView.InlineSet.untitled - if let objectDetails = objectDetailsProvider.details { + if let objectDetails = targetDetails { let setOfIsNotEmpty = objectDetails.setOf.first { $0.isNotEmpty } != nil content = DataViewBlockContent( title: objectDetails.title, @@ -107,7 +90,7 @@ final class DataViewBlockViewModel: BlockViewModelProtocol { } func didSelectRowInTableView(editorEditingState: EditorEditingState) { - guard let objectDetails = objectDetailsProvider.details else { + guard let objectDetails = targetDetails else { showFailureToast(Loc.Content.DataView.InlineSet.Toast.failure) return } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Link/BlockLinkViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Link/BlockLinkViewModel.swift index 4bf269ef1c..d7e5ccd652 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Link/BlockLinkViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Link/BlockLinkViewModel.swift @@ -9,7 +9,7 @@ final class BlockLinkViewModel: BlockViewModelProtocol { var info: BlockInformation { informationProvider.info } private let informationProvider: BlockModelInfomationProvider - private let objectDetailsProvider: ObjectDetailsInfomationProvider + private let document: BaseDocumentProtocol private let blocksController: EditorBlockCollectionController private let openLink: (EditorScreenData) -> () private let detailsService: DetailsServiceProtocol @@ -19,32 +19,35 @@ final class BlockLinkViewModel: BlockViewModelProtocol { guard case let .link(blockLink) = info.content else { return .empty() } return blockLink } + + private var targetDetails: ObjectDetails? init( informationProvider: BlockModelInfomationProvider, - objectDetailsProvider: ObjectDetailsInfomationProvider, + document: BaseDocumentProtocol, blocksController: EditorBlockCollectionController, detailsService: DetailsServiceProtocol, openLink: @escaping (EditorScreenData) -> () ) { self.informationProvider = informationProvider - self.objectDetailsProvider = objectDetailsProvider + self.document = document self.blocksController = blocksController self.openLink = openLink self.detailsService = detailsService - objectDetailsSubscription = objectDetailsProvider - .$details - .receiveOnMain() - .sink { [weak self] _ in - guard let self else { return } + + objectDetailsSubscription = document + .subscibeForDetails(objectId: content.targetBlockID) + .sinkOnMain { [weak self] details in + guard let self else { return } + self.targetDetails = details blocksController.reconfigure(items: [.block(self)]) blocksController.itemDidChangeFrame(item: .block(self)) } } func makeContentConfiguration(maxWidth width: CGFloat) -> UIContentConfiguration { - guard let details = objectDetailsProvider.details else { + guard let details = targetDetails else { anytypeAssertionFailure("Coudn't find object details for blockLink") return UnsupportedBlockViewModel(info: info) .makeContentConfiguration(maxWidth: width) @@ -72,8 +75,8 @@ final class BlockLinkViewModel: BlockViewModelProtocol { } func didSelectRowInTableView(editorEditingState: EditorEditingState) { - guard let details = objectDetailsProvider.details else { return } - let state = BlockLinkState(details: details, blockLink: content) + guard let targetDetails else { return } + let state = BlockLinkState(details: targetDetails, blockLink: content) if state.deleted { return } @@ -81,9 +84,9 @@ final class BlockLinkViewModel: BlockViewModelProtocol { } private func toggleTodo() { - guard let details = objectDetailsProvider.details else { return } + guard let targetDetails else { return } - let state = BlockLinkState(details: details, blockLink: content) + let state = BlockLinkState(details: targetDetails, blockLink: content) guard case let .object(.todo(isChecked)) = state.icon else { return } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Models/BlockViewModelBuilder.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Models/BlockViewModelBuilder.swift index a9e57d9d73..122c47293f 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Models/BlockViewModelBuilder.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Models/BlockViewModelBuilder.swift @@ -243,7 +243,7 @@ final class BlockViewModelBuilder { return BlockBookmarkViewModel( editorCollectionController: blockCollectionController, infoProvider: blockInformationProvider, - detailsStorage: document.detailsStorage, + document: document, showBookmarkBar: { [weak self] info in self?.showBookmarkBar(info: info) }, @@ -262,11 +262,7 @@ final class BlockViewModelBuilder { return BlockLinkViewModel( informationProvider: blockInformationProvider, - objectDetailsProvider: ObjectDetailsInfomationProvider( - detailsStorage: document.detailsStorage, - targetObjectId: content.targetBlockID, - details: details - ), + document: document, blocksController: blockCollectionController, detailsService: detailsService, openLink: { [weak self] data in @@ -332,18 +328,12 @@ final class BlockViewModelBuilder { focusSubject: subjectsHolder.focusSubject(for: info.id) ) case let .dataView(data): - let objectDetailsProvider = ObjectDetailsInfomationProvider( - detailsStorage: document.detailsStorage, - targetObjectId: data.targetObjectID, - details: document.detailsStorage.get(id: data.targetObjectID) - ) - return DataViewBlockViewModel( blockInformationProvider: BlockModelInfomationProvider( infoContainer: infoContainer, info: info ), - objectDetailsProvider: objectDetailsProvider, + document: document, reloadable: blockCollectionController, showFailureToast: { [weak self] message in self?.output?.showFailureToast(message: message) diff --git a/Modules/Services/Sources/Models/Details/Storage/ObjectDetailsStorage.swift b/Modules/Services/Sources/Models/Details/Storage/ObjectDetailsStorage.swift index e5d877b817..e3cb5ee3c4 100644 --- a/Modules/Services/Sources/Models/Details/Storage/ObjectDetailsStorage.swift +++ b/Modules/Services/Sources/Models/Details/Storage/ObjectDetailsStorage.swift @@ -6,7 +6,7 @@ import Combine public final class ObjectDetailsStorage { - fileprivate var storage = PassthroughSubjectDictionary() + fileprivate var storage = SynchronizedDictionary() public init() {} @@ -17,8 +17,6 @@ public final class ObjectDetailsStorage { public func add(details: ObjectDetails) { storage[details.id] = details - // Should we move to Published instead of subject here? - storage.publishValue(for: details.id) } @discardableResult @@ -37,9 +35,3 @@ public final class ObjectDetailsStorage { storage.removeAll() } } - -public extension ObjectDetailsStorage { - func publisherFor(id: String) -> AnyPublisher { - storage.publisher(id) - } -} From f23d08a5190b99dc78d364715478922b20b498a3 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Wed, 29 May 2024 16:41:01 +0300 Subject: [PATCH 02/17] IOS-2936 Fix --- .../BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift index 1cdf9aea21..a16440c492 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift @@ -43,7 +43,8 @@ final class BlockBookmarkViewModel: BlockViewModelProtocol { private func setupSubscription() { document.subscibeForDetails(objectId: bookmarkData.targetObjectID) - .sinkOnMain { [weak editorCollectionController, weak self] details in + .receiveOnMain() + .sink { [weak editorCollectionController, weak self] details in guard let self else { return } self.targetDetails = details editorCollectionController?.reconfigure(items: [.block(self)]) From cfd9ab240bac6a135c7ec6592cd29094756f4a15 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Wed, 29 May 2024 17:18:37 +0300 Subject: [PATCH 03/17] IOS-2937 Info Container Change document update --- .../Documents/Document/BaseDocument.swift | 74 +++++-------------- .../EventConverters/LocalEventConverter.swift | 14 ++-- .../MiddlewareEventConverter.swift | 30 +++----- .../Documents/Events/EventsListener.swift | 6 +- .../Events/MentionMarkupEventProvider.swift | 8 +- .../Events/Model/DocumentUpdate.swift | 17 +---- .../Bookmark/BlockBookmarkViewModel.swift | 3 +- 7 files changed, 46 insertions(+), 106 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index a9b96f5a8e..db7565b336 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -213,22 +213,23 @@ final class BaseDocument: BaseDocumentProtocol { } private func triggerSync(updates: [DocumentUpdate]) { - for update in updates.merged { - guard update.hasUpdate else { return } - + var resetBlockIds = [String]() + var hasChildren = false + + for update in Set(updates) { switch update { case .general: infoContainer.publishAllValues() reorderChilder() - case .children(let blockIds): - blockIds.forEach { infoContainer.publishValue(for: $0) } - _resetBlocksSubject.send(blockIds) - reorderChilder() - case .blocks(let blockIds): - blockIds.forEach { infoContainer.publishValue(for: $0) } - _resetBlocksSubject.send(blockIds) - case .unhandled(let blockIds): - blockIds.forEach { infoContainer.publishValue(for: $0) } + case .children(let blockId): + infoContainer.publishValue(for: blockId) + resetBlockIds.append(blockId) + hasChildren = true + case .block(let blockId): + infoContainer.publishValue(for: blockId) + resetBlockIds.append(blockId) + case .unhandled(let blockId): + infoContainer.publishValue(for: blockId) case .syncStatus(let status): syncStatus = status case .details: @@ -236,9 +237,14 @@ final class BaseDocument: BaseDocumentProtocol { } } + if hasChildren { + reorderChilder() + } + + _resetBlocksSubject.send(Set(resetBlockIds)) parsedRelationsSubject.send(parsedRelations) - syncSubject.send(updates) sync = () + syncSubject.send(updates) } func subscibeFor(update: [DocumentUpdate]) -> AnyPublisher<[DocumentUpdate], Never> { @@ -268,45 +274,3 @@ final class BaseDocument: BaseDocumentProtocol { }.store(in: &subscriptions) } } - -private extension Array where Element == DocumentUpdate { - var merged: Self { - if contains(.general) { return [.general] } - var childIds = Set() - var blockIds = Set() - var unhandled = Set() - - var output = [DocumentUpdate]() - - self.forEach { update in - switch update { - case let .blocks(ids): - blockIds.formUnion(ids) - case let .children(ids): - childIds.formUnion(ids) - case let .unhandled(ids): - unhandled.formUnion(ids) - case .details, .syncStatus: - output.append(update) - case .general: - break - } - } - - - if childIds.isNotEmpty { - childIds.formUnion(blockIds) - output.append(.children(blockIds: childIds)) - } else { - if blockIds.isNotEmpty { - output.append(.blocks(blockIds: blockIds)) - } - } - - if unhandled.isNotEmpty { - output.append(.unhandled(blockIds: unhandled)) - } - - return output - } -} diff --git a/Anytype/Sources/Models/Documents/Events/EventConverters/LocalEventConverter.swift b/Anytype/Sources/Models/Documents/Events/EventConverters/LocalEventConverter.swift index 23e9c87075..3726c74136 100644 --- a/Anytype/Sources/Models/Documents/Events/EventConverters/LocalEventConverter.swift +++ b/Anytype/Sources/Models/Documents/Events/EventConverters/LocalEventConverter.swift @@ -15,7 +15,7 @@ final class LocalEventConverter { case .setToggled, .general: return .general case let .setStyle(blockId): - return .blocks(blockIds: [blockId]) + return .block(blockId: blockId) case let .setText(blockId: blockId, text: text): return blockSetTextUpdate(blockId: blockId, text: text) case .setLoadingState(blockId: let blockId): @@ -31,9 +31,9 @@ final class LocalEventConverter { content.state = .uploading info = info.updated(content: .file(content)) infoContainer.add(info) - return .blocks(blockIds: [blockId]) + return .block(blockId: blockId) case .reload(blockId: let blockId): - return .blocks(blockIds: [blockId]) + return .block(blockId: blockId) } } @@ -43,11 +43,11 @@ final class LocalEventConverter { private func blockSetTextUpdate(blockId: String, text: MiddlewareString) -> DocumentUpdate { guard var info = infoContainer.get(id: blockId) else { anytypeAssertionFailure("Block model not found in container", info: ["blockId": "\(blockId)"]) - return .unhandled(blockIds: .init([blockId])) + return .unhandled(blockId: blockId) } guard case let .text(oldText) = info.content else { anytypeAssertionFailure("Block model doesn't support text", info: ["contentType": "\(info.content.type)"]) - return .unhandled(blockIds: .init([blockId])) + return .unhandled(blockId: blockId) } let middleContent = Anytype_Model_Block.Content.Text.with { @@ -62,7 +62,7 @@ final class LocalEventConverter { guard var textContent = middleContent.textContent else { anytypeAssertionFailure("We cannot block content") - return .unhandled(blockIds: .init([blockId])) + return .unhandled(blockId: blockId) } textContent.contentType = oldText.contentType @@ -72,6 +72,6 @@ final class LocalEventConverter { info = blockValidator.validated(information: info) infoContainer.add(info) - return .unhandled(blockIds: .init([blockId])) + return .unhandled(blockId: blockId) } } diff --git a/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift b/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift index 915530c363..a88f0f54f3 100644 --- a/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift +++ b/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift @@ -33,7 +33,7 @@ final class MiddlewareEventConverter { return SyncStatus(status.summary.status).flatMap { .syncStatus($0) } case let .blockSetFields(data): infoContainer.setFields(data: data) - return .blocks(blockIds: [data.id]) + return .block(blockId: data.id) case let .blockAdd(data): infoContainer.add(data: data) // Because blockAdd message will always come together with blockSetChildrenIds @@ -48,19 +48,15 @@ final class MiddlewareEventConverter { case let .blockSetChildrenIds(data): infoContainer .setChildren(ids: data.childrenIds, parentId: data.id) - return .children(blockIds: [data.id]) + return .children(blockId: data.id) case let .blockSetText(newData): return blockSetTextUpdate(newData) case let .blockSetBackgroundColor(data): infoContainer.setBackgroundColor(data: data) - - var childIds = infoContainer.recursiveChildren(of: data.id).map { $0.id } - childIds.append(data.id) - return .blocks(blockIds: Set(childIds)) - + return .block(blockId: data.id) case let .blockSetAlign(value): infoContainer.setAlign(data: value) - return .blocks(blockIds: [value.id]) + return .block(blockId: value.id) case let .objectDetailsSet(data): guard let details = detailsStorage.set(data: data) else { return nil } @@ -113,10 +109,10 @@ final class MiddlewareEventConverter { return .general } - return .blocks(blockIds: [data.id]) + return .block(blockId: data.id) case let .blockSetBookmark(data): infoContainer.setBookmark(data: data) - return .blocks(blockIds: [data.id]) + return .block(blockId: data.id) case let .blockSetDiv(data): infoContainer.setDiv(data: data) @@ -125,10 +121,10 @@ final class MiddlewareEventConverter { return .general } - return .blocks(blockIds: [data.id]) + return .block(blockId: data.id) case .blockSetLink(let data): infoContainer.setLink(data: data) - return .blocks(blockIds: [data.id]) + return .block(blockId: data.id) //MARK: - Dataview case .blockDataviewViewSet(let data): @@ -226,14 +222,6 @@ final class MiddlewareEventConverter { } infoContainer.add(newInformation) - var childIds = infoContainer.recursiveChildren(of: newData.id).map { $0.id } - childIds.append(newData.id) - - if let newInformationContentType = newInformation.textContent?.contentType, - newInformationContentType != oldText.contentType { - return .children(blockIds: Set(childIds)) - } else { - return .blocks(blockIds: Set(childIds)) - } + return .block(blockId: newData.id) } } diff --git a/Anytype/Sources/Models/Documents/Events/EventsListener.swift b/Anytype/Sources/Models/Documents/Events/EventsListener.swift index 05f1ffcbc1..1cb7d5f51e 100644 --- a/Anytype/Sources/Models/Documents/Events/EventsListener.swift +++ b/Anytype/Sources/Models/Documents/Events/EventsListener.swift @@ -90,15 +90,15 @@ final class EventsListener: EventsListenerProtocol { } private func handle(events: EventsBunch) { - let middlewareUpdates = events.middlewareEvents.compactMap(\.value).compactMap { middlewareConverter.convert($0) } + let middlewareUpdates = events.middlewareEvents.compactMap(\.value).flatMap { middlewareConverter.convert($0) } let localUpdates = events.localEvents.compactMap { localConverter.convert($0) } - let markupUpdates = [mentionMarkupEventProvider.updateMentionsEvent()].compactMap { $0 } + let markupUpdates = mentionMarkupEventProvider.updateMentionsEvent() let builderChangedIds = IndentationBuilder.build( container: infoContainer, id: objectId ) - let builderUpdates: [DocumentUpdate] = builderChangedIds.isNotEmpty ? [.blocks(blockIds: Set(builderChangedIds))] : [] + let builderUpdates: [DocumentUpdate] = builderChangedIds.map { .block(blockId: $0) } let updates = middlewareUpdates + markupUpdates + localUpdates + builderUpdates receiveUpdates(updates.filteredUpdates) diff --git a/Anytype/Sources/Models/Documents/Events/MentionMarkupEventProvider.swift b/Anytype/Sources/Models/Documents/Events/MentionMarkupEventProvider.swift index cdb0c72ac9..d868c96264 100644 --- a/Anytype/Sources/Models/Documents/Events/MentionMarkupEventProvider.swift +++ b/Anytype/Sources/Models/Documents/Events/MentionMarkupEventProvider.swift @@ -18,13 +18,11 @@ final class MentionMarkupEventProvider { self.detailsStorage = detailsStorage } - func updateMentionsEvent() -> DocumentUpdate? { - let blockIds = infoContainer + func updateMentionsEvent() -> [DocumentUpdate] { + return infoContainer .recursiveChildren(of: objectId) .compactMap { updateIfNeeded(info: $0) } - guard blockIds.count > 0 else { return nil } - - return .blocks(blockIds: Set(blockIds)) + .map { .block(blockId: $0) } } func updateIfNeeded(info: BlockInformation) -> String? { diff --git a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift index 61d0257e23..3b4ab165b0 100644 --- a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift +++ b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift @@ -4,19 +4,8 @@ import AnytypeCore enum DocumentUpdate: Hashable { case general case syncStatus(SyncStatus) - case blocks(blockIds: Set) - case children(blockIds: Set) + case block(blockId: String) + case children(blockId: String) case details(id: String) - case unhandled(blockIds: Set) - - var hasUpdate: Bool { - switch self { - case .general, .syncStatus, .details, .children: - return true - case let .blocks(blockIds): - return !blockIds.isEmpty - case .unhandled: - return true - } - } + case unhandled(blockId: String) } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift index a16440c492..e9a0858fc0 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift @@ -61,7 +61,8 @@ final class BlockBookmarkViewModel: BlockViewModelProtocol { return emptyViewConfiguration(text: Loc.Content.Bookmark.loading, state: .uploading) case .done: guard let objectDetails = targetDetails else { - anytypeAssertionFailure("Coudn't find object details for bookmark") + // TODO: Rollback +// anytypeAssertionFailure("Coudn't find object details for bookmark") return UnsupportedBlockViewModel(info: info).makeContentConfiguration(maxWidth: width) } From 06d27721a1e81e1d2befee08218bc77e4e252174 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Wed, 29 May 2024 17:42:46 +0300 Subject: [PATCH 04/17] IOS-2937 Delete PassthroughSubjectDictionary --- .../Documents/Document/BaseDocument.swift | 7 +-- .../Document/BaseDocumentProtocol.swift | 9 ++++ .../Documents/Events/EventsListener.swift | 2 +- .../Block/SimpleTableCellsBuilder.swift | 2 +- .../SimpleTableDependenciesBuilder.swift | 4 +- .../TableOfContentsContentProvider.swift | 4 +- .../Blocks/Text/Base/TextBlockViewModel.swift | 12 +++--- .../Models/BlockViewModelBuilder.swift | 4 +- .../Utils/Publishers/AnytypePublished.swift | 18 -------- .../Publishers/PublishedDictionary.swift | 43 ------------------- .../Block/BlockContainer/InfoContainer.swift | 16 +------ .../InfoContainerProtocol.swift | 4 -- 12 files changed, 26 insertions(+), 99 deletions(-) delete mode 100644 Modules/AnytypeCore/AnytypeCore/Utils/Publishers/AnytypePublished.swift delete mode 100644 Modules/AnytypeCore/AnytypeCore/Utils/Publishers/PublishedDictionary.swift diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index db7565b336..9c96b934aa 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -219,17 +219,14 @@ final class BaseDocument: BaseDocumentProtocol { for update in Set(updates) { switch update { case .general: - infoContainer.publishAllValues() reorderChilder() case .children(let blockId): - infoContainer.publishValue(for: blockId) resetBlockIds.append(blockId) hasChildren = true case .block(let blockId): - infoContainer.publishValue(for: blockId) resetBlockIds.append(blockId) - case .unhandled(let blockId): - infoContainer.publishValue(for: blockId) + case .unhandled: + break case .syncStatus(let status): syncStatus = status case .details: diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index fd77450bbf..f0448b899e 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -48,4 +48,13 @@ extension BaseDocumentProtocol { .removeDuplicates() .eraseToAnyPublisher() } + + func subscribeForBlockInfo(blockId: String) -> AnyPublisher { + subscibeFor(update: [.block(blockId: objectId), .children(blockId: blockId), .unhandled(blockId: blockId)]) + .compactMap { [weak self] _ in + self?.infoContainer.get(id: blockId) + } + .removeDuplicates() + .eraseToAnyPublisher() + } } diff --git a/Anytype/Sources/Models/Documents/Events/EventsListener.swift b/Anytype/Sources/Models/Documents/Events/EventsListener.swift index 1cb7d5f51e..b51bc5c741 100644 --- a/Anytype/Sources/Models/Documents/Events/EventsListener.swift +++ b/Anytype/Sources/Models/Documents/Events/EventsListener.swift @@ -90,7 +90,7 @@ final class EventsListener: EventsListenerProtocol { } private func handle(events: EventsBunch) { - let middlewareUpdates = events.middlewareEvents.compactMap(\.value).flatMap { middlewareConverter.convert($0) } + let middlewareUpdates = events.middlewareEvents.compactMap(\.value).compactMap { middlewareConverter.convert($0) } let localUpdates = events.localEvents.compactMap { localConverter.convert($0) } let markupUpdates = mentionMarkupEventProvider.updateMentionsEvent() diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/Block/SimpleTableCellsBuilder.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/Block/SimpleTableCellsBuilder.swift index 825c6ec20d..fbff84e77f 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/Block/SimpleTableCellsBuilder.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/Block/SimpleTableCellsBuilder.swift @@ -155,7 +155,7 @@ final class SimpleTableCellsBuilder { let textBlockViewModel = TextBlockViewModel( document: document, - blockInformationProvider: .init(infoContainer: document.infoContainer, info: information), + blockInformationProvider: BlockModelInfomationProvider(document: document, info: information), actionHandler: textBlockActionHandler, cursorManager: cursorManager ) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/SimpleTableDependenciesBuilder.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/SimpleTableDependenciesBuilder.swift index 46926b32fe..f9ac233b1b 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/SimpleTableDependenciesBuilder.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/SimpleTableDependenciesBuilder.swift @@ -56,7 +56,7 @@ final class SimpleTableDependenciesBuilder { } func buildDependenciesContainer(blockInformation: BlockInformation) -> SimpleTableDependenciesContainer { - let blockInformationProvider = BlockModelInfomationProvider(infoContainer: document.infoContainer, info: blockInformation) + let blockInformationProvider = BlockModelInfomationProvider(document: document, info: blockInformation) let selectionOptionHandler = SimpleTableSelectionOptionHandler( router: router, @@ -93,7 +93,7 @@ final class SimpleTableDependenciesBuilder { let viewModel = SimpleTableViewModel( document: document, - tableBlockInfoProvider: .init(infoContainer: document.infoContainer, info: blockInformation), + tableBlockInfoProvider: BlockModelInfomationProvider(document: document, info: blockInformation), cellBuilder: cellsBuilder, stateManager: stateManager, cursorManager: cursorManager diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift index 4217bfd21f..221a882e32 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift @@ -79,8 +79,8 @@ final class TableOfContentsContentProvider { } private func setupSubsriptionFor(item: TableOfContentItem) { - blockSubscriptions[item.blockId] = document.infoContainer.publisherFor(id: item.blockId).sink { [weak item] information in - item?.title = information?.textContent?.text ?? Loc.Object.Title.placeholder + blockSubscriptions[item.blockId] = document.subscribeForBlockInfo(blockId: item.blockId).sink { [weak item] information in + item?.title = information.textContent?.text ?? Loc.Object.Title.placeholder } } } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Text/Base/TextBlockViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Text/Base/TextBlockViewModel.swift index 9dd7c7e697..c9972a52e9 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Text/Base/TextBlockViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Text/Base/TextBlockViewModel.swift @@ -2,26 +2,26 @@ import Combine import UIKit import Services +// TODO: Delete it. Use document subscription in blocks final class BlockModelInfomationProvider { @Published private(set) var info: BlockInformation - private let infoContainer: InfoContainerProtocol + private let document: BaseDocumentProtocol private var subscription: AnyCancellable? init( - infoContainer: InfoContainerProtocol, + document: BaseDocumentProtocol, info: BlockInformation ) { - self.infoContainer = infoContainer + self.document = document self.info = info setupPublisher() } private func setupPublisher() { - subscription = infoContainer - .publisherFor(id: info.id) - .sink { [weak self] in $0.map { self?.info = $0 } } + subscription = document.subscribeForBlockInfo(blockId: info.id) + .sinkOnMain { [weak self] in self?.info = $0 } } } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Models/BlockViewModelBuilder.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Models/BlockViewModelBuilder.swift index 122c47293f..0dac13f603 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Models/BlockViewModelBuilder.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Models/BlockViewModelBuilder.swift @@ -109,7 +109,7 @@ final class BlockViewModelBuilder { } let documentId = document.objectId - let blockInformationProvider = BlockModelInfomationProvider(infoContainer: infoContainer, info: info) + let blockInformationProvider = BlockModelInfomationProvider(document: document, info: info) switch info.content { case let .text(content): @@ -330,7 +330,7 @@ final class BlockViewModelBuilder { case let .dataView(data): return DataViewBlockViewModel( blockInformationProvider: BlockModelInfomationProvider( - infoContainer: infoContainer, + document: document, info: info ), document: document, diff --git a/Modules/AnytypeCore/AnytypeCore/Utils/Publishers/AnytypePublished.swift b/Modules/AnytypeCore/AnytypeCore/Utils/Publishers/AnytypePublished.swift deleted file mode 100644 index 30db40f970..0000000000 --- a/Modules/AnytypeCore/AnytypeCore/Utils/Publishers/AnytypePublished.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation -import Combine - -struct AnytypePassthroughSubject { - var value: Value - var publisher: AnyPublisher { - subject.eraseToAnyPublisher() - } - private var subject = PassthroughSubject() - - init(_ value: Value) { - self.value = value - } - - func sendUpdate() { - subject.send(value) - } -} diff --git a/Modules/AnytypeCore/AnytypeCore/Utils/Publishers/PublishedDictionary.swift b/Modules/AnytypeCore/AnytypeCore/Utils/Publishers/PublishedDictionary.swift deleted file mode 100644 index 47f5168533..0000000000 --- a/Modules/AnytypeCore/AnytypeCore/Utils/Publishers/PublishedDictionary.swift +++ /dev/null @@ -1,43 +0,0 @@ -import Foundation -import Combine - -public final class PassthroughSubjectDictionary where K: Hashable, V: Equatable { - - private let dictionary = SynchronizedDictionary>() - - public init() {} - - public subscript(key: K) -> V? { - get { - return self.dictionary[key]?.value - } - set { - var published = dictionary[key] ?? AnytypePassthroughSubject(nil) - published.value = newValue - dictionary[key] = published - } - } - - public func publisher(_ key: K) -> AnyPublisher { - let published = dictionary[key] ?? AnytypePassthroughSubject(nil) - dictionary[key] = published - return published.publisher - } - - public func removeValue(forKey key: K) { - dictionary[key]?.value = nil - } - - public func publishAllValues() { - dictionary.values.forEach { $0.sendUpdate() } - } - - public func publishValue(for key: K) { - guard let value = dictionary[key] else { return } - value.sendUpdate() - } - - public func removeAll() { - dictionary.removeAll() - } -} diff --git a/Modules/Services/Sources/Models/Block/BlockContainer/InfoContainer.swift b/Modules/Services/Sources/Models/Block/BlockContainer/InfoContainer.swift index f0bae88c48..38c4164ace 100644 --- a/Modules/Services/Sources/Models/Block/BlockContainer/InfoContainer.swift +++ b/Modules/Services/Sources/Models/Block/BlockContainer/InfoContainer.swift @@ -4,7 +4,7 @@ import AnytypeCore public final class InfoContainer: InfoContainerProtocol { - private var models = PassthroughSubjectDictionary() + private var models = SynchronizedDictionary() public init() {} public func children(of id: String) -> [BlockInformation] { @@ -57,18 +57,4 @@ public final class InfoContainer: InfoContainerProtocol { updateAction(entry).flatMap { add($0) } } - - // MARK: - Published - - public func publisherFor(id: String) -> AnyPublisher { - return models.publisher(id) - } - - public func publishAllValues() { - models.publishAllValues() - } - - public func publishValue(for key: String) { - models.publishValue(for: key) - } } diff --git a/Modules/Services/Sources/Models/Block/BlockContainer/InfoContainerProtocol.swift b/Modules/Services/Sources/Models/Block/BlockContainer/InfoContainerProtocol.swift index 27da31d330..ca7ab65445 100644 --- a/Modules/Services/Sources/Models/Block/BlockContainer/InfoContainerProtocol.swift +++ b/Modules/Services/Sources/Models/Block/BlockContainer/InfoContainerProtocol.swift @@ -2,10 +2,6 @@ import Combine public protocol InfoContainerProtocol: AnyObject { - func publisherFor(id: String) -> AnyPublisher - func publishAllValues() - func publishValue(for key: String) - func children(of id: String) -> [BlockInformation] func recursiveChildren(of id: String) -> [BlockInformation] From d4a57b3b00d1f2e16f26ed9b910c2f7c05bc8043 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Wed, 29 May 2024 18:11:13 +0300 Subject: [PATCH 05/17] IOS-2940 Update sync status --- Anytype.xcodeproj/project.pbxproj | 16 +++++++--- .../Documents/Document/BaseDocument.swift | 14 +++++--- .../BaseDocumentGeneralProtocol+Sync.swift | 21 ------------ .../Document/BaseDocumentProtocol+Sync.swift | 32 +++++++++++++++++++ .../Document/BaseDocumentProtocol.swift | 20 ------------ .../Document/DocumentStatusStorage.swift | 13 ++++++++ .../Document/DocumentSyncStatusData.swift | 20 ++++++++++++ .../MiddlewareEventConverter.swift | 9 ++++-- .../Documents/Events/EventsListener.swift | 6 ++-- .../Events/Model/DocumentUpdate.swift | 2 +- .../EditorPage/EditorPageViewModel.swift | 2 +- .../Views/Navigation/SyncStatusData.swift | 6 ++-- .../TextEditor/Set/Models/SetDocument.swift | 3 +- .../DocumentsProvider/DocumentsProvider.swift | 3 +- .../AnytypeCore/Utils/Atomic.swift | 29 +++++++++++++++++ .../Services/Sources/Models/SyncStatus.swift | 28 +--------------- 16 files changed, 135 insertions(+), 89 deletions(-) delete mode 100644 Anytype/Sources/Models/Documents/Document/BaseDocumentGeneralProtocol+Sync.swift create mode 100644 Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift create mode 100644 Anytype/Sources/Models/Documents/Document/DocumentStatusStorage.swift create mode 100644 Anytype/Sources/Models/Documents/Document/DocumentSyncStatusData.swift create mode 100644 Modules/AnytypeCore/AnytypeCore/Utils/Atomic.swift diff --git a/Anytype.xcodeproj/project.pbxproj b/Anytype.xcodeproj/project.pbxproj index 45bb52a66d..5123b08ce3 100644 --- a/Anytype.xcodeproj/project.pbxproj +++ b/Anytype.xcodeproj/project.pbxproj @@ -287,7 +287,7 @@ 2A0303A128ABD4C9002EFC46 /* SystemURLServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A0303A028ABD4C9002EFC46 /* SystemURLServiceProtocol.swift */; }; 2A03FD562A7A46C500CB7191 /* WidgetTypeChangeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A03FD552A7A46C500CB7191 /* WidgetTypeChangeData.swift */; }; 2A03FD5D2A7A85DD00CB7191 /* RecentWidgetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A03FD5C2A7A85DD00CB7191 /* RecentWidgetType.swift */; }; - 2A07E7C42BF270BF00BCFFA3 /* BaseDocumentGeneralProtocol+Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A07E7C32BF270BF00BCFFA3 /* BaseDocumentGeneralProtocol+Sync.swift */; }; + 2A07E7C42BF270BF00BCFFA3 /* DocumentSyncStatusData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A07E7C32BF270BF00BCFFA3 /* DocumentSyncStatusData.swift */; }; 2A081A2E2AE7B3CF0040178D /* TipsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A081A2D2AE7B3CF0040178D /* TipsConfiguration.swift */; }; 2A0B7151297697230003B1C5 /* HomeEditButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A0B7150297697230003B1C5 /* HomeEditButtonStyle.swift */; }; 2A0BAD2F29E01E33005BC5D9 /* View+Focused.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A0BAD2E29E01E33005BC5D9 /* View+Focused.swift */; }; @@ -816,6 +816,8 @@ 2AEABE0328C7546200BDE3AC /* RelationEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEABE0228C7546200BDE3AC /* RelationEvent.swift */; }; 2AEABE0528C7548A00BDE3AC /* RelationEventsBunch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEABE0428C7548A00BDE3AC /* RelationEventsBunch.swift */; }; 2AEABE0728C7559500BDE3AC /* RelationEventConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEABE0628C7559500BDE3AC /* RelationEventConverter.swift */; }; + 2AEAEE502C07773A0060FE4C /* DocumentStatusStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEAEE4F2C07773A0060FE4C /* DocumentStatusStorage.swift */; }; + 2AEAEE542C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEAEE532C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift */; }; 2AEB0C262B7231B7007BB10C /* NotificationDismissEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEB0C252B7231B7007BB10C /* NotificationDismissEnvironment.swift */; }; 2AEDACC42B9F3A4600FA757A /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEDACC32B9F3A4600FA757A /* AppContext.swift */; }; 2AEE49EE292E27FA00EA53AA /* ObjectSettingsCoordinatorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEE49ED292E27FA00EA53AA /* ObjectSettingsCoordinatorViewModel.swift */; }; @@ -2048,7 +2050,7 @@ 2A0554DE2AE965E000D299C4 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; 2A0554DF2AE965E400D299C4 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; 2A0554E02AE965E700D299C4 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Localizable.stringsdict"; sourceTree = ""; }; - 2A07E7C32BF270BF00BCFFA3 /* BaseDocumentGeneralProtocol+Sync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseDocumentGeneralProtocol+Sync.swift"; sourceTree = ""; }; + 2A07E7C32BF270BF00BCFFA3 /* DocumentSyncStatusData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentSyncStatusData.swift; sourceTree = ""; }; 2A081A2D2AE7B3CF0040178D /* TipsConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipsConfiguration.swift; sourceTree = ""; }; 2A0B7150297697230003B1C5 /* HomeEditButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeEditButtonStyle.swift; sourceTree = ""; }; 2A0BAD2E29E01E33005BC5D9 /* View+Focused.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Focused.swift"; sourceTree = ""; }; @@ -2585,6 +2587,8 @@ 2AEABE0228C7546200BDE3AC /* RelationEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationEvent.swift; sourceTree = ""; }; 2AEABE0428C7548A00BDE3AC /* RelationEventsBunch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationEventsBunch.swift; sourceTree = ""; }; 2AEABE0628C7559500BDE3AC /* RelationEventConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationEventConverter.swift; sourceTree = ""; }; + 2AEAEE4F2C07773A0060FE4C /* DocumentStatusStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentStatusStorage.swift; sourceTree = ""; }; + 2AEAEE532C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseDocumentProtocol+Sync.swift"; sourceTree = ""; }; 2AEB0C252B7231B7007BB10C /* NotificationDismissEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDismissEnvironment.swift; sourceTree = ""; }; 2AEDACC32B9F3A4600FA757A /* AppContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContext.swift; sourceTree = ""; }; 2AEE49ED292E27FA00EA53AA /* ObjectSettingsCoordinatorViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectSettingsCoordinatorViewModel.swift; sourceTree = ""; }; @@ -4017,7 +4021,9 @@ 0A09661725C0AB4F00C004BD /* BaseDocument.swift */, C92DCD96272998D800A03D91 /* BaseDocument+Extensions.swift */, 2A9C187F2BA1B76D00DFE560 /* EditBlocksPermission.swift */, - 2A07E7C32BF270BF00BCFFA3 /* BaseDocumentGeneralProtocol+Sync.swift */, + 2A07E7C32BF270BF00BCFFA3 /* DocumentSyncStatusData.swift */, + 2AEAEE4F2C07773A0060FE4C /* DocumentStatusStorage.swift */, + 2AEAEE532C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift */, ); path = Document; sourceTree = ""; @@ -10985,6 +10991,7 @@ C9C49A8527049D0200D86463 /* AlertsFactory.swift in Sources */, 2A96B5B42ABB454800B59A1D /* EditorPageView.swift in Sources */, C98F6A6C27A90F210063DD7A /* BlockConfiguration.swift in Sources */, + 2AEAEE502C07773A0060FE4C /* DocumentStatusStorage.swift in Sources */, 0AD6B24C24CF633F0097A841 /* BlockToolbarBookmark.swift in Sources */, 2ABBAEDD2BF4EEBC00EBA8FA /* WidgetObjectListFilesView.swift in Sources */, 2ABF214929B2052900E872D9 /* DashboardClearCacheAlertModel.swift in Sources */, @@ -11270,7 +11277,7 @@ 2AE85FF72B20D85900268E9D /* EditorPageViewState.swift in Sources */, 3D2D4C6327BA76270070A4C6 /* EditorRouterProtocol.swift in Sources */, 2ABBAEDA2BF4EE8E00EBA8FA /* WidgetObjectListCollectionsView.swift in Sources */, - 2A07E7C42BF270BF00BCFFA3 /* BaseDocumentGeneralProtocol+Sync.swift in Sources */, + 2A07E7C42BF270BF00BCFFA3 /* DocumentSyncStatusData.swift in Sources */, 12A52E2926CAA17B000F0333 /* AnimatableView.swift in Sources */, 2A35FF922AAF20FD00D6006D /* CodeLanguageRowModel.swift in Sources */, 1247BCA727FF3827005F2DB4 /* FeedbackGeneratorExamplesView.swift in Sources */, @@ -11967,6 +11974,7 @@ 2E6CF1602BE111C7002C7023 /* RelationsSearchCoordinatorView.swift in Sources */, 129D78952694AE6900DA6757 /* ObjectLayoutPicker.swift in Sources */, 53F7441927F4A119005C9BB0 /* AnytypePopupViewModel.swift in Sources */, + 2AEAEE542C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift in Sources */, EA54129C26C6856B00C9B6F3 /* AttributedStringChange.swift in Sources */, 3D3A183927340187000D0842 /* NSAttributedString+Convenience.swift in Sources */, EA57F39826B74D2E007D4292 /* BlockInformationCreator.swift in Sources */, diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index 9c96b934aa..ef71c1823a 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -5,7 +5,7 @@ import Foundation final class BaseDocument: BaseDocumentProtocol { - var syncStatus: SyncStatus = .unknown + var syncStatus: SyncStatus { statusStorage.status } var childrenPublisher: AnyPublisher<[BlockInformation], Never> { $_children.eraseToAnyPublisher() } @Published private var _children = [BlockInformation]() @@ -29,6 +29,7 @@ final class BaseDocument: BaseDocumentProtocol { private let relationDetailsStorage: RelationDetailsStorageProtocol private let objectTypeProvider: ObjectTypeProviderProtocol private let accountParticipantsStorage: AccountParticipantsStorageProtocol + private let statusStorage: DocumentStatusStorageProtocol private let viewModelSetter: DocumentViewModelSetterProtocol private var participantIsEditor: Bool = false @@ -105,7 +106,8 @@ final class BaseDocument: BaseDocumentProtocol { objectLifecycleService: ObjectLifecycleServiceProtocol, relationDetailsStorage: RelationDetailsStorageProtocol, objectTypeProvider: ObjectTypeProviderProtocol, - accountParticipantsStorage: AccountParticipantsStorageProtocol + accountParticipantsStorage: AccountParticipantsStorageProtocol, + statusStorage: DocumentStatusStorageProtocol ) { self.objectId = objectId self.forPreview = forPreview @@ -115,7 +117,8 @@ final class BaseDocument: BaseDocumentProtocol { infoContainer: infoContainer, relationLinksStorage: relationLinksStorage, restrictionsContainer: restrictionsContainer, - detailsStorage: detailsStorage + detailsStorage: detailsStorage, + statusStorage: statusStorage ) self.viewModelSetter = DocumentViewModelSetter( @@ -130,6 +133,7 @@ final class BaseDocument: BaseDocumentProtocol { self.relationDetailsStorage = relationDetailsStorage self.objectTypeProvider = objectTypeProvider self.accountParticipantsStorage = accountParticipantsStorage + self.statusStorage = statusStorage setup() } @@ -227,8 +231,8 @@ final class BaseDocument: BaseDocumentProtocol { resetBlockIds.append(blockId) case .unhandled: break - case .syncStatus(let status): - syncStatus = status + case .syncStatus: + break case .details: break // Sync will be send always } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentGeneralProtocol+Sync.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentGeneralProtocol+Sync.swift deleted file mode 100644 index ec3232aa5a..0000000000 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentGeneralProtocol+Sync.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation -import Services -import Combine - -struct DocumentSyncStatusData: Equatable { - let syncStatus: SyncStatus - let layout: DetailsLayout -} - -extension BaseDocumentGeneralProtocol { - - var syncStatusPublisher: AnyPublisher { - syncPublisher.compactMap { [weak self] in - guard let syncStatus = self?.syncStatus, let layoutValue = self?.details?.layoutValue else { return nil } - return DocumentSyncStatusData(syncStatus: syncStatus, layout: layoutValue) - } - .removeDuplicates() - .eraseToAnyPublisher() - } - -} diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift new file mode 100644 index 0000000000..b85c648724 --- /dev/null +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift @@ -0,0 +1,32 @@ +import Foundation +import Combine +import Services + +extension BaseDocumentProtocol { + var syncStatusPublisher: AnyPublisher { + subscibeFor(update: [.syncStatus]) + .compactMap { [weak self] _ in + self?.syncStatus + } + .removeDuplicates() + .eraseToAnyPublisher() + } + + func subscibeForDetails(objectId: String) -> AnyPublisher { + subscibeFor(update: [.details(id: objectId)]) + .compactMap { [weak self] _ in + self?.detailsStorage.get(id: objectId) + } + .removeDuplicates() + .eraseToAnyPublisher() + } + + func subscribeForBlockInfo(blockId: String) -> AnyPublisher { + subscibeFor(update: [.block(blockId: objectId), .children(blockId: blockId), .unhandled(blockId: blockId)]) + .compactMap { [weak self] _ in + self?.infoContainer.get(id: blockId) + } + .removeDuplicates() + .eraseToAnyPublisher() + } +} diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index f0448b899e..3ccd98f390 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -38,23 +38,3 @@ protocol BaseDocumentProtocol: AnyObject, BaseDocumentGeneralProtocol { func subscibeFor(update: [DocumentUpdate]) -> AnyPublisher<[DocumentUpdate], Never> } - -extension BaseDocumentProtocol { - func subscibeForDetails(objectId: String) -> AnyPublisher { - subscibeFor(update: [.details(id: objectId)]) - .compactMap { [weak self] _ in - self?.detailsStorage.get(id: objectId) - } - .removeDuplicates() - .eraseToAnyPublisher() - } - - func subscribeForBlockInfo(blockId: String) -> AnyPublisher { - subscibeFor(update: [.block(blockId: objectId), .children(blockId: blockId), .unhandled(blockId: blockId)]) - .compactMap { [weak self] _ in - self?.infoContainer.get(id: blockId) - } - .removeDuplicates() - .eraseToAnyPublisher() - } -} diff --git a/Anytype/Sources/Models/Documents/Document/DocumentStatusStorage.swift b/Anytype/Sources/Models/Documents/Document/DocumentStatusStorage.swift new file mode 100644 index 0000000000..9efcb81b35 --- /dev/null +++ b/Anytype/Sources/Models/Documents/Document/DocumentStatusStorage.swift @@ -0,0 +1,13 @@ +import Foundation +import AnytypeCore +import Services + +protocol DocumentStatusStorageProtocol: AnyObject { + var status: SyncStatus { get set } +} + +final class DocumentStatusStorage: DocumentStatusStorageProtocol { + + @Atomic + var status: SyncStatus = .unknown +} diff --git a/Anytype/Sources/Models/Documents/Document/DocumentSyncStatusData.swift b/Anytype/Sources/Models/Documents/Document/DocumentSyncStatusData.swift new file mode 100644 index 0000000000..19f7958d21 --- /dev/null +++ b/Anytype/Sources/Models/Documents/Document/DocumentSyncStatusData.swift @@ -0,0 +1,20 @@ +import Foundation +import Services +import Combine + +struct DocumentSyncStatusData: Equatable { + let syncStatus: SyncStatus + let layout: DetailsLayout +} + +extension BaseDocumentProtocol { + + var syncStatusDataPublisher: AnyPublisher { + subscibeForDetails(objectId: objectId) + .map { $0.layoutValue } + .combineLatest(syncStatusPublisher) + .map { DocumentSyncStatusData(syncStatus: $1, layout: $0) } + .removeDuplicates() + .eraseToAnyPublisher() + } +} diff --git a/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift b/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift index a88f0f54f3..89a42146f5 100644 --- a/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift +++ b/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift @@ -11,26 +11,29 @@ final class MiddlewareEventConverter { private let restrictionsContainer: ObjectRestrictionsContainer private let informationCreator: BlockInformationCreator - + private let statusStorage: DocumentStatusStorageProtocol init( infoContainer: InfoContainerProtocol, relationLinksStorage: RelationLinksStorageProtocol, informationCreator: BlockInformationCreator, detailsStorage: ObjectDetailsStorage, - restrictionsContainer: ObjectRestrictionsContainer + restrictionsContainer: ObjectRestrictionsContainer, + statusStorage: DocumentStatusStorageProtocol ) { self.infoContainer = infoContainer self.relationLinksStorage = relationLinksStorage self.informationCreator = informationCreator self.detailsStorage = detailsStorage self.restrictionsContainer = restrictionsContainer + self.statusStorage = statusStorage } func convert(_ event: Anytype_Event.Message.OneOf_Value) -> DocumentUpdate? { switch event { case let .threadStatus(status): - return SyncStatus(status.summary.status).flatMap { .syncStatus($0) } + statusStorage.status = status.summary.status + return .syncStatus case let .blockSetFields(data): infoContainer.setFields(data: data) return .block(blockId: data.id) diff --git a/Anytype/Sources/Models/Documents/Events/EventsListener.swift b/Anytype/Sources/Models/Documents/Events/EventsListener.swift index b51bc5c741..63603c581b 100644 --- a/Anytype/Sources/Models/Documents/Events/EventsListener.swift +++ b/Anytype/Sources/Models/Documents/Events/EventsListener.swift @@ -30,7 +30,8 @@ final class EventsListener: EventsListenerProtocol { infoContainer: InfoContainerProtocol, relationLinksStorage: RelationLinksStorageProtocol, restrictionsContainer: ObjectRestrictionsContainer, - detailsStorage: ObjectDetailsStorage + detailsStorage: ObjectDetailsStorage, + statusStorage: DocumentStatusStorageProtocol ) { self.objectId = objectId self.infoContainer = infoContainer @@ -44,7 +45,8 @@ final class EventsListener: EventsListenerProtocol { relationLinksStorage: relationLinksStorage, informationCreator: informationCreator, detailsStorage: detailsStorage, - restrictionsContainer: restrictionsContainer + restrictionsContainer: restrictionsContainer, + statusStorage: statusStorage ) self.localConverter = LocalEventConverter( infoContainer: infoContainer diff --git a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift index 3b4ab165b0..75ff126907 100644 --- a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift +++ b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift @@ -3,7 +3,7 @@ import AnytypeCore enum DocumentUpdate: Hashable { case general - case syncStatus(SyncStatus) + case syncStatus case block(blockId: String) case children(blockId: String) case details(id: String) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/EditorPageViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/EditorPageViewModel.swift index e5b05332e8..24422c1ca4 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/EditorPageViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/EditorPageViewModel.swift @@ -77,7 +77,7 @@ final class EditorPageViewModel: EditorPageViewModelProtocol, EditorBottomNaviga func setupSubscriptions() { subscriptions = [] - document.syncStatusPublisher.receiveOnMain().sink { [weak self] data in + document.syncStatusDataPublisher.receiveOnMain().sink { [weak self] data in self?.handleSyncStatus(data: data) }.store(in: &subscriptions) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Navigation/SyncStatusData.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Navigation/SyncStatusData.swift index d0ad6d3d71..11e82c28a8 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Navigation/SyncStatusData.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Navigation/SyncStatusData.swift @@ -17,7 +17,7 @@ struct SyncStatusData { return Loc.syncing case .synced: return Loc.synced - case .failed, .incompatibleVersion: + case .failed, .incompatibleVersion, .UNRECOGNIZED: return Loc.notSyncing } } @@ -35,7 +35,7 @@ struct SyncStatusData { return syncedDescription case .failed: return Loc.failedToSyncTryingAgain - case .incompatibleVersion: + case .incompatibleVersion, .UNRECOGNIZED: return Loc.Sync.Status.Version.Outdated.description } } @@ -60,7 +60,7 @@ struct SyncStatusData { return UIColor.System.amber100 case .synced: return UIColor.System.green - case .unknown, .offline: + case .unknown, .offline, .UNRECOGNIZED: return nil } } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocument.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocument.swift index 4f7d2139d8..2f563ebad6 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocument.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocument.swift @@ -4,6 +4,7 @@ import Combine import AnytypeCore final class SetDocument: SetDocumentProtocol { + let document: BaseDocumentProtocol var objectId: String { document.objectId } @@ -234,7 +235,7 @@ final class SetDocument: SetDocumentProtocol { } .store(in: &subscriptions) - document.syncStatusPublisher.sink { [weak self] status in + document.syncStatusDataPublisher.sink { [weak self] status in self?.updateSubject.send(.syncStatus(status)) } .store(in: &subscriptions) diff --git a/Anytype/Sources/ServiceLayer/DocumentsProvider/DocumentsProvider.swift b/Anytype/Sources/ServiceLayer/DocumentsProvider/DocumentsProvider.swift index 548aa1f5a3..327e00fc95 100644 --- a/Anytype/Sources/ServiceLayer/DocumentsProvider/DocumentsProvider.swift +++ b/Anytype/Sources/ServiceLayer/DocumentsProvider/DocumentsProvider.swift @@ -68,7 +68,8 @@ final class DocumentsProvider: DocumentsProviderProtocol { objectLifecycleService: objectLifecycleService, relationDetailsStorage: relationDetailsStorage, objectTypeProvider: objectTypeProvider, - accountParticipantsStorage: accountParticipantsStorage + accountParticipantsStorage: accountParticipantsStorage, + statusStorage: DocumentStatusStorage() ) } } diff --git a/Modules/AnytypeCore/AnytypeCore/Utils/Atomic.swift b/Modules/AnytypeCore/AnytypeCore/Utils/Atomic.swift new file mode 100644 index 0000000000..1cbb9af221 --- /dev/null +++ b/Modules/AnytypeCore/AnytypeCore/Utils/Atomic.swift @@ -0,0 +1,29 @@ +import Foundation + +@propertyWrapper +public struct Atomic { + + private var value: Value + private let lock = NSLock() + + public init(wrappedValue value: Value) { + self.value = value + } + + public var wrappedValue: Value { + get { return load() } + set { store(newValue: newValue) } + } + + func load() -> Value { + lock.lock() + defer { lock.unlock() } + return value + } + + mutating func store(newValue: Value) { + lock.lock() + defer { lock.unlock() } + value = newValue + } +} diff --git a/Modules/Services/Sources/Models/SyncStatus.swift b/Modules/Services/Sources/Models/SyncStatus.swift index 10900cc021..d3aaceee42 100644 --- a/Modules/Services/Sources/Models/SyncStatus.swift +++ b/Modules/Services/Sources/Models/SyncStatus.swift @@ -1,29 +1,3 @@ import ProtobufMessages -public enum SyncStatus: Sendable { - case unknown - case offline - case syncing - case synced - case failed - case incompatibleVersion - - public init?(_ model: Anytype_Event.Status.Thread.SyncStatus) { - switch model { - case .unknown: - self = .unknown - case .offline: - self = .offline - case .syncing: - self = .syncing - case .synced: - self = .synced - case .failed: - self = .failed - case .incompatibleVersion: - self = .incompatibleVersion - case .UNRECOGNIZED: - return nil - } - } -} +public typealias SyncStatus = Anytype_Event.Status.Thread.SyncStatus From 9b1b98cbbabeb653a4c11cfa3d9b0043341207a0 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 30 May 2024 15:13:27 +0300 Subject: [PATCH 06/17] IOS-2935 Delete resetBlocksSubject --- .../Documents/Document/BaseDocument.swift | 23 +++++++-------- .../Document/BaseDocumentProtocol+Sync.swift | 23 ++++++++++++++- .../Document/BaseDocumentProtocol.swift | 3 +- .../Block/SimpleTableViewModel.swift | 28 ++++++++++--------- .../TableOfContentsContentProvider.swift | 4 +-- .../EditorPage/EditorPageViewModel.swift | 2 +- 6 files changed, 53 insertions(+), 30 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index ef71c1823a..bb3347df89 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -10,9 +10,6 @@ final class BaseDocument: BaseDocumentProtocol { var childrenPublisher: AnyPublisher<[BlockInformation], Never> { $_children.eraseToAnyPublisher() } @Published private var _children = [BlockInformation]() - private var _resetBlocksSubject = PassthroughSubject, Never>() - var resetBlocksSubject: PassthroughSubject, Never> { _resetBlocksSubject } - let objectId: String private(set) var isOpened = false let forPreview: Bool @@ -35,9 +32,17 @@ final class BaseDocument: BaseDocumentProtocol { private var participantIsEditor: Bool = false private var subscriptions = [AnyCancellable]() - @Published private var sync: Void? var syncPublisher: AnyPublisher { - return $sync.compactMap { $0 }.eraseToAnyPublisher() + return syncDocPublisher + .map { _ in Void() } + .eraseToAnyPublisher() + } + + var syncDocPublisher: AnyPublisher<[DocumentUpdate], Never> { + return syncSubject + .merge(with: Just(isOpened ? [.general] : [])) + .filter { $0.isNotEmpty } + .eraseToAnyPublisher() } private var syncSubject = PassthroughSubject<[DocumentUpdate], Never>() @@ -217,7 +222,6 @@ final class BaseDocument: BaseDocumentProtocol { } private func triggerSync(updates: [DocumentUpdate]) { - var resetBlockIds = [String]() var hasChildren = false for update in Set(updates) { @@ -225,10 +229,9 @@ final class BaseDocument: BaseDocumentProtocol { case .general: reorderChilder() case .children(let blockId): - resetBlockIds.append(blockId) hasChildren = true case .block(let blockId): - resetBlockIds.append(blockId) + break case .unhandled: break case .syncStatus: @@ -242,15 +245,13 @@ final class BaseDocument: BaseDocumentProtocol { reorderChilder() } - _resetBlocksSubject.send(Set(resetBlockIds)) parsedRelationsSubject.send(parsedRelations) - sync = () syncSubject.send(updates) } func subscibeFor(update: [DocumentUpdate]) -> AnyPublisher<[DocumentUpdate], Never> { return syncSubject - .merge(with: Just(update)) + .merge(with: Just(isOpened ? update : [])) .map { syncUpdate in if syncUpdate.contains(.general) { return update diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift index b85c648724..4b18c222e5 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift @@ -3,6 +3,7 @@ import Combine import Services extension BaseDocumentProtocol { + var syncStatusPublisher: AnyPublisher { subscibeFor(update: [.syncStatus]) .compactMap { [weak self] _ in @@ -21,7 +22,8 @@ extension BaseDocumentProtocol { .eraseToAnyPublisher() } - func subscribeForBlockInfo(blockId: String) -> AnyPublisher { + func subscribeForBlockInfo(blockId: String) -> AnyPublisher + { subscibeFor(update: [.block(blockId: objectId), .children(blockId: blockId), .unhandled(blockId: blockId)]) .compactMap { [weak self] _ in self?.infoContainer.get(id: blockId) @@ -29,4 +31,23 @@ extension BaseDocumentProtocol { .removeDuplicates() .eraseToAnyPublisher() } + + var resetBlocksPublisher: AnyPublisher, Never> { + syncDocPublisher + .map { updates in + let ids = updates.compactMap { update in + switch update { + case .block(let blockId): + return blockId + case .children(let blockId): + return blockId + default: + return nil + } + } + return Set(ids) + } + .filter { $0.isNotEmpty } + .eraseToAnyPublisher() + } } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index 3ccd98f390..bd378ce9c6 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -32,9 +32,8 @@ protocol BaseDocumentProtocol: AnyObject, BaseDocumentGeneralProtocol { var parsedRelationsPublisher: AnyPublisher { get } var childrenPublisher: AnyPublisher<[BlockInformation], Never> { get } - var syncPublisher: AnyPublisher { get } - var resetBlocksSubject: PassthroughSubject, Never> { get } var permissionsPublisher: AnyPublisher { get } func subscibeFor(update: [DocumentUpdate]) -> AnyPublisher<[DocumentUpdate], Never> + var syncDocPublisher: AnyPublisher<[DocumentUpdate], Never> { get } } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/Block/SimpleTableViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/Block/SimpleTableViewModel.swift index 678961c834..635f8dda01 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/Block/SimpleTableViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/SimpleTable/Block/SimpleTableViewModel.swift @@ -39,19 +39,21 @@ final class SimpleTableViewModel { } private func setupHandlers() { - document.resetBlocksSubject.sink { [weak self] blockIds in - guard let self else { return } - - let computedTable = ComputedTable(blockInformation: tableBlockInfoProvider.info, infoContainer: document.infoContainer) - guard computedTable.isNotNil else { return } - - let allRelatedIds = [tableBlockInfoProvider.info.id] + document.infoContainer.recursiveChildren(of: tableBlockInfoProvider.info.id).map { $0.id } - - if Set(allRelatedIds).intersection(blockIds).count > 0 { - forceUpdate(shouldApplyFocus: true) - stateManager.checkOpenedState() - } - }.store(in: &cancellables) + document.resetBlocksPublisher + .receiveOnMain() + .sink { [weak self] blockIds in + guard let self else { return } + + let computedTable = ComputedTable(blockInformation: tableBlockInfoProvider.info, infoContainer: document.infoContainer) + guard computedTable.isNotNil else { return } + + let allRelatedIds = [tableBlockInfoProvider.info.id] + document.infoContainer.recursiveChildren(of: tableBlockInfoProvider.info.id).map { $0.id } + + if Set(allRelatedIds).intersection(blockIds).count > 0 { + forceUpdate(shouldApplyFocus: true) + stateManager.checkOpenedState() + } + }.store(in: &cancellables) } private func updateDifference(newItems: [[EditorItem]]) { diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift index 221a882e32..15599a767a 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift @@ -28,11 +28,11 @@ final class TableOfContentsContentProvider { private func startUpdateContent() { updateContent() - document.flattenBlockIds.sink { [weak self] _ in + document.flattenBlockIds.receiveOnMain().sink { [weak self] _ in self?.updateContent() }.store(in: &subscriptions) - document.resetBlocksSubject.sink { [weak self] _ in + document.resetBlocksPublisher.receiveOnMain().sink { [weak self] _ in self?.updateContent() }.store(in: &subscriptions) } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/EditorPageViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/EditorPageViewModel.swift index 24422c1ca4..e82168a592 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/EditorPageViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/EditorPageViewModel.swift @@ -100,7 +100,7 @@ final class EditorPageViewModel: EditorPageViewModelProtocol, EditorBottomNaviga self?.updateHeaderIfNeeded(headerModel: headerModel) }.store(in: &subscriptions) - document.resetBlocksSubject.receiveOnMain().sink { [weak self] blockIds in + document.resetBlocksPublisher.receiveOnMain().sink { [weak self] blockIds in guard let self else { return } let filtered = Set(blockIds).intersection(modelsHolder.blocksMapping.keys) From eb9998a4be6b3d9865ae2ce8ebf5bcb75f27995d Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 30 May 2024 16:04:58 +0300 Subject: [PATCH 07/17] IOS-2935 Change childrenPublisher --- Anytype.xcodeproj/project.pbxproj | 4 ++ .../Documents/Document/BaseDocument.swift | 54 ++++--------------- .../Document/BaseDocumentProtocol+Sync.swift | 18 +++++-- .../Document/BaseDocumentProtocol.swift | 5 +- .../Document/BaseDocumentUpdate.swift | 32 +++++++++++ .../Document/DocumentSyncStatusData.swift | 2 +- .../Events/Model/DocumentUpdate.swift | 11 ++++ .../Bookmark/BlockBookmarkViewModel.swift | 2 +- .../DataView/DataViewBlockViewModel.swift | 2 +- .../Blocks/Link/BlockLinkViewModel.swift | 2 +- 10 files changed, 77 insertions(+), 55 deletions(-) create mode 100644 Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift diff --git a/Anytype.xcodeproj/project.pbxproj b/Anytype.xcodeproj/project.pbxproj index 5123b08ce3..d24d154261 100644 --- a/Anytype.xcodeproj/project.pbxproj +++ b/Anytype.xcodeproj/project.pbxproj @@ -818,6 +818,7 @@ 2AEABE0728C7559500BDE3AC /* RelationEventConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEABE0628C7559500BDE3AC /* RelationEventConverter.swift */; }; 2AEAEE502C07773A0060FE4C /* DocumentStatusStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEAEE4F2C07773A0060FE4C /* DocumentStatusStorage.swift */; }; 2AEAEE542C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEAEE532C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift */; }; + 2AEAEE582C08AB7E0060FE4C /* BaseDocumentUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEAEE572C08AB7E0060FE4C /* BaseDocumentUpdate.swift */; }; 2AEB0C262B7231B7007BB10C /* NotificationDismissEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEB0C252B7231B7007BB10C /* NotificationDismissEnvironment.swift */; }; 2AEDACC42B9F3A4600FA757A /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEDACC32B9F3A4600FA757A /* AppContext.swift */; }; 2AEE49EE292E27FA00EA53AA /* ObjectSettingsCoordinatorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEE49ED292E27FA00EA53AA /* ObjectSettingsCoordinatorViewModel.swift */; }; @@ -2589,6 +2590,7 @@ 2AEABE0628C7559500BDE3AC /* RelationEventConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationEventConverter.swift; sourceTree = ""; }; 2AEAEE4F2C07773A0060FE4C /* DocumentStatusStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentStatusStorage.swift; sourceTree = ""; }; 2AEAEE532C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseDocumentProtocol+Sync.swift"; sourceTree = ""; }; + 2AEAEE572C08AB7E0060FE4C /* BaseDocumentUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDocumentUpdate.swift; sourceTree = ""; }; 2AEB0C252B7231B7007BB10C /* NotificationDismissEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDismissEnvironment.swift; sourceTree = ""; }; 2AEDACC32B9F3A4600FA757A /* AppContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContext.swift; sourceTree = ""; }; 2AEE49ED292E27FA00EA53AA /* ObjectSettingsCoordinatorViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectSettingsCoordinatorViewModel.swift; sourceTree = ""; }; @@ -4024,6 +4026,7 @@ 2A07E7C32BF270BF00BCFFA3 /* DocumentSyncStatusData.swift */, 2AEAEE4F2C07773A0060FE4C /* DocumentStatusStorage.swift */, 2AEAEE532C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift */, + 2AEAEE572C08AB7E0060FE4C /* BaseDocumentUpdate.swift */, ); path = Document; sourceTree = ""; @@ -11896,6 +11899,7 @@ 3D1B5F4C274BDE5D007D97AE /* SetContentViewItemConfiguration.swift in Sources */, 3D44984526D53D61005A3E06 /* EditorPageViewModelProtocol.swift in Sources */, 2A8A2DCC2AB9960F00105549 /* AppActionStorage.swift in Sources */, + 2AEAEE582C08AB7E0060FE4C /* BaseDocumentUpdate.swift in Sources */, 2AD06B8328572A520006C255 /* TableOfContentsView.swift in Sources */, 1211AF6627F338EC00BACB88 /* AnytypeImageDownloader.swift in Sources */, 2AA8108A29A66854007DD64B /* View+DragAndDrop.swift in Sources */, diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index bb3347df89..f1bf1fa03c 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -6,9 +6,7 @@ import Foundation final class BaseDocument: BaseDocumentProtocol { var syncStatus: SyncStatus { statusStorage.status } - - var childrenPublisher: AnyPublisher<[BlockInformation], Never> { $_children.eraseToAnyPublisher() } - @Published private var _children = [BlockInformation]() + private(set) var children = [BlockInformation]() let objectId: String private(set) var isOpened = false @@ -38,14 +36,14 @@ final class BaseDocument: BaseDocumentProtocol { .eraseToAnyPublisher() } - var syncDocPublisher: AnyPublisher<[DocumentUpdate], Never> { + var syncDocPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { return syncSubject .merge(with: Just(isOpened ? [.general] : [])) .filter { $0.isNotEmpty } .eraseToAnyPublisher() } - private var syncSubject = PassthroughSubject<[DocumentUpdate], Never>() + private var syncSubject = PassthroughSubject<[BaseDocumentUpdate], Never>() // MARK: - State private var parsedRelationsSubject = CurrentValueSubject(.empty) @@ -94,17 +92,7 @@ final class BaseDocument: BaseDocumentProtocol { var details: ObjectDetails? { detailsStorage.get(id: objectId) } - - var detailsPublisher: AnyPublisher { - syncPublisher - .receiveOnMain() - .compactMap { [weak self, objectId] in - self?.detailsStorage.get(id: objectId) - } - .removeDuplicates() - .eraseToAnyPublisher() - } - + init( objectId: String, forPreview: Bool, @@ -186,15 +174,11 @@ final class BaseDocument: BaseDocumentProtocol { isOpened = false } - var children: [BlockInformation] { - return _children - } - var isEmpty: Bool { - let filteredBlocks = _children.filter { $0.isFeaturedRelations || $0.isText } + let filteredBlocks = children.filter { $0.isFeaturedRelations || $0.isText } if filteredBlocks.count > 0 { return false } - let allTextChilds = _children.filter(\.isText) + let allTextChilds = children.filter(\.isText) if allTextChilds.count > 1 { return false } @@ -218,38 +202,20 @@ final class BaseDocument: BaseDocumentProtocol { return } let flatten = model.flatChildrenTree(container: infoContainer) - _children = flatten + children = flatten } private func triggerSync(updates: [DocumentUpdate]) { - var hasChildren = false - - for update in Set(updates) { - switch update { - case .general: - reorderChilder() - case .children(let blockId): - hasChildren = true - case .block(let blockId): - break - case .unhandled: - break - case .syncStatus: - break - case .details: - break // Sync will be send always - } - } - if hasChildren { + if updates.contains(where: { $0 == .general || $0.isChildren }) { reorderChilder() } parsedRelationsSubject.send(parsedRelations) - syncSubject.send(updates) + syncSubject.send(updates.flatMap(\.toBaseDocumentUpdate)) } - func subscibeFor(update: [DocumentUpdate]) -> AnyPublisher<[DocumentUpdate], Never> { + func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> { return syncSubject .merge(with: Just(isOpened ? update : [])) .map { syncUpdate in diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift index 4b18c222e5..fe2ebf9b0a 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift @@ -13,7 +13,7 @@ extension BaseDocumentProtocol { .eraseToAnyPublisher() } - func subscibeForDetails(objectId: String) -> AnyPublisher { + func subscribeForDetails(objectId: String) -> AnyPublisher { subscibeFor(update: [.details(id: objectId)]) .compactMap { [weak self] _ in self?.detailsStorage.get(id: objectId) @@ -24,7 +24,7 @@ extension BaseDocumentProtocol { func subscribeForBlockInfo(blockId: String) -> AnyPublisher { - subscibeFor(update: [.block(blockId: objectId), .children(blockId: blockId), .unhandled(blockId: blockId)]) + subscibeFor(update: [.block(blockId: objectId), .unhandled(blockId: blockId)]) .compactMap { [weak self] _ in self?.infoContainer.get(id: blockId) } @@ -39,8 +39,6 @@ extension BaseDocumentProtocol { switch update { case .block(let blockId): return blockId - case .children(let blockId): - return blockId default: return nil } @@ -50,4 +48,16 @@ extension BaseDocumentProtocol { .filter { $0.isNotEmpty } .eraseToAnyPublisher() } + + var childrenPublisher: AnyPublisher<[BlockInformation], Never> { + subscibeFor(update: [.children]) + .compactMap { [weak self] _ in + self?.children + } + .eraseToAnyPublisher() + } + + var detailsPublisher: AnyPublisher { + subscribeForDetails(objectId: objectId) + } } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index bd378ce9c6..443d6bb578 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -31,9 +31,8 @@ protocol BaseDocumentProtocol: AnyObject, BaseDocumentGeneralProtocol { var isOpened: Bool { get } var parsedRelationsPublisher: AnyPublisher { get } - var childrenPublisher: AnyPublisher<[BlockInformation], Never> { get } var permissionsPublisher: AnyPublisher { get } - func subscibeFor(update: [DocumentUpdate]) -> AnyPublisher<[DocumentUpdate], Never> - var syncDocPublisher: AnyPublisher<[DocumentUpdate], Never> { get } + func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> + var syncDocPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { get } } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift new file mode 100644 index 0000000000..11589e0ce4 --- /dev/null +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift @@ -0,0 +1,32 @@ +import Services +import AnytypeCore + +enum BaseDocumentUpdate: Hashable { + // From DocumentUpdate + case general + case syncStatus + case block(blockId: String) + case children + case details(id: String) + case unhandled(blockId: String) + // Local State +} + +extension DocumentUpdate { + var toBaseDocumentUpdate: [BaseDocumentUpdate] { + switch self { + case .general: + return [.general] + case .syncStatus: + return [.syncStatus] + case .block(let blockId): + return [.block(blockId: blockId)] + case .children(let blockId): + return [.block(blockId: blockId), .children] + case .details(let id): + return [.details(id: id)] + case .unhandled(let blockId): + return [.unhandled(blockId: blockId)] + } + } +} diff --git a/Anytype/Sources/Models/Documents/Document/DocumentSyncStatusData.swift b/Anytype/Sources/Models/Documents/Document/DocumentSyncStatusData.swift index 19f7958d21..298142dbae 100644 --- a/Anytype/Sources/Models/Documents/Document/DocumentSyncStatusData.swift +++ b/Anytype/Sources/Models/Documents/Document/DocumentSyncStatusData.swift @@ -10,7 +10,7 @@ struct DocumentSyncStatusData: Equatable { extension BaseDocumentProtocol { var syncStatusDataPublisher: AnyPublisher { - subscibeForDetails(objectId: objectId) + subscribeForDetails(objectId: objectId) .map { $0.layoutValue } .combineLatest(syncStatusPublisher) .map { DocumentSyncStatusData(syncStatus: $1, layout: $0) } diff --git a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift index 75ff126907..6932831852 100644 --- a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift +++ b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift @@ -9,3 +9,14 @@ enum DocumentUpdate: Hashable { case details(id: String) case unhandled(blockId: String) } + +extension DocumentUpdate { + var isChildren: Bool { + switch self { + case .children(blockId: _): + return true + default: + return false + } + } +} diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift index e9a0858fc0..2368e88a2b 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Bookmark/BlockBookmarkViewModel.swift @@ -42,7 +42,7 @@ final class BlockBookmarkViewModel: BlockViewModelProtocol { } private func setupSubscription() { - document.subscibeForDetails(objectId: bookmarkData.targetObjectID) + document.subscribeForDetails(objectId: bookmarkData.targetObjectID) .receiveOnMain() .sink { [weak editorCollectionController, weak self] details in guard let self else { return } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/DataView/DataViewBlockViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/DataView/DataViewBlockViewModel.swift index 6aff18e905..2f4654eac3 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/DataView/DataViewBlockViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/DataView/DataViewBlockViewModel.swift @@ -41,7 +41,7 @@ final class DataViewBlockViewModel: BlockViewModelProtocol { self.openSet = openSet detailsSubscription = document - .subscibeForDetails(objectId: blockData.targetObjectID) + .subscribeForDetails(objectId: blockData.targetObjectID) .sinkOnMain { [weak self] _ in guard let self = self else { return } self.targetDetails = targetDetails diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Link/BlockLinkViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Link/BlockLinkViewModel.swift index d7e5ccd652..4314093d79 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Link/BlockLinkViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/Link/BlockLinkViewModel.swift @@ -37,7 +37,7 @@ final class BlockLinkViewModel: BlockViewModelProtocol { objectDetailsSubscription = document - .subscibeForDetails(objectId: content.targetBlockID) + .subscribeForDetails(objectId: content.targetBlockID) .sinkOnMain { [weak self] details in guard let self else { return } self.targetDetails = details From 6856271559d6d6d9261b793081ea123a42e371dc Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 30 May 2024 16:54:31 +0300 Subject: [PATCH 08/17] IOS-2935 Reorder --- .../Documents/Document/BaseDocument.swift | 30 +++++++------------ .../Document/BaseDocumentProtocol+Sync.swift | 6 ++++ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index f1bf1fa03c..5018ef926a 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -5,19 +5,25 @@ import Foundation final class BaseDocument: BaseDocumentProtocol { + // Init state + let objectId: String + let forPreview: Bool + + // From Containers var syncStatus: SyncStatus { statusStorage.status } - private(set) var children = [BlockInformation]() + var isLocked: Bool { infoContainer.get(id: objectId)?.isLocked ?? false } + var details: ObjectDetails? { detailsStorage.get(id: objectId) } + var objectRestrictions: ObjectRestrictions { restrictionsContainer.restrinctions } - let objectId: String + // Custom state + private(set) var children = [BlockInformation]() private(set) var isOpened = false - let forPreview: Bool let infoContainer: InfoContainerProtocol = InfoContainer() let relationLinksStorage: RelationLinksStorageProtocol = RelationLinksStorage() let restrictionsContainer: ObjectRestrictionsContainer = ObjectRestrictionsContainer() let detailsStorage = ObjectDetailsStorage() - var objectRestrictions: ObjectRestrictions { restrictionsContainer.restrinctions } private let objectLifecycleService: ObjectLifecycleServiceProtocol private let eventsListener: EventsListenerProtocol private let relationBuilder: RelationsBuilder @@ -30,19 +36,13 @@ final class BaseDocument: BaseDocumentProtocol { private var participantIsEditor: Bool = false private var subscriptions = [AnyCancellable]() - var syncPublisher: AnyPublisher { - return syncDocPublisher - .map { _ in Void() } - .eraseToAnyPublisher() - } - + // Sync Handle var syncDocPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { return syncSubject .merge(with: Just(isOpened ? [.general] : [])) .filter { $0.isNotEmpty } .eraseToAnyPublisher() } - private var syncSubject = PassthroughSubject<[BaseDocumentUpdate], Never>() // MARK: - State @@ -84,14 +84,6 @@ final class BaseDocument: BaseDocumentProtocol { .receiveOnMain() .eraseToAnyPublisher() } - - var isLocked: Bool { - return infoContainer.get(id: objectId)?.isLocked ?? false - } - - var details: ObjectDetails? { - detailsStorage.get(id: objectId) - } init( objectId: String, diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift index fe2ebf9b0a..fa25fe8f9b 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift @@ -60,4 +60,10 @@ extension BaseDocumentProtocol { var detailsPublisher: AnyPublisher { subscribeForDetails(objectId: objectId) } + + var syncPublisher: AnyPublisher { + return syncDocPublisher + .map { _ in Void() } + .eraseToAnyPublisher() + } } From 87fa249e5de3e2a32aa7b1a7db01fe9b11675960 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 6 Jun 2024 11:23:02 +0300 Subject: [PATCH 09/17] IOS-2935 Change relation links --- .../Models/Documents/Document/BaseDocumentUpdate.swift | 2 +- .../Events/EventConverters/MiddlewareEventConverter.swift | 4 ++-- .../Models/Documents/Events/Model/DocumentUpdate.swift | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift index 11589e0ce4..47af4a2c2e 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift @@ -15,7 +15,7 @@ enum BaseDocumentUpdate: Hashable { extension DocumentUpdate { var toBaseDocumentUpdate: [BaseDocumentUpdate] { switch self { - case .general: + case .general, .relationLinks: return [.general] case .syncStatus: return [.syncStatus] diff --git a/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift b/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift index 89a42146f5..b76c31da76 100644 --- a/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift +++ b/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift @@ -99,11 +99,11 @@ final class MiddlewareEventConverter { case .objectRelationsAmend(let data): relationLinksStorage.ammend(data: data) - return .general + return .relationLinks case .objectRelationsRemove(let data): relationLinksStorage.remove(data: data) - return .general + return .relationLinks case let .blockSetFile(data): infoContainer.setFile(data: data) diff --git a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift index 6932831852..c5f431bd33 100644 --- a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift +++ b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift @@ -8,6 +8,7 @@ enum DocumentUpdate: Hashable { case children(blockId: String) case details(id: String) case unhandled(blockId: String) + case relationLinks } extension DocumentUpdate { From 013ec9217bfbfd0b8668f9ff43481c083cf42bd5 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 6 Jun 2024 13:47:40 +0300 Subject: [PATCH 10/17] IOS-2935 Improve parsed relations --- Anytype.xcodeproj/project.pbxproj | 4 + .../Documents/Document/BaseDocument.swift | 89 ++++++++++++++----- .../Document/BaseDocumentProtocol+Sync.swift | 8 ++ .../Document/BaseDocumentProtocol.swift | 1 - .../Document/BaseDocumentUpdate.swift | 20 +---- .../Document/Relation+DependedObject.swift | 34 +++++++ .../Events/Model/DocumentUpdate.swift | 1 + .../RelationDetailsStorageProtocol.swift | 6 ++ 8 files changed, 119 insertions(+), 44 deletions(-) create mode 100644 Anytype/Sources/Models/Documents/Document/Relation+DependedObject.swift diff --git a/Anytype.xcodeproj/project.pbxproj b/Anytype.xcodeproj/project.pbxproj index d24d154261..c372530e81 100644 --- a/Anytype.xcodeproj/project.pbxproj +++ b/Anytype.xcodeproj/project.pbxproj @@ -321,6 +321,7 @@ 2A0F665D2BFC81C000F34BBD /* View+TwoWayBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A0F665C2BFC81C000F34BBD /* View+TwoWayBinding.swift */; }; 2A0F665F2BFC856000F34BBD /* HomeWidgetsDI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A0F665E2BFC856000F34BBD /* HomeWidgetsDI.swift */; }; 2A0F6C402BF219BA0041176E /* SpacePermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A0F6C3F2BF219BA0041176E /* SpacePermissions.swift */; }; + 2A12F0A22C11B0DD00E866BA /* Relation+DependedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A12F0A12C11B0DD00E866BA /* Relation+DependedObject.swift */; }; 2A150BE028AA8E9700F2E569 /* TextRelationURLActionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A150BDF28AA8E9700F2E569 /* TextRelationURLActionViewModel.swift */; }; 2A1731BA2B9A0A8200FD990D /* AudioSessionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1731B92B9A0A6400FD990D /* AudioSessionService.swift */; }; 2A176438283E2211002853D3 /* AnytypePopupProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A176437283E2211002853D3 /* AnytypePopupProxy.swift */; }; @@ -2085,6 +2086,7 @@ 2A0F665C2BFC81C000F34BBD /* View+TwoWayBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+TwoWayBinding.swift"; sourceTree = ""; }; 2A0F665E2BFC856000F34BBD /* HomeWidgetsDI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeWidgetsDI.swift; sourceTree = ""; }; 2A0F6C3F2BF219BA0041176E /* SpacePermissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacePermissions.swift; sourceTree = ""; }; + 2A12F0A12C11B0DD00E866BA /* Relation+DependedObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Relation+DependedObject.swift"; sourceTree = ""; }; 2A1331462B03681D00E8F3A5 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 2A1331472B03682100E8F3A5 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = ""; }; 2A150BDF28AA8E9700F2E569 /* TextRelationURLActionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRelationURLActionViewModel.swift; sourceTree = ""; }; @@ -4027,6 +4029,7 @@ 2AEAEE4F2C07773A0060FE4C /* DocumentStatusStorage.swift */, 2AEAEE532C077C9D0060FE4C /* BaseDocumentProtocol+Sync.swift */, 2AEAEE572C08AB7E0060FE4C /* BaseDocumentUpdate.swift */, + 2A12F0A12C11B0DD00E866BA /* Relation+DependedObject.swift */, ); path = Document; sourceTree = ""; @@ -11325,6 +11328,7 @@ 3D86244B2B738D6400A5FCF4 /* TypesService.swift in Sources */, 2AB5083B2AE9395600E1C986 /* SpaceCreateModuleOutput.swift in Sources */, 2A62B71D2BA8431F001EAD28 /* RequestToLeaveNotificationView.swift in Sources */, + 2A12F0A22C11B0DD00E866BA /* Relation+DependedObject.swift in Sources */, 1215E8B126A860D20089AF7E /* AppConfigurator.swift in Sources */, 2E975EDE287C2325004DE574 /* SetFilterRowConfiguration.swift in Sources */, C91FAF5D2812865B0055BC4A /* UndoRedoViewModel.swift in Sources */, diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index 5018ef926a..d63c629643 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -44,28 +44,11 @@ final class BaseDocument: BaseDocumentProtocol { .eraseToAnyPublisher() } private var syncSubject = PassthroughSubject<[BaseDocumentUpdate], Never>() + private var parsedRelationDependedDetailsEvents = [DocumentUpdate]() // MARK: - State - private var parsedRelationsSubject = CurrentValueSubject(.empty) - var parsedRelationsPublisher: AnyPublisher { - parsedRelationsSubject.eraseToAnyPublisher() - } - // All places, where parsedRelations used, should be subscribe on parsedRelationsPublisher. - var parsedRelations: ParsedRelations { - let objectRelationsDetails = relationDetailsStorage.relationsDetails( - for: relationLinksStorage.relationLinks, - spaceId: spaceId - ) - let recommendedRelations = relationDetailsStorage.relationsDetails(for: details?.objectType.recommendedRelations ?? [], spaceId: spaceId) - let typeRelationsDetails = recommendedRelations.filter { !objectRelationsDetails.contains($0) } - return relationBuilder.parsedRelations( - relationsDetails: objectRelationsDetails, - typeRelationsDetails: typeRelationsDetails, - objectId: objectId, - relationValuesIsLocked: !permissions.canEditRelationValues, - storage: detailsStorage - ) - } + @Atomic + var parsedRelations: ParsedRelations = ParsedRelations(featuredRelations: [], deletedRelations: [], typeRelations: [], otherRelations: []) var permissions: ObjectPermissions { ObjectPermissions( @@ -199,12 +182,43 @@ final class BaseDocument: BaseDocumentProtocol { private func triggerSync(updates: [DocumentUpdate]) { + var docUpdates = updates.flatMap { update -> [BaseDocumentUpdate] in + switch update { + case .general: + return [.general] + case .syncStatus: + return [.syncStatus] + case .block(let blockId): + return [.block(blockId: blockId)] + case .children(let blockId): + return [.block(blockId: blockId), .children] + case .details(let id): + return [.details(id: id)] + case .unhandled(let blockId): + return [.unhandled(blockId: blockId)] + case .relationLinks, .restrictions: + return [] // A lot of casese for update relations + } + } + if updates.contains(where: { $0 == .general || $0.isChildren }) { reorderChilder() } - parsedRelationsSubject.send(parsedRelations) - syncSubject.send(updates.flatMap(\.toBaseDocumentUpdate)) + var updatesForRelations: [DocumentUpdate] = [.general, .relationLinks, .restrictions, .details(id: objectId)] + updatesForRelations.append(contentsOf: parsedRelationDependedDetailsEvents) + + if updates.contains(where: { updatesForRelations.contains($0) }) { + let newRelations = convertRelations() + let dependedObjectIds = newRelations.all.flatMap(\.dependedObjects) + parsedRelationDependedDetailsEvents = dependedObjectIds.map { .details(id: $0) } + if parsedRelations != newRelations { + parsedRelations = newRelations + docUpdates.append(.relations) + } + } + + syncSubject.send(docUpdates) } func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> { @@ -230,7 +244,34 @@ final class BaseDocument: BaseDocumentProtocol { private func setupSubscriptions() async { await accountParticipantsStorage.canEditPublisher(spaceId: spaceId).sink { [weak self] canEdit in self?.participantIsEditor = canEdit - self?.triggerSync(updates: [.general]) - }.store(in: &subscriptions) + self?.triggerSync(updates: [.restrictions]) + } + .store(in: &subscriptions) + + relationDetailsStorage.relationsDetailsPublisher(spaceId: spaceId) + .sink { [weak self] details in + guard let self else { return } + let contains = details.contains { self.relationLinksStorage.contains(relationKeys: [$0.key]) } + if contains { + triggerSync(updates: [.relationLinks]) + } + } + .store(in: &subscriptions) + } + + private func convertRelations() -> ParsedRelations { + let objectRelationsDetails = relationDetailsStorage.relationsDetails( + for: relationLinksStorage.relationLinks, + spaceId: spaceId + ) + let recommendedRelations = relationDetailsStorage.relationsDetails(for: details?.objectType.recommendedRelations ?? [], spaceId: spaceId) + let typeRelationsDetails = recommendedRelations.filter { !objectRelationsDetails.contains($0) } + return relationBuilder.parsedRelations( + relationsDetails: objectRelationsDetails, + typeRelationsDetails: typeRelationsDetails, + objectId: objectId, + relationValuesIsLocked: !permissions.canEditRelationValues, + storage: detailsStorage + ) } } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift index fa25fe8f9b..19f1d9855e 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift @@ -66,4 +66,12 @@ extension BaseDocumentProtocol { .map { _ in Void() } .eraseToAnyPublisher() } + + var parsedRelationsPublisher: AnyPublisher { + subscibeFor(update: [.relations]) + .compactMap { [weak self] _ in + self?.parsedRelations + } + .eraseToAnyPublisher() + } } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index 443d6bb578..e82324e292 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -30,7 +30,6 @@ protocol BaseDocumentProtocol: AnyObject, BaseDocumentGeneralProtocol { var isEmpty: Bool { get } var isOpened: Bool { get } - var parsedRelationsPublisher: AnyPublisher { get } var permissionsPublisher: AnyPublisher { get } func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift index 47af4a2c2e..7107c03489 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift @@ -10,23 +10,5 @@ enum BaseDocumentUpdate: Hashable { case details(id: String) case unhandled(blockId: String) // Local State -} - -extension DocumentUpdate { - var toBaseDocumentUpdate: [BaseDocumentUpdate] { - switch self { - case .general, .relationLinks: - return [.general] - case .syncStatus: - return [.syncStatus] - case .block(let blockId): - return [.block(blockId: blockId)] - case .children(let blockId): - return [.block(blockId: blockId), .children] - case .details(let id): - return [.details(id: id)] - case .unhandled(let blockId): - return [.unhandled(blockId: blockId)] - } - } + case relations } diff --git a/Anytype/Sources/Models/Documents/Document/Relation+DependedObject.swift b/Anytype/Sources/Models/Documents/Document/Relation+DependedObject.swift new file mode 100644 index 0000000000..05ba79dd96 --- /dev/null +++ b/Anytype/Sources/Models/Documents/Document/Relation+DependedObject.swift @@ -0,0 +1,34 @@ +import Foundation + +extension Relation { + + // For calculated changes in base document + var dependedObjects: [String] { + switch self { + case .text(let text): + return [] + case .number(let text): + return [] + case .status(let status): + return status.values.map(\.id) + case .date(let date): + return [] + case .object(let object): + return object.selectedObjects.map(\.id) + case .checkbox(let checkbox): + return [] + case .url(let text): + return [] + case .email(let text): + return [] + case .phone(let text): + return [] + case .tag(let tag): + return tag.selectedTags.map(\.id) + case .file(let file): + return file.files.map(\.id) + case .unknown(let unknown): + return [] + } + } +} diff --git a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift index c5f431bd33..6978cd9cef 100644 --- a/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift +++ b/Anytype/Sources/Models/Documents/Events/Model/DocumentUpdate.swift @@ -9,6 +9,7 @@ enum DocumentUpdate: Hashable { case details(id: String) case unhandled(blockId: String) case relationLinks + case restrictions } extension DocumentUpdate { diff --git a/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorageProtocol.swift b/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorageProtocol.swift index 2067dd371b..8ca893962e 100644 --- a/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorageProtocol.swift +++ b/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorageProtocol.swift @@ -20,4 +20,10 @@ extension RelationDetailsStorageProtocol { func relationsDetails(for links: [RelationLink], spaceId: String, includeDeleted: Bool = false) -> [RelationDetails] { return relationsDetails(for: links, spaceId: spaceId).filter { !$0.isDeleted } } + + func relationsDetailsPublisher(spaceId: String) -> AnyPublisher<[RelationDetails], Never> { + relationsDetailsPublisher + .map { $0.filter { $0.spaceId == spaceId } } + .eraseToAnyPublisher() + } } From f62d7d7fd8962296638a36b69134906ae8e6099e Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 6 Jun 2024 13:55:49 +0300 Subject: [PATCH 11/17] IOS-2935 Change init --- .../Documents/Document/BaseDocument.swift | 39 ++++++++----------- .../DocumentsProvider/DocumentsProvider.swift | 27 ++++++++++++- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index d63c629643..c1b48f5fed 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -19,10 +19,10 @@ final class BaseDocument: BaseDocumentProtocol { private(set) var children = [BlockInformation]() private(set) var isOpened = false - let infoContainer: InfoContainerProtocol = InfoContainer() - let relationLinksStorage: RelationLinksStorageProtocol = RelationLinksStorage() - let restrictionsContainer: ObjectRestrictionsContainer = ObjectRestrictionsContainer() - let detailsStorage = ObjectDetailsStorage() + let infoContainer: InfoContainerProtocol + let relationLinksStorage: RelationLinksStorageProtocol + let restrictionsContainer: ObjectRestrictionsContainer + let detailsStorage: ObjectDetailsStorage private let objectLifecycleService: ObjectLifecycleServiceProtocol private let eventsListener: EventsListenerProtocol @@ -75,33 +75,28 @@ final class BaseDocument: BaseDocumentProtocol { relationDetailsStorage: RelationDetailsStorageProtocol, objectTypeProvider: ObjectTypeProviderProtocol, accountParticipantsStorage: AccountParticipantsStorageProtocol, - statusStorage: DocumentStatusStorageProtocol + statusStorage: DocumentStatusStorageProtocol, + eventsListener: EventsListenerProtocol, + viewModelSetter: DocumentViewModelSetterProtocol, + infoContainer: InfoContainerProtocol, + relationLinksStorage: RelationLinksStorageProtocol, + restrictionsContainer: ObjectRestrictionsContainer, + detailsStorage: ObjectDetailsStorage ) { self.objectId = objectId self.forPreview = forPreview - - self.eventsListener = EventsListener( - objectId: objectId, - infoContainer: infoContainer, - relationLinksStorage: relationLinksStorage, - restrictionsContainer: restrictionsContainer, - detailsStorage: detailsStorage, - statusStorage: statusStorage - ) - - self.viewModelSetter = DocumentViewModelSetter( - detailsStorage: detailsStorage, - relationLinksStorage: relationLinksStorage, - restrictionsContainer: restrictionsContainer, - infoContainer: infoContainer - ) - + self.eventsListener = eventsListener + self.viewModelSetter = viewModelSetter self.objectLifecycleService = objectLifecycleService self.relationBuilder = RelationsBuilder() self.relationDetailsStorage = relationDetailsStorage self.objectTypeProvider = objectTypeProvider self.accountParticipantsStorage = accountParticipantsStorage self.statusStorage = statusStorage + self.infoContainer = infoContainer + self.relationLinksStorage = relationLinksStorage + self.restrictionsContainer = restrictionsContainer + self.detailsStorage = detailsStorage setup() } diff --git a/Anytype/Sources/ServiceLayer/DocumentsProvider/DocumentsProvider.swift b/Anytype/Sources/ServiceLayer/DocumentsProvider/DocumentsProvider.swift index 327e00fc95..7e7f07f231 100644 --- a/Anytype/Sources/ServiceLayer/DocumentsProvider/DocumentsProvider.swift +++ b/Anytype/Sources/ServiceLayer/DocumentsProvider/DocumentsProvider.swift @@ -62,6 +62,25 @@ final class DocumentsProvider: DocumentsProviderProtocol { } private func createBaseDocument(objectId: String, forPreview: Bool) -> BaseDocumentProtocol { + let statusStorage = DocumentStatusStorage() + let infoContainer = InfoContainer() + let relationLinksStorage = RelationLinksStorage() + let restrictionsContainer = ObjectRestrictionsContainer() + let detailsStorage = ObjectDetailsStorage() + let viewModelSetter = DocumentViewModelSetter( + detailsStorage: detailsStorage, + relationLinksStorage: relationLinksStorage, + restrictionsContainer: restrictionsContainer, + infoContainer: infoContainer + ) + let eventsListener = EventsListener( + objectId: objectId, + infoContainer: infoContainer, + relationLinksStorage: relationLinksStorage, + restrictionsContainer: restrictionsContainer, + detailsStorage: detailsStorage, + statusStorage: statusStorage + ) return BaseDocument( objectId: objectId, forPreview: forPreview, @@ -69,7 +88,13 @@ final class DocumentsProvider: DocumentsProviderProtocol { relationDetailsStorage: relationDetailsStorage, objectTypeProvider: objectTypeProvider, accountParticipantsStorage: accountParticipantsStorage, - statusStorage: DocumentStatusStorage() + statusStorage: statusStorage, + eventsListener: eventsListener, + viewModelSetter: viewModelSetter, + infoContainer: infoContainer, + relationLinksStorage: relationLinksStorage, + restrictionsContainer: restrictionsContainer, + detailsStorage: detailsStorage ) } } From 6d3a10db7931689ed1e80de246222f19686b4c9a Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 6 Jun 2024 16:32:01 +0300 Subject: [PATCH 12/17] IOS-2935 Delete BaseDocumentGeneralProtocol --- .../Document/BaseDocumentProtocol.swift | 34 ++++++++----------- .../EditorPageCoordinatorViewModel.swift | 4 +-- .../EditorSetCoordinatorViewModel.swift | 4 +-- .../ObjectSettingsCoordinatorViewModel.swift | 4 +-- .../Page/EditorPageModuleOutput.swift | 2 +- .../Assembly/Set/EditorSetModuleOutput.swift | 4 +-- .../Entities/ObjectHeaderViewModel.swift | 10 +++--- .../Cover/ObjectCoverPickerViewModel.swift | 4 +-- .../Object/ObjectIconPickerViewModel.swift | 6 ++-- .../Routing/Editor/EditorRouter.swift | 2 +- .../TextEditor/Set/EditorSetViewModel.swift | 4 +-- .../Set/Models/SetDocumentProtocol.swift | 5 ++- .../OpenedDocumentsProvider.swift | 30 +++++++--------- 13 files changed, 53 insertions(+), 60 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index e82324e292..2b4a1883d1 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -2,16 +2,27 @@ import Services import Combine import AnytypeCore -protocol BaseDocumentGeneralProtocol: AnyObject { +protocol BaseDocumentProtocol: AnyObject { + var infoContainer: InfoContainerProtocol { get } + var detailsStorage: ObjectDetailsStorage { get } + var children: [BlockInformation] { get } + var parsedRelations: ParsedRelations { get } var syncStatus: SyncStatus { get } - var objectId: String { get } var spaceId: String { get } + var isLocked: Bool { get } + var isEmpty: Bool { get } + var isOpened: Bool { get } + var forPreview: Bool { get } + var details: ObjectDetails? { get } var detailsPublisher: AnyPublisher { get } - var syncPublisher: AnyPublisher { get } - var forPreview: Bool { get } + var permissions: ObjectPermissions { get } + var permissionsPublisher: AnyPublisher { get } + + func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> + var syncDocPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { get } @MainActor func open() async throws @@ -20,18 +31,3 @@ protocol BaseDocumentGeneralProtocol: AnyObject { @MainActor func close() async throws } - -protocol BaseDocumentProtocol: AnyObject, BaseDocumentGeneralProtocol { - var infoContainer: InfoContainerProtocol { get } - var detailsStorage: ObjectDetailsStorage { get } - var children: [BlockInformation] { get } - var parsedRelations: ParsedRelations { get } - var isLocked: Bool { get } - var isEmpty: Bool { get } - var isOpened: Bool { get } - - var permissionsPublisher: AnyPublisher { get } - - func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> - var syncDocPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { get } -} diff --git a/Anytype/Sources/PresentationLayer/Flows/EditorPageFlow/EditorPageCoordinatorViewModel.swift b/Anytype/Sources/PresentationLayer/Flows/EditorPageFlow/EditorPageCoordinatorViewModel.swift index a2081028e4..db7cd0a093 100644 --- a/Anytype/Sources/PresentationLayer/Flows/EditorPageFlow/EditorPageCoordinatorViewModel.swift +++ b/Anytype/Sources/PresentationLayer/Flows/EditorPageFlow/EditorPageCoordinatorViewModel.swift @@ -62,7 +62,7 @@ final class EditorPageCoordinatorViewModel: ObservableObject, EditorPageModuleOu handleRelationValue(relation: relation, objectDetails: objectDetails) } - func showCoverPicker(document: BaseDocumentGeneralProtocol) { + func showCoverPicker(document: BaseDocumentProtocol) { covertPickerData = ObjectCoverPickerData(document: document) } @@ -74,7 +74,7 @@ final class EditorPageCoordinatorViewModel: ObservableObject, EditorPageModuleOu linkToObjectData = data } - func showIconPicker(document: BaseDocumentGeneralProtocol) { + func showIconPicker(document: BaseDocumentProtocol) { objectIconPickerData = ObjectIconPickerData(document: document) } diff --git a/Anytype/Sources/PresentationLayer/Flows/EditorSetFlow/EditorSetCoordinatorViewModel.swift b/Anytype/Sources/PresentationLayer/Flows/EditorSetFlow/EditorSetCoordinatorViewModel.swift index a04065c5dd..a87d65d39b 100644 --- a/Anytype/Sources/PresentationLayer/Flows/EditorSetFlow/EditorSetCoordinatorViewModel.swift +++ b/Anytype/Sources/PresentationLayer/Flows/EditorSetFlow/EditorSetCoordinatorViewModel.swift @@ -135,11 +135,11 @@ final class EditorSetCoordinatorViewModel: navigationContext.present(popup) } - func showCoverPicker(document: BaseDocumentGeneralProtocol) { + func showCoverPicker(document: BaseDocumentProtocol) { covertPickerData = ObjectCoverPickerData(document: document) } - func showIconPicker(document: BaseDocumentGeneralProtocol) { + func showIconPicker(document: BaseDocumentProtocol) { objectIconPickerData = ObjectIconPickerData(document: document) } diff --git a/Anytype/Sources/PresentationLayer/Flows/ObjectSettingsFlow/ObjectSettingsCoordinatorViewModel.swift b/Anytype/Sources/PresentationLayer/Flows/ObjectSettingsFlow/ObjectSettingsCoordinatorViewModel.swift index f87284eba4..07877a7957 100644 --- a/Anytype/Sources/PresentationLayer/Flows/ObjectSettingsFlow/ObjectSettingsCoordinatorViewModel.swift +++ b/Anytype/Sources/PresentationLayer/Flows/ObjectSettingsFlow/ObjectSettingsCoordinatorViewModel.swift @@ -42,11 +42,11 @@ final class ObjectSettingsCoordinatorViewModel: ObservableObject, layoutPickerObjectId = document.objectId.identifiable } - func showCoverPicker(document: BaseDocumentGeneralProtocol) { + func showCoverPicker(document: BaseDocumentProtocol) { coverPickerData = ObjectCoverPickerData(document: document) } - func showIconPicker(document: BaseDocumentGeneralProtocol) { + func showIconPicker(document: BaseDocumentProtocol) { objectIconPickerData = ObjectIconPickerData(document: document) } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Assembly/Page/EditorPageModuleOutput.swift b/Anytype/Sources/PresentationLayer/TextEditor/Assembly/Page/EditorPageModuleOutput.swift index f619663022..d307de8ba4 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Assembly/Page/EditorPageModuleOutput.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Assembly/Page/EditorPageModuleOutput.swift @@ -9,7 +9,7 @@ protocol EditorPageModuleOutput: AnyObject, ObjectHeaderModuleOutput { func onSelectCodeLanguage(objectId: String, blockId: String) func showRelationValueEditingView(document: BaseDocumentProtocol, relation: Relation) func showLinkToObject(data: LinkToObjectSearchModuleData) - func showIconPicker(document: BaseDocumentGeneralProtocol) + func showIconPicker(document: BaseDocumentProtocol) func showTextIconPicker(data: TextIconPickerData) func showBlockObjectSearch(data: BlockObjectSearchData) func didUndoRedo() diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Assembly/Set/EditorSetModuleOutput.swift b/Anytype/Sources/PresentationLayer/TextEditor/Assembly/Set/EditorSetModuleOutput.swift index 608d5727a2..c85c0a80dc 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Assembly/Set/EditorSetModuleOutput.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Assembly/Set/EditorSetModuleOutput.swift @@ -19,8 +19,8 @@ protocol EditorSetModuleOutput: AnyObject, ObjectHeaderModuleOutput { onSelect: @escaping (Bool, BlockBackgroundColor?) -> Void ) func showSettings() - func showCoverPicker(document: BaseDocumentGeneralProtocol) - func showIconPicker(document: BaseDocumentGeneralProtocol) + func showCoverPicker(document: BaseDocumentProtocol) + func showIconPicker(document: BaseDocumentProtocol) func showRelationValueEditingView(objectDetails: ObjectDetails, relation: Relation) func showSetObjectCreationSettings( document: SetDocumentProtocol, diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Header/Entities/ObjectHeaderViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Header/Entities/ObjectHeaderViewModel.swift index eb1f21482e..6313fcbbbb 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Header/Entities/ObjectHeaderViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Header/Entities/ObjectHeaderViewModel.swift @@ -5,12 +5,12 @@ import AnytypeCore @MainActor protocol ObjectHeaderRouterProtocol: AnyObject { - func showIconPicker(document: BaseDocumentGeneralProtocol) + func showIconPicker(document: BaseDocumentProtocol) } @MainActor protocol ObjectHeaderModuleOutput: AnyObject { - func showCoverPicker(document: BaseDocumentGeneralProtocol) + func showCoverPicker(document: BaseDocumentProtocol) } @MainActor @@ -38,19 +38,19 @@ final class ObjectHeaderViewModel: ObservableObject { output?.showCoverPicker(document: document) } - private let document: BaseDocumentGeneralProtocol + private let document: BaseDocumentProtocol private let targetObjectId: String private var subscription: AnyCancellable? private var uploadingStatusSubscription: AnyCancellable? private let configuration: EditorPageViewModelConfiguration private weak var output: ObjectHeaderModuleOutput? - var onIconPickerTap: RoutingAction? + var onIconPickerTap: RoutingAction? // MARK: - Initializers init( - document: BaseDocumentGeneralProtocol, + document: BaseDocumentProtocol, targetObjectId: String, configuration: EditorPageViewModelConfiguration, output: ObjectHeaderModuleOutput? diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Cover/ObjectCoverPickerViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Cover/ObjectCoverPickerViewModel.swift index 74f5e5f02c..f158ac2bda 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Cover/ObjectCoverPickerViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Cover/ObjectCoverPickerViewModel.swift @@ -16,7 +16,7 @@ enum ObjectCoverPickerAction { } struct ObjectCoverPickerData: Identifiable { - let document: BaseDocumentGeneralProtocol + let document: BaseDocumentProtocol var id: String { document.objectId } } @@ -30,7 +30,7 @@ final class ObjectCoverPickerViewModel: ObservableObject { @Injected(\.objectHeaderUploadingService) private var objectHeaderUploadingService: ObjectHeaderUploadingServiceProtocol - private let document: BaseDocumentGeneralProtocol + private let document: BaseDocumentProtocol // MARK: - Initializer diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Icon/Object/ObjectIconPickerViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Icon/Object/ObjectIconPickerViewModel.swift index 63d25d199d..9125f79114 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Icon/Object/ObjectIconPickerViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Icon/Object/ObjectIconPickerViewModel.swift @@ -4,7 +4,7 @@ import Services import AnytypeCore struct ObjectIconPickerData: Identifiable { - let document: BaseDocumentGeneralProtocol + let document: BaseDocumentProtocol var id: String { document.objectId } } @@ -31,7 +31,7 @@ final class ObjectIconPickerViewModel: ObservableObject { // MARK: - Private variables - private let document: BaseDocumentGeneralProtocol + private let document: BaseDocumentProtocol private var subscription: AnyCancellable? // MARK: - Initializer @@ -82,7 +82,7 @@ final class ObjectIconPickerViewModel: ObservableObject { } } - private func handleIconAction(document: BaseDocumentGeneralProtocol, action: ObjectIconPickerAction) { + private func handleIconAction(document: BaseDocumentProtocol, action: ObjectIconPickerAction) { Task { try await objectHeaderUploadingService.handleIconAction( objectId: document.objectId, diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Routing/Editor/EditorRouter.swift b/Anytype/Sources/PresentationLayer/TextEditor/Routing/Editor/EditorRouter.swift index dfe754e1cc..e95e15c514 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Routing/Editor/EditorRouter.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Routing/Editor/EditorRouter.swift @@ -288,7 +288,7 @@ final class EditorRouter: NSObject, EditorRouterProtocol, ObjectSettingsCoordina navigationContext.present(popup) } - func showIconPicker(document: BaseDocumentGeneralProtocol) { + func showIconPicker(document: BaseDocumentProtocol) { output?.showIconPicker(document: document) } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/EditorSetViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/EditorSetViewModel.swift index 5751112dc0..929a0be5ad 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/EditorSetViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/EditorSetViewModel.swift @@ -173,7 +173,7 @@ final class EditorSetViewModel: ObservableObject { inlineParameters: data.inline ) self.headerModel = ObjectHeaderViewModel( - document: setDocument, + document: setDocument.document, targetObjectId: setDocument.targetObjectId, configuration: EditorPageViewModelConfiguration( isOpenedForPreview: false, @@ -670,7 +670,7 @@ extension EditorSetViewModel { } func showIconPicker() { - output?.showIconPicker(document: setDocument) + output?.showIconPicker(document: setDocument.document) } func showSetOfTypeSelection() { diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocumentProtocol.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocumentProtocol.swift index 834fdaaf1c..d36b8f4db2 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocumentProtocol.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocumentProtocol.swift @@ -6,15 +6,17 @@ enum SetDocumentUpdate { case syncStatus(DocumentSyncStatusData) } -protocol SetDocumentProtocol: BaseDocumentGeneralProtocol { +protocol SetDocumentProtocol: AnyObject { var document: BaseDocumentProtocol { get } var objectId: String { get } + var spaceId: String { get } var blockId: String { get } var targetObjectId: String { get } var inlineParameters: EditorInlineSetObject? { get } var blockDataview: BlockDataview? { get } var dataViewRelationsDetails: [RelationDetails] { get } var analyticsType: AnalyticsObjectType { get } + var details: ObjectDetails? { get } // TODO Refactor this var dataBuilder: SetContentViewDataBuilder { get } @@ -23,6 +25,7 @@ protocol SetDocumentProtocol: BaseDocumentGeneralProtocol { var setPermissions: SetPermissions { get } var setUpdatePublisher: AnyPublisher { get } + var detailsPublisher: AnyPublisher { get } var dataView: BlockDataview { get } var dataviewPublisher: AnyPublisher { get } diff --git a/Anytype/Sources/ServiceLayer/Subscriptions/OpenedDocumentsProvider.swift b/Anytype/Sources/ServiceLayer/Subscriptions/OpenedDocumentsProvider.swift index a39c501a8e..6258a843bf 100644 --- a/Anytype/Sources/ServiceLayer/Subscriptions/OpenedDocumentsProvider.swift +++ b/Anytype/Sources/ServiceLayer/Subscriptions/OpenedDocumentsProvider.swift @@ -24,8 +24,12 @@ final class OpenedDocumentsProvider: OpenedDocumentsProviderProtocol { func document(objectId: String, forPreview: Bool) -> BaseDocumentProtocol { let document = documentsProvider.document(objectId: objectId, forPreview: forPreview) - defer { - openDocument(document: document) + Task { @MainActor in + if forPreview { + try await document.openForPreview() + } else { + try await document.open() + } } return document @@ -38,24 +42,14 @@ final class OpenedDocumentsProvider: OpenedDocumentsProviderProtocol { inlineParameters: nil ) - defer { - openDocument(document: document) - } - - return document - } - - // MARK: - Private func - private func openDocument(document: BaseDocumentGeneralProtocol) { - if document.forPreview { - Task { @MainActor in - try? await document.openForPreview() + Task { @MainActor in + if forPreview { + try await document.openForPreview() + } else { + try await document.open() } - return } - Task { @MainActor in - try? await document.open() - } + return document } } From 78fe464116884be20c233d192bb5876300d6bf1b Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 6 Jun 2024 16:40:54 +0300 Subject: [PATCH 13/17] IOS-2935 Migrate to new sync publisher --- .../Documents/Document/BaseDocument.swift | 6 +++--- .../Document/BaseDocumentProtocol+Sync.swift | 8 +------- .../Document/BaseDocumentProtocol.swift | 2 +- .../TextAttributesView/MarkupViewModel.swift | 2 +- .../Icon/Object/ObjectIconPickerViewModel.swift | 17 ++++++++--------- .../Relations/RelationsListViewModel.swift | 6 +++--- .../FavoriteSubscriptionService.swift | 2 +- 7 files changed, 18 insertions(+), 25 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index c1b48f5fed..7b2b484100 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -37,7 +37,7 @@ final class BaseDocument: BaseDocumentProtocol { private var subscriptions = [AnyCancellable]() // Sync Handle - var syncDocPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { + var syncPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { return syncSubject .merge(with: Just(isOpened ? [.general] : [])) .filter { $0.isNotEmpty } @@ -60,8 +60,8 @@ final class BaseDocument: BaseDocumentProtocol { } var permissionsPublisher: AnyPublisher { - syncPublisher.compactMap { - [weak self] in self?.permissions + syncPublisher.compactMap { [weak self] _ in + self?.permissions } .removeDuplicates() .receiveOnMain() diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift index 19f1d9855e..6448a9f64e 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift @@ -33,7 +33,7 @@ extension BaseDocumentProtocol { } var resetBlocksPublisher: AnyPublisher, Never> { - syncDocPublisher + syncPublisher .map { updates in let ids = updates.compactMap { update in switch update { @@ -61,12 +61,6 @@ extension BaseDocumentProtocol { subscribeForDetails(objectId: objectId) } - var syncPublisher: AnyPublisher { - return syncDocPublisher - .map { _ in Void() } - .eraseToAnyPublisher() - } - var parsedRelationsPublisher: AnyPublisher { subscibeFor(update: [.relations]) .compactMap { [weak self] _ in diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index 2b4a1883d1..b9ec214357 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -22,7 +22,7 @@ protocol BaseDocumentProtocol: AnyObject { var permissionsPublisher: AnyPublisher { get } func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> - var syncDocPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { get } + var syncPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { get } @MainActor func open() async throws diff --git a/Anytype/Sources/PresentationLayer/Common/UIKit/BottomSheets/TextAttributesView/MarkupViewModel.swift b/Anytype/Sources/PresentationLayer/Common/UIKit/BottomSheets/TextAttributesView/MarkupViewModel.swift index 0b7d8687e1..4fd680da42 100644 --- a/Anytype/Sources/PresentationLayer/Common/UIKit/BottomSheets/TextAttributesView/MarkupViewModel.swift +++ b/Anytype/Sources/PresentationLayer/Common/UIKit/BottomSheets/TextAttributesView/MarkupViewModel.swift @@ -97,7 +97,7 @@ final class MarkupViewModel: MarkupViewModelProtocol { } private func subscribeToPublishers() { - cancellable = document.syncPublisher.sink { [weak self] in + cancellable = document.syncPublisher.sink { [weak self] _ in self?.updateState() } } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Icon/Object/ObjectIconPickerViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Icon/Object/ObjectIconPickerViewModel.swift index 9125f79114..c1a033516b 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Icon/Object/ObjectIconPickerViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Icon/Object/ObjectIconPickerViewModel.swift @@ -38,10 +38,10 @@ final class ObjectIconPickerViewModel: ObservableObject { init(data: ObjectIconPickerData) { self.document = data.document - subscription = document.syncPublisher + subscription = document.detailsPublisher .receiveOnMain() - .sink { [weak self] in - self?.updateState() + .sink { [weak self] details in + self?.updateState(details: details) } } @@ -60,18 +60,17 @@ final class ObjectIconPickerViewModel: ObservableObject { // MARK: - Private - private func updateState() { - isRemoveButtonAvailable = document.details?.objectIcon != nil - detailsLayout = document.details?.layoutValue - isRemoveEnabled = makeIsRemoveEnabled() + private func updateState(details: ObjectDetails) { + isRemoveButtonAvailable = details.objectIcon != nil + detailsLayout = details.layoutValue + isRemoveEnabled = makeIsRemoveEnabled(details: details) } - private func makeIsRemoveEnabled() -> Bool { + private func makeIsRemoveEnabled(details: ObjectDetails) -> Bool { switch detailsLayout { case .basic, .set, .collection: return true case .profile, .participant, .space, .spaceView: - guard let details = document.details else { return false } return details.iconImage.isNotEmpty default: anytypeAssertionFailure( diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Relations/RelationsListViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Relations/RelationsListViewModel.swift index 6011cdd2f7..ec705b9fc1 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Relations/RelationsListViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/Relations/RelationsListViewModel.swift @@ -42,11 +42,11 @@ final class RelationsListViewModel: ObservableObject { .receiveOnMain() .assign(to: &$sections) - document.syncPublisher + document.permissionsPublisher .receiveOnMain() - .sink { [weak self] in + .sink { [weak self] permissions in guard let self else { return } - navigationBarButtonsDisabled = !document.permissions.canEditRelationsList + navigationBarButtonsDisabled = permissions.canEditRelationsList } .store(in: &subscriptions) } diff --git a/Anytype/Sources/ServiceLayer/Subscriptions/FavoriteSubscriptionService.swift b/Anytype/Sources/ServiceLayer/Subscriptions/FavoriteSubscriptionService.swift index 53938fda3b..841a11d39e 100644 --- a/Anytype/Sources/ServiceLayer/Subscriptions/FavoriteSubscriptionService.swift +++ b/Anytype/Sources/ServiceLayer/Subscriptions/FavoriteSubscriptionService.swift @@ -33,7 +33,7 @@ final class FavoriteSubscriptionService: FavoriteSubscriptionServiceProtocol { } homeDocument.syncPublisher - .map { [weak self, homeDocument] in self?.createChildren(document: homeDocument, objectLimit: objectLimit) ?? [] } + .map { [weak self, homeDocument] _ in self?.createChildren(document: homeDocument, objectLimit: objectLimit) ?? [] } .removeDuplicates() .receiveOnMain() .sink { result in From 123d415b5931255c90a592052f11610690b24bd6 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 6 Jun 2024 17:08:19 +0300 Subject: [PATCH 14/17] IOS-2935 Add permissions, fix some atomic opetaions --- .../Documents/Document/BaseDocument.swift | 128 ++++++++++-------- .../Document/BaseDocumentProtocol+Sync.swift | 8 ++ .../Document/BaseDocumentProtocol.swift | 4 - .../Document/BaseDocumentUpdate.swift | 1 + .../ObjectPermissions/ObjectPermissions.swift | 45 +++--- .../TypeProvider/ObjectTypeProvider.swift | 2 +- .../RelationDetailsStorage.swift | 2 +- 7 files changed, 105 insertions(+), 85 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift index 7b2b484100..3dd40b633d 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocument.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocument.swift @@ -5,19 +5,24 @@ import Foundation final class BaseDocument: BaseDocumentProtocol { - // Init state - let objectId: String - let forPreview: Bool + // MARK: - State from Containers - // From Containers var syncStatus: SyncStatus { statusStorage.status } var isLocked: Bool { infoContainer.get(id: objectId)?.isLocked ?? false } var details: ObjectDetails? { detailsStorage.get(id: objectId) } var objectRestrictions: ObjectRestrictions { restrictionsContainer.restrinctions } - // Custom state + // MARK: - Local state + let objectId: String + let forPreview: Bool + @Atomic private(set) var children = [BlockInformation]() + @Atomic private(set) var isOpened = false + @Atomic + private(set) var parsedRelations = ParsedRelations.empty + @Atomic + private(set) var permissions = ObjectPermissions() let infoContainer: InfoContainerProtocol let relationLinksStorage: RelationLinksStorageProtocol @@ -33,10 +38,14 @@ final class BaseDocument: BaseDocumentProtocol { private let statusStorage: DocumentStatusStorageProtocol private let viewModelSetter: DocumentViewModelSetterProtocol + // MARK: - Local private state + @Atomic private var participantIsEditor: Bool = false private var subscriptions = [AnyCancellable]() + @Atomic + private var parsedRelationDependedDetailsEvents = [DocumentUpdate]() - // Sync Handle + // MARK: - Sync Handle var syncPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { return syncSubject .merge(with: Just(isOpened ? [.general] : [])) @@ -44,29 +53,6 @@ final class BaseDocument: BaseDocumentProtocol { .eraseToAnyPublisher() } private var syncSubject = PassthroughSubject<[BaseDocumentUpdate], Never>() - private var parsedRelationDependedDetailsEvents = [DocumentUpdate]() - - // MARK: - State - @Atomic - var parsedRelations: ParsedRelations = ParsedRelations(featuredRelations: [], deletedRelations: [], typeRelations: [], otherRelations: []) - - var permissions: ObjectPermissions { - ObjectPermissions( - details: details ?? ObjectDetails(id: ""), - isLocked: isLocked, - participantCanEdit: participantIsEditor, - objectRestrictions: objectRestrictions.objectRestriction - ) - } - - var permissionsPublisher: AnyPublisher { - syncPublisher.compactMap { [weak self] _ in - self?.permissions - } - .removeDuplicates() - .receiveOnMain() - .eraseToAnyPublisher() - } init( objectId: String, @@ -155,7 +141,21 @@ final class BaseDocument: BaseDocumentProtocol { return allTextChilds.first?.content.isEmpty ?? false } + func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> { + return syncSubject + .merge(with: Just(isOpened ? update : [])) + .map { syncUpdate in + if syncUpdate.contains(.general) { + return update + } + return update.filter { syncUpdate.contains($0) } + } + .filter { $0.isNotEmpty } + .eraseToAnyPublisher() + } + // MARK: - Private methods + private func setup() { eventsListener.onUpdatesReceive = { [weak self] updates in DispatchQueue.main.async { [weak self] in @@ -196,39 +196,19 @@ final class BaseDocument: BaseDocumentProtocol { } } - if updates.contains(where: { $0 == .general || $0.isChildren }) { - reorderChilder() - } + let permissioUpdates = triggerUpdatePermissions(updates: updates) + docUpdates.append(contentsOf: permissioUpdates) - var updatesForRelations: [DocumentUpdate] = [.general, .relationLinks, .restrictions, .details(id: objectId)] - updatesForRelations.append(contentsOf: parsedRelationDependedDetailsEvents) + let relationUpdates = triggerUpdateRelations(updates: updates, permissionsChanged: permissioUpdates.isNotEmpty) + docUpdates.append(contentsOf: relationUpdates) - if updates.contains(where: { updatesForRelations.contains($0) }) { - let newRelations = convertRelations() - let dependedObjectIds = newRelations.all.flatMap(\.dependedObjects) - parsedRelationDependedDetailsEvents = dependedObjectIds.map { .details(id: $0) } - if parsedRelations != newRelations { - parsedRelations = newRelations - docUpdates.append(.relations) - } + if updates.contains(where: { $0 == .general || $0.isChildren }) { + reorderChilder() } syncSubject.send(docUpdates) } - func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> { - return syncSubject - .merge(with: Just(isOpened ? update : [])) - .map { syncUpdate in - if syncUpdate.contains(.general) { - return update - } - return update.filter { syncUpdate.contains($0) } - } - .filter { $0.isNotEmpty } - .eraseToAnyPublisher() - } - private func setupView(_ model: ObjectViewModel) async { viewModelSetter.objectViewUpdate(model) isOpened = true @@ -254,19 +234,53 @@ final class BaseDocument: BaseDocumentProtocol { .store(in: &subscriptions) } - private func convertRelations() -> ParsedRelations { + private func triggerUpdateRelations(updates: [DocumentUpdate], permissionsChanged: Bool) -> [BaseDocumentUpdate] { + + var updatesForRelations: [DocumentUpdate] = [.general, .relationLinks, .details(id: objectId)] + updatesForRelations.append(contentsOf: parsedRelationDependedDetailsEvents) + + guard updates.contains(where: { updatesForRelations.contains($0) }) || permissionsChanged else { return [] } + let objectRelationsDetails = relationDetailsStorage.relationsDetails( for: relationLinksStorage.relationLinks, spaceId: spaceId ) let recommendedRelations = relationDetailsStorage.relationsDetails(for: details?.objectType.recommendedRelations ?? [], spaceId: spaceId) let typeRelationsDetails = recommendedRelations.filter { !objectRelationsDetails.contains($0) } - return relationBuilder.parsedRelations( + let newRelations = relationBuilder.parsedRelations( relationsDetails: objectRelationsDetails, typeRelationsDetails: typeRelationsDetails, objectId: objectId, relationValuesIsLocked: !permissions.canEditRelationValues, storage: detailsStorage ) + + let dependedObjectIds = newRelations.all.flatMap(\.dependedObjects) + parsedRelationDependedDetailsEvents = dependedObjectIds.map { .details(id: $0) } + if parsedRelations != newRelations { + parsedRelations = newRelations + return [.relations] + } + + return [] + } + + private func triggerUpdatePermissions(updates: [DocumentUpdate]) -> [BaseDocumentUpdate] { + let updatesForPermissions: [DocumentUpdate] = [.general, .details(id: objectId), .block(blockId: objectId), .restrictions] + guard updates.contains(where: { updatesForPermissions.contains($0) }) else { return [] } + + let newPermissios = ObjectPermissions( + details: details ?? ObjectDetails(id: ""), + isLocked: isLocked, + participantCanEdit: participantIsEditor, + objectRestrictions: objectRestrictions.objectRestriction + ) + + if permissions != newPermissios { + permissions = newPermissios + return [.permissions] + } + + return [] } } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift index 6448a9f64e..953e93464d 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol+Sync.swift @@ -68,4 +68,12 @@ extension BaseDocumentProtocol { } .eraseToAnyPublisher() } + + var permissionsPublisher: AnyPublisher { + subscibeFor(update: [.permissions]) + .compactMap { [weak self] _ in + self?.permissions + } + .eraseToAnyPublisher() + } } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift index b9ec214357..2103295142 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentProtocol.swift @@ -14,12 +14,8 @@ protocol BaseDocumentProtocol: AnyObject { var isEmpty: Bool { get } var isOpened: Bool { get } var forPreview: Bool { get } - var details: ObjectDetails? { get } - var detailsPublisher: AnyPublisher { get } - var permissions: ObjectPermissions { get } - var permissionsPublisher: AnyPublisher { get } func subscibeFor(update: [BaseDocumentUpdate]) -> AnyPublisher<[BaseDocumentUpdate], Never> var syncPublisher: AnyPublisher<[BaseDocumentUpdate], Never> { get } diff --git a/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift b/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift index 7107c03489..18fc1fe3c6 100644 --- a/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift +++ b/Anytype/Sources/Models/Documents/Document/BaseDocumentUpdate.swift @@ -11,4 +11,5 @@ enum BaseDocumentUpdate: Hashable { case unhandled(blockId: String) // Local State case relations + case permissions } diff --git a/Anytype/Sources/Models/ObjectPermissions/ObjectPermissions.swift b/Anytype/Sources/Models/ObjectPermissions/ObjectPermissions.swift index aa5617fbf9..f374ed5658 100644 --- a/Anytype/Sources/Models/ObjectPermissions/ObjectPermissions.swift +++ b/Anytype/Sources/Models/ObjectPermissions/ObjectPermissions.swift @@ -2,28 +2,29 @@ import Foundation import Services struct ObjectPermissions: Equatable { - - let canChangeType: Bool - let canDelete: Bool - let canTemplateSetAsDefault: Bool - let canArchive: Bool - let canDuplicate: Bool - let canUndoRedo: Bool - let canMakeAsTemplate: Bool - let canCreateWidget: Bool - let canFavorite: Bool - let canLinkItself: Bool - let canLock: Bool - let canChangeIcon: Bool - let canChangeCover: Bool - let canChangeLayout: Bool - let canEditRelationValues: Bool - let canEditRelationsList: Bool - let canApplyTemplates: Bool - let canShare: Bool - let canEditBlocks: Bool - let editBlocks: EditBlocksPermission - + var canChangeType: Bool = false + var canDelete: Bool = false + var canTemplateSetAsDefault: Bool = false + var canArchive: Bool = false + var canDuplicate: Bool = false + var canUndoRedo: Bool = false + var canMakeAsTemplate: Bool = false + var canCreateWidget: Bool = false + var canFavorite: Bool = false + var canLinkItself: Bool = false + var canLock: Bool = false + var canChangeIcon: Bool = false + var canChangeCover: Bool = false + var canChangeLayout: Bool = false + var canEditRelationValues: Bool = false + var canEditRelationsList: Bool = false + var canApplyTemplates: Bool = false + var canShare: Bool = false + var canEditBlocks: Bool = false + var editBlocks: EditBlocksPermission = .readonly(.restrictions) +} + +extension ObjectPermissions { init( details: ObjectDetails, isLocked: Bool, diff --git a/Anytype/Sources/ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift b/Anytype/Sources/ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift index 2f78f99329..17627cae9f 100644 --- a/Anytype/Sources/ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift +++ b/Anytype/Sources/ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift @@ -25,7 +25,7 @@ final class ObjectTypeProvider: ObjectTypeProviderProtocol { private let subscriptionStorage: SubscriptionStorageProtocol private let subscriptionBuilder: ObjectTypeSubscriptionDataBuilderProtocol - private(set) var objectTypes = [ObjectType]() + private(set) var objectTypes = SynchronizedArray() private var searchTypesById = SynchronizedDictionary() @Published var sync: () = () diff --git a/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorage.swift b/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorage.swift index 5fbf366112..24c1362491 100644 --- a/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorage.swift +++ b/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorage.swift @@ -24,7 +24,7 @@ final class RelationDetailsStorage: RelationDetailsStorageProtocol { subscriptionStorageProvider.createSubscriptionStorage(subId: Self.subscriptionId) }() - private var details = [RelationDetails]() + private var details = SynchronizedArray() private var searchDetailsByKey = SynchronizedDictionary() private var relationsDetailsSubject = CurrentValueSubject<[RelationDetails], Never>([]) From 2741a8085e7c0812c28ec5070afce61d655efa12 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 6 Jun 2024 17:21:23 +0300 Subject: [PATCH 15/17] IOS-2935 Add receive on main --- .../BottomSheets/TextAttributesView/MarkupViewModel.swift | 2 +- .../SetObjectWidgetInternalViewModel.swift | 2 +- .../Dataview/SetObjectCreationSettingsInteractor.swift | 6 +++--- .../TableOfContents/TableOfContentsContentProvider.swift | 3 ++- .../Views/Header/Entities/ObjectHeaderViewModel.swift | 2 +- .../Settings/ObjectActions/ObjectActionsViewModel.swift | 2 +- .../Settings/ObjectSettings/ObjectSettingsViewModel.swift | 2 +- .../TextEditor/Set/Models/SetDocument.swift | 2 +- .../Set/Views/Header/SetHeaderSettingsViewModel.swift | 1 + .../Views/Popups/Filters/List/SetFiltersListViewModel.swift | 2 +- .../Popups/RelationsSettings/SetRelationsViewModel.swift | 2 +- .../SetLayoutSettingsView/SetLayoutSettingsViewModel.swift | 2 +- .../SetViewSettingsList/SetViewSettingsListModel.swift | 2 +- .../Set/Views/Popups/Sorts/List/SetSortsListViewModel.swift | 2 +- .../Views/Popups/ViewPicker/SetViewPickerViewModel.swift | 2 +- 15 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Anytype/Sources/PresentationLayer/Common/UIKit/BottomSheets/TextAttributesView/MarkupViewModel.swift b/Anytype/Sources/PresentationLayer/Common/UIKit/BottomSheets/TextAttributesView/MarkupViewModel.swift index 4fd680da42..5df1a98f30 100644 --- a/Anytype/Sources/PresentationLayer/Common/UIKit/BottomSheets/TextAttributesView/MarkupViewModel.swift +++ b/Anytype/Sources/PresentationLayer/Common/UIKit/BottomSheets/TextAttributesView/MarkupViewModel.swift @@ -97,7 +97,7 @@ final class MarkupViewModel: MarkupViewModelProtocol { } private func subscribeToPublishers() { - cancellable = document.syncPublisher.sink { [weak self] _ in + cancellable = document.syncPublisher.receiveOnMain().sink { [weak self] _ in self?.updateState() } } diff --git a/Anytype/Sources/PresentationLayer/Modules/HomeWidgets/Widgets/SpecificInternalModels/SetObjectWidgetInternalViewModel.swift b/Anytype/Sources/PresentationLayer/Modules/HomeWidgets/Widgets/SpecificInternalModels/SetObjectWidgetInternalViewModel.swift index 620af9d1fa..5994e8fffa 100644 --- a/Anytype/Sources/PresentationLayer/Modules/HomeWidgets/Widgets/SpecificInternalModels/SetObjectWidgetInternalViewModel.swift +++ b/Anytype/Sources/PresentationLayer/Modules/HomeWidgets/Widgets/SpecificInternalModels/SetObjectWidgetInternalViewModel.swift @@ -81,7 +81,7 @@ final class SetObjectWidgetInternalViewModel: ObservableObject, WidgetDataviewIn } func startContentSubscription() async { - setDocument?.syncPublisher.sink { [weak self] in + setDocument?.syncPublisher.receiveOnMain().sink { [weak self] in self?.updateDataviewState() Task { await self?.updateViewSubscription() } } diff --git a/Anytype/Sources/PresentationLayer/ObjectCreationSettings/Views/Selection/Dataview/SetObjectCreationSettingsInteractor.swift b/Anytype/Sources/PresentationLayer/ObjectCreationSettings/Views/Selection/Dataview/SetObjectCreationSettingsInteractor.swift index 8edabee9a1..629f713faa 100644 --- a/Anytype/Sources/PresentationLayer/ObjectCreationSettings/Views/Selection/Dataview/SetObjectCreationSettingsInteractor.swift +++ b/Anytype/Sources/PresentationLayer/ObjectCreationSettings/Views/Selection/Dataview/SetObjectCreationSettingsInteractor.swift @@ -115,7 +115,7 @@ final class SetObjectCreationSettingsInteractor: SetObjectCreationSettingsIntera } private func subscribeOnDocmentUpdates() { - setDocument.syncPublisher.sink { [weak self] in + setDocument.syncPublisher.receiveOnMain().sink { [weak self] in guard let self else { return } dataView = setDocument.view(by: dataView.id) if defaultTemplateId != dataView.defaultTemplateID { @@ -124,7 +124,7 @@ final class SetObjectCreationSettingsInteractor: SetObjectCreationSettingsIntera updateDefaultObjectTypeIdIfNeeded() }.store(in: &cancellables) - setDocument.detailsPublisher.sink { [weak self] details in + setDocument.detailsPublisher.receiveOnMain().sink { [weak self] details in guard let self else { return } let isNotTypeSet = !setDocument.isTypeSet() if canChangeObjectType != isNotTypeSet { @@ -133,7 +133,7 @@ final class SetObjectCreationSettingsInteractor: SetObjectCreationSettingsIntera } .store(in: &cancellables) - objectTypesProvider.syncPublisher.sink { [weak self] in + objectTypesProvider.syncPublisher.receiveOnMain().sink { [weak self] in self?.updateObjectTypes() self?.updateDefaultObjectTypeIdIfNeeded() self?.updateTypeDefaultTemplateId() diff --git a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift index 15599a767a..87996982b0 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/BlocksViews/Blocks/TableOfContents/TableOfContentsContentProvider.swift @@ -79,7 +79,8 @@ final class TableOfContentsContentProvider { } private func setupSubsriptionFor(item: TableOfContentItem) { - blockSubscriptions[item.blockId] = document.subscribeForBlockInfo(blockId: item.blockId).sink { [weak item] information in + blockSubscriptions[item.blockId] = document.subscribeForBlockInfo(blockId: item.blockId) + .receiveOnMain().sink { [weak item] information in item?.title = information.textContent?.text ?? Loc.Object.Title.placeholder } } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Header/Entities/ObjectHeaderViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Header/Entities/ObjectHeaderViewModel.swift index 6313fcbbbb..1ce6abca5f 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Header/Entities/ObjectHeaderViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Header/Entities/ObjectHeaderViewModel.swift @@ -67,7 +67,7 @@ final class ObjectHeaderViewModel: ObservableObject { // MARK: - Private private func setupSubscription() { - subscription = document.detailsPublisher.sink { [weak self] details in + subscription = document.detailsPublisher.receiveOnMain().sink { [weak self] details in self?.onUpdate(details: details) } } 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 29637bc1bc..a430a0c28f 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectActions/ObjectActionsViewModel.swift @@ -42,7 +42,7 @@ final class ObjectActionsViewModel: ObservableObject { } func startDocumentTask() async { - for await _ in document.syncPublisher.values { + for await _ in document.syncPublisher.receiveOnMain().values { guard let details = document.details else { objectActions = [] return 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 24f06e3c75..961c2adffe 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/EditorPage/Views/Settings/ObjectSettings/ObjectSettingsViewModel.swift @@ -49,7 +49,7 @@ final class ObjectSettingsViewModel: ObservableObject, ObjectActionsOutput { } func startDocumentTask() async { - for await _ in document.syncPublisher.values { + for await _ in document.syncPublisher.receiveOnMain().values { if let details = document.details { settings = settingsBuilder.build( details: details, diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocument.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocument.swift index 2f563ebad6..fce69ab07c 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocument.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Models/SetDocument.swift @@ -230,7 +230,7 @@ final class SetDocument: SetDocumentProtocol { // MARK: - Private private func setup() async { - document.syncPublisher.sink { [weak self] update in + document.syncPublisher.receiveOnMain().sink { [weak self] update in self?.updateData() } .store(in: &subscriptions) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Header/SetHeaderSettingsViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Header/SetHeaderSettingsViewModel.swift index 193609a13a..ac38432b9f 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Header/SetHeaderSettingsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Header/SetHeaderSettingsViewModel.swift @@ -39,6 +39,7 @@ class SetHeaderSettingsViewModel: ObservableObject { .store(in: &subscriptions) setDocument.syncPublisher + .receiveOnMain() .sink { [weak self, weak setDocument] details in guard let self, let setDocument else { return } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/Filters/List/SetFiltersListViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/Filters/List/SetFiltersListViewModel.swift index 92279168c8..ce046c9ca2 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/Filters/List/SetFiltersListViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/Filters/List/SetFiltersListViewModel.swift @@ -75,7 +75,7 @@ extension SetFiltersListViewModel { // MARK: - Private methods private func setup() { - cancellable = setDocument.syncPublisher.sink { [weak self] in + cancellable = setDocument.syncPublisher.receiveOnMain().sink { [weak self] in guard let self else { return } let filters = setDocument.filters(for: viewId) updateRows(with: filters) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/RelationsSettings/SetRelationsViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/RelationsSettings/SetRelationsViewModel.swift index 6a50a49130..9f8c205920 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/RelationsSettings/SetRelationsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/RelationsSettings/SetRelationsViewModel.swift @@ -110,7 +110,7 @@ final class SetRelationsViewModel: ObservableObject { } private func setup() { - cancellable = setDocument.syncPublisher.sink { [weak self] in + cancellable = setDocument.syncPublisher.receiveOnMain().sink { [weak self] in guard let self else { return } view = setDocument.view(by: viewId) } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/SetViewSettings/SetLayoutSettingsView/SetLayoutSettingsViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/SetViewSettings/SetLayoutSettingsView/SetLayoutSettingsViewModel.swift index 98768111b6..60a75d840b 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/SetViewSettings/SetLayoutSettingsView/SetLayoutSettingsViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/SetViewSettings/SetLayoutSettingsView/SetLayoutSettingsViewModel.swift @@ -36,7 +36,7 @@ final class SetLayoutSettingsViewModel: ObservableObject { } private func setupSubscription() { - cancellable = setDocument.syncPublisher.sink { [weak self] in + cancellable = setDocument.syncPublisher.receiveOnMain().sink { [weak self] in guard let self else { return } view = setDocument.view(by: viewId) selectedType = view.type diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/SetViewSettings/SetViewSettingsList/SetViewSettingsListModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/SetViewSettings/SetViewSettingsList/SetViewSettingsListModel.swift index a3fcdf377c..ebdf1c8247 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/SetViewSettings/SetViewSettingsList/SetViewSettingsListModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/SetViewSettings/SetViewSettingsList/SetViewSettingsListModel.swift @@ -93,7 +93,7 @@ final class SetViewSettingsListModel: ObservableObject { } private func setupSubscriptions() { - setDocument.syncPublisher.sink { [weak self] in + setDocument.syncPublisher.receiveOnMain().sink { [weak self] in self?.updateState() }.store(in: &cancellables) } diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/Sorts/List/SetSortsListViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/Sorts/List/SetSortsListViewModel.swift index 0e177337ee..4f1513a59d 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/Sorts/List/SetSortsListViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/Sorts/List/SetSortsListViewModel.swift @@ -104,7 +104,7 @@ extension SetSortsListViewModel { } private func setup() { - cancellable = setDocument.syncPublisher.sink { [weak self] in + cancellable = setDocument.syncPublisher.receiveOnMain().sink { [weak self] in guard let self else { return } let sorts = setDocument.sorts(for: viewId) updateRows(with: sorts) diff --git a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/ViewPicker/SetViewPickerViewModel.swift b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/ViewPicker/SetViewPickerViewModel.swift index 33a525c39f..13c83f9b69 100644 --- a/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/ViewPicker/SetViewPickerViewModel.swift +++ b/Anytype/Sources/PresentationLayer/TextEditor/Set/Views/Popups/ViewPicker/SetViewPickerViewModel.swift @@ -54,7 +54,7 @@ final class SetViewPickerViewModel: ObservableObject { } func startSyncTask() async { - for await _ in setDocument.syncPublisher.values { + for await _ in setDocument.syncPublisher.receiveOnMain().values { canEditViews = setDocument.setPermissions.canEditView } } From 23fd9397a65e852b3d19ed44c7e3e3f274d7b3eb Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Thu, 6 Jun 2024 17:57:52 +0300 Subject: [PATCH 16/17] IOS-2935 Revery sync array --- .../ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift | 2 +- .../ServiceLayer/RelationStorage/RelationDetailsStorage.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Anytype/Sources/ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift b/Anytype/Sources/ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift index 17627cae9f..2f78f99329 100644 --- a/Anytype/Sources/ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift +++ b/Anytype/Sources/ServiceLayer/Object/TypeProvider/ObjectTypeProvider.swift @@ -25,7 +25,7 @@ final class ObjectTypeProvider: ObjectTypeProviderProtocol { private let subscriptionStorage: SubscriptionStorageProtocol private let subscriptionBuilder: ObjectTypeSubscriptionDataBuilderProtocol - private(set) var objectTypes = SynchronizedArray() + private(set) var objectTypes = [ObjectType]() private var searchTypesById = SynchronizedDictionary() @Published var sync: () = () diff --git a/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorage.swift b/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorage.swift index 24c1362491..5fbf366112 100644 --- a/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorage.swift +++ b/Anytype/Sources/ServiceLayer/RelationStorage/RelationDetailsStorage.swift @@ -24,7 +24,7 @@ final class RelationDetailsStorage: RelationDetailsStorageProtocol { subscriptionStorageProvider.createSubscriptionStorage(subId: Self.subscriptionId) }() - private var details = SynchronizedArray() + private var details = [RelationDetails]() private var searchDetailsByKey = SynchronizedDictionary() private var relationsDetailsSubject = CurrentValueSubject<[RelationDetails], Never>([]) From eac2cc8daa293642e6397917dc2351c6059bea38 Mon Sep 17 00:00:00 2001 From: Mikhail Golovko Date: Tue, 11 Jun 2024 13:23:23 +0300 Subject: [PATCH 17/17] IOS-2935 Changes --- .../MiddlewareEventConverter.swift | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift b/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift index b76c31da76..fc31aa9fec 100644 --- a/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift +++ b/Anytype/Sources/Models/Documents/Events/EventConverters/MiddlewareEventConverter.swift @@ -132,34 +132,34 @@ final class MiddlewareEventConverter { //MARK: - Dataview case .blockDataviewViewSet(let data): infoContainer.dataviewViewSet(data: data) - return .general + return .block(blockId: data.id) case .blockDataviewViewOrder(let data): infoContainer.dataviewViewOrder(data: data) - return .general + return .block(blockId: data.id) case .blockDataviewViewDelete(let data): infoContainer.dataviewViewDelete(data: data) - return .general + return .block(blockId: data.id) case .blockDataviewRelationDelete(let data): infoContainer.dataviewRelationDelete(data: data) - return .general + return .block(blockId: data.id) case .blockDataviewRelationSet(let data): infoContainer.dataviewRelationSet(data: data) - return .general + return .block(blockId: data.id) case .blockDataViewGroupOrderUpdate(let data): infoContainer.dataViewGroupOrderUpdate(data: data) - return .general + return .block(blockId: data.id) case .blockDataViewObjectOrderUpdate(let data): infoContainer.dataViewObjectOrderUpdate(data: data) - return .general + return .block(blockId: data.id) case .blockDataviewTargetObjectIDSet(let data): infoContainer.dataviewTargetObjectIDSet(data: data) - return .general + return .block(blockId: data.id) case .blockDataviewIsCollectionSet(let data): infoContainer.dataviewIsCollectionSet(data: data) - return .general + return .block(blockId: data.id) case .blockDataviewViewUpdate(let data): infoContainer.dataviewViewUpdate(data: data) - return .general + return .block(blockId: data.id) case .blockSetRelation(let data): infoContainer.setRelation(data: data) return .general // Relace to `.blocks(blockIds: [data.id])` after implment task https://linear.app/anytype/issue/IOS-914 @@ -168,7 +168,7 @@ final class MiddlewareEventConverter { return nil case .blockSetWidget(let data): infoContainer.setWidget(data: data) - return .general + return .block(blockId: data.id) case .accountShow, .accountUpdate, // Event not working on middleware. See AccountManager. .accountDetails, // Skipped