From e7a37b4299e1326bb82be86cde88a4dfb65ca967 Mon Sep 17 00:00:00 2001
From: Konstantin Ivanov <54908981+konstantiniiv@users.noreply.github.com>
Date: Thu, 22 Aug 2024 10:07:48 +0200
Subject: [PATCH] DROID-2722 Membership | Open tier deeplink (#1493)
---
app/src/main/AndroidManifest.xml | 7 ++++++
.../com/anytypeio/anytype/other/Deeplinks.kt | 8 +++++++
.../anytype/ui/home/HomeScreenFragment.kt | 9 ++++++++
.../anytypeio/anytype/ui/main/MainActivity.kt | 12 ++++++++++
.../anytype/ui/payments/MembershipFragment.kt | 17 ++++++++++++--
.../anytype/domain/misc/DeepLinkResolver.kt | 4 ++++
.../payments/viewmodel/MembershipViewModel.kt | 23 +++++++++++++++++++
.../presentation/home/HomeScreenViewModel.kt | 10 ++++++++
.../presentation/main/MainViewModel.kt | 7 ++++++
9 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f094add445..060e6040ef 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -66,6 +66,13 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/anytypeio/anytype/other/Deeplinks.kt b/app/src/main/java/com/anytypeio/anytype/other/Deeplinks.kt
index 059bd7471e..2fbd504ebb 100644
--- a/app/src/main/java/com/anytypeio/anytype/other/Deeplinks.kt
+++ b/app/src/main/java/com/anytypeio/anytype/other/Deeplinks.kt
@@ -20,12 +20,14 @@ const val MAIN_PATH = "main"
const val OBJECT_PATH = "object"
const val IMPORT_PATH = "import"
const val INVITE_PATH = "invite"
+const val MEMBERSHIP_PATH = "membership"
const val TYPE_PARAM = "type"
const val OBJECT_ID_PARAM = "objectId"
const val SPACE_ID_PARAM = "spaceId"
const val SOURCE_PARAM = "source"
const val TYPE_VALUE_EXPERIENCE = "experience"
+const val TIER_ID_PARAM = "tier"
const val IMPORT_EXPERIENCE_DEEPLINK = "$DEEP_LINK_PATTERN$MAIN_PATH/$IMPORT_PATH/?$TYPE_PARAM=$TYPE_VALUE_EXPERIENCE"
@@ -67,6 +69,12 @@ object DefaultDeepLinkResolver : DeepLinkResolver {
DeepLinkResolver.Action.Unknown
}
}
+ deeplink.contains(MEMBERSHIP_PATH) -> {
+ val uri = Uri.parse(deeplink)
+ DeepLinkResolver.Action.DeepLinkToMembership(
+ tierId = uri.getQueryParameter(TIER_ID_PARAM)
+ )
+ }
else -> DeepLinkResolver.Action.Unknown
}
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreenFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreenFragment.kt
index 59f0f18151..16a8502686 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreenFragment.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreenFragment.kt
@@ -16,6 +16,7 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import androidx.navigation.NavOptions
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
@@ -36,6 +37,7 @@ import com.anytypeio.anytype.ui.gallery.GalleryInstallationFragment
import com.anytypeio.anytype.ui.multiplayer.RequestJoinSpaceFragment
import com.anytypeio.anytype.ui.multiplayer.ShareSpaceFragment
import com.anytypeio.anytype.ui.objects.creation.SelectObjectTypeFragment
+import com.anytypeio.anytype.ui.payments.MembershipFragment
import com.anytypeio.anytype.ui.settings.space.SpaceSettingsFragment
import com.anytypeio.anytype.ui.settings.typography
import com.anytypeio.anytype.ui.widgets.SelectWidgetSourceFragment
@@ -245,6 +247,13 @@ class HomeScreenFragment : BaseComposeFragment() {
)
)
}
+ is Command.Deeplink.MembershipScreen -> {
+ findNavController().navigate(
+ R.id.paymentsScreen,
+ MembershipFragment.args(command.tierId),
+ NavOptions.Builder().setLaunchSingleTop(true).build()
+ )
+ }
is Command.Deeplink.DeepLinkToObjectNotWorking -> {
toast(
getString(R.string.multiplayer_deeplink_to_your_object_error)
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt b/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt
index fb0b89b1fb..9c267a2cca 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt
@@ -55,6 +55,7 @@ import com.anytypeio.anytype.ui.multiplayer.RequestJoinSpaceFragment
import com.anytypeio.anytype.ui.multiplayer.ShareSpaceFragment
import com.anytypeio.anytype.ui.multiplayer.SpaceJoinRequestFragment
import com.anytypeio.anytype.ui.notifications.NotificationsFragment
+import com.anytypeio.anytype.ui.payments.MembershipFragment
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
import com.anytypeio.anytype.ui.sharing.SharingFragment
import com.anytypeio.anytype.ui_settings.appearance.ThemeApplicator
@@ -251,6 +252,17 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
Timber.e(it, "Error while navigation for deep link invite")
}
}
+ is Command.Deeplink.MembershipScreen -> {
+ runCatching {
+ findNavController(R.id.fragment).navigate(
+ R.id.paymentsScreen,
+ MembershipFragment.args(tierId = command.tierId),
+ NavOptions.Builder().setLaunchSingleTop(true).build()
+ )
+ }.onFailure {
+ Timber.w(it, "Error while navigation for deep link membership tier")
+ }
+ }
}
}
}
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/payments/MembershipFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/payments/MembershipFragment.kt
index c52018e3c3..8c869887c5 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/payments/MembershipFragment.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/payments/MembershipFragment.kt
@@ -12,6 +12,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
+import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
@@ -23,6 +24,7 @@ import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
import com.anytypeio.anytype.core_ui.views.BaseTwoButtonsDarkThemeAlertDialog
+import com.anytypeio.anytype.core_utils.ext.argOrNull
import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.toast
@@ -59,6 +61,8 @@ class MembershipFragment : BaseBottomSheetComposeFragment() {
private val vm by viewModels { factory }
private lateinit var navController: NavHostController
+ private val argTierId get() = argOrNull(ARG_TIER_ID)
+
@Inject
lateinit var billingClientLifecycle: BillingClientLifecycle
@@ -183,6 +187,7 @@ class MembershipFragment : BaseBottomSheetComposeFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ vm.showTierOnStart(tierId = argTierId)
setupBottomSheetBehavior(DEFAULT_PADDING_TOP)
subscribe(vm.navigation) { command ->
Timber.d("MembershipFragment command: $command")
@@ -209,10 +214,12 @@ class MembershipFragment : BaseBottomSheetComposeFragment() {
toast("Couldn't parse url: ${command.url}")
}
}
+
MembershipNavigation.Main -> {}
is MembershipNavigation.OpenEmail -> {
val mail = resources.getString(R.string.payments_email_to)
- val subject = resources.getString(R.string.payments_email_subject, command.accountId)
+ val subject =
+ resources.getString(R.string.payments_email_subject, command.accountId)
val body = resources.getString(R.string.payments_email_body)
val mailBody = mail +
"?subject=$subject" +
@@ -228,7 +235,8 @@ class MembershipFragment : BaseBottomSheetComposeFragment() {
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
currentDateTime = sdf.format(Date())
val mail = resources.getString(R.string.membership_support_email)
- val subject = resources.getString(R.string.membership_support_subject, command.accountId)
+ val subject =
+ resources.getString(R.string.membership_support_subject, command.accountId)
val body = getString(
R.string.membership_support_body,
command.error, currentDateTime, deviceModel, osVersion, appVersion
@@ -260,4 +268,9 @@ class MembershipFragment : BaseBottomSheetComposeFragment() {
override fun releaseDependencies() {
componentManager().membershipComponent.release()
}
+
+ companion object {
+ const val ARG_TIER_ID = "args.membership.tier"
+ fun args(tierId: String?) = bundleOf(ARG_TIER_ID to tierId)
+ }
}
\ No newline at end of file
diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/misc/DeepLinkResolver.kt b/domain/src/main/java/com/anytypeio/anytype/domain/misc/DeepLinkResolver.kt
index 3618a98a36..7e1cd90f7c 100644
--- a/domain/src/main/java/com/anytypeio/anytype/domain/misc/DeepLinkResolver.kt
+++ b/domain/src/main/java/com/anytypeio/anytype/domain/misc/DeepLinkResolver.kt
@@ -2,6 +2,7 @@ package com.anytypeio.anytype.domain.misc
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Url
+import com.anytypeio.anytype.core_models.membership.TierId
import com.anytypeio.anytype.core_models.primitives.SpaceId
interface DeepLinkResolver {
@@ -22,5 +23,8 @@ interface DeepLinkResolver {
val obj: Id,
val space: SpaceId
) : Action()
+ data class DeepLinkToMembership(
+ val tierId: String?
+ ) : Action()
}
}
\ No newline at end of file
diff --git a/payments/src/main/java/com/anytypeio/anytype/payments/viewmodel/MembershipViewModel.kt b/payments/src/main/java/com/anytypeio/anytype/payments/viewmodel/MembershipViewModel.kt
index 87d4a0b753..f7679913e0 100644
--- a/payments/src/main/java/com/anytypeio/anytype/payments/viewmodel/MembershipViewModel.kt
+++ b/payments/src/main/java/com/anytypeio/anytype/payments/viewmodel/MembershipViewModel.kt
@@ -22,6 +22,7 @@ import com.anytypeio.anytype.domain.payments.SetMembershipEmail
import com.anytypeio.anytype.domain.payments.VerifyMembershipEmailCode
import com.anytypeio.anytype.core_models.membership.MembershipConstants.EXPLORER_ID
import com.anytypeio.anytype.core_models.membership.MembershipConstants.MEMBERSHIP_NAME_MIN_LENGTH
+import com.anytypeio.anytype.core_models.membership.MembershipConstants.NONE_ID
import com.anytypeio.anytype.payments.mapping.toMainView
import com.anytypeio.anytype.payments.models.MembershipPurchase
import com.anytypeio.anytype.payments.models.TierAnyName
@@ -46,6 +47,8 @@ import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -71,6 +74,8 @@ class MembershipViewModel(
val navigation = MutableSharedFlow(0)
+ private val _showTierOnStart = MutableStateFlow(NONE_ID)
+
/**
* Local billing purchase data.
*/
@@ -93,6 +98,17 @@ class MembershipViewModel(
val anyEmailState = TextFieldState(initialText = "")
init {
+ Timber.i("MembershipViewModel, init")
+ viewModelScope.launch {
+ combine(
+ _showTierOnStart.filter { it != NONE_ID },
+ viewState.filterIsInstance()
+ ) { tierId, _ -> tierId }.collect { tierId ->
+ Timber.d("_showTierOnStart, get new value:$tierId")
+ proceedWithShowingTier(TierId(tierId))
+ }
+ }
+
viewModelScope.launch {
val account = getAccount.async(Unit)
val accountId = account.getOrNull()?.id.orEmpty()
@@ -131,6 +147,13 @@ class MembershipViewModel(
}
}
+ fun showTierOnStart(tierId: String?) {
+ val tier = tierId?.toIntOrNull()
+ if (tier != null && tier != NONE_ID) {
+ _showTierOnStart.value = tier
+ }
+ }
+
private fun proceedWithUpdatingVisibleTier(mainState: MembershipMainState) {
val actualTier = tierState.value
if (actualTier is MembershipTierState.Visible && mainState is MembershipMainState.Default) {
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt
index e5e99685fe..d0b29a5ed6 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt
@@ -1272,6 +1272,15 @@ class HomeScreenViewModel(
}
}
}
+ is DeepLinkResolver.Action.DeepLinkToMembership -> {
+ viewModelScope.launch {
+ commands.emit(
+ Command.Deeplink.MembershipScreen(
+ tierId = deeplink.tierId
+ )
+ )
+ }
+ }
else -> {
Timber.d("No deep link")
}
@@ -2190,6 +2199,7 @@ sealed class Command {
val deepLinkType: String,
val deepLinkSource: String
) : Deeplink()
+ data class MembershipScreen(val tierId: String?) : Deeplink()
}
}
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt
index 4032673446..28ce749e55 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt
@@ -12,6 +12,7 @@ import com.anytypeio.anytype.core_models.NotificationPayload
import com.anytypeio.anytype.core_models.NotificationStatus
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.core_models.exceptions.NeedToUpdateApplicationException
+import com.anytypeio.anytype.core_models.membership.TierId
import com.anytypeio.anytype.core_utils.ext.cancel
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
import com.anytypeio.anytype.domain.account.InterceptAccountStatus
@@ -350,6 +351,11 @@ class MainViewModel(
}
}
}
+ is DeepLinkResolver.Action.DeepLinkToMembership -> {
+ commands.emit(
+ Command.Deeplink.MembershipScreen(tierId = deeplink.tierId)
+ )
+ }
else -> {
Timber.d("No deep link")
}
@@ -381,6 +387,7 @@ class MainViewModel(
val deepLinkType: String,
val deepLinkSource: String
) : Deeplink()
+ data class MembershipScreen(val tierId: String?) : Deeplink()
}
}