diff --git a/app/src/androidTest/java/com/anytypeio/anytype/features/editor/base/EditorTestSetup.kt b/app/src/androidTest/java/com/anytypeio/anytype/features/editor/base/EditorTestSetup.kt index b92d83e230..04c008767e 100644 --- a/app/src/androidTest/java/com/anytypeio/anytype/features/editor/base/EditorTestSetup.kt +++ b/app/src/androidTest/java/com/anytypeio/anytype/features/editor/base/EditorTestSetup.kt @@ -57,6 +57,7 @@ import com.anytypeio.anytype.domain.cover.RemoveDocCover import com.anytypeio.anytype.domain.cover.SetDocCoverImage import com.anytypeio.anytype.domain.download.DownloadFile import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon import com.anytypeio.anytype.domain.launch.GetDefaultObjectType import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer @@ -109,7 +110,6 @@ import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder import com.anytypeio.anytype.presentation.search.ObjectSearchConstants -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory import com.anytypeio.anytype.presentation.util.Dispatcher diff --git a/app/src/androidTest/java/com/anytypeio/anytype/features/sets/dv/TestObjectSetSetup.kt b/app/src/androidTest/java/com/anytypeio/anytype/features/sets/dv/TestObjectSetSetup.kt index faa7c78e53..f4e397f5b1 100644 --- a/app/src/androidTest/java/com/anytypeio/anytype/features/sets/dv/TestObjectSetSetup.kt +++ b/app/src/androidTest/java/com/anytypeio/anytype/features/sets/dv/TestObjectSetSetup.kt @@ -29,6 +29,7 @@ import com.anytypeio.anytype.domain.dataview.SetDataViewQuery import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject import com.anytypeio.anytype.domain.dataview.interactor.UpdateDataViewViewer import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.launch.GetDefaultObjectType import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer import com.anytypeio.anytype.domain.misc.UrlBuilder @@ -68,7 +69,6 @@ import com.anytypeio.anytype.presentation.sets.ObjectSetViewModelFactory import com.anytypeio.anytype.presentation.sets.state.DefaultObjectStateReducer import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription import com.anytypeio.anytype.presentation.sets.viewer.ViewerDelegate -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.Dispatcher import com.anytypeio.anytype.presentation.widgets.collection.DateProviderImpl diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/EditorDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/EditorDI.kt index 7f7431e359..08e894c702 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/EditorDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/EditorDI.kt @@ -52,11 +52,11 @@ import com.anytypeio.anytype.domain.download.DownloadFile import com.anytypeio.anytype.domain.download.Downloader import com.anytypeio.anytype.domain.event.interactor.EventChannel import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon import com.anytypeio.anytype.domain.launch.GetDefaultObjectType import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer import com.anytypeio.anytype.domain.misc.UrlBuilder -import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider import com.anytypeio.anytype.domain.networkmode.GetNetworkMode import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection @@ -100,9 +100,7 @@ import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage import com.anytypeio.anytype.domain.unsplash.UnsplashRepository import com.anytypeio.anytype.domain.workspace.FileLimitsEventChannel import com.anytypeio.anytype.domain.workspace.InterceptFileLimitEvents -import com.anytypeio.anytype.domain.workspace.P2PStatusChannel import com.anytypeio.anytype.domain.workspace.SpaceManager -import com.anytypeio.anytype.domain.workspace.SpaceSyncStatusChannel import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate import com.anytypeio.anytype.presentation.common.Action import com.anytypeio.anytype.presentation.common.Delegator @@ -127,7 +125,6 @@ import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProv import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider.Companion.INTRINSIC_PROVIDER_TYPE import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider import com.anytypeio.anytype.presentation.relations.providers.RelationListProvider -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory import com.anytypeio.anytype.presentation.util.DefaultCopyFileToCacheDirectory @@ -1204,18 +1201,6 @@ object EditorUseCaseModule { spaceManager = spaceManager ) - @Provides - @PerScreen - fun provideSpaceSyncStatusProvider( - activeSpace: ActiveSpaceMemberSubscriptionContainer, - syncChannel: SpaceSyncStatusChannel, - p2PStatusChannel: P2PStatusChannel - ): SpaceSyncAndP2PStatusProvider = SpaceSyncAndP2PStatusProvider.Impl( - activeSpace = activeSpace, - spaceSyncStatusChannel = syncChannel, - p2PStatusChannel = p2PStatusChannel - ) - @Module interface Bindings { diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/ObjectSetDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/ObjectSetDI.kt index 783cb096f7..725b99d10c 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/ObjectSetDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/ObjectSetDI.kt @@ -32,12 +32,12 @@ import com.anytypeio.anytype.domain.dataview.interactor.SetDataViewViewerPositio import com.anytypeio.anytype.domain.dataview.interactor.UpdateDataViewViewer import com.anytypeio.anytype.domain.event.interactor.EventChannel import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon import com.anytypeio.anytype.domain.launch.GetDefaultObjectType import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer import com.anytypeio.anytype.domain.misc.DateProvider import com.anytypeio.anytype.domain.misc.UrlBuilder -import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection import com.anytypeio.anytype.domain.`object`.DuplicateObject @@ -65,9 +65,7 @@ import com.anytypeio.anytype.domain.templates.CreateTemplate import com.anytypeio.anytype.domain.templates.GetTemplates import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage import com.anytypeio.anytype.domain.unsplash.UnsplashRepository -import com.anytypeio.anytype.domain.workspace.P2PStatusChannel import com.anytypeio.anytype.domain.workspace.SpaceManager -import com.anytypeio.anytype.domain.workspace.SpaceSyncStatusChannel import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate import com.anytypeio.anytype.presentation.common.Action import com.anytypeio.anytype.presentation.common.Delegator @@ -95,7 +93,6 @@ import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription import com.anytypeio.anytype.presentation.sets.viewer.DefaultViewerDelegate import com.anytypeio.anytype.presentation.sets.viewer.ViewerDelegate -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory import com.anytypeio.anytype.presentation.util.DefaultCopyFileToCacheDirectory @@ -714,18 +711,6 @@ object ObjectSetModule { analytics = analytics, dispatcher = dispatcher ) - - @Provides - @PerScreen - fun provideSpaceSyncStatusProvider( - activeSpace: ActiveSpaceMemberSubscriptionContainer, - syncChannel: SpaceSyncStatusChannel, - p2PStatusChannel: P2PStatusChannel - ): SpaceSyncAndP2PStatusProvider = SpaceSyncAndP2PStatusProvider.Impl( - activeSpace = activeSpace, - spaceSyncStatusChannel = syncChannel, - p2PStatusChannel = p2PStatusChannel - ) } data class DefaultComponentParam( diff --git a/app/src/main/java/com/anytypeio/anytype/di/main/EventModule.kt b/app/src/main/java/com/anytypeio/anytype/di/main/EventModule.kt index 9f5fd8a01e..5ba5cff144 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/main/EventModule.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/main/EventModule.kt @@ -9,21 +9,29 @@ import com.anytypeio.anytype.data.auth.event.FileLimitsDataChannel import com.anytypeio.anytype.data.auth.event.FileLimitsRemoteChannel import com.anytypeio.anytype.data.auth.event.SubscriptionDataChannel import com.anytypeio.anytype.data.auth.event.SubscriptionEventRemoteChannel +import com.anytypeio.anytype.data.auth.status.SyncAndP2PStatusEventsStore +import com.anytypeio.anytype.di.main.ConfigModule.DEFAULT_APP_COROUTINE_SCOPE import com.anytypeio.anytype.domain.account.AccountStatusChannel import com.anytypeio.anytype.domain.event.interactor.EventChannel import com.anytypeio.anytype.domain.search.SubscriptionEventChannel import com.anytypeio.anytype.domain.workspace.FileLimitsEventChannel import com.anytypeio.anytype.middleware.EventProxy import com.anytypeio.anytype.middleware.interactor.AccountStatusMiddlewareChannel +import com.anytypeio.anytype.middleware.interactor.EventHandlerChannel +import com.anytypeio.anytype.middleware.interactor.EventHandlerChannelImpl import com.anytypeio.anytype.middleware.interactor.EventHandler import com.anytypeio.anytype.middleware.interactor.FileLimitsMiddlewareChannel import com.anytypeio.anytype.middleware.interactor.MiddlewareEventChannel +import com.anytypeio.anytype.middleware.interactor.MiddlewareProtobufLogger import com.anytypeio.anytype.middleware.interactor.MiddlewareSubscriptionEventChannel +import com.anytypeio.anytype.middleware.interactor.SyncAndP2PStatusEventsStoreImpl import com.anytypeio.anytype.presentation.common.PayloadDelegator import dagger.Binds import dagger.Module import dagger.Provides +import javax.inject.Named import javax.inject.Singleton +import kotlinx.coroutines.CoroutineScope @Module(includes = [EventModule.Bindings::class]) object EventModule { @@ -110,13 +118,40 @@ object EventModule { channel: FileLimitsRemoteChannel ): FileLimitsEventChannel = FileLimitsDataChannel(channel = channel) + @JvmStatic + @Provides + @Singleton + fun provideEventHandler( + logger: MiddlewareProtobufLogger, + @Named(DEFAULT_APP_COROUTINE_SCOPE) scope: CoroutineScope, + channel: EventHandlerChannel, + syncP2PStore: SyncAndP2PStatusEventsStore + ): EventProxy = EventHandler( + scope = scope, + logger = logger, + channel = channel, + syncP2PStore = syncP2PStore + ) + + @JvmStatic + @Provides + @Singleton + fun provideSyncAndP2PStatusEventsSubscription( + @Named(DEFAULT_APP_COROUTINE_SCOPE) scope: CoroutineScope, + channel: EventHandlerChannel + ): SyncAndP2PStatusEventsStore = SyncAndP2PStatusEventsStoreImpl( + scope = scope, + channel = channel + ) + + @JvmStatic + @Provides + @Singleton + fun provideDefaultEventChannel(): EventHandlerChannel = EventHandlerChannelImpl() + @Module interface Bindings { - @Binds - @Singleton - fun bindEventProxy(handler: EventHandler): EventProxy - @Binds @Singleton fun payloadDelegator(default: PayloadDelegator.Default): PayloadDelegator diff --git a/app/src/main/java/com/anytypeio/anytype/di/main/SubscriptionsModule.kt b/app/src/main/java/com/anytypeio/anytype/di/main/SubscriptionsModule.kt index 9d07df715d..51713c7808 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/main/SubscriptionsModule.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/main/SubscriptionsModule.kt @@ -7,6 +7,7 @@ import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.block.repo.BlockRepository import com.anytypeio.anytype.domain.config.ConfigStorage import com.anytypeio.anytype.domain.debugging.Logger +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer import com.anytypeio.anytype.domain.multiplayer.DefaultUserPermissionProvider @@ -24,6 +25,8 @@ import com.anytypeio.anytype.domain.search.SubscriptionEventChannel import com.anytypeio.anytype.domain.spaces.SpaceDeletedStatusWatcher import com.anytypeio.anytype.domain.subscriptions.GlobalSubscriptionManager import com.anytypeio.anytype.domain.workspace.SpaceManager +import com.anytypeio.anytype.domain.workspace.SyncAndP2PStatusChannel +import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProviderImpl import dagger.Module import dagger.Provides import javax.inject.Named @@ -163,6 +166,17 @@ object SubscriptionsModule { awaitAccountStart = awaitAccountStartManager ) + @JvmStatic + @Provides + @Singleton + fun provideSpaceSyncStatusProvider( + syncChannel: SyncAndP2PStatusChannel, + spaceManager: SpaceManager + ): SpaceSyncAndP2PStatusProvider = SpaceSyncAndP2PStatusProviderImpl( + spaceManager = spaceManager, + spaceSyncStatusChannel = syncChannel + ) + @JvmStatic @Provides @Singleton diff --git a/app/src/main/java/com/anytypeio/anytype/di/main/WorkspaceModule.kt b/app/src/main/java/com/anytypeio/anytype/di/main/WorkspaceModule.kt index 37f4ee7a2a..e79387d973 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/main/WorkspaceModule.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/main/WorkspaceModule.kt @@ -1,30 +1,27 @@ package com.anytypeio.anytype.di.main -import com.anytypeio.anytype.data.auth.status.P2PStatusDataChannel -import com.anytypeio.anytype.data.auth.status.P2PStatusRemoteChannel -import com.anytypeio.anytype.data.auth.status.SpaceStatusDataChannel -import com.anytypeio.anytype.data.auth.status.SpaceStatusRemoteChannel +import com.anytypeio.anytype.data.auth.status.SyncAndP2PStatusDataChannel +import com.anytypeio.anytype.data.auth.status.SyncAndP2PStatusEventsStore import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.block.repo.BlockRepository import com.anytypeio.anytype.domain.config.ConfigStorage import com.anytypeio.anytype.domain.debugging.Logger -import com.anytypeio.anytype.domain.workspace.P2PStatusChannel import com.anytypeio.anytype.domain.workspace.SpaceManager -import com.anytypeio.anytype.domain.workspace.SpaceSyncStatusChannel +import com.anytypeio.anytype.domain.workspace.SyncAndP2PStatusChannel import com.anytypeio.anytype.domain.workspace.WorkspaceManager -import com.anytypeio.anytype.middleware.EventProxy -import com.anytypeio.anytype.middleware.interactor.P2PStatusRemoteChannelImpl -import com.anytypeio.anytype.middleware.interactor.SpaceSyncStatusRemoteChannelImpl import dagger.Module import dagger.Provides import javax.inject.Singleton @Module object WorkspaceModule { + + @JvmStatic @Provides @Singleton fun manager(): WorkspaceManager = WorkspaceManager.DefaultWorkspaceManager() + @JvmStatic @Provides @Singleton fun spaces( @@ -40,29 +37,9 @@ object WorkspaceModule { ) @JvmStatic - @Provides - @Singleton - fun provideSyncStatusRemoteChannel( - proxy: EventProxy - ): SpaceStatusRemoteChannel = SpaceSyncStatusRemoteChannelImpl(events = proxy) - @Provides @Singleton fun spaceSyncStatusChannel( - channel: SpaceStatusRemoteChannel - ): SpaceSyncStatusChannel = SpaceStatusDataChannel(channel) - - @JvmStatic - @Provides - @Singleton - fun p2pStatusRemoteChannel( - proxy: EventProxy - ): P2PStatusRemoteChannel = P2PStatusRemoteChannelImpl(events = proxy) - - @JvmStatic - @Provides - @Singleton - fun p2pStatusChannel( - channel: P2PStatusRemoteChannel - ): P2PStatusChannel = P2PStatusDataChannel(channel) + store: SyncAndP2PStatusEventsStore + ): SyncAndP2PStatusChannel = SyncAndP2PStatusDataChannel(store) } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index 446ba2d11e..a51103909d 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -53,6 +53,7 @@ import com.anytypeio.anytype.core_models.Key import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.ThemeColor import com.anytypeio.anytype.core_models.Url +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_ui.extensions.addTextFromSelectedStart import com.anytypeio.anytype.core_ui.extensions.color @@ -125,7 +126,6 @@ import com.anytypeio.anytype.presentation.editor.model.EditorFooter import com.anytypeio.anytype.presentation.editor.template.SelectTemplateViewState import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationContext -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.ui.alert.AlertUpdateAppFragment import com.anytypeio.anytype.ui.base.NavigationFragment import com.anytypeio.anytype.ui.base.navigation @@ -871,7 +871,7 @@ open class EditorFragment : NavigationFragment(R.layout.f private fun bindSyncStatus(status: SpaceSyncAndP2PStatusState) { binding.topToolbar.status.bind(status) - if (status is SpaceSyncAndP2PStatusState.Initial) { + if (status is SpaceSyncAndP2PStatusState.Init) { binding.topToolbar.hideStatusContainer() } else { binding.topToolbar.showStatusContainer() diff --git a/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt index 9785aba303..60eb0b3763 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt @@ -44,6 +44,7 @@ import com.anytypeio.anytype.R import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.Key import com.anytypeio.anytype.core_models.ObjectWrapper +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_ui.extensions.setEmojiOrNull import com.anytypeio.anytype.core_ui.features.dataview.ViewerGridAdapter @@ -100,7 +101,6 @@ import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi import com.anytypeio.anytype.presentation.sets.isVisible import com.anytypeio.anytype.presentation.sets.model.Viewer -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.ui.base.NavigationFragment import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectSetFragment import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/multiplayer/Multiplayer.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/multiplayer/Multiplayer.kt index 60a8de41c3..bf8f5792d1 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/multiplayer/Multiplayer.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/multiplayer/Multiplayer.kt @@ -113,4 +113,13 @@ enum class P2PStatus { NOT_CONNECTED, NOT_POSSIBLE, CONNECTED +} + +sealed class SpaceSyncAndP2PStatusState { + data object Init : SpaceSyncAndP2PStatusState() + data class Error(val message: String) : SpaceSyncAndP2PStatusState() + data class Success( + val spaceSyncUpdate: SpaceSyncUpdate, + val p2PStatusUpdate: P2PStatusUpdate + ) : SpaceSyncAndP2PStatusState() } \ No newline at end of file diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/syncstatus/SpaceSyncStatusScreen.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/syncstatus/SpaceSyncStatusScreen.kt index 759fcca993..e958d6e787 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/syncstatus/SpaceSyncStatusScreen.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/syncstatus/SpaceSyncStatusScreen.kt @@ -148,8 +148,8 @@ private fun ColumnScope.ErrorState() { private fun SuccessState(spaceSyncUpdate: SpaceSyncUpdate, p2pStatus: P2PStatusUpdate) { if (spaceSyncUpdate is SpaceSyncUpdate.Update) { SpaceSyncStatusItem(spaceSyncUpdate) + Divider() } - Divider() if (p2pStatus is P2PStatusUpdate.Update) { P2PStatusItem(p2pStatus) } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/StatusBadgeWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/StatusBadgeWidget.kt index a7e0680003..606224e823 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/StatusBadgeWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/StatusBadgeWidget.kt @@ -3,13 +3,14 @@ package com.anytypeio.anytype.core_ui.widgets import android.content.Context import android.util.AttributeSet import androidx.appcompat.widget.AppCompatImageView +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncError import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncStatus import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate import com.anytypeio.anytype.core_ui.R import com.anytypeio.anytype.core_utils.ext.gone import com.anytypeio.anytype.core_utils.ext.visible -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusState + class StatusBadgeWidget @JvmOverloads constructor( context: Context, @@ -22,7 +23,7 @@ class StatusBadgeWidget @JvmOverloads constructor( visible() setImageResource(R.drawable.ic_sync_error_10) } - SpaceSyncAndP2PStatusState.Initial -> { + SpaceSyncAndP2PStatusState.Init -> { gone() } is SpaceSyncAndP2PStatusState.Success -> { diff --git a/data/src/main/java/com/anytypeio/anytype/data/auth/status/P2PStatusRemoteChannel.kt b/data/src/main/java/com/anytypeio/anytype/data/auth/status/P2PStatusRemoteChannel.kt deleted file mode 100644 index 69815b67a3..0000000000 --- a/data/src/main/java/com/anytypeio/anytype/data/auth/status/P2PStatusRemoteChannel.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.anytypeio.anytype.data.auth.status - -import com.anytypeio.anytype.core_models.Id -import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate -import com.anytypeio.anytype.domain.workspace.P2PStatusChannel -import kotlinx.coroutines.flow.Flow - -interface P2PStatusRemoteChannel { - fun observe(activeSpaceId: Id): Flow -} - -class P2PStatusDataChannel( - private val channel: P2PStatusRemoteChannel -) : P2PStatusChannel { - - override fun observe(activeSpaceId: Id): Flow { - return channel.observe(activeSpaceId) - } -} \ No newline at end of file diff --git a/data/src/main/java/com/anytypeio/anytype/data/auth/status/SpaceStatusRemoteChannel.kt b/data/src/main/java/com/anytypeio/anytype/data/auth/status/SpaceStatusRemoteChannel.kt index 16b59f9499..88eec04855 100644 --- a/data/src/main/java/com/anytypeio/anytype/data/auth/status/SpaceStatusRemoteChannel.kt +++ b/data/src/main/java/com/anytypeio/anytype/data/auth/status/SpaceStatusRemoteChannel.kt @@ -1,19 +1,14 @@ package com.anytypeio.anytype.data.auth.status -import com.anytypeio.anytype.core_models.Id +import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate -import com.anytypeio.anytype.domain.workspace.SpaceSyncStatusChannel +import com.anytypeio.anytype.domain.workspace.SyncAndP2PStatusChannel import kotlinx.coroutines.flow.Flow -interface SpaceStatusRemoteChannel { - fun observe(activeSpaceId: String): Flow -} +class SyncAndP2PStatusDataChannel( + private val store: SyncAndP2PStatusEventsStore +) : SyncAndP2PStatusChannel { -class SpaceStatusDataChannel( - private val channel: SpaceStatusRemoteChannel -) : SpaceSyncStatusChannel { - - override fun observe(activeSpaceId: Id): Flow { - return channel.observe(activeSpaceId) - } + override fun p2pStatus(): Flow> = store.p2pStatus + override fun syncStatus(): Flow> = store.syncStatus } \ No newline at end of file diff --git a/data/src/main/java/com/anytypeio/anytype/data/auth/status/SyncAndP2PStatusEventsStore.kt b/data/src/main/java/com/anytypeio/anytype/data/auth/status/SyncAndP2PStatusEventsStore.kt new file mode 100644 index 0000000000..16d02bc0be --- /dev/null +++ b/data/src/main/java/com/anytypeio/anytype/data/auth/status/SyncAndP2PStatusEventsStore.kt @@ -0,0 +1,13 @@ +package com.anytypeio.anytype.data.auth.status + +import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate +import kotlinx.coroutines.flow.Flow + +interface SyncAndP2PStatusEventsStore { + val p2pStatus: Flow> + val syncStatus: Flow> + + fun start() + fun stop() +} \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/event/interactor/SpaceSyncAndP2PStatusProvider {.kt b/domain/src/main/java/com/anytypeio/anytype/domain/event/interactor/SpaceSyncAndP2PStatusProvider {.kt new file mode 100644 index 0000000000..d40baac348 --- /dev/null +++ b/domain/src/main/java/com/anytypeio/anytype/domain/event/interactor/SpaceSyncAndP2PStatusProvider {.kt @@ -0,0 +1,8 @@ +package com.anytypeio.anytype.domain.event.interactor + +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState +import kotlinx.coroutines.flow.Flow + +interface SpaceSyncAndP2PStatusProvider { + fun observe(): Flow +} \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/workspace/P2PStatusChannel.kt b/domain/src/main/java/com/anytypeio/anytype/domain/workspace/P2PStatusChannel.kt deleted file mode 100644 index ce1fd1e69a..0000000000 --- a/domain/src/main/java/com/anytypeio/anytype/domain/workspace/P2PStatusChannel.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.anytypeio.anytype.domain.workspace - -import com.anytypeio.anytype.core_models.Id -import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate -import kotlinx.coroutines.flow.Flow - -interface P2PStatusChannel { - fun observe(activeSpaceId: Id): Flow -} \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/workspace/SpaceSyncStatusChannel.kt b/domain/src/main/java/com/anytypeio/anytype/domain/workspace/SpaceSyncStatusChannel.kt deleted file mode 100644 index 14e1e5b93b..0000000000 --- a/domain/src/main/java/com/anytypeio/anytype/domain/workspace/SpaceSyncStatusChannel.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.anytypeio.anytype.domain.workspace - -import com.anytypeio.anytype.core_models.Id -import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate -import kotlinx.coroutines.flow.Flow - -interface SpaceSyncStatusChannel { - fun observe(activeSpaceId: Id): Flow -} \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/workspace/SyncAndP2PStatusChannel.kt b/domain/src/main/java/com/anytypeio/anytype/domain/workspace/SyncAndP2PStatusChannel.kt new file mode 100644 index 0000000000..a1c0c2ad3d --- /dev/null +++ b/domain/src/main/java/com/anytypeio/anytype/domain/workspace/SyncAndP2PStatusChannel.kt @@ -0,0 +1,10 @@ +package com.anytypeio.anytype.domain.workspace + +import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate +import kotlinx.coroutines.flow.Flow + +interface SyncAndP2PStatusChannel { + fun p2pStatus(): Flow> + fun syncStatus(): Flow> +} \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/EventHandler.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/EventHandler.kt index 6f707f3332..5dcdbde33c 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/EventHandler.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/EventHandler.kt @@ -1,24 +1,29 @@ package com.anytypeio.anytype.middleware.interactor import anytype.Event -import com.anytypeio.anytype.middleware.BuildConfig +import com.anytypeio.anytype.data.auth.status.SyncAndP2PStatusEventsStore import com.anytypeio.anytype.middleware.EventProxy -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import service.Service.setEventHandlerMobile -import timber.log.Timber import java.io.IOException import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import service.Service.setEventHandlerMobile +import timber.log.Timber class EventHandler @Inject constructor( - private val logger: MiddlewareProtobufLogger + private val logger: MiddlewareProtobufLogger, + private val scope: CoroutineScope, + private val channel: EventHandlerChannel, + private val syncP2PStore: SyncAndP2PStatusEventsStore ) : EventProxy { - private val scope: CoroutineScope = GlobalScope - private val channel = MutableSharedFlow(0, 1) - init { + scope.launch { + syncP2PStore.start() + } scope.launch { setEventHandlerMobile { bytes -> if (bytes != null) { @@ -43,5 +48,5 @@ class EventHandler @Inject constructor( logger.logEvent(event) } - override fun flow(): Flow = channel + override fun flow(): Flow = channel.flow() } \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/EventHandlerChannel.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/EventHandlerChannel.kt new file mode 100644 index 0000000000..26b12ef47e --- /dev/null +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/EventHandlerChannel.kt @@ -0,0 +1,29 @@ +package com.anytypeio.anytype.middleware.interactor + +import anytype.Event +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow + +interface EventHandlerChannel { + fun flow(): Flow + suspend fun emit(event: Event) +} + +class EventHandlerChannelImpl() : EventHandlerChannel { + + private val _channel = MutableSharedFlow( + replay = 0, + extraBufferCapacity = 1 + ) + + override fun flow(): Flow = _channel + + override suspend fun emit(event: Event) { + _channel.emit(event) + } + + fun trySend(event: Event) { + _channel.tryEmit(event) + } +} \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/P2PStatusRemoteChannelImpl.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/P2PStatusRemoteChannelImpl.kt deleted file mode 100644 index 82c95f65b3..0000000000 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/P2PStatusRemoteChannelImpl.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.anytypeio.anytype.middleware.interactor - -import com.anytypeio.anytype.core_models.Id -import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate -import com.anytypeio.anytype.data.auth.status.P2PStatusRemoteChannel -import com.anytypeio.anytype.middleware.EventProxy -import com.anytypeio.anytype.middleware.mappers.toCoreModel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull - -class P2PStatusRemoteChannelImpl(private val events: EventProxy) : P2PStatusRemoteChannel { - - override fun observe(activeSpaceId: Id): Flow { - return events.flow() - .mapNotNull { emission -> - emission.messages.lastOrNull()?.p2pStatusUpdate - } - .filter { event -> event.spaceId == activeSpaceId } - .map { event -> - P2PStatusUpdate.Update( - spaceId = event.spaceId, - status = event.status.toCoreModel(), - devicesCounter = event.devicesCounter - ) - } - } -} \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/SpaceSyncStatusRemoteChannelImpl.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/SpaceSyncStatusRemoteChannelImpl.kt deleted file mode 100644 index 8ad3480231..0000000000 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/SpaceSyncStatusRemoteChannelImpl.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.anytypeio.anytype.middleware.interactor - -import com.anytypeio.anytype.core_models.Id -import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate -import com.anytypeio.anytype.data.auth.status.SpaceStatusRemoteChannel -import com.anytypeio.anytype.middleware.EventProxy -import com.anytypeio.anytype.middleware.mappers.toCoreModel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull - -class SpaceSyncStatusRemoteChannelImpl(private val events: EventProxy) : SpaceStatusRemoteChannel { - - override fun observe(activeSpaceId: Id): Flow { - return events.flow() - .mapNotNull { emission -> - emission.messages.lastOrNull()?.spaceSyncStatusUpdate - } - .filter { event -> event.id == activeSpaceId } - .map { event -> - SpaceSyncUpdate.Update( - id = event.id, - status = event.status.toCoreModel(), - network = event.network.toCoreModel(), - error = event.error.toCoreModel(), - syncingObjectsCounter = event.syncingObjectsCounter - ) - } - } -} \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/SyncAndP2PChannelContainer.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/SyncAndP2PChannelContainer.kt new file mode 100644 index 0000000000..8fc586c1fc --- /dev/null +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/SyncAndP2PChannelContainer.kt @@ -0,0 +1,84 @@ +package com.anytypeio.anytype.middleware.interactor + +import com.anytypeio.anytype.core_models.Id +import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate +import com.anytypeio.anytype.core_utils.ext.cancel +import com.anytypeio.anytype.data.auth.status.SyncAndP2PStatusEventsStore +import com.anytypeio.anytype.middleware.mappers.MP2PStatusUpdate +import com.anytypeio.anytype.middleware.mappers.MSyncStatusUpdate +import com.anytypeio.anytype.middleware.mappers.toCoreModel +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber + +class SyncAndP2PStatusEventsStoreImpl( + private val scope: CoroutineScope, + private val channel: EventHandlerChannel, + private val dispatcher: CoroutineDispatcher = Dispatchers.IO +) : SyncAndP2PStatusEventsStore { + + private val _p2pStatus = MutableStateFlow>(mutableMapOf()) + private val _syncStatus = MutableStateFlow>(mutableMapOf()) + + override val p2pStatus: Flow> get() = _p2pStatus + override val syncStatus: Flow> get() = _syncStatus + + private val jobs = mutableListOf() + + override fun start() { + Timber.i("SyncAndP2PStatusEventsStoreImpl start") + jobs.cancel() + jobs += scope.launch(dispatcher) { + channel.flow() + .catch { e -> + Timber.e(e, "Error collecting P2P & Sync status updates") + } + .collect { emission -> + emission.messages.forEach { message -> + message.p2pStatusUpdate?.let { processP2PStatusUpdate(it) } + message.spaceSyncStatusUpdate?.let { processSpaceSyncUpdate(it) } + } + } + } + } + + override fun stop() { + Timber.i("SyncAndP2PStatusEventsStoreImpl stop") + jobs.cancel() + } + + private fun processP2PStatusUpdate(update: MP2PStatusUpdate) { + val p2pUpdate = P2PStatusUpdate.Update( + spaceId = update.spaceId, + status = update.status.toCoreModel(), + devicesCounter = update.devicesCounter + ) + _p2pStatus.update { currentMap -> + currentMap[update.spaceId] = p2pUpdate + currentMap + } + } + + private fun processSpaceSyncUpdate(update: MSyncStatusUpdate) { + val syncUpdate = SpaceSyncUpdate.Update( + id = update.id, + status = update.status.toCoreModel(), + network = update.network.toCoreModel(), + error = update.error.toCoreModel(), + syncingObjectsCounter = update.syncingObjectsCounter + ) + + _syncStatus.update { currentMap -> + currentMap[update.id] = syncUpdate + currentMap + } + } +} \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt index d2febc6061..2fd046a7b8 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt @@ -1,5 +1,8 @@ package com.anytypeio.anytype.middleware.mappers +import anytype.Event.P2PStatus +import anytype.Event.Space + typealias MAccount = anytype.model.Account typealias MAccountStatus = anytype.model.Account.Status typealias MAccountStatusType = anytype.model.Account.StatusType @@ -95,4 +98,6 @@ typealias MSpaceSyncStatus = anytype.Event.Space.Status typealias MSpaceSyncNetwork = anytype.Event.Space.Network typealias MSpaceSyncError = anytype.Event.Space.SyncError -typealias MP2PStatus = anytype.Event.P2PStatus.Status \ No newline at end of file +typealias MP2PStatus = anytype.Event.P2PStatus.Status +typealias MP2PStatusUpdate = P2PStatus.Update +typealias MSyncStatusUpdate = Space.SyncStatus.Update \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/EventMappers.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/EventMappers.kt index 499cf98f80..4783bfdde2 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/EventMappers.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/EventMappers.kt @@ -1,11 +1,11 @@ package com.anytypeio.anytype.middleware.mappers import anytype.Event -import com.anytypeio.anytype.core_models.SearchResult import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVFilterUpdate import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVSortUpdate -import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVViewerRelationUpdate import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVViewerFields +import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVViewerRelationUpdate +import com.anytypeio.anytype.core_models.SearchResult fun Event.Object.Subscription.Counters.parse(): SearchResult.Counter = SearchResult.Counter( total = total.toInt(), diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index 17c194360d..fc1c63ff87 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -49,6 +49,7 @@ import com.anytypeio.anytype.core_models.ext.sortByType import com.anytypeio.anytype.core_models.ext.supportNesting import com.anytypeio.anytype.core_models.ext.title import com.anytypeio.anytype.core_models.ext.updateTextContent +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_models.primitives.TypeId import com.anytypeio.anytype.core_models.primitives.TypeKey @@ -77,6 +78,7 @@ import com.anytypeio.anytype.domain.cover.SetDocCoverImage import com.anytypeio.anytype.domain.editor.Editor import com.anytypeio.anytype.domain.error.Error import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon import com.anytypeio.anytype.domain.icon.SetImageIcon import com.anytypeio.anytype.domain.launch.GetDefaultObjectType @@ -251,8 +253,6 @@ import com.anytypeio.anytype.presentation.relations.getObjectRelations import com.anytypeio.anytype.presentation.relations.views import com.anytypeio.anytype.presentation.search.ObjectSearchConstants import com.anytypeio.anytype.presentation.search.ObjectSearchViewModel -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.presentation.sync.SyncStatusWidgetState import com.anytypeio.anytype.presentation.sync.toSyncStatusWidgetState import com.anytypeio.anytype.presentation.sync.updateStatus @@ -7347,7 +7347,7 @@ class EditorViewModel( } //region SYNC STATUS - val spaceSyncStatus = MutableStateFlow(SpaceSyncAndP2PStatusState.Initial) + val spaceSyncStatus = MutableStateFlow(SpaceSyncAndP2PStatusState.Init) val syncStatusWidget = MutableStateFlow(SyncStatusWidgetState.Hidden) fun onSyncStatusBadgeClicked() { @@ -7359,6 +7359,9 @@ class EditorViewModel( jobs += viewModelScope.launch { spaceSyncAndP2PStatusProvider .observe() + .catch { + Timber.e(it, "Error while observing sync status") + } .collect { syncAndP2pState -> spaceSyncStatus.value = syncAndP2pState syncStatusWidget.value = syncStatusWidget.value.updateStatus(syncAndP2pState) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModelFactory.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModelFactory.kt index 7b81c565c6..4e1407d0a3 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModelFactory.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModelFactory.kt @@ -16,6 +16,7 @@ import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet import com.anytypeio.anytype.domain.block.interactor.sets.GetObjectTypes import com.anytypeio.anytype.domain.cover.SetDocCoverImage import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon import com.anytypeio.anytype.domain.launch.GetDefaultObjectType import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer @@ -46,7 +47,6 @@ import com.anytypeio.anytype.presentation.common.StateReducer import com.anytypeio.anytype.presentation.editor.editor.Orchestrator import com.anytypeio.anytype.presentation.editor.editor.table.EditorTableDelegate import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory import com.anytypeio.anytype.presentation.util.Dispatcher diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt index d4b04ac025..0e5acdbf09 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt @@ -22,6 +22,7 @@ import com.anytypeio.anytype.core_models.RelationFormat import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.isDataView import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_models.primitives.TypeId import com.anytypeio.anytype.core_models.primitives.TypeKey @@ -39,6 +40,7 @@ import com.anytypeio.anytype.domain.cover.SetDocCoverImage import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject import com.anytypeio.anytype.domain.error.Error import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer import com.anytypeio.anytype.domain.misc.DateProvider @@ -96,8 +98,6 @@ import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubsc import com.anytypeio.anytype.presentation.sets.viewer.ViewerDelegate import com.anytypeio.anytype.presentation.sets.viewer.ViewerEvent import com.anytypeio.anytype.presentation.sets.viewer.ViewerView -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.presentation.sync.SyncStatusWidgetState import com.anytypeio.anytype.presentation.sync.toSyncStatusWidgetState import com.anytypeio.anytype.presentation.sync.updateStatus @@ -2821,7 +2821,7 @@ class ObjectSetViewModel( //endregion //region SYNC STATUS - val spaceSyncStatus = MutableStateFlow(SpaceSyncAndP2PStatusState.Initial) + val spaceSyncStatus = MutableStateFlow(SpaceSyncAndP2PStatusState.Init) val syncStatusWidget = MutableStateFlow(SyncStatusWidgetState.Hidden) fun onSyncStatusBadgeClicked() { @@ -2833,6 +2833,9 @@ class ObjectSetViewModel( jobs += viewModelScope.launch { spaceSyncAndP2PStatusProvider .observe() + .catch { + Timber.e(it, "Error while observing sync status") + } .collect { syncAndP2pState -> spaceSyncStatus.value = syncAndP2pState syncStatusWidget.value = syncStatusWidget.value.updateStatus(syncAndP2pState) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModelFactory.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModelFactory.kt index 226c852125..b95126aae0 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModelFactory.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModelFactory.kt @@ -12,6 +12,7 @@ import com.anytypeio.anytype.domain.collections.AddObjectToCollection import com.anytypeio.anytype.domain.cover.SetDocCoverImage import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer import com.anytypeio.anytype.domain.misc.DateProvider import com.anytypeio.anytype.domain.misc.UrlBuilder @@ -38,7 +39,6 @@ import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription import com.anytypeio.anytype.presentation.sets.viewer.ViewerDelegate -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.Dispatcher diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sync/SpaceSyncAndP2PStatusProvider.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sync/SpaceSyncAndP2PStatusProvider.kt index 0ce009f67d..082d947ded 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sync/SpaceSyncAndP2PStatusProvider.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sync/SpaceSyncAndP2PStatusProvider.kt @@ -1,74 +1,44 @@ package com.anytypeio.anytype.presentation.sync -import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate +import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate -import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer -import com.anytypeio.anytype.domain.workspace.P2PStatusChannel -import com.anytypeio.anytype.domain.workspace.SpaceSyncStatusChannel +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider +import com.anytypeio.anytype.domain.workspace.SpaceManager +import com.anytypeio.anytype.domain.workspace.SyncAndP2PStatusChannel import javax.inject.Inject -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.onStart +import timber.log.Timber -interface SpaceSyncAndP2PStatusProvider { +class SpaceSyncAndP2PStatusProviderImpl @Inject constructor( + private val spaceSyncStatusChannel: SyncAndP2PStatusChannel, + private val spaceManager: SpaceManager +) : SpaceSyncAndP2PStatusProvider { - suspend fun observe(): Flow + override fun observe(): Flow = + combine( + spaceManager.observe(), + spaceSyncStatusChannel.p2pStatus(), + spaceSyncStatusChannel.syncStatus() + ) { activeSpace, p2pStatus, syncStatus -> + val p2PStatusUpdate = p2pStatus[activeSpace.space] + val spaceSyncUpdate = syncStatus[activeSpace.space] - class Impl @Inject constructor( - private val activeSpace: ActiveSpaceMemberSubscriptionContainer, - private val spaceSyncStatusChannel: SpaceSyncStatusChannel, - private val p2PStatusChannel: P2PStatusChannel - ) : SpaceSyncAndP2PStatusProvider { - - @OptIn(ExperimentalCoroutinesApi::class) - override suspend fun observe(): Flow { - return activeSpace - .observe() - .flatMapLatest { activeSpace -> - when (activeSpace) { - is ActiveSpaceMemberSubscriptionContainer.Store.Data -> { - observeSpaceSyncStatus(spaceId = activeSpace.config.space) - } - - ActiveSpaceMemberSubscriptionContainer.Store.Empty -> { - emptyFlow() - } - } - } - } - - private fun observeSpaceSyncStatus(spaceId: Id): Flow { - val syncFlow = - spaceSyncStatusChannel.observe(spaceId).onStart { emit(SpaceSyncUpdate.Initial) } - val p2pFlow = - p2PStatusChannel.observe(spaceId).onStart { emit(P2PStatusUpdate.Initial) } - - return combine(syncFlow, p2pFlow) { syncStatus, p2PStatus -> - if (syncStatus is SpaceSyncUpdate.Initial && p2PStatus is P2PStatusUpdate.Initial) { - SpaceSyncAndP2PStatusState.Initial - } else { - SpaceSyncAndP2PStatusState.Success( - spaceSyncUpdate = syncStatus, - p2PStatusUpdate = p2PStatus - ) - } + if (p2PStatusUpdate == null && spaceSyncUpdate == null) { + SpaceSyncAndP2PStatusState.Init + } else { + SpaceSyncAndP2PStatusState.Success( + spaceSyncUpdate = spaceSyncUpdate ?: SpaceSyncUpdate.Initial, + p2PStatusUpdate = p2PStatusUpdate ?: P2PStatusUpdate.Initial + ) } + }.catch { e -> + Timber.e(e, "Error observing sync and P2P status") + emit(SpaceSyncAndP2PStatusState.Error("Error observing sync and P2P status, ${e.message}")) } } -} - -sealed class SpaceSyncAndP2PStatusState { - data object Initial : SpaceSyncAndP2PStatusState() - data class Error(val message: String) : SpaceSyncAndP2PStatusState() - data class Success( - val spaceSyncUpdate: SpaceSyncUpdate, - val p2PStatusUpdate: P2PStatusUpdate - ) : SpaceSyncAndP2PStatusState() -} fun SyncStatusWidgetState.updateStatus(newState: SpaceSyncAndP2PStatusState): SyncStatusWidgetState { return when (this) { @@ -89,7 +59,7 @@ fun SpaceSyncAndP2PStatusState.toSyncStatusWidgetState(): SyncStatusWidgetState SyncStatusWidgetState.Error(message = message) } - SpaceSyncAndP2PStatusState.Initial -> { + SpaceSyncAndP2PStatusState.Init -> { SyncStatusWidgetState.Hidden } @@ -106,7 +76,7 @@ sealed class SyncStatusWidgetState { data object Hidden : SyncStatusWidgetState() data class Error(val message: String) : SyncStatusWidgetState() data class Success( - val spaceSyncUpdate: SpaceSyncUpdate, - val p2PStatusUpdate: P2PStatusUpdate + val spaceSyncUpdate: SpaceSyncUpdate = SpaceSyncUpdate.Initial, + val p2PStatusUpdate: P2PStatusUpdate = P2PStatusUpdate.Initial ) : SyncStatusWidgetState() } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt index 4816c95bd7..62fe1bbc83 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt @@ -60,6 +60,7 @@ import com.anytypeio.anytype.domain.config.Gateway import com.anytypeio.anytype.domain.cover.SetDocCoverImage import com.anytypeio.anytype.domain.download.DownloadFile import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon import com.anytypeio.anytype.domain.launch.GetDefaultObjectType import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer @@ -119,7 +120,6 @@ import com.anytypeio.anytype.presentation.editor.render.parseThemeBackgroundColo import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder import com.anytypeio.anytype.presentation.navigation.AppNavigation -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory import com.anytypeio.anytype.presentation.util.CoroutinesTestRule diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorPresentationTestSetup.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorPresentationTestSetup.kt index e2db2a01da..43db3ead99 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorPresentationTestSetup.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorPresentationTestSetup.kt @@ -52,6 +52,7 @@ import com.anytypeio.anytype.domain.config.Gateway import com.anytypeio.anytype.domain.cover.SetDocCoverImage import com.anytypeio.anytype.domain.download.DownloadFile import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon import com.anytypeio.anytype.domain.launch.GetDefaultObjectType import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer @@ -111,7 +112,6 @@ import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder import com.anytypeio.anytype.presentation.home.UserPermissionProviderStub -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory import com.anytypeio.anytype.presentation.util.Dispatcher diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt index ebb8a965d0..5594a2e51f 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt @@ -36,6 +36,7 @@ import com.anytypeio.anytype.domain.cover.SetDocCoverImage import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject import com.anytypeio.anytype.domain.dataview.interactor.UpdateDataViewViewer import com.anytypeio.anytype.domain.event.interactor.InterceptEvents +import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.domain.launch.GetDefaultObjectType import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer @@ -81,7 +82,6 @@ import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription import com.anytypeio.anytype.presentation.sets.updateFormatForSubscription import com.anytypeio.anytype.presentation.sets.viewer.ViewerDelegate -import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProvider import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule import com.anytypeio.anytype.presentation.util.Dispatcher