mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3397 Deep Links | Fix | Misc. fixes (#2139)
This commit is contained in:
parent
4f56a5ffc5
commit
9ab300175e
9 changed files with 250 additions and 86 deletions
|
@ -75,7 +75,9 @@ object MainEntryModule {
|
|||
awaitAccountStartManager: AwaitAccountStartManager,
|
||||
membershipProvider: MembershipProvider,
|
||||
globalSubscriptionManager: GlobalSubscriptionManager,
|
||||
spaceInviteResolver: SpaceInviteResolver
|
||||
spaceInviteResolver: SpaceInviteResolver,
|
||||
spaceManager: SpaceManager,
|
||||
spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer
|
||||
): MainViewModelFactory = MainViewModelFactory(
|
||||
resumeAccount = resumeAccount,
|
||||
analytics = analytics,
|
||||
|
@ -93,7 +95,9 @@ object MainEntryModule {
|
|||
awaitAccountStartManager = awaitAccountStartManager,
|
||||
membershipProvider = membershipProvider,
|
||||
globalSubscriptionManager = globalSubscriptionManager,
|
||||
spaceInviteResolver = spaceInviteResolver
|
||||
spaceInviteResolver = spaceInviteResolver,
|
||||
spaceManager = spaceManager,
|
||||
spaceViews = spaceViewSubscriptionContainer
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.anytypeio.anytype.core_models.Url
|
|||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import timber.log.Timber
|
||||
|
||||
const val DEEP_LINK_PATTERN = "anytype://"
|
||||
|
||||
|
@ -93,6 +94,8 @@ object DefaultDeepLinkResolver : DeepLinkResolver {
|
|||
)
|
||||
}
|
||||
else -> DeepLinkResolver.Action.Unknown
|
||||
}.also {
|
||||
Timber.d("Resolving deep link: $deeplink")
|
||||
}
|
||||
|
||||
override fun createObjectDeepLink(obj: Id, space: SpaceId): Url {
|
||||
|
|
|
@ -50,6 +50,7 @@ import com.anytypeio.anytype.presentation.notifications.NotificationAction
|
|||
import com.anytypeio.anytype.presentation.notifications.NotificationCommand
|
||||
import com.anytypeio.anytype.presentation.wallpaper.WallpaperColor
|
||||
import com.anytypeio.anytype.presentation.wallpaper.WallpaperView
|
||||
import com.anytypeio.anytype.ui.chats.ChatFragment
|
||||
import com.anytypeio.anytype.ui.date.DateObjectFragment
|
||||
import com.anytypeio.anytype.ui.editor.CreateObjectFragment
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
|
@ -193,82 +194,38 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
|
|||
}
|
||||
}
|
||||
is Command.Navigate -> {
|
||||
when(val dest = command.destination) {
|
||||
is OpenObjectNavigation.OpenDataView -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
R.id.dataViewNavigation,
|
||||
args = ObjectSetFragment.args(
|
||||
ctx = dest.target,
|
||||
space = dest.space
|
||||
),
|
||||
navOptions = NavOptions.Builder()
|
||||
.setPopUpTo(R.id.homeScreen, true)
|
||||
.build()
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while data view navigation")
|
||||
}
|
||||
}
|
||||
is OpenObjectNavigation.OpenParticipant -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
R.id.participantScreen,
|
||||
ParticipantFragment.args(
|
||||
objectId = dest.target,
|
||||
space = dest.space
|
||||
)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.w("Error while opening participant screen")
|
||||
}
|
||||
}
|
||||
is OpenObjectNavigation.OpenEditor -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
R.id.objectNavigation,
|
||||
args = EditorFragment.args(
|
||||
ctx = dest.target,
|
||||
space = dest.space
|
||||
),
|
||||
navOptions = NavOptions.Builder()
|
||||
.setPopUpTo(R.id.homeScreen, true)
|
||||
.build()
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while editor navigation")
|
||||
}
|
||||
}
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
toast("Cannot open chat from here")
|
||||
}
|
||||
is OpenObjectNavigation.UnexpectedLayoutError -> {
|
||||
toast(getString(R.string.error_unexpected_layout))
|
||||
}
|
||||
OpenObjectNavigation.NonValidObject -> {
|
||||
toast(getString(R.string.error_non_valid_object))
|
||||
}
|
||||
is OpenObjectNavigation.OpenDateObject -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
R.id.dateObjectScreen,
|
||||
args = DateObjectFragment.args(
|
||||
objectId = dest.target,
|
||||
space = dest.space
|
||||
),
|
||||
navOptions = Builder()
|
||||
.setPopUpTo(R.id.homeScreen, true)
|
||||
.build()
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while date object navigation")
|
||||
}
|
||||
}
|
||||
}
|
||||
proceedWithOpenObjectNavigation(command.destination)
|
||||
}
|
||||
is Command.Deeplink.DeepLinkToObjectNotWorking -> {
|
||||
toast(getString(R.string.multiplayer_deeplink_to_your_object_error))
|
||||
}
|
||||
is Command.Deeplink.DeepLinkToObject -> {
|
||||
when(val effect = command.sideEffect) {
|
||||
is Command.Deeplink.DeepLinkToObject.SideEffect.SwitchSpace -> {
|
||||
runCatching {
|
||||
val controller = findNavController(R.id.fragment)
|
||||
controller.popBackStack(R.id.vaultScreen, false)
|
||||
if (effect.chat != null) {
|
||||
controller.navigate(
|
||||
R.id.actionOpenChatFromVault,
|
||||
ChatFragment.args(
|
||||
space = command.space,
|
||||
ctx = effect.chat.orEmpty()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
controller.navigate(R.id.actionOpenSpaceFromVault)
|
||||
}
|
||||
proceedWithOpenObjectNavigation(command.navigation)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while switching space when handling deep link to object")
|
||||
}
|
||||
}
|
||||
null -> {
|
||||
proceedWithOpenObjectNavigation(command.navigation)
|
||||
}
|
||||
}
|
||||
}
|
||||
is Command.Deeplink.GalleryInstallation -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
|
@ -319,6 +276,84 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedWithOpenObjectNavigation(dest: OpenObjectNavigation) {
|
||||
when (dest) {
|
||||
is OpenObjectNavigation.OpenDataView -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
R.id.dataViewNavigation,
|
||||
args = ObjectSetFragment.args(
|
||||
ctx = dest.target,
|
||||
space = dest.space
|
||||
),
|
||||
navOptions = Builder()
|
||||
.setPopUpTo(R.id.homeScreen, true)
|
||||
.build()
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while data view navigation")
|
||||
}
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenParticipant -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
R.id.participantScreen,
|
||||
ParticipantFragment.args(
|
||||
objectId = dest.target,
|
||||
space = dest.space
|
||||
)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.w("Error while opening participant screen")
|
||||
}
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenEditor -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
R.id.objectNavigation,
|
||||
args = EditorFragment.args(
|
||||
ctx = dest.target,
|
||||
space = dest.space
|
||||
)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while editor navigation")
|
||||
}
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
toast("Cannot open chat from here")
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.UnexpectedLayoutError -> {
|
||||
toast(getString(R.string.error_unexpected_layout))
|
||||
}
|
||||
|
||||
OpenObjectNavigation.NonValidObject -> {
|
||||
toast(getString(R.string.error_non_valid_object))
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenDateObject -> {
|
||||
runCatching {
|
||||
findNavController(R.id.fragment).navigate(
|
||||
R.id.dateObjectScreen,
|
||||
args = DateObjectFragment.args(
|
||||
objectId = dest.target,
|
||||
space = dest.space
|
||||
),
|
||||
navOptions = Builder()
|
||||
.setPopUpTo(R.id.homeScreen, true)
|
||||
.build()
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while date object navigation")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAppUpdater() {
|
||||
if (featureToggles.isAutoUpdateEnabled) {
|
||||
AppUpdater(this)
|
||||
|
|
|
@ -63,9 +63,6 @@ class CreateSpaceFragment : BaseBottomSheetComposeFragment() {
|
|||
deeplink = null
|
||||
)
|
||||
)
|
||||
// if (command.showMultiplayerTooltip) {
|
||||
// findNavController().navigate(R.id.multiplayerFeatureDialog)
|
||||
// }
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while exiting to vault or opening created space")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.anytypeio.anytype.other
|
||||
|
||||
import android.os.Build
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -26,6 +28,29 @@ class DefaultDeepLinkResolverTest {
|
|||
assertEquals(DeepLinkResolver.Action.Import.Experience(type = "experience123", source = "source321"), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve link to object deep link`() {
|
||||
// Given
|
||||
|
||||
val obj = MockDataFactory.randomUuid()
|
||||
|
||||
val space = MockDataFactory.randomUuid()
|
||||
|
||||
val deeplink = "anytype://object?objectId=$obj&spaceId=$space"
|
||||
|
||||
// When
|
||||
val result = deepLinkResolver.resolve(deeplink)
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
DeepLinkResolver.Action.DeepLinkToObject(
|
||||
space = SpaceId(space),
|
||||
obj = obj
|
||||
),
|
||||
result
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve returns Invite with deeplink for invite deep links`() {
|
||||
// Given
|
||||
|
|
|
@ -25,10 +25,12 @@ import com.anytypeio.anytype.domain.config.ConfigStorage
|
|||
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.notifications.SystemNotificationService
|
||||
import com.anytypeio.anytype.domain.subscriptions.GlobalSubscriptionManager
|
||||
import com.anytypeio.anytype.domain.wallpaper.ObserveWallpaper
|
||||
import com.anytypeio.anytype.domain.wallpaper.RestoreWallpaper
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
|
||||
|
@ -64,7 +66,9 @@ class MainViewModel(
|
|||
private val awaitAccountStartManager: AwaitAccountStartManager,
|
||||
private val membershipProvider: MembershipProvider,
|
||||
private val globalSubscriptionManager: GlobalSubscriptionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val spaceViews: SpaceViewSubscriptionContainer
|
||||
) : ViewModel(),
|
||||
NotificationActionDelegate by notificationActionDelegate,
|
||||
DeepLinkToObjectDelegate by deepLinkToObjectDelegate {
|
||||
|
@ -334,9 +338,8 @@ class MainViewModel(
|
|||
val result = onDeepLinkToObject(
|
||||
obj = deeplink.obj,
|
||||
space = deeplink.space,
|
||||
switchSpaceIfObjectFound = true
|
||||
switchSpaceIfObjectFound = false
|
||||
)
|
||||
Timber.d("Deep link to object result: $result")
|
||||
when (result) {
|
||||
is DeepLinkToObjectDelegate.Result.Error -> {
|
||||
val link = deeplink.invite
|
||||
|
@ -354,9 +357,59 @@ class MainViewModel(
|
|||
}
|
||||
}
|
||||
is DeepLinkToObjectDelegate.Result.Success -> {
|
||||
commands.emit(
|
||||
Command.Navigate(result.obj.navigation())
|
||||
)
|
||||
val targetSpace = result.obj.spaceId
|
||||
if (targetSpace != null) {
|
||||
val currentState = spaceManager.getState()
|
||||
Timber.d("Space manager state before processing deep link: $currentState")
|
||||
when(currentState) {
|
||||
SpaceManager.State.Init -> {
|
||||
// Do nothing.
|
||||
}
|
||||
SpaceManager.State.NoSpace -> {
|
||||
proceedWithSpaceSwitchDueToDeepLinkToObject(
|
||||
targetSpace = targetSpace,
|
||||
deeplink = deeplink,
|
||||
result = result
|
||||
)
|
||||
}
|
||||
is SpaceManager.State.Space.Idle -> {
|
||||
if (currentState.space.id != deeplink.space.id) {
|
||||
proceedWithSpaceSwitchDueToDeepLinkToObject(
|
||||
targetSpace = targetSpace,
|
||||
deeplink = deeplink,
|
||||
result = result
|
||||
)
|
||||
} else {
|
||||
commands.emit(
|
||||
Command.Deeplink.DeepLinkToObject(
|
||||
navigation = result.obj.navigation(),
|
||||
sideEffect = null,
|
||||
space = deeplink.space.id,
|
||||
obj = deeplink.obj
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
is SpaceManager.State.Space.Active -> {
|
||||
if (currentState.config.space != deeplink.space.id) {
|
||||
proceedWithSpaceSwitchDueToDeepLinkToObject(
|
||||
targetSpace = targetSpace,
|
||||
deeplink = deeplink,
|
||||
result = result
|
||||
)
|
||||
} else {
|
||||
commands.emit(
|
||||
Command.Deeplink.DeepLinkToObject(
|
||||
navigation = result.obj.navigation(),
|
||||
sideEffect = null,
|
||||
space = deeplink.space.id,
|
||||
obj = deeplink.obj
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -371,6 +424,33 @@ class MainViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithSpaceSwitchDueToDeepLinkToObject(
|
||||
targetSpace: Id,
|
||||
deeplink: DeepLinkResolver.Action.DeepLinkToObject,
|
||||
result: DeepLinkToObjectDelegate.Result.Success
|
||||
) {
|
||||
spaceManager
|
||||
.set(targetSpace)
|
||||
.onSuccess { config ->
|
||||
val home = config.home
|
||||
val spaceView = spaceViews
|
||||
.get(deeplink.space)
|
||||
val chat = spaceView?.chatId?.ifEmpty { null }
|
||||
val sideEffect = Command.Deeplink.DeepLinkToObject.SideEffect.SwitchSpace(
|
||||
chat = chat,
|
||||
home = home
|
||||
)
|
||||
commands.emit(
|
||||
Command.Deeplink.DeepLinkToObject(
|
||||
navigation = result.obj.navigation(),
|
||||
sideEffect = sideEffect,
|
||||
space = deeplink.space.id,
|
||||
obj = deeplink.obj
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Command {
|
||||
data class ShowDeletedAccountScreen(val deadline: Long) : Command()
|
||||
data object LogoutDueToAccountDeletion : Command()
|
||||
|
@ -391,6 +471,19 @@ class MainViewModel(
|
|||
|
||||
sealed class Deeplink : Command() {
|
||||
data object DeepLinkToObjectNotWorking: Deeplink()
|
||||
data class DeepLinkToObject(
|
||||
val obj: Id,
|
||||
val space: Id,
|
||||
val navigation: OpenObjectNavigation,
|
||||
val sideEffect: SideEffect? = null
|
||||
) : Deeplink() {
|
||||
sealed class SideEffect {
|
||||
data class SwitchSpace(
|
||||
val home: Id,
|
||||
val chat: Id?
|
||||
): SideEffect()
|
||||
}
|
||||
}
|
||||
data class Invite(val link: String) : Deeplink()
|
||||
data class GalleryInstallation(
|
||||
val deepLinkType: String,
|
||||
|
|
|
@ -11,10 +11,12 @@ import com.anytypeio.anytype.domain.auth.interactor.ResumeAccount
|
|||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.notifications.SystemNotificationService
|
||||
import com.anytypeio.anytype.domain.subscriptions.GlobalSubscriptionManager
|
||||
import com.anytypeio.anytype.domain.wallpaper.ObserveWallpaper
|
||||
import com.anytypeio.anytype.domain.wallpaper.RestoreWallpaper
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
|
||||
import com.anytypeio.anytype.presentation.navigation.DeepLinkToObjectDelegate
|
||||
import com.anytypeio.anytype.presentation.notifications.NotificationActionDelegate
|
||||
|
@ -38,7 +40,9 @@ class MainViewModelFactory @Inject constructor(
|
|||
private val awaitAccountStartManager: AwaitAccountStartManager,
|
||||
private val membershipProvider: MembershipProvider,
|
||||
private val globalSubscriptionManager: GlobalSubscriptionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val spaceViews: SpaceViewSubscriptionContainer
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
|
@ -60,6 +64,8 @@ class MainViewModelFactory @Inject constructor(
|
|||
awaitAccountStartManager = awaitAccountStartManager,
|
||||
membershipProvider = membershipProvider,
|
||||
globalSubscriptionManager = globalSubscriptionManager,
|
||||
spaceInviteResolver = spaceInviteResolver
|
||||
spaceInviteResolver = spaceInviteResolver,
|
||||
spaceManager = spaceManager,
|
||||
spaceViews = spaceViews
|
||||
) as T
|
||||
}
|
|
@ -346,6 +346,7 @@ class SplashViewModel(
|
|||
* @see [LaunchAccount] use-case
|
||||
*/
|
||||
private suspend fun proceedWithVaultNavigation(deeplink: String? = null) {
|
||||
Timber.d("proceedWithVaultNavigation deep link: $deeplink")
|
||||
val space = getLastOpenedSpace.async(Unit).getOrNull()
|
||||
if (space != null && spaceManager.getState() != SpaceManager.State.NoSpace) {
|
||||
spaceManager
|
||||
|
|
|
@ -175,7 +175,7 @@ class VaultViewModel(
|
|||
}
|
||||
|
||||
fun onResume(deeplink: DeepLinkResolver.Action? = null) {
|
||||
Timber.d("onResume")
|
||||
Timber.d("onResume, deep link: $deeplink")
|
||||
viewModelScope.launch {
|
||||
analytics.sendEvent(
|
||||
eventName = EventsDictionary.screenVault,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue