1
0
Fork 0
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:
Konstantin Ivanov 2025-05-27 10:13:05 +02:00 committed by konstantiniiv
parent ab2f0ea838
commit 42e227ee69
17 changed files with 241 additions and 148 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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()
}

View file

@ -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)

View file

@ -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 -> {

View file

@ -78,7 +78,7 @@ fun MnemonicPhraseScreenWrapper(
copyMnemonicToClipboard = copyMnemonicToClipboard,
mnemonicColorPalette = mnemonicColorPalette,
onGoToAppClicked = {
vm.onGoToTheAppClicked(
vm.handleAppEntryClick(
space = space,
startingObject = startingObject
)

View file

@ -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() {

View file

@ -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
}
}

View file

@ -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()

View file

@ -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
}

View file

@ -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.")
}
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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
}