mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-2275 Membership | Status provider (#1119)
This commit is contained in:
parent
2ccbdc37d4
commit
6ece50176c
25 changed files with 372 additions and 66 deletions
|
@ -25,6 +25,7 @@ import com.anytypeio.anytype.domain.wallpaper.RestoreWallpaper
|
|||
import com.anytypeio.anytype.domain.wallpaper.WallpaperStore
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.main.MainViewModelFactory
|
||||
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
|
||||
import com.anytypeio.anytype.presentation.notifications.NotificationsProvider
|
||||
import com.anytypeio.anytype.ui.main.MainActivity
|
||||
import com.anytypeio.anytype.ui_settings.appearance.ThemeApplicator
|
||||
|
@ -67,7 +68,8 @@ object MainEntryModule {
|
|||
spaceDeletedStatusWatcher: SpaceDeletedStatusWatcher,
|
||||
localeProvider: LocaleProvider,
|
||||
userPermissionProvider: UserPermissionProvider,
|
||||
notificationsProvider: NotificationsProvider
|
||||
notificationsProvider: NotificationsProvider,
|
||||
membershipProvider: MembershipProvider
|
||||
): MainViewModelFactory = MainViewModelFactory(
|
||||
resumeAccount = resumeAccount,
|
||||
analytics = analytics,
|
||||
|
@ -82,7 +84,8 @@ object MainEntryModule {
|
|||
spaceDeletedStatusWatcher = spaceDeletedStatusWatcher,
|
||||
localeProvider = localeProvider,
|
||||
userPermissionProvider = userPermissionProvider,
|
||||
notificationsProvider = notificationsProvider
|
||||
notificationsProvider = notificationsProvider,
|
||||
membershipProvider = membershipProvider
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
@ -8,6 +8,8 @@ import com.anytypeio.anytype.di.common.ComponentDependencies
|
|||
import com.anytypeio.anytype.domain.auth.interactor.GetAccount
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.payments.GetMembershipTiers
|
||||
import com.anytypeio.anytype.payments.playbilling.BillingClientLifecycle
|
||||
import com.anytypeio.anytype.ui.payments.PaymentsFragment
|
||||
import com.anytypeio.anytype.payments.viewmodel.PaymentsViewModelFactory
|
||||
|
@ -45,6 +47,14 @@ object PaymentsModule {
|
|||
dispatchers: AppCoroutineDispatchers
|
||||
): GetAccount = GetAccount(repo = repo, dispatcher = dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideGetTiersUseCase(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): GetMembershipTiers = GetMembershipTiers(repo = repo, dispatchers = dispatchers)
|
||||
|
||||
@Module
|
||||
interface Declarations {
|
||||
|
||||
|
@ -62,5 +72,6 @@ interface PaymentsComponentDependencies : ComponentDependencies {
|
|||
fun context(): Context
|
||||
fun billingListener(): BillingClientLifecycle
|
||||
fun authRepository(): AuthRepository
|
||||
fun blockRepository(): BlockRepository
|
||||
fun appCoroutineDispatchers(): AppCoroutineDispatchers
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package com.anytypeio.anytype.di.main
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.payments.playbilling.BillingClientLifecycle
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@Module
|
||||
object BillingModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideBillingLifecycle(
|
||||
context: Context,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
@Named(ConfigModule.DEFAULT_APP_COROUTINE_SCOPE) scope: CoroutineScope
|
||||
): BillingClientLifecycle {
|
||||
return BillingClientLifecycle(
|
||||
dispatchers = dispatchers,
|
||||
applicationContext = context,
|
||||
scope = scope
|
||||
)
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@ import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
|||
import com.anytypeio.anytype.domain.device.FileSharer
|
||||
import com.anytypeio.anytype.domain.download.Downloader
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.providers.DefaultUriFileProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Singleton
|
||||
|
|
|
@ -80,7 +80,7 @@ import javax.inject.Singleton
|
|||
TemplatesModule::class,
|
||||
NetworkModeModule::class,
|
||||
NotificationsModule::class,
|
||||
BillingModule::class
|
||||
MembershipModule::class
|
||||
]
|
||||
)
|
||||
interface MainComponent :
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package com.anytypeio.anytype.di.main
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.data.auth.event.MembershipDateChannel
|
||||
import com.anytypeio.anytype.data.auth.event.MembershipRemoteChannel
|
||||
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.workspace.MembershipChannel
|
||||
import com.anytypeio.anytype.middleware.EventProxy
|
||||
import com.anytypeio.anytype.middleware.interactor.MembershipMiddlewareChannel
|
||||
import com.anytypeio.anytype.payments.playbilling.BillingClientLifecycle
|
||||
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@Module
|
||||
object MembershipModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideBillingLifecycle(
|
||||
context: Context,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
@Named(ConfigModule.DEFAULT_APP_COROUTINE_SCOPE) scope: CoroutineScope
|
||||
): BillingClientLifecycle {
|
||||
return BillingClientLifecycle(
|
||||
dispatchers = dispatchers,
|
||||
applicationContext = context,
|
||||
scope = scope
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideMembershipRemoteChannel(
|
||||
proxy: EventProxy
|
||||
): MembershipRemoteChannel = MembershipMiddlewareChannel(
|
||||
eventsProxy = proxy
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideMembershipChannel(
|
||||
channel: MembershipRemoteChannel
|
||||
): MembershipChannel = MembershipDateChannel(
|
||||
channel = channel
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideMembershipProvider(
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
awaitAccountStartManager: AwaitAccountStartManager,
|
||||
membershipChannel: MembershipChannel,
|
||||
localeProvider: LocaleProvider,
|
||||
repo: BlockRepository
|
||||
): MembershipProvider = MembershipProvider.Default(
|
||||
dispatchers = dispatchers,
|
||||
membershipChannel = membershipChannel,
|
||||
awaitAccountStartManager = awaitAccountStartManager,
|
||||
localeProvider = localeProvider,
|
||||
repo = repo
|
||||
)
|
||||
}
|
|
@ -2,7 +2,7 @@ package com.anytypeio.anytype.core_models.membership
|
|||
|
||||
data class Membership(
|
||||
val tier: Int,
|
||||
val membershipStatus: MembershipStatus,
|
||||
val membershipStatusModel: MembershipStatusModel,
|
||||
val dateStarted: Long,
|
||||
val dateEnds: Long,
|
||||
val isAutoRenew: Boolean,
|
||||
|
@ -10,9 +10,12 @@ data class Membership(
|
|||
val requestedAnyName: String,
|
||||
val userEmail: String,
|
||||
val subscribeToNewsletter: Boolean
|
||||
)
|
||||
) {
|
||||
|
||||
enum class MembershipStatus {
|
||||
data class Event(val membership: Membership)
|
||||
}
|
||||
|
||||
enum class MembershipStatusModel {
|
||||
STATUS_UNKNOWN,
|
||||
STATUS_PENDING,
|
||||
STATUS_ACTIVE,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.anytypeio.anytype.data.auth.event
|
||||
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.domain.workspace.MembershipChannel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface MembershipRemoteChannel {
|
||||
fun observe(): Flow<List<Membership.Event>>
|
||||
}
|
||||
|
||||
class MembershipDateChannel(
|
||||
private val channel: MembershipRemoteChannel
|
||||
) : MembershipChannel {
|
||||
|
||||
override fun observe(): Flow<List<Membership.Event>> {
|
||||
return channel.observe()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.anytypeio.anytype.domain.payments
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetMembershipStatus @Inject constructor(
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
private val repo: BlockRepository
|
||||
) : ResultInteractor<GetMembershipStatus.Params, Membership?>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params): Membership? {
|
||||
val command = Command.Membership.GetStatus(
|
||||
noCache = params.noCache
|
||||
)
|
||||
return repo.membershipStatus(command)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val noCache: Boolean
|
||||
)
|
||||
}
|
|
@ -7,10 +7,10 @@ import com.anytypeio.anytype.domain.base.ResultInteractor
|
|||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetTiers @Inject constructor(
|
||||
class GetMembershipTiers @Inject constructor(
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
private val repo: BlockRepository
|
||||
) : ResultInteractor<GetTiers.Params, List<MembershipTierData>>(dispatchers.io) {
|
||||
) : ResultInteractor<GetMembershipTiers.Params, List<MembershipTierData>>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params): List<MembershipTierData> {
|
||||
val command = Command.Membership.GetTiers(
|
|
@ -0,0 +1,8 @@
|
|||
package com.anytypeio.anytype.domain.workspace
|
||||
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface MembershipChannel {
|
||||
fun observe(): Flow<List<Membership.Event>>
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.anytypeio.anytype.middleware.interactor
|
||||
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.data.auth.event.MembershipRemoteChannel
|
||||
import com.anytypeio.anytype.middleware.EventProxy
|
||||
import com.anytypeio.anytype.middleware.mappers.toCoreModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
|
||||
class MembershipMiddlewareChannel(
|
||||
private val eventsProxy: EventProxy
|
||||
): MembershipRemoteChannel {
|
||||
|
||||
override fun observe(): Flow<List<Membership.Event>> {
|
||||
return eventsProxy.flow()
|
||||
.mapNotNull { emission ->
|
||||
emission.messages.mapNotNull { message ->
|
||||
when {
|
||||
message.membershipUpdate != null -> {
|
||||
val event = message.membershipUpdate
|
||||
checkNotNull(event)
|
||||
val membership = event.data_
|
||||
if (membership != null) {
|
||||
Membership.Event(
|
||||
membership = membership.toCoreModel()
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}.filter { events -> events.isNotEmpty() }
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ import com.anytypeio.anytype.core_models.membership.EmailVerificationStatus
|
|||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipPaymentMethod
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipPeriodType
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipStatus
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipStatusModel
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipTierData
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
|
@ -975,12 +975,12 @@ fun MNotification.toCoreModel(): Notification {
|
|||
//endregion
|
||||
|
||||
//region MEMBERSHIP
|
||||
fun MMembershipStatus.toCoreModel(): MembershipStatus {
|
||||
fun MMembershipStatus.toCoreModel(): MembershipStatusModel {
|
||||
return when (this) {
|
||||
MMembershipStatus.StatusUnknown -> MembershipStatus.STATUS_UNKNOWN
|
||||
MMembershipStatus.StatusPending -> MembershipStatus.STATUS_PENDING
|
||||
MMembershipStatus.StatusActive -> MembershipStatus.STATUS_ACTIVE
|
||||
MMembershipStatus.StatusPendingRequiresFinalization -> MembershipStatus.STATUS_PENDING_FINALIZATION
|
||||
MMembershipStatus.StatusUnknown -> MembershipStatusModel.STATUS_UNKNOWN
|
||||
MMembershipStatus.StatusPending -> MembershipStatusModel.STATUS_PENDING
|
||||
MMembershipStatus.StatusActive -> MembershipStatusModel.STATUS_ACTIVE
|
||||
MMembershipStatus.StatusPendingRequiresFinalization -> MembershipStatusModel.STATUS_PENDING_FINALIZATION
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -997,7 +997,7 @@ fun MMembershipPaymentMethod.toCoreModel(): MembershipPaymentMethod {
|
|||
fun MMembership.toCoreModel(): Membership {
|
||||
return Membership(
|
||||
tier = tier,
|
||||
membershipStatus = status.toCoreModel(),
|
||||
membershipStatusModel = status.toCoreModel(),
|
||||
dateStarted = dateStarted,
|
||||
dateEnds = dateEnds,
|
||||
isAutoRenew = isAutoRenew,
|
||||
|
|
|
@ -52,7 +52,7 @@ import com.anytypeio.anytype.core_ui.views.HeadlineTitle
|
|||
import com.anytypeio.anytype.core_ui.views.PreviewTitle1Regular
|
||||
import com.anytypeio.anytype.payments.R
|
||||
import com.anytypeio.anytype.payments.viewmodel.PaymentsCodeState
|
||||
import com.anytypeio.anytype.payments.viewmodel.TierId
|
||||
import com.anytypeio.anytype.presentation.membership.models.TierId
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
|
@ -53,9 +53,9 @@ import com.anytypeio.anytype.core_ui.views.BodyRegular
|
|||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.Relations2
|
||||
import com.anytypeio.anytype.core_ui.views.fontRiccioneRegular
|
||||
import com.anytypeio.anytype.payments.models.Tier
|
||||
import com.anytypeio.anytype.presentation.membership.models.Tier
|
||||
import com.anytypeio.anytype.payments.viewmodel.PaymentsMainState
|
||||
import com.anytypeio.anytype.payments.viewmodel.TierId
|
||||
import com.anytypeio.anytype.presentation.membership.models.TierId
|
||||
|
||||
@Composable
|
||||
fun MainPaymentsScreen(state: PaymentsMainState, tierClicked: (TierId) -> Unit) {
|
||||
|
|
|
@ -58,9 +58,9 @@ import com.anytypeio.anytype.core_ui.views.HeadlineTitle
|
|||
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||
import com.anytypeio.anytype.core_ui.views.Relations2
|
||||
import com.anytypeio.anytype.payments.R
|
||||
import com.anytypeio.anytype.payments.models.Tier
|
||||
import com.anytypeio.anytype.presentation.membership.models.Tier
|
||||
import com.anytypeio.anytype.payments.viewmodel.PaymentsTierState
|
||||
import com.anytypeio.anytype.payments.viewmodel.TierId
|
||||
import com.anytypeio.anytype.presentation.membership.models.TierId
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
|
@ -27,9 +27,9 @@ import com.anytypeio.anytype.core_ui.views.ButtonSecondary
|
|||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineHeading
|
||||
import com.anytypeio.anytype.payments.R
|
||||
import com.anytypeio.anytype.payments.models.Tier
|
||||
import com.anytypeio.anytype.presentation.membership.models.Tier
|
||||
import com.anytypeio.anytype.payments.viewmodel.PaymentsWelcomeState
|
||||
import com.anytypeio.anytype.payments.viewmodel.TierId
|
||||
import com.anytypeio.anytype.presentation.membership.models.TierId
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
|
@ -37,7 +37,7 @@ import com.anytypeio.anytype.core_ui.views.ButtonSize
|
|||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.Relations3
|
||||
import com.anytypeio.anytype.core_ui.views.fontInterSemibold
|
||||
import com.anytypeio.anytype.payments.models.Tier
|
||||
import com.anytypeio.anytype.presentation.membership.models.Tier
|
||||
|
||||
@Composable
|
||||
fun TierView(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.payments.viewmodel
|
||||
|
||||
import com.anytypeio.anytype.payments.models.Tier
|
||||
import com.anytypeio.anytype.presentation.membership.models.Tier
|
||||
import com.anytypeio.anytype.presentation.membership.models.TierId
|
||||
|
||||
|
||||
sealed class PaymentsMainState {
|
||||
|
@ -44,7 +45,4 @@ sealed class PaymentsNavigation(val route: String) {
|
|||
object Code : PaymentsNavigation("code")
|
||||
object Welcome : PaymentsNavigation("welcome")
|
||||
object Dismiss : PaymentsNavigation("")
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class TierId(val value: String)
|
||||
}
|
|
@ -7,9 +7,12 @@ import com.android.billingclient.api.ProductDetails
|
|||
import com.android.billingclient.api.Purchase
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.domain.auth.interactor.GetAccount
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.payments.GetMembershipTiers
|
||||
import com.anytypeio.anytype.payments.constants.BillingConstants
|
||||
import com.anytypeio.anytype.payments.models.Tier
|
||||
import com.anytypeio.anytype.presentation.membership.models.Tier
|
||||
import com.anytypeio.anytype.payments.playbilling.BillingClientLifecycle
|
||||
import com.anytypeio.anytype.presentation.membership.models.TierId
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
|
@ -19,7 +22,8 @@ import timber.log.Timber
|
|||
class PaymentsViewModel(
|
||||
private val analytics: Analytics,
|
||||
private val billingClientLifecycle: BillingClientLifecycle,
|
||||
private val getAccount: GetAccount
|
||||
private val getAccount: GetAccount,
|
||||
private val getMembershipTiers: GetMembershipTiers
|
||||
) : ViewModel() {
|
||||
|
||||
val viewState = MutableStateFlow<PaymentsMainState>(PaymentsMainState.Loading)
|
||||
|
@ -50,9 +54,19 @@ class PaymentsViewModel(
|
|||
|
||||
init {
|
||||
Timber.d("PaymentsViewModel init")
|
||||
_tiers.addAll(gertTiers())
|
||||
proceedWithGetTiers()
|
||||
setupActiveTierName()
|
||||
viewState.value = PaymentsMainState.Default(_tiers)
|
||||
}
|
||||
|
||||
private fun proceedWithGetTiers() {
|
||||
viewModelScope.launch {
|
||||
getMembershipTiers.async(GetMembershipTiers.Params("en", false)).fold(
|
||||
onSuccess = { result ->
|
||||
///todo handle the result
|
||||
},
|
||||
onFailure = Timber::e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onTierClicked(tierId: TierId) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.domain.auth.interactor.GetAccount
|
||||
import com.anytypeio.anytype.domain.payments.GetMembershipTiers
|
||||
import com.anytypeio.anytype.payments.playbilling.BillingClientLifecycle
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -11,13 +12,15 @@ class PaymentsViewModelFactory @Inject constructor(
|
|||
private val analytics: Analytics,
|
||||
private val billingClientLifecycle: BillingClientLifecycle,
|
||||
private val getAccount: GetAccount,
|
||||
private val getMembershipTiers: GetMembershipTiers
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return PaymentsViewModel(
|
||||
analytics = analytics,
|
||||
billingClientLifecycle = billingClientLifecycle,
|
||||
getAccount = getAccount
|
||||
getAccount = getAccount,
|
||||
getMembershipTiers = getMembershipTiers
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager
|
|||
import com.anytypeio.anytype.domain.spaces.SpaceDeletedStatusWatcher
|
||||
import com.anytypeio.anytype.domain.wallpaper.ObserveWallpaper
|
||||
import com.anytypeio.anytype.domain.wallpaper.RestoreWallpaper
|
||||
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
|
||||
import com.anytypeio.anytype.presentation.notifications.NotificationsProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -32,7 +33,8 @@ class MainViewModelFactory @Inject constructor(
|
|||
private val spaceDeletedStatusWatcher: SpaceDeletedStatusWatcher,
|
||||
private val localeProvider: LocaleProvider,
|
||||
private val userPermissionProvider: UserPermissionProvider,
|
||||
private val notificationsProvider: NotificationsProvider
|
||||
private val notificationsProvider: NotificationsProvider,
|
||||
private val membershipProvider: MembershipProvider
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
|
@ -51,6 +53,7 @@ class MainViewModelFactory @Inject constructor(
|
|||
spaceDeletedStatusWatcher = spaceDeletedStatusWatcher,
|
||||
localeProvider = localeProvider,
|
||||
userPermissionProvider = userPermissionProvider,
|
||||
notificationsProvider = notificationsProvider
|
||||
notificationsProvider = notificationsProvider,
|
||||
membershipProvider = membershipProvider
|
||||
) as T
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.anytypeio.anytype.presentation.membership.models
|
||||
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipPaymentMethod
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipStatusModel
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipTierData
|
||||
|
||||
sealed class MembershipStatus {
|
||||
object Unknown : MembershipStatus()
|
||||
object Pending : MembershipStatus()
|
||||
object Finalization : MembershipStatus()
|
||||
data class Active(
|
||||
val tier: MembershipTierData?,
|
||||
val status: MembershipStatusModel,
|
||||
val dateEnds: Long,
|
||||
val paymentMethod: MembershipPaymentMethod,
|
||||
val anyName: String
|
||||
) : MembershipStatus()
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class TierId(val value: String)
|
|
@ -1,6 +1,5 @@
|
|||
package com.anytypeio.anytype.payments.models
|
||||
package com.anytypeio.anytype.presentation.membership.models
|
||||
|
||||
import com.anytypeio.anytype.payments.viewmodel.TierId
|
||||
|
||||
sealed class Tier {
|
||||
abstract val id: TierId
|
|
@ -0,0 +1,120 @@
|
|||
package com.anytypeio.anytype.presentation.membership.provider
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipStatusModel.STATUS_ACTIVE
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipStatusModel.STATUS_PENDING
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipStatusModel.STATUS_PENDING_FINALIZATION
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipStatusModel.STATUS_UNKNOWN
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipTierData
|
||||
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.workspace.MembershipChannel
|
||||
import com.anytypeio.anytype.presentation.membership.models.MembershipStatus
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.scan
|
||||
import timber.log.Timber
|
||||
|
||||
interface MembershipProvider {
|
||||
|
||||
fun status(): Flow<MembershipStatus>
|
||||
|
||||
class Default(
|
||||
private val dispatchers: AppCoroutineDispatchers,
|
||||
private val membershipChannel: MembershipChannel,
|
||||
private val awaitAccountStartManager: AwaitAccountStartManager,
|
||||
private val localeProvider: LocaleProvider,
|
||||
private val repo: BlockRepository
|
||||
) : MembershipProvider {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun status(): Flow<MembershipStatus> {
|
||||
return awaitAccountStartManager.isStarted().flatMapLatest { isStarted ->
|
||||
if (isStarted) {
|
||||
buildStatusFlow(
|
||||
initial = proceedWithGettingMembership()
|
||||
)
|
||||
} else {
|
||||
emptyFlow()
|
||||
}
|
||||
}.catch { e -> Timber.e(e) }
|
||||
.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private fun buildStatusFlow(
|
||||
initial: Membership?
|
||||
): Flow<MembershipStatus> {
|
||||
return membershipChannel
|
||||
.observe()
|
||||
.scan(initial) { _, events ->
|
||||
events.lastOrNull()?.membership
|
||||
}.mapNotNull { status ->
|
||||
val tiers = proceedWithGettingTiers()
|
||||
toMembershipStatus(status, tiers)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithGettingMembership(): Membership? {
|
||||
val command = Command.Membership.GetStatus(
|
||||
noCache = false
|
||||
)
|
||||
return repo.membershipStatus(command)
|
||||
}
|
||||
|
||||
private suspend fun proceedWithGettingTiers(): List<MembershipTierData> {
|
||||
val tiersParams = Command.Membership.GetTiers(
|
||||
noCache = false,
|
||||
locale = localeProvider.language() ?: DEFAULT_LOCALE
|
||||
)
|
||||
return repo.membershipGetTiers(tiersParams)
|
||||
}
|
||||
|
||||
private fun toMembershipStatus(
|
||||
membership: Membership?,
|
||||
tiers: List<MembershipTierData>
|
||||
): MembershipStatus {
|
||||
return when (membership?.membershipStatusModel) {
|
||||
STATUS_PENDING -> MembershipStatus.Pending
|
||||
STATUS_PENDING_FINALIZATION -> MembershipStatus.Finalization
|
||||
STATUS_ACTIVE -> toActiveMembershipStatus(membership, tiers)
|
||||
STATUS_UNKNOWN, null -> {
|
||||
Timber.e("Invalid or unknown membership status")
|
||||
MembershipStatus.Unknown
|
||||
}
|
||||
|
||||
else -> MembershipStatus.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
private fun toActiveMembershipStatus(
|
||||
membership: Membership,
|
||||
tiers: List<MembershipTierData>
|
||||
): MembershipStatus {
|
||||
val tier = tiers.firstOrNull { it.id == membership.tier }
|
||||
return if (tier != null) {
|
||||
MembershipStatus.Active(
|
||||
tier = tier,
|
||||
status = membership.membershipStatusModel,
|
||||
dateEnds = membership.dateEnds,
|
||||
paymentMethod = membership.paymentMethod,
|
||||
anyName = membership.requestedAnyName
|
||||
)
|
||||
} else {
|
||||
Timber.e("Membership tier not found: ${membership.tier}")
|
||||
MembershipStatus.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_LOCALE = "en"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue