1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-2378 Membership | Enhancement | Membership upgrade screen (#1312)

This commit is contained in:
Konstantin Ivanov 2024-06-24 22:22:51 +02:00 committed by GitHub
parent 242694df4d
commit e34bb538f4
Signed by: github
GPG key ID: B5690EEEBB952194
11 changed files with 319 additions and 9 deletions

View file

@ -64,6 +64,7 @@ import com.anytypeio.anytype.di.feature.gallery.DaggerGalleryInstallationCompone
import com.anytypeio.anytype.di.feature.home.DaggerHomeScreenComponent
import com.anytypeio.anytype.di.feature.library.DaggerLibraryComponent
import com.anytypeio.anytype.di.feature.membership.DaggerMembershipComponent
import com.anytypeio.anytype.di.feature.membership.DaggerMembershipUpdateComponent
import com.anytypeio.anytype.di.feature.multiplayer.DaggerRequestJoinSpaceComponent
import com.anytypeio.anytype.di.feature.multiplayer.DaggerShareSpaceComponent
import com.anytypeio.anytype.di.feature.multiplayer.DaggerSpaceJoinRequestComponent
@ -1143,6 +1144,10 @@ class ComponentManager(
DaggerMembershipComponent.factory().create(findComponentDependencies())
}
val membershipUpgradeComponent = Component {
DaggerMembershipUpdateComponent.factory().create(findComponentDependencies())
}
val galleryInstallationsComponent =
ComponentWithParams { params: GalleryInstallationViewModel.ViewModelParams ->
DaggerGalleryInstallationComponent.builder()

View file

@ -0,0 +1,60 @@
package com.anytypeio.anytype.di.feature.membership
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
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.presentation.membership.MembershipUpgradeViewModel
import com.anytypeio.anytype.ui.payments.MembershipUpgradeFragment
import dagger.Binds
import dagger.Component
import dagger.Module
import dagger.Provides
@Component(
dependencies = [MembershipUpdateComponentDependencies::class],
modules = [
MembershipUpdateModule::class,
MembershipUpdateModule.Declarations::class
]
)
@PerScreen
interface MembershipUpdateComponent {
@Component.Factory
interface Factory {
fun create(dependencies: MembershipUpdateComponentDependencies): MembershipUpdateComponent
}
fun inject(fragment: MembershipUpgradeFragment)
}
@Module
object MembershipUpdateModule {
@JvmStatic
@Provides
@PerScreen
fun provideGetAccountUseCase(
repo: AuthRepository,
dispatchers: AppCoroutineDispatchers
): GetAccount = GetAccount(repo = repo, dispatcher = dispatchers)
@Module
interface Declarations {
@PerScreen
@Binds
fun bindViewModelFactory(
factory: MembershipUpgradeViewModel.Factory
): ViewModelProvider.Factory
}
}
interface MembershipUpdateComponentDependencies : ComponentDependencies {
fun authRepository(): AuthRepository
fun appCoroutineDispatchers(): AppCoroutineDispatchers
}

View file

@ -33,6 +33,7 @@ import com.anytypeio.anytype.di.feature.onboarding.login.OnboardingMnemonicLogin
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingMnemonicDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingSoulCreationDependencies
import com.anytypeio.anytype.di.feature.membership.MembershipComponentDependencies
import com.anytypeio.anytype.di.feature.membership.MembershipUpdateComponentDependencies
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromLibraryDependencies
import com.anytypeio.anytype.di.feature.relations.RelationEditDependencies
import com.anytypeio.anytype.di.feature.search.GlobalSearchDependencies
@ -122,7 +123,8 @@ interface MainComponent :
MembershipComponentDependencies,
GalleryInstallationComponentDependencies,
NotificationDependencies,
GlobalSearchDependencies
GlobalSearchDependencies,
MembershipUpdateComponentDependencies
{
fun inject(app: AndroidApplication)
@ -338,4 +340,9 @@ abstract class ComponentDependenciesModule {
@IntoMap
@ComponentDependenciesKey(GlobalSearchDependencies::class)
abstract fun provideGlobalSearchDependencies(component: MainComponent): ComponentDependencies
@Binds
@IntoMap
@ComponentDependenciesKey(MembershipUpdateComponentDependencies::class)
abstract fun provideMembershipUpdateComponentDependencies(component: MainComponent): ComponentDependencies
}

View file

@ -23,6 +23,7 @@ import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.multiplayer.SpaceJoinRequestViewModel
import com.anytypeio.anytype.ui.settings.typography
import javax.inject.Inject
import timber.log.Timber
class SpaceJoinRequestFragment : BaseBottomSheetComposeFragment() {
@ -69,17 +70,19 @@ class SpaceJoinRequestFragment : BaseBottomSheetComposeFragment() {
}
}
private fun proceedWithCommand(command: SpaceJoinRequestViewModel.Command?) {
when(command) {
private fun proceedWithCommand(command: SpaceJoinRequestViewModel.Command) {
Timber.d("proceedWithCommand: $command")
when (command) {
SpaceJoinRequestViewModel.Command.NavigateToMembership -> {
findNavController().navigate(R.id.paymentsScreen)
}
null -> {
// Do nothing.
SpaceJoinRequestViewModel.Command.NavigateToMembershipUpdate -> {
findNavController().navigate(R.id.membershipUpdateScreen)
}
}
}
override fun injectDependencies() {
componentManager().spaceJoinRequestComponent.get(
SpaceJoinRequestViewModel.VmParams(

View file

@ -0,0 +1,78 @@
package com.anytypeio.anytype.ui.payments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.viewModels
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
import com.anytypeio.anytype.core_ui.extensions.color
import com.anytypeio.anytype.core_utils.intents.SystemAction
import com.anytypeio.anytype.core_utils.intents.proceedWithAction
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.payments.screens.MembershipUpgradeScreen
import com.anytypeio.anytype.presentation.membership.MembershipUpgradeViewModel
import com.google.android.material.bottomsheet.BottomSheetDialog
import javax.inject.Inject
class MembershipUpgradeFragment : BaseBottomSheetComposeFragment() {
@Inject
lateinit var factory: MembershipUpgradeViewModel.Factory
private val vm by viewModels<MembershipUpgradeViewModel> { factory }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeDialogView(context = requireContext(), dialog = requireDialog()).apply {
dialog?.setOnShowListener { dg ->
val bottomSheet = (dg as? BottomSheetDialog)?.findViewById<FrameLayout>(
com.google.android.material.R.id.design_bottom_sheet
)
bottomSheet?.setBackgroundColor(requireContext().color(android.R.color.transparent))
}
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MembershipUpgradeScreen(
onDismiss = { },
onButtonClicked = vm::onContactButtonClicked
)
LaunchedEffect(Unit) {
vm.commands.collect { command ->
when (command) {
is MembershipUpgradeViewModel.Command.ShowEmail -> {
proceedWithEmailCreate(command.account)
}
}
}
}
}
}
}
private fun proceedWithEmailCreate(accountId: Id) {
val mail = resources.getString(R.string.payments_email_to)
val subject = resources.getString(R.string.payments_email_subject, accountId)
val body = resources.getString(R.string.payments_email_body)
val mailBody = mail +
"?subject=$subject" +
"&body=$body"
proceedWithAction(SystemAction.MailTo(mailBody))
}
override fun injectDependencies() {
componentManager().membershipUpgradeComponent.get().inject(this)
}
override fun releaseDependencies() {
componentManager().membershipUpgradeComponent.release()
}
}

View file

@ -276,6 +276,10 @@
android:id="@+id/paymentsScreen"
android:name="com.anytypeio.anytype.ui.payments.MembershipFragment" />
<dialog
android:id="@+id/membershipUpdateScreen"
android:name="com.anytypeio.anytype.ui.payments.MembershipUpgradeFragment" />
<dialog
android:id="@+id/galleryInstallationScreen"
android:name="com.anytypeio.anytype.ui.gallery.GalleryInstallationFragment" />

View file

@ -1640,11 +1640,15 @@ Please provide specific details of your needs here.</string>
<string name="membership_support_already_acquired">Youve already acquired a Membership plan using another Anytype account.</string>
<string name="membership_support_different_subscription">Found a subscription with a different id</string>
<string name="membership_support_more_then_one_subscription">Found more than one subscription</string>
<string name="multiplayer_space_join_request_participants_error">Space participant's error</string>
<string name="multiplayer_space_join_request_new_member_error">Request's member error</string>
<string name="multiplayer_space_join_request_participants_error">Space participant\'s error</string>
<string name="multiplayer_space_join_request_new_member_error">Request\'s member error</string>
<string name="multiplayer_space_join_request_current_user_error">Current user status error</string>
<string name="multiplayer_space_join_request_membership_status_error">Current membership status error</string>
<string name="membership_upgrade_title">Membership upgrade</string>
<string name="membership_upgrade_description">Reach us for extra storage, space editors, or more shared spaces. Anytype team will provide details and conditions tailored to your needs.</string>
<string name="membership_upgrade_button">Contact Anytype Team</string>
<plurals name="period_years">
<item quantity="one">year</item>
<item quantity="other">%d years</item>

View file

@ -0,0 +1,108 @@
package com.anytypeio.anytype.payments.screens
import android.content.res.Configuration
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.ButtonPrimary
import com.anytypeio.anytype.core_ui.views.ButtonSize
import com.anytypeio.anytype.core_ui.views.HeadlineHeading
import com.anytypeio.anytype.payments.R
@Composable
fun MembershipUpgradeScreen(
onButtonClicked: () -> Unit,
onDismiss: () -> Unit
) {
Log.d("MembershipUpgradeScreen", "onButtonClicked: $onButtonClicked, onDismiss: $onDismiss")
ElevatedCard(
modifier = Modifier.padding(20.dp),
colors = CardDefaults.cardColors(
containerColor = colorResource(id = R.color.background_primary)
),
elevation = CardDefaults.elevatedCardElevation(
defaultElevation = 16.dp
),
shape = RoundedCornerShape(16.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.padding(vertical = 6.dp)
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Dragger()
}
Spacer(modifier = Modifier.height(19.dp))
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
text = stringResource(id = R.string.membership_upgrade_title),
color = colorResource(id = R.color.text_primary),
style = HeadlineHeading,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(14.dp))
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
text = stringResource(id = R.string.membership_upgrade_description),
color = colorResource(id = R.color.text_primary),
style = BodyRegular,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(30.dp))
ButtonPrimary(
text = stringResource(id = R.string.membership_upgrade_button),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
onClick = { onButtonClicked() },
size = ButtonSize.LargeSecondary
)
Spacer(modifier = Modifier.height(16.dp))
}
}
}
@Preview(
name = "Dark Mode",
showBackground = true,
uiMode = Configuration.UI_MODE_NIGHT_YES
)
@Preview(
name = "Light Mode",
showBackground = true,
uiMode = Configuration.UI_MODE_NIGHT_NO
)
@Composable
fun MembershipUpgradeScreenPreview() {
MembershipUpgradeScreen(onDismiss = {}, onButtonClicked = {})
}

View file

@ -0,0 +1,40 @@
package com.anytypeio.anytype.presentation.membership
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.auth.interactor.GetAccount
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
class MembershipUpgradeViewModel(
private val getAccount: GetAccount
) : ViewModel() {
val commands = MutableSharedFlow<Command>(0)
fun onContactButtonClicked() {
viewModelScope.launch {
val account = getAccount.async(Unit).getOrNull() ?: return@launch
commands.emit(Command.ShowEmail(account.id))
}
}
sealed class Command {
data class ShowEmail(val account: Id) : Command()
}
class Factory @Inject constructor(
private val getAccount: GetAccount,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MembershipUpgradeViewModel(
getAccount = getAccount
) as T
}
}
}

View file

@ -392,7 +392,7 @@ class SpaceJoinRequestViewModel(
if (isPossibleToUpgrade) {
_commands.emit(Command.NavigateToMembership)
} else {
//todo navigate to membership email screen
_commands.emit(Command.NavigateToMembershipUpdate)
}
}
}
@ -476,6 +476,7 @@ class SpaceJoinRequestViewModel(
sealed class Command {
data object NavigateToMembership : Command()
data object NavigateToMembershipUpdate : Command()
}
}

View file

@ -95,7 +95,7 @@ class EditorCreateBlockTest : EditorPresentationTestSetup() {
position = Position.BOTTOM,
prototype = Block.Prototype.Link(
target = linkToObject,
cardStyle = Block.Content.Link.CardStyle.TEXT,
cardStyle = Block.Content.Link.CardStyle.CARD,
iconSize = Block.Content.Link.IconSize.SMALL,
description = Block.Content.Link.Description.NONE
)