mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-07 21:37:02 +09:00
DROID-3696 Invite link | Onboarding flow with a no approval invite link (#2469)
This commit is contained in:
parent
ab2f0ea838
commit
42e227ee69
17 changed files with 241 additions and 148 deletions
|
@ -1,7 +1,6 @@
|
|||
package com.anytypeio.anytype.di.feature
|
||||
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerDialog
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.domain.account.AccountStatusChannel
|
||||
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
|
||||
|
@ -13,6 +12,7 @@ import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
|||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.config.UserSettingsRepository
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.device.PathProvider
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
|
@ -77,7 +77,8 @@ object MainEntryModule {
|
|||
globalSubscriptionManager: GlobalSubscriptionManager,
|
||||
spaceInviteResolver: SpaceInviteResolver,
|
||||
spaceManager: SpaceManager,
|
||||
spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer
|
||||
spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
|
||||
pendingIntentStore: PendingIntentStore
|
||||
): MainViewModelFactory = MainViewModelFactory(
|
||||
resumeAccount = resumeAccount,
|
||||
analytics = analytics,
|
||||
|
@ -97,7 +98,8 @@ object MainEntryModule {
|
|||
globalSubscriptionManager = globalSubscriptionManager,
|
||||
spaceInviteResolver = spaceInviteResolver,
|
||||
spaceManager = spaceManager,
|
||||
spaceViews = spaceViewSubscriptionContainer
|
||||
spaceViews = spaceViewSubscriptionContainer,
|
||||
pendingIntentStore = pendingIntentStore
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
@ -30,7 +30,6 @@ import com.anytypeio.anytype.domain.relations.RemoveFromFeaturedRelations
|
|||
import com.anytypeio.anytype.domain.templates.CreateTemplateFromObject
|
||||
import com.anytypeio.anytype.domain.widgets.CreateWidget
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.other.DefaultDeepLinkResolver
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.common.Action
|
||||
import com.anytypeio.anytype.presentation.common.Delegator
|
||||
|
@ -223,11 +222,6 @@ object ObjectMenuModule {
|
|||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun provideDeeplinkResolver() : DeepLinkResolver = DefaultDeepLinkResolver
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
|
@ -377,11 +371,6 @@ object ObjectSetMenuModule {
|
|||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun provideDeeplinkResolver() : DeepLinkResolver = DefaultDeepLinkResolver
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.anytypeio.anytype.di.common.ComponentDependencies
|
|||
import com.anytypeio.anytype.domain.auth.interactor.GetMnemonic
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.device.NetworkConnectionStatus
|
||||
import com.anytypeio.anytype.domain.network.NetworkModeProvider
|
||||
import com.anytypeio.anytype.presentation.onboarding.signup.OnboardingMnemonicViewModel
|
||||
|
@ -61,6 +62,7 @@ interface OnboardingMnemonicDependencies : ComponentDependencies {
|
|||
fun config(): ConfigStorage
|
||||
fun networkModeProvider(): NetworkModeProvider
|
||||
fun networkConnectionStatus(): NetworkConnectionStatus
|
||||
fun pendingIntentStore(): PendingIntentStore
|
||||
}
|
||||
|
||||
@Scope
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.anytypeio.anytype.di.feature.onboarding.signup
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.CrashReporter
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
|
||||
import com.anytypeio.anytype.domain.auth.interactor.CreateAccount
|
||||
|
@ -13,6 +12,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.config.UserSettingsRepository
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.device.PathProvider
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
|
@ -131,6 +131,7 @@ interface OnboardingSoulCreationDependencies : ComponentDependencies {
|
|||
fun awaitAccountStartManager(): AwaitAccountStartManager
|
||||
fun globalSubscriptionManager(): GlobalSubscriptionManager
|
||||
fun stringResourceProvider(): StringResourceProvider
|
||||
fun providePendingIntentStore(): PendingIntentStore
|
||||
}
|
||||
|
||||
@Scope
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.anytypeio.anytype.di.feature.vault
|
|||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerDialog
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
|
||||
|
@ -12,6 +11,7 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
|||
import com.anytypeio.anytype.domain.chats.ChatPreviewContainer
|
||||
import com.anytypeio.anytype.domain.config.UserSettingsRepository
|
||||
import com.anytypeio.anytype.domain.debugging.Logger
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.misc.AppActionManager
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
|
@ -83,4 +83,5 @@ interface VaultComponentDependencies : ComponentDependencies {
|
|||
fun awaitAccount(): AwaitAccountStartManager
|
||||
fun profileContainer(): ProfileSubscriptionManager
|
||||
fun chatPreviewContainer(): ChatPreviewContainer
|
||||
fun pendingIntentStore(): PendingIntentStore
|
||||
}
|
|
@ -13,10 +13,12 @@ import com.anytypeio.anytype.domain.chats.ChatPreviewContainer
|
|||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.debugging.DebugAccountSelectTrace
|
||||
import com.anytypeio.anytype.domain.debugging.Logger
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.device.NetworkConnectionStatus
|
||||
import com.anytypeio.anytype.domain.device.DeviceTokenStoringService
|
||||
import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.multiplayer.DefaultUserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
|
@ -37,6 +39,7 @@ 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.other.DefaultDeepLinkResolver
|
||||
import com.anytypeio.anytype.presentation.sync.SpaceSyncAndP2PStatusProviderImpl
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
@ -298,4 +301,14 @@ object SubscriptionsModule {
|
|||
dispatchers = dispatchers,
|
||||
scope = scope
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDeeplinkResolver() : DeepLinkResolver = DefaultDeepLinkResolver
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePendingIntentStore(): PendingIntentStore = PendingIntentStore()
|
||||
}
|
|
@ -455,7 +455,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
|
|||
intent.data?.let { uri ->
|
||||
val data = uri.toString()
|
||||
if (DefaultDeepLinkResolver.isDeepLink(data)) {
|
||||
vm.onNewDeepLink(DefaultDeepLinkResolver.resolve(data))
|
||||
vm.handleNewDeepLink(DefaultDeepLinkResolver.resolve(data))
|
||||
|
||||
// Optionally clear to prevent repeat
|
||||
intent.action = null
|
||||
|
@ -544,7 +544,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
|
|||
|
||||
when {
|
||||
checkDeepLink && DefaultDeepLinkResolver.isDeepLink(raw) -> {
|
||||
vm.onNewDeepLink(DefaultDeepLinkResolver.resolve(raw))
|
||||
vm.handleNewDeepLink(DefaultDeepLinkResolver.resolve(raw))
|
||||
}
|
||||
raw.isNotEmpty() && !DefaultDeepLinkResolver.isDeepLink(raw) -> {
|
||||
vm.onIntentTextShare(raw)
|
||||
|
|
|
@ -145,7 +145,7 @@ class ShareSpaceFragment : BaseBottomSheetComposeFragment() {
|
|||
}
|
||||
dialog.show(childFragmentManager, null)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while navigation")
|
||||
Timber.e(it, "Error while showing remove member warning")
|
||||
}
|
||||
}
|
||||
is Command.ShowStopSharingWarning -> {
|
||||
|
|
|
@ -78,7 +78,7 @@ fun MnemonicPhraseScreenWrapper(
|
|||
copyMnemonicToClipboard = copyMnemonicToClipboard,
|
||||
mnemonicColorPalette = mnemonicColorPalette,
|
||||
onGoToAppClicked = {
|
||||
vm.onGoToTheAppClicked(
|
||||
vm.handleAppEntryClick(
|
||||
space = space,
|
||||
startingObject = startingObject
|
||||
)
|
||||
|
|
|
@ -14,7 +14,6 @@ import androidx.fragment.app.viewModels
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.BuildConfig.USE_EDGE_TO_EDGE
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_utils.ext.argOrNull
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
|
@ -245,6 +244,7 @@ class VaultFragment : BaseComposeFragment() {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
proceedWithDeepLinks()
|
||||
vm.processPendingDeeplink()
|
||||
}
|
||||
|
||||
private fun proceedWithDeepLinks() {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.anytypeio.anytype.domain.deeplink
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Store for keeping pending invite deeplinks in memory.
|
||||
* This is used to handle invite deeplinks that were received while user was not logged in.
|
||||
*/
|
||||
@Singleton
|
||||
class PendingIntentStore @Inject constructor() {
|
||||
private var deepLinkInvite: String? = null
|
||||
|
||||
fun setDeepLinkInvite(link: String?) {
|
||||
deepLinkInvite = link
|
||||
}
|
||||
|
||||
fun getDeepLinkInvite(): String? = deepLinkInvite
|
||||
|
||||
fun clearDeepLinkInvite() {
|
||||
deepLinkInvite = null
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import com.anytypeio.anytype.domain.auth.model.AuthStatus
|
|||
import com.anytypeio.anytype.domain.base.BaseUseCase
|
||||
import com.anytypeio.anytype.domain.base.Interactor
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
|
@ -69,7 +70,8 @@ class MainViewModel(
|
|||
private val globalSubscriptionManager: GlobalSubscriptionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val spaceViews: SpaceViewSubscriptionContainer
|
||||
private val spaceViews: SpaceViewSubscriptionContainer,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : ViewModel(),
|
||||
NotificationActionDelegate by notificationActionDelegate,
|
||||
DeepLinkToObjectDelegate by deepLinkToObjectDelegate {
|
||||
|
@ -308,8 +310,34 @@ class MainViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun onNewDeepLink(deeplink: DeepLinkResolver.Action) {
|
||||
fun handleNewDeepLink(deeplink: DeepLinkResolver.Action) {
|
||||
deepLinkJobs.cancel()
|
||||
viewModelScope.launch {
|
||||
checkAuthorizationStatus(Unit).process(
|
||||
failure = { Timber.e(it, "Failed to check authentication status") },
|
||||
success = { authStatus -> processDeepLinkBasedOnAuth(authStatus, deeplink) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processDeepLinkBasedOnAuth(
|
||||
authStatus: AuthStatus,
|
||||
deeplink: DeepLinkResolver.Action
|
||||
) {
|
||||
if (authStatus == AuthStatus.UNAUTHORIZED && deeplink is DeepLinkResolver.Action.Invite) {
|
||||
saveInviteDeepLinkForLater(deeplink)
|
||||
} else {
|
||||
Timber.d("Proceeding with deeplink: $deeplink")
|
||||
launchDeepLinkProcessing(deeplink)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveInviteDeepLinkForLater(deeplink: DeepLinkResolver.Action.Invite) {
|
||||
pendingIntentStore.setDeepLinkInvite(deeplink.link)
|
||||
Timber.d("Saved invite deeplink for later processing: ${deeplink.link}")
|
||||
}
|
||||
|
||||
private fun launchDeepLinkProcessing(deeplink: DeepLinkResolver.Action) {
|
||||
deepLinkJobs += viewModelScope.launch {
|
||||
awaitAccountStartManager
|
||||
.awaitStart()
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.anytypeio.anytype.domain.auth.interactor.CheckAuthorizationStatus
|
|||
import com.anytypeio.anytype.domain.auth.interactor.Logout
|
||||
import com.anytypeio.anytype.domain.auth.interactor.ResumeAccount
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
|
@ -42,7 +43,8 @@ class MainViewModelFactory @Inject constructor(
|
|||
private val globalSubscriptionManager: GlobalSubscriptionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val spaceViews: SpaceViewSubscriptionContainer
|
||||
private val spaceViews: SpaceViewSubscriptionContainer,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
|
@ -66,6 +68,7 @@ class MainViewModelFactory @Inject constructor(
|
|||
globalSubscriptionManager = globalSubscriptionManager,
|
||||
spaceInviteResolver = spaceInviteResolver,
|
||||
spaceManager = spaceManager,
|
||||
spaceViews = spaceViews
|
||||
spaceViews = spaceViews,
|
||||
pendingIntentStore = pendingIntentStore
|
||||
) as T
|
||||
}
|
|
@ -416,23 +416,39 @@ class ShareSpaceViewModel(
|
|||
}
|
||||
|
||||
fun onRemoveMemberAccepted(identity: Id) {
|
||||
Timber.d("onRemoveMemberAccepted")
|
||||
Timber.d("onRemoveMemberAccepted: Starting member removal process for identity: $identity")
|
||||
viewModelScope.launch {
|
||||
removeSpaceMembers.async(
|
||||
RemoveSpaceMembers.Params(
|
||||
space = vmParams.space,
|
||||
identities = listOf(identity)
|
||||
try {
|
||||
removeSpaceMembers.async(
|
||||
RemoveSpaceMembers.Params(
|
||||
space = vmParams.space,
|
||||
identities = listOf(identity)
|
||||
)
|
||||
).fold(
|
||||
onFailure = { e ->
|
||||
Timber.e(
|
||||
e,
|
||||
"Error while removing space member (identity: $identity, space: ${vmParams.space})"
|
||||
)
|
||||
when (e) {
|
||||
is java.net.SocketTimeoutException,
|
||||
is java.net.UnknownHostException,
|
||||
is java.io.IOException -> {
|
||||
sendToast("Network error occurred. Please check your connection and try again.")
|
||||
}
|
||||
|
||||
else -> proceedWithMultiplayerError(e)
|
||||
}
|
||||
},
|
||||
onSuccess = {
|
||||
Timber.d("Successfully removed space member (identity: $identity, space: ${vmParams.space})")
|
||||
analytics.sendEvent(eventName = removeSpaceMember)
|
||||
}
|
||||
)
|
||||
).fold(
|
||||
onFailure = { e ->
|
||||
Timber.e(e, "Error while removing space member")
|
||||
proceedWithMultiplayerError(e)
|
||||
},
|
||||
onSuccess = {
|
||||
Timber.d("Successfully removed space member")
|
||||
analytics.sendEvent(eventName = removeSpaceMember)
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Unexpected error while removing space member")
|
||||
sendToast("An unexpected error occurred. Please try again.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.ClickOnboardingButton
|
||||
import com.anytypeio.anytype.core_models.DeviceNetworkType
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.NetworkMode
|
||||
|
@ -16,6 +17,7 @@ import com.anytypeio.anytype.domain.network.NetworkModeProvider
|
|||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsOnboardingClickEvent
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsOnboardingScreenEvent
|
||||
import com.anytypeio.anytype.presentation.extension.sendOpenAccountEvent
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -27,7 +29,8 @@ class OnboardingMnemonicViewModel @Inject constructor(
|
|||
private val analytics: Analytics,
|
||||
private val configStorage: ConfigStorage,
|
||||
private val networkModeProvider: NetworkModeProvider,
|
||||
private val networkConnectionStatus: NetworkConnectionStatus
|
||||
private val networkConnectionStatus: NetworkConnectionStatus,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : ViewModel() {
|
||||
|
||||
val state = MutableStateFlow<State>(State.Idle(""))
|
||||
|
@ -35,9 +38,7 @@ class OnboardingMnemonicViewModel @Inject constructor(
|
|||
|
||||
init {
|
||||
Timber.i("OnboardingMnemonicViewModel, init")
|
||||
viewModelScope.sendAnalyticsOnboardingScreenEvent(analytics,
|
||||
EventsDictionary.ScreenOnboardingStep.PHRASE
|
||||
)
|
||||
sendScreenViewAnalytics()
|
||||
viewModelScope.launch {
|
||||
proceedWithMnemonicPhrase()
|
||||
}
|
||||
|
@ -47,92 +48,73 @@ class OnboardingMnemonicViewModel @Inject constructor(
|
|||
if (state.value is State.Mnemonic) {
|
||||
state.value = State.MnemonicOpened((state.value as State.Mnemonic).mnemonicPhrase)
|
||||
}
|
||||
viewModelScope.sendAnalyticsOnboardingClickEvent(
|
||||
analytics = analytics,
|
||||
type = EventsDictionary.ClickOnboardingButton.SHOW_AND_COPY,
|
||||
step = EventsDictionary.ScreenOnboardingStep.PHRASE
|
||||
)
|
||||
sendClickAnalytics(ClickOnboardingButton.SHOW_AND_COPY)
|
||||
}
|
||||
|
||||
fun onCheckLaterClicked(
|
||||
space: Id,
|
||||
startingObject: Id?,
|
||||
) {
|
||||
viewModelScope.sendAnalyticsOnboardingClickEvent(
|
||||
analytics = analytics,
|
||||
type = EventsDictionary.ClickOnboardingButton.CHECK_LATER,
|
||||
step = EventsDictionary.ScreenOnboardingStep.PHRASE
|
||||
)
|
||||
if (shouldShowEmail()) {
|
||||
viewModelScope.launch {
|
||||
commands.emit(
|
||||
Command.NavigateToAddEmailScreen(
|
||||
startingObject = startingObject,
|
||||
space = space
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
viewModelScope.launch {
|
||||
val config = configStorage.getOrNull()
|
||||
if (config != null) {
|
||||
analytics.sendOpenAccountEvent(
|
||||
analytics = config.analytics
|
||||
)
|
||||
} else {
|
||||
Timber.w("config was missing before the end of onboarding")
|
||||
}
|
||||
if (!startingObject.isNullOrEmpty()) {
|
||||
commands.emit(
|
||||
Command.OpenStartingObject(
|
||||
space = SpaceId(space),
|
||||
startingObject = startingObject
|
||||
)
|
||||
)
|
||||
} else {
|
||||
commands.emit(Command.OpenVault)
|
||||
}
|
||||
}
|
||||
fun onCheckLaterClicked(space: Id, startingObject: Id?) {
|
||||
sendClickAnalytics(ClickOnboardingButton.CHECK_LATER)
|
||||
viewModelScope.launch {
|
||||
navigateNextStep(space, startingObject)
|
||||
}
|
||||
}
|
||||
|
||||
fun onGoToTheAppClicked(
|
||||
space: Id,
|
||||
startingObject: Id?,
|
||||
) {
|
||||
if (shouldShowEmail()) {
|
||||
viewModelScope.launch {
|
||||
commands.emit(
|
||||
Command.NavigateToAddEmailScreen(
|
||||
startingObject = startingObject,
|
||||
space = space
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
viewModelScope.launch {
|
||||
val config = configStorage.getOrNull()
|
||||
if (config != null) {
|
||||
analytics.sendOpenAccountEvent(
|
||||
analytics = config.analytics
|
||||
)
|
||||
} else {
|
||||
Timber.w("config was missing before the end of onboarding")
|
||||
}
|
||||
if (!startingObject.isNullOrEmpty()) {
|
||||
commands.emit(
|
||||
Command.OpenStartingObject(
|
||||
space = SpaceId(space),
|
||||
startingObject = startingObject
|
||||
)
|
||||
)
|
||||
} else {
|
||||
commands.emit(Command.OpenVault)
|
||||
}
|
||||
}
|
||||
fun handleAppEntryClick(space: Id, startingObject: Id?) {
|
||||
viewModelScope.launch {
|
||||
navigateNextStep(space, startingObject)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun navigateNextStep(space: Id, startingObject: Id?) {
|
||||
if (shouldShowEmail()) {
|
||||
emitNavigateToAddEmail(space, startingObject)
|
||||
return
|
||||
}
|
||||
|
||||
logOpenAccountIfAvailable()
|
||||
|
||||
val deeplink = pendingIntentStore.getDeepLinkInvite()
|
||||
when {
|
||||
!deeplink.isNullOrEmpty() -> emitCommand(Command.OpenVault)
|
||||
!startingObject.isNullOrEmpty() -> emitCommand(
|
||||
Command.OpenStartingObject(
|
||||
space = SpaceId(space),
|
||||
startingObject = startingObject
|
||||
)
|
||||
)
|
||||
|
||||
else -> emitCommand(Command.OpenVault)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun emitNavigateToAddEmail(space: Id, startingObject: Id?) {
|
||||
emitCommand(
|
||||
Command.NavigateToAddEmailScreen(
|
||||
space = space,
|
||||
startingObject = startingObject
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun emitCommand(command: Command) {
|
||||
commands.emit(command)
|
||||
}
|
||||
|
||||
private suspend fun logOpenAccountIfAvailable() {
|
||||
val config = configStorage.getOrNull()
|
||||
if (config != null) {
|
||||
analytics.sendOpenAccountEvent(config.analytics)
|
||||
} else {
|
||||
Timber.w("Missing config during onboarding")
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendScreenViewAnalytics() {
|
||||
viewModelScope.sendAnalyticsOnboardingScreenEvent(
|
||||
analytics,
|
||||
EventsDictionary.ScreenOnboardingStep.PHRASE
|
||||
)
|
||||
}
|
||||
|
||||
fun shouldShowEmail(): Boolean {
|
||||
val networkStatus = networkConnectionStatus.getCurrentNetworkType()
|
||||
if (networkStatus == DeviceNetworkType.NOT_CONNECTED) {
|
||||
|
@ -152,13 +134,21 @@ class OnboardingMnemonicViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun sendClickAnalytics(type: ClickOnboardingButton) {
|
||||
viewModelScope.sendAnalyticsOnboardingClickEvent(
|
||||
analytics = analytics,
|
||||
type = type,
|
||||
step = EventsDictionary.ScreenOnboardingStep.PHRASE
|
||||
)
|
||||
}
|
||||
|
||||
sealed interface State {
|
||||
|
||||
val mnemonicPhrase: String
|
||||
|
||||
class Idle(override val mnemonicPhrase: String): State
|
||||
class Mnemonic(override val mnemonicPhrase: String): State
|
||||
class MnemonicOpened(override val mnemonicPhrase: String): State
|
||||
class Idle(override val mnemonicPhrase: String) : State
|
||||
class Mnemonic(override val mnemonicPhrase: String) : State
|
||||
class MnemonicOpened(override val mnemonicPhrase: String) : State
|
||||
}
|
||||
|
||||
class Factory @Inject constructor(
|
||||
|
@ -166,7 +156,8 @@ class OnboardingMnemonicViewModel @Inject constructor(
|
|||
private val analytics: Analytics,
|
||||
private val configStorage: ConfigStorage,
|
||||
private val networkModeProvider: NetworkModeProvider,
|
||||
private val networkConnectionStatus: NetworkConnectionStatus
|
||||
private val networkConnectionStatus: NetworkConnectionStatus,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
|
@ -175,7 +166,8 @@ class OnboardingMnemonicViewModel @Inject constructor(
|
|||
analytics = analytics,
|
||||
configStorage = configStorage,
|
||||
networkModeProvider = networkModeProvider,
|
||||
networkConnectionStatus = networkConnectionStatus
|
||||
networkConnectionStatus = networkConnectionStatus,
|
||||
pendingIntentStore = pendingIntentStore
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
@ -186,6 +178,7 @@ class OnboardingMnemonicViewModel @Inject constructor(
|
|||
val space: SpaceId,
|
||||
val startingObject: Id
|
||||
) : Command()
|
||||
|
||||
data class NavigateToAddEmailScreen(
|
||||
val startingObject: String?,
|
||||
val space: String
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.anytypeio.anytype.domain.auth.interactor.CreateAccount
|
|||
import com.anytypeio.anytype.domain.auth.interactor.SetupWallet
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.device.PathProvider
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.`object`.ImportGetStartedUseCase
|
||||
|
@ -54,6 +55,7 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
|
|||
private val spaceManager: SpaceManager,
|
||||
private val stringProvider: StringResourceProvider,
|
||||
private val setMembershipEmail: SetMembershipEmail,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : BaseViewModel() {
|
||||
|
||||
init {
|
||||
|
@ -295,16 +297,26 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
|
|||
private fun proceedWithNavigation(space: Id, startingObject: String?) {
|
||||
viewModelScope.launch {
|
||||
sendOpenAccountAnalytics()
|
||||
if (!startingObject.isNullOrEmpty()) {
|
||||
navigation.emit(
|
||||
OpenStartingObject(
|
||||
space = SpaceId(space),
|
||||
startingObject = startingObject
|
||||
)
|
||||
navigateNextStep(
|
||||
space = space,
|
||||
startingObject = startingObject
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun navigateNextStep(space: Id, startingObject: Id?) {
|
||||
delay(LOADING_AFTER_SUCCESS_DELAY)
|
||||
val deeplink = pendingIntentStore.getDeepLinkInvite()
|
||||
when {
|
||||
!deeplink.isNullOrEmpty() -> navigation.emit(Navigation.OpenVault)
|
||||
!startingObject.isNullOrEmpty() -> navigation.emit(
|
||||
OpenStartingObject(
|
||||
space = SpaceId(space),
|
||||
startingObject = startingObject
|
||||
)
|
||||
} else {
|
||||
navigation.emit(Navigation.OpenVault)
|
||||
}
|
||||
)
|
||||
|
||||
else -> navigation.emit(Navigation.OpenVault)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,7 +371,8 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
|
|||
private val globalSubscriptionManager: GlobalSubscriptionManager,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val stringProvider: StringResourceProvider,
|
||||
private val setMembershipEmail: SetMembershipEmail
|
||||
private val setMembershipEmail: SetMembershipEmail,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
|
@ -378,7 +391,8 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
|
|||
globalSubscriptionManager = globalSubscriptionManager,
|
||||
spaceManager = spaceManager,
|
||||
stringProvider = stringProvider,
|
||||
setMembershipEmail = setMembershipEmail
|
||||
setMembershipEmail = setMembershipEmail,
|
||||
pendingIntentStore = pendingIntentStore
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.anytypeio.anytype.core_models.primitives.Space
|
|||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.chats.ChatPreviewContainer
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.misc.AppActionManager
|
||||
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -48,7 +49,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
@ -64,8 +64,6 @@ class VaultViewModel(
|
|||
private val getSpaceWallpapers: GetSpaceWallpapers,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val saveCurrentSpace: SaveCurrentSpace,
|
||||
private val getVaultSettings: GetVaultSettings,
|
||||
private val setVaultSettings: SetVaultSettings,
|
||||
private val observeVaultSettings: ObserveVaultSettings,
|
||||
private val setVaultSpaceOrder: SetVaultSpaceOrder,
|
||||
private val analytics: Analytics,
|
||||
|
@ -73,7 +71,8 @@ class VaultViewModel(
|
|||
private val appActionManager: AppActionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val profileContainer: ProfileSubscriptionManager,
|
||||
private val chatPreviewContainer: ChatPreviewContainer
|
||||
private val chatPreviewContainer: ChatPreviewContainer,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : NavigationViewModel<VaultViewModel.Navigation>(), DeepLinkToObjectDelegate by deepLinkToObjectDelegate {
|
||||
|
||||
val spaces = MutableStateFlow<List<VaultSpaceView>>(emptyList())
|
||||
|
@ -261,6 +260,17 @@ class VaultViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun processPendingDeeplink() {
|
||||
viewModelScope.launch {
|
||||
delay(1000) // Simulate some delay
|
||||
pendingIntentStore.getDeepLinkInvite()?.let { deeplink ->
|
||||
Timber.d("Processing pending deeplink: $deeplink")
|
||||
commands.emit(Command.Deeplink.Invite(deeplink))
|
||||
pendingIntentStore.clearDeepLinkInvite()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithSavingCurrentSpace(
|
||||
targetSpace: String,
|
||||
chat: Id?,
|
||||
|
@ -357,8 +367,6 @@ class VaultViewModel(
|
|||
private val urlBuilder: UrlBuilder,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val saveCurrentSpace: SaveCurrentSpace,
|
||||
private val getVaultSettings: GetVaultSettings,
|
||||
private val setVaultSettings: SetVaultSettings,
|
||||
private val setVaultSpaceOrder: SetVaultSpaceOrder,
|
||||
private val observeVaultSettings: ObserveVaultSettings,
|
||||
private val analytics: Analytics,
|
||||
|
@ -366,7 +374,8 @@ class VaultViewModel(
|
|||
private val appActionManager: AppActionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val profileContainer: ProfileSubscriptionManager,
|
||||
private val chatPreviewContainer: ChatPreviewContainer
|
||||
private val chatPreviewContainer: ChatPreviewContainer,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
|
@ -377,8 +386,6 @@ class VaultViewModel(
|
|||
urlBuilder = urlBuilder,
|
||||
spaceManager = spaceManager,
|
||||
saveCurrentSpace = saveCurrentSpace,
|
||||
getVaultSettings = getVaultSettings,
|
||||
setVaultSettings = setVaultSettings,
|
||||
setVaultSpaceOrder = setVaultSpaceOrder,
|
||||
observeVaultSettings = observeVaultSettings,
|
||||
analytics = analytics,
|
||||
|
@ -386,7 +393,8 @@ class VaultViewModel(
|
|||
appActionManager = appActionManager,
|
||||
spaceInviteResolver = spaceInviteResolver,
|
||||
profileContainer = profileContainer,
|
||||
chatPreviewContainer = chatPreviewContainer
|
||||
chatPreviewContainer = chatPreviewContainer,
|
||||
pendingIntentStore = pendingIntentStore
|
||||
) as T
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue