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

DROID-2274 Payments | Module + integration (#960)

This commit is contained in:
Konstantin Ivanov 2024-03-04 14:44:16 +01:00 committed by GitHub
parent 5c0c4f048b
commit 47e6696694
Signed by: github
GPG key ID: B5690EEEBB952194
17 changed files with 381 additions and 57 deletions

View file

@ -170,6 +170,7 @@ dependencies {
implementation project(':analytics')
implementation project(':ui-settings')
implementation project(':crash-reporting')
implementation project(':payments')
//Compile time dependencies
kapt libs.daggerCompiler

View file

@ -71,6 +71,7 @@ import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingStartComponen
import com.anytypeio.anytype.di.feature.onboarding.login.DaggerOnboardingMnemonicLoginComponent
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingMnemonicComponent
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingSoulCreationComponent
import com.anytypeio.anytype.di.feature.payments.DaggerPaymentsComponent
import com.anytypeio.anytype.di.feature.relations.DaggerRelationCreateFromLibraryComponent
import com.anytypeio.anytype.di.feature.relations.DaggerRelationEditComponent
import com.anytypeio.anytype.di.feature.relations.LimitObjectTypeModule
@ -1067,6 +1068,10 @@ class ComponentManager(
.build()
}
val paymentsComponent = Component {
DaggerPaymentsComponent.factory().create(findComponentDependencies())
}
class Component<T>(private val builder: () -> T) {
private var instance: T? = null

View file

@ -0,0 +1,48 @@
package com.anytypeio.anytype.di.feature.payments
import androidx.lifecycle.ViewModelProvider
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.ui.payments.PaymentsFragment
import com.anytypeio.anytype.viewmodel.PaymentsViewModelFactory
import dagger.Binds
import dagger.Component
import dagger.Module
@Component(
dependencies = [PaymentsComponentDependencies::class],
modules = [
PaymentsModule::class,
PaymentsModule.Declarations::class
]
)
@PerScreen
interface PaymentsComponent {
@Component.Factory
interface Factory {
fun create(dependencies: PaymentsComponentDependencies): PaymentsComponent
}
fun inject(fragment: PaymentsFragment)
}
@Module
object PaymentsModule {
@Module
interface Declarations {
@PerScreen
@Binds
fun bindViewModelFactory(
factory: PaymentsViewModelFactory
): ViewModelProvider.Factory
}
}
interface PaymentsComponentDependencies : ComponentDependencies {
fun analytics(): Analytics
}

View file

@ -1,61 +1,62 @@
package com.anytypeio.anytype.di.main
import com.anytypeio.anytype.app.AndroidApplication
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.di.common.ComponentDependenciesKey
import com.anytypeio.anytype.di.feature.AppPreferencesDependencies
import com.anytypeio.anytype.di.feature.BacklinkOrAddToObjectDependencies
import com.anytypeio.anytype.di.feature.CreateBookmarkSubComponent
import com.anytypeio.anytype.di.feature.CreateObjectSubComponent
import com.anytypeio.anytype.di.feature.DebugSettingsSubComponent
import com.anytypeio.anytype.di.feature.EditorSubComponent
import com.anytypeio.anytype.di.feature.KeychainPhraseSubComponent
import com.anytypeio.anytype.di.feature.LinkToObjectSubComponent
import com.anytypeio.anytype.di.feature.MainEntrySubComponent
import com.anytypeio.anytype.di.feature.MoveToSubComponent
import com.anytypeio.anytype.di.feature.ObjectSearchSubComponent
import com.anytypeio.anytype.di.feature.ObjectSetSubComponent
import com.anytypeio.anytype.di.feature.ObjectTypeChangeSubComponent
import com.anytypeio.anytype.di.feature.PersonalizationSettingsSubComponent
import com.anytypeio.anytype.di.feature.SplashDependencies
import com.anytypeio.anytype.di.feature.auth.DeletedAccountDependencies
import com.anytypeio.anytype.di.feature.home.HomeScreenDependencies
import com.anytypeio.anytype.di.feature.library.LibraryDependencies
import com.anytypeio.anytype.di.feature.multiplayer.RequestJoinSpaceDependencies
import com.anytypeio.anytype.di.feature.multiplayer.ShareSpaceDependencies
import com.anytypeio.anytype.di.feature.multiplayer.SpaceJoinRequestDependencies
import com.anytypeio.anytype.di.feature.objects.SelectObjectTypeDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingStartDependencies
import com.anytypeio.anytype.di.feature.onboarding.login.OnboardingMnemonicLoginDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingMnemonicDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingSoulCreationDependencies
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromLibraryDependencies
import com.anytypeio.anytype.di.feature.relations.RelationEditDependencies
import com.anytypeio.anytype.di.feature.settings.AboutAppDependencies
import com.anytypeio.anytype.di.feature.settings.AppearanceDependencies
import com.anytypeio.anytype.di.feature.settings.FilesStorageDependencies
import com.anytypeio.anytype.di.feature.settings.LogoutWarningSubComponent
import com.anytypeio.anytype.di.feature.settings.ProfileSubComponent
import com.anytypeio.anytype.di.feature.settings.SpacesStorageDependencies
import com.anytypeio.anytype.di.feature.sharing.AddToAnytypeDependencies
import com.anytypeio.anytype.di.feature.spaces.CreateSpaceDependencies
import com.anytypeio.anytype.di.feature.spaces.SelectSpaceDependencies
import com.anytypeio.anytype.di.feature.spaces.SpaceSettingsDependencies
import com.anytypeio.anytype.di.feature.templates.TemplateBlankDependencies
import com.anytypeio.anytype.di.feature.templates.TemplateSelectDependencies
import com.anytypeio.anytype.di.feature.templates.TemplateSubComponent
import com.anytypeio.anytype.di.feature.types.CreateObjectTypeDependencies
import com.anytypeio.anytype.di.feature.types.TypeEditDependencies
import com.anytypeio.anytype.di.feature.types.TypeIconPickDependencies
import com.anytypeio.anytype.di.feature.update.MigrationErrorDependencies
import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectSubComponent
import com.anytypeio.anytype.ui.widgets.collection.CollectionDependencies
import dagger.Binds
import dagger.Component
import dagger.Module
import dagger.multibindings.IntoMap
import javax.inject.Singleton
import com.anytypeio.anytype.app.AndroidApplication
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.di.common.ComponentDependenciesKey
import com.anytypeio.anytype.di.feature.AppPreferencesDependencies
import com.anytypeio.anytype.di.feature.BacklinkOrAddToObjectDependencies
import com.anytypeio.anytype.di.feature.CreateBookmarkSubComponent
import com.anytypeio.anytype.di.feature.CreateObjectSubComponent
import com.anytypeio.anytype.di.feature.DebugSettingsSubComponent
import com.anytypeio.anytype.di.feature.EditorSubComponent
import com.anytypeio.anytype.di.feature.KeychainPhraseSubComponent
import com.anytypeio.anytype.di.feature.LinkToObjectSubComponent
import com.anytypeio.anytype.di.feature.MainEntrySubComponent
import com.anytypeio.anytype.di.feature.MoveToSubComponent
import com.anytypeio.anytype.di.feature.ObjectSearchSubComponent
import com.anytypeio.anytype.di.feature.ObjectSetSubComponent
import com.anytypeio.anytype.di.feature.ObjectTypeChangeSubComponent
import com.anytypeio.anytype.di.feature.PersonalizationSettingsSubComponent
import com.anytypeio.anytype.di.feature.SplashDependencies
import com.anytypeio.anytype.di.feature.auth.DeletedAccountDependencies
import com.anytypeio.anytype.di.feature.home.HomeScreenDependencies
import com.anytypeio.anytype.di.feature.library.LibraryDependencies
import com.anytypeio.anytype.di.feature.multiplayer.RequestJoinSpaceDependencies
import com.anytypeio.anytype.di.feature.multiplayer.ShareSpaceDependencies
import com.anytypeio.anytype.di.feature.multiplayer.SpaceJoinRequestDependencies
import com.anytypeio.anytype.di.feature.objects.SelectObjectTypeDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingStartDependencies
import com.anytypeio.anytype.di.feature.onboarding.login.OnboardingMnemonicLoginDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingMnemonicDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingSoulCreationDependencies
import com.anytypeio.anytype.di.feature.payments.PaymentsComponentDependencies
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromLibraryDependencies
import com.anytypeio.anytype.di.feature.relations.RelationEditDependencies
import com.anytypeio.anytype.di.feature.settings.AboutAppDependencies
import com.anytypeio.anytype.di.feature.settings.AppearanceDependencies
import com.anytypeio.anytype.di.feature.settings.FilesStorageDependencies
import com.anytypeio.anytype.di.feature.settings.LogoutWarningSubComponent
import com.anytypeio.anytype.di.feature.settings.ProfileSubComponent
import com.anytypeio.anytype.di.feature.settings.SpacesStorageDependencies
import com.anytypeio.anytype.di.feature.sharing.AddToAnytypeDependencies
import com.anytypeio.anytype.di.feature.spaces.CreateSpaceDependencies
import com.anytypeio.anytype.di.feature.spaces.SelectSpaceDependencies
import com.anytypeio.anytype.di.feature.spaces.SpaceSettingsDependencies
import com.anytypeio.anytype.di.feature.templates.TemplateBlankDependencies
import com.anytypeio.anytype.di.feature.templates.TemplateSelectDependencies
import com.anytypeio.anytype.di.feature.templates.TemplateSubComponent
import com.anytypeio.anytype.di.feature.types.CreateObjectTypeDependencies
import com.anytypeio.anytype.di.feature.types.TypeEditDependencies
import com.anytypeio.anytype.di.feature.types.TypeIconPickDependencies
import com.anytypeio.anytype.di.feature.update.MigrationErrorDependencies
import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectSubComponent
import com.anytypeio.anytype.ui.widgets.collection.CollectionDependencies
import dagger.Binds
import dagger.Component
import dagger.Module
import dagger.multibindings.IntoMap
import javax.inject.Singleton
@Singleton
@Component(
@ -297,6 +298,11 @@ private abstract class ComponentDependenciesModule private constructor() {
@ComponentDependenciesKey(SpaceJoinRequestDependencies::class)
abstract fun provideSpaceJoinRequestDependencies(component: MainComponent): ComponentDependencies
@Binds
@IntoMap
@ComponentDependenciesKey(PaymentsComponentDependencies::class)
abstract fun providePaymentsComponentDependencies(component: MainComponent): ComponentDependencies
@Binds
@IntoMap
@ComponentDependenciesKey(RequestJoinSpaceDependencies::class)

View file

@ -0,0 +1,49 @@
package com.anytypeio.anytype.ui.payments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.screens.MainPaymentsScreen
import com.anytypeio.anytype.ui.settings.typography
import com.anytypeio.anytype.viewmodel.PaymentsViewModel
import com.anytypeio.anytype.viewmodel.PaymentsViewModelFactory
import javax.inject.Inject
class PaymentsFragment: BaseBottomSheetComposeFragment() {
@Inject
lateinit var factory: PaymentsViewModelFactory
private val vm by viewModels<PaymentsViewModel> { factory }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme(typography = typography) {
MainPaymentsScreen(vm.viewState.collectAsStateWithLifecycle().value)
}
}
}
}
override fun injectDependencies() {
componentManager().paymentsComponent.get().inject(this)
}
override fun releaseDependencies() {
componentManager().paymentsComponent.release()
}
}

View file

@ -108,6 +108,11 @@ class ProfileSettingsFragment : BaseBottomSheetComposeFragment() {
onClick = {
findNavController().navigate(R.id.aboutAppScreen)
}
),
onMembershipClicked = throttledClick(
onClick = {
findNavController().navigate(R.id.paymentsScreen)
}
)
)
}

View file

@ -275,6 +275,10 @@
android:id="@+id/deleteAccountWarningScreen"
android:name="com.anytypeio.anytype.ui.auth.account.DeleteAccountWarning"/>
<dialog
android:id="@+id/paymentsScreen"
android:name="com.anytypeio.anytype.ui.payments.PaymentsFragment" />
<fragment
android:id="@+id/splashScreen"
android:name="com.anytypeio.anytype.ui.splash.SplashFragment"

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z"
android:fillColor="@color/glyph_selected"/>
<path
android:pathData="M4,14C10,13 13,10 14,4V14V14V24C13,18 10,15 4,14H4L4,14L4,14H4ZM14,4C15,10 18,13 24,14H24C24,14 24,14 24,14C24,14 24,14 24,14H24C18,15 15,18 14,24V14V14V4Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -77,6 +77,7 @@
<string name="deleted_account_danger_zone_msg">Once you request your account to be deleted, you have 30 days to cancel this request. After 30 days, your encrypted account data is permanently removed from the backup node, you won\'t be able to sign into Anytype on new devices.</string>
<string name="danger_zone">Danger zone</string>
<string name="snackbar_template_add">New template was added to the type</string>
<string name="settings_membership">Membership</string>
<string name="default_text_placeholder">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</string>
<string name="default_url_placeholder">https://humanparts.medium.com/how-i-overcame-cult-trauma-with-psychedelics-4342c03a074c</string>

57
payments/build.gradle Normal file
View file

@ -0,0 +1,57 @@
plugins {
id "com.android.library"
id "kotlin-android"
}
android {
def config = rootProject.extensions.getByName("ext")
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion libs.versions.composeKotlinCompilerVersion.get()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlin {
jvmToolchain(17)
}
namespace 'com.anytypeio.anytype.peyments'
}
dependencies {
implementation project(':domain')
implementation project(':core-ui')
implementation project(':analytics')
implementation project(':core-models')
implementation project(':core-utils')
implementation project(':localization')
implementation project(':presentation')
implementation project(':library-emojifier')
compileOnly libs.javaxInject
implementation libs.lifecycleViewModel
implementation libs.lifecycleRuntime
implementation libs.appcompat
implementation libs.compose
implementation libs.composeFoundation
implementation libs.composeMaterial
implementation libs.composeToolingPreview
implementation libs.coilCompose
debugImplementation libs.composeTooling
implementation libs.timber
testImplementation libs.junit
testImplementation libs.kotlinTest
}

View file

@ -0,0 +1 @@
<manifest />

View file

@ -0,0 +1,76 @@
package com.anytypeio.anytype.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.viewmodel.PaymentsState
@Composable
fun MainPaymentsScreen(state: PaymentsState) {
Box(
modifier = Modifier
.nestedScroll(rememberNestedScrollInteropConnection())
.fillMaxWidth()
.wrapContentHeight()
.background(
color = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
),
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 20.dp)
) {
Header(state = state)
}
}
}
@Composable
private fun Header(state: PaymentsState) {
// Dragger at the top, centered
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 6.dp)
) {
Dragger(modifier = Modifier.align(Alignment.Center))
}
// Main content box
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState())
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "Let's build together",
color = colorResource(id = R.color.text_primary),
style = Title1,
textAlign = TextAlign.Center
)
}
}

View file

@ -0,0 +1,7 @@
package com.anytypeio.anytype.viewmodel
sealed class PaymentsState {
object Loading : PaymentsState()
object Error : PaymentsState()
object Success : PaymentsState()
}

View file

@ -0,0 +1,22 @@
package com.anytypeio.anytype.viewmodel
import androidx.lifecycle.ViewModel
import com.anytypeio.anytype.analytics.base.Analytics
import kotlinx.coroutines.flow.MutableStateFlow
import timber.log.Timber
class PaymentsViewModel(
private val analytics: Analytics,
) : ViewModel() {
val viewState = MutableStateFlow<PaymentsState>(PaymentsState.Loading)
init {
Timber.d("PaymentsViewModel created")
}
interface PaymentsNavigation {
object MembershipMain : PaymentsNavigation
object MembershipLevel : PaymentsNavigation
}
}

View file

@ -0,0 +1,17 @@
package com.anytypeio.anytype.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import javax.inject.Inject
class PaymentsViewModelFactory @Inject constructor(
private val analytics: Analytics,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return PaymentsViewModel(
analytics = analytics,
) as T
}
}

View file

@ -58,3 +58,4 @@ include ':app',
include ':ui-settings'
include ':crash-reporting'
include ':localization'
include ':payments'

View file

@ -73,7 +73,8 @@ fun ProfileSettingsScreen(
account: ProfileSettingsViewModel.AccountProfile,
onAppearanceClicked: () -> Unit,
onDataManagementClicked: () -> Unit,
onAboutClicked: () -> Unit
onAboutClicked: () -> Unit,
onMembershipClicked: () -> Unit
) {
LazyColumn(
modifier = Modifier
@ -121,6 +122,16 @@ fun ProfileSettingsScreen(
item {
Divider(paddingStart = 60.dp)
}
item {
Option(
image = R.drawable.ic_membership,
text = stringResource(R.string.settings_membership),
onClick = onMembershipClicked
)
}
item {
Divider(paddingStart = 60.dp)
}
item {
Option(
image = R.drawable.ic_about,