mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-2274 Payment | Design | Tier and code screens (#998)
This commit is contained in:
parent
8ea6f0d3db
commit
7d65104c34
16 changed files with 787 additions and 102 deletions
|
@ -211,6 +211,7 @@ dependencies {
|
|||
implementation libs.composeAccompanistThemeAdapter
|
||||
implementation libs.composeAccompanistPagerIndicators
|
||||
implementation libs.composeAccompanistPermissions
|
||||
implementation libs.composeAccompanistNavigation
|
||||
implementation libs.preference
|
||||
implementation libs.activityCompose
|
||||
implementation libs.composeReorderable
|
||||
|
|
|
@ -5,40 +5,123 @@ 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.runtime.Composable
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.screens.CodeScreen
|
||||
import com.anytypeio.anytype.screens.MainPaymentsScreen
|
||||
import com.anytypeio.anytype.screens.TierScreen
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
import com.anytypeio.anytype.viewmodel.PaymentsNavigation
|
||||
import com.anytypeio.anytype.viewmodel.PaymentsViewModel
|
||||
import com.anytypeio.anytype.viewmodel.PaymentsViewModelFactory
|
||||
import com.google.accompanist.navigation.material.BottomSheetNavigator
|
||||
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
|
||||
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
|
||||
import com.google.accompanist.navigation.material.bottomSheet
|
||||
import com.google.accompanist.navigation.material.rememberBottomSheetNavigator
|
||||
import javax.inject.Inject
|
||||
|
||||
class PaymentsFragment: BaseBottomSheetComposeFragment() {
|
||||
class PaymentsFragment : BaseBottomSheetComposeFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var factory: PaymentsViewModelFactory
|
||||
|
||||
private val vm by viewModels<PaymentsViewModel> { factory }
|
||||
private lateinit var navController: NavHostController
|
||||
|
||||
@OptIn(ExperimentalMaterialNavigationApi::class)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return ComposeView(requireContext()).apply {
|
||||
return ComposeDialogView(context = requireContext(), dialog = requireDialog()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MaterialTheme(typography = typography) {
|
||||
MainPaymentsScreen(vm.viewState.collectAsStateWithLifecycle().value)
|
||||
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
||||
navController = rememberNavController(bottomSheetNavigator)
|
||||
SetupNavigation(bottomSheetNavigator, navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
jobs += subscribe(vm.command) { command ->
|
||||
when (command) {
|
||||
PaymentsNavigation.Tier -> navController.navigate(PaymentsNavigation.Tier.route)
|
||||
PaymentsNavigation.Code -> navController.navigate(PaymentsNavigation.Code.route)
|
||||
PaymentsNavigation.Dismiss -> navController.popBackStack()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialNavigationApi::class)
|
||||
@Composable
|
||||
private fun SetupNavigation(
|
||||
bottomSheetNavigator: BottomSheetNavigator,
|
||||
navController: NavHostController
|
||||
) {
|
||||
ModalBottomSheetLayout(bottomSheetNavigator = bottomSheetNavigator) {
|
||||
NavigationGraph(navController = navController)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialNavigationApi::class)
|
||||
@Composable
|
||||
private fun NavigationGraph(navController: NavHostController) {
|
||||
NavHost(navController = navController, startDestination = PaymentsNavigation.Main.route) {
|
||||
composable(PaymentsNavigation.Main.route) {
|
||||
MainPaymentsScreen()
|
||||
}
|
||||
bottomSheet(PaymentsNavigation.Tier.route) {
|
||||
TierScreen()
|
||||
}
|
||||
bottomSheet(PaymentsNavigation.Code.route) {
|
||||
CodeScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainPaymentsScreen() {
|
||||
MainPaymentsScreen(
|
||||
state = vm.viewState.collectAsStateWithLifecycle().value,
|
||||
tierClicked = vm::onTierClicked
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TierScreen() {
|
||||
TierScreen(
|
||||
tier = vm.selectedTier.collectAsStateWithLifecycle().value,
|
||||
onDismiss = vm::onDismissTier,
|
||||
actionPay = vm::onPayButtonClicked
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CodeScreen() {
|
||||
CodeScreen(
|
||||
state = vm.codeViewState.collectAsStateWithLifecycle().value,
|
||||
actionResend = { },
|
||||
actionCode = vm::onActionCode,
|
||||
onDismiss = vm::onDismissCode
|
||||
)
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
componentManager().paymentsComponent.get().inject(this)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
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:fillColor="@color/text_button_label"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
|
14
core-ui/src/main/res/drawable/ic_system_checkbox.xml
Normal file
14
core-ui/src/main/res/drawable/ic_system_checkbox.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M0,6.4C0,4.16 0,3.04 0.436,2.184C0.819,1.431 1.431,0.819 2.184,0.436C3.04,0 4.16,0 6.4,0H9.6C11.84,0 12.96,0 13.816,0.436C14.569,0.819 15.181,1.431 15.564,2.184C16,3.04 16,4.16 16,6.4V9.6C16,11.84 16,12.96 15.564,13.816C15.181,14.569 14.569,15.181 13.816,15.564C12.96,16 11.84,16 9.6,16H6.4C4.16,16 3.04,16 2.184,15.564C1.431,15.181 0.819,14.569 0.436,13.816C0,12.96 0,11.84 0,9.6V6.4Z"
|
||||
android:fillColor="@color/text_primary"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M4,7.5L7.531,11L12,4"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/text_button_label"/>
|
||||
</vector>
|
11
core-ui/src/main/res/drawable/ic_system_checkbox_empty.xml
Normal file
11
core-ui/src/main/res/drawable/ic_system_checkbox_empty.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M0.5,4C0.5,2.067 2.067,0.5 4,0.5H12C13.933,0.5 15.5,2.067 15.5,4V12C15.5,13.933 13.933,15.5 12,15.5H4C2.067,15.5 0.5,13.933 0.5,12V4Z"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/shape_primary"/>
|
||||
</vector>
|
|
@ -14,7 +14,7 @@ dokkaVersion = '1.8.20'
|
|||
|
||||
activityComposeVersion = '1.8.1'
|
||||
composeReorderableVersion = '0.9.6'
|
||||
accompanistVersion = "0.30.0"
|
||||
accompanistVersion = "0.34.0"
|
||||
appcompatVersion = '1.6.1'
|
||||
androidXAnnotationVersion = '1.7.0'
|
||||
fragmentVersion = "1.6.2"
|
||||
|
@ -84,6 +84,7 @@ composeAccompanistPager = { module = "com.google.accompanist:accompanist-pager",
|
|||
composeAccompanistPermissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistVersion" }
|
||||
composeAccompanistPagerIndicators = { module = "com.google.accompanist:accompanist-pager-indicators", version.ref = "accompanistVersion" }
|
||||
composeAccompanistThemeAdapter = { module = "com.google.accompanist:accompanist-themeadapter-material", version.ref = "accompanistVersion" }
|
||||
composeAccompanistNavigation = { module = "com.google.accompanist:accompanist-navigation-material", version.ref = "accompanistVersion" }
|
||||
composeReorderable = { module = "org.burnoutcrew.composereorderable:reorderable", version.ref = "composeReorderableVersion" }
|
||||
composeConstraintLayout = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "composeConstraintLayoutVersion" }
|
||||
kotlinxSerializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.4.1" }
|
||||
|
|
|
@ -1378,9 +1378,10 @@
|
|||
|
||||
<!-- Membership Level Details -->
|
||||
<string name="payments_details_name_title">Pick your unique name</string>
|
||||
<string name="payments_details_name_subtitle">This name acts like a personal domain, making it easier for others to find you</string>
|
||||
<string name="payments_details_name_subtitle">This is your unique name on the Anytype network, confirming your Membership. It acts as your personal domain and cannot be changed.</string>
|
||||
<string name="payments_details_name_hint">Myself</string>
|
||||
<string name="payments_details_name_domain">.any</string>
|
||||
<string name="payments_details_name_min">Min 7 characters</string>
|
||||
<string name="payments_details_name_error">This name is already taken!</string>
|
||||
<string name="payments_details_name_success">This name is up for grabs!</string>
|
||||
|
||||
|
@ -1411,5 +1412,15 @@
|
|||
<string name="payments_detials_button_pay">Pay by Card</string>
|
||||
<string name="payments_detials_button_submit">Submit</string>
|
||||
<string name="payments_details_whats_included">What’s included</string>
|
||||
<string name="payments_email_title">Get your free membership</string>
|
||||
<string name="payments_email_subtitle">We need your email to keep spam at bay and the fun in play!</string>
|
||||
<string name="payments_email_checkbox_text">I\'d like to get updates on products and enjoy free perks!</string>
|
||||
<string name="payments_email_hint">E-mail</string>
|
||||
|
||||
<!-- Payments Code -->
|
||||
<string name="payments_code_title">Enter the code sent to your email</string>
|
||||
<string name="payments_code_resend">Resend</string>
|
||||
<string name="payments_code_resend_in">Resend in %1$n sec</string>
|
||||
|
||||
|
||||
</resources>
|
|
@ -43,8 +43,8 @@ dependencies {
|
|||
implementation libs.appcompat
|
||||
implementation libs.compose
|
||||
implementation libs.composeFoundation
|
||||
implementation libs.composeMaterial
|
||||
implementation libs.composeToolingPreview
|
||||
implementation libs.composeMaterial3
|
||||
|
||||
implementation libs.coilCompose
|
||||
|
||||
|
|
|
@ -7,19 +7,30 @@ sealed class Tier {
|
|||
data class Explorer(
|
||||
override val id: String,
|
||||
override val isCurrent: Boolean,
|
||||
val price: String = ""
|
||||
val price: String = "",
|
||||
val email: String = "",
|
||||
val isChecked: Boolean = true
|
||||
) : Tier()
|
||||
|
||||
data class Builder(
|
||||
override val id: String,
|
||||
override val isCurrent: Boolean,
|
||||
val price: String = ""
|
||||
val price: String = "",
|
||||
val interval: String = "",
|
||||
val name: String = "",
|
||||
val nameIsTaken: Boolean = false,
|
||||
val nameIsFree: Boolean = false
|
||||
|
||||
) : Tier()
|
||||
|
||||
data class CoCreator(
|
||||
override val id: String,
|
||||
override val isCurrent: Boolean,
|
||||
val price: String = ""
|
||||
val price: String = "",
|
||||
val interval: String = "",
|
||||
val name: String = "",
|
||||
val nameIsTaken: Boolean = false,
|
||||
val nameIsFree: Boolean = false
|
||||
) : Tier()
|
||||
|
||||
data class Custom(
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
package com.anytypeio.anytype.screens
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
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.views.BodyBold
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineTitle
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle1Regular
|
||||
import com.anytypeio.anytype.peyments.R
|
||||
import com.anytypeio.anytype.viewmodel.PaymentsCodeState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CodeScreen(
|
||||
state: PaymentsCodeState,
|
||||
actionResend: () -> Unit,
|
||||
actionCode: (String) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
ModalBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismissRequest = onDismiss,
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
content = { ModalCodeContent(state = state, actionCode = actionCode) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ModalCodeContent(state: PaymentsCodeState, actionCode: (String) -> Unit) {
|
||||
val focusRequesters = remember { List(4) { FocusRequester() } }
|
||||
val enteredDigits = remember { mutableStateListOf<Char>() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
LaunchedEffect(key1 = enteredDigits.size) {
|
||||
if (enteredDigits.size == 4) {
|
||||
actionCode(enteredDigits.joinToString(""))
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = state) {
|
||||
if (state is PaymentsCodeState.Loading) {
|
||||
focusManager.clearFocus(true)
|
||||
}
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
Spacer(modifier = Modifier.padding(118.dp))
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp),
|
||||
text = stringResource(id = com.anytypeio.anytype.localization.R.string.payments_code_title),
|
||||
style = BodyBold,
|
||||
color = colorResource(
|
||||
id = R.color.text_primary
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.height(44.dp))
|
||||
val modifier = Modifier
|
||||
.width(48.dp)
|
||||
.height(64.dp)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.CenterHorizontally),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
focusRequesters.forEachIndexed { index, focusRequester ->
|
||||
CodeNumber(
|
||||
isEnabled = state !is PaymentsCodeState.Loading,
|
||||
modifier = modifier,
|
||||
focusRequester = focusRequester,
|
||||
onDigitEntered = { digit ->
|
||||
if (enteredDigits.size < 4) {
|
||||
enteredDigits.add(digit)
|
||||
}
|
||||
if (index < 3) focusRequesters[index + 1].requestFocus()
|
||||
},
|
||||
onBackspace = {
|
||||
if (enteredDigits.isNotEmpty()) enteredDigits.removeLast()
|
||||
if (index > 0) focusRequesters[index - 1].requestFocus()
|
||||
}
|
||||
)
|
||||
if (index < 3) Spacer(modifier = Modifier.width(8.dp))
|
||||
}
|
||||
}
|
||||
if (state is PaymentsCodeState.Error) {
|
||||
Text(
|
||||
text = state.message,
|
||||
color = colorResource(id = R.color.palette_system_red),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp, top = 7.dp),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(149.dp))
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = com.anytypeio.anytype.localization.R.string.payments_code_resend),
|
||||
style = PreviewTitle1Regular,
|
||||
color = colorResource(id = R.color.text_tertiary),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
visible = state is PaymentsCodeState.Loading,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(24.dp),
|
||||
color = colorResource(R.color.shape_secondary),
|
||||
trackColor = colorResource(R.color.shape_primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CodeNumber(
|
||||
isEnabled: Boolean,
|
||||
focusRequester: FocusRequester,
|
||||
onDigitEntered: (Char) -> Unit,
|
||||
onBackspace: () -> Unit,
|
||||
modifier: Modifier
|
||||
) {
|
||||
val (text, setText) = remember { mutableStateOf("") }
|
||||
|
||||
val borderColor = colorResource(id = R.color.shape_primary)
|
||||
BasicTextField(
|
||||
value = text,
|
||||
onValueChange = { newValue ->
|
||||
when {
|
||||
newValue.length == 1 && newValue[0].isDigit() && text.isEmpty() -> {
|
||||
setText(newValue)
|
||||
onDigitEntered(newValue[0])
|
||||
}
|
||||
|
||||
newValue.isEmpty() -> {
|
||||
if (text.isNotEmpty()) {
|
||||
setText("")
|
||||
onBackspace()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
.focusRequester(focusRequester)
|
||||
.onKeyEvent { event ->
|
||||
if (event.type == KeyEventType.KeyUp && event.key == Key.Backspace && text.isEmpty()) {
|
||||
onBackspace()
|
||||
true
|
||||
} else false
|
||||
},
|
||||
singleLine = true,
|
||||
enabled = isEnabled,
|
||||
cursorBrush = SolidColor(colorResource(id = R.color.text_primary)),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Next,
|
||||
keyboardType = KeyboardType.Number
|
||||
),
|
||||
decorationBox = { innerTextField ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.border(BorderStroke(1.dp, borderColor), RoundedCornerShape(8.dp))
|
||||
.padding(horizontal = 15.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
innerTextField()
|
||||
}
|
||||
},
|
||||
textStyle = HeadlineTitle.copy(color = colorResource(id = R.color.text_primary))
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun EnterCodeModalPreview() {
|
||||
ModalCodeContent(
|
||||
state = PaymentsCodeState.Loading,
|
||||
actionCode = {}
|
||||
)
|
||||
}
|
|
@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
|
@ -26,7 +26,7 @@ import androidx.compose.foundation.rememberScrollState
|
|||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -57,7 +57,7 @@ import com.anytypeio.anytype.models.Tier
|
|||
import com.anytypeio.anytype.viewmodel.PaymentsState
|
||||
|
||||
@Composable
|
||||
fun MainPaymentsScreen(state: PaymentsState) {
|
||||
fun MainPaymentsScreen(state: PaymentsState, tierClicked: (Tier) -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.nestedScroll(rememberNestedScrollInteropConnection())
|
||||
|
@ -74,11 +74,24 @@ fun MainPaymentsScreen(state: PaymentsState) {
|
|||
.padding(bottom = 20.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
if (state is PaymentsState.Success) {
|
||||
Header(state = state)
|
||||
if (state is PaymentsState.Default) {
|
||||
Header()
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
InfoCards()
|
||||
TiersList(state = state)
|
||||
TiersList(tiers = state.tiers, tierClicked = tierClicked)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
LinkButton(text = stringResource(id = R.string.payments_member_link), action = {})
|
||||
Divider()
|
||||
LinkButton(text = stringResource(id = R.string.payments_privacy_link), action = {})
|
||||
Divider()
|
||||
LinkButton(text = stringResource(id = R.string.payments_terms_link), action = {})
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
BottomText()
|
||||
}
|
||||
if (state is PaymentsState.PaymentSuccess) {
|
||||
Header()
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
TiersList(tiers = state.tiers, tierClicked = tierClicked)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
LinkButton(text = stringResource(id = R.string.payments_member_link), action = {})
|
||||
Divider()
|
||||
|
@ -90,11 +103,10 @@ fun MainPaymentsScreen(state: PaymentsState) {
|
|||
}
|
||||
}
|
||||
}
|
||||
MembershipLevels(tier = Tier.Explorer(id = "888", isCurrent = true))
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(state: PaymentsState.Success) {
|
||||
private fun Header() {
|
||||
|
||||
// Dragger at the top, centered
|
||||
Box(
|
||||
|
@ -141,7 +153,7 @@ private fun Header(state: PaymentsState.Success) {
|
|||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun TiersList(state: PaymentsState.Success) {
|
||||
fun TiersList(tiers: List<Tier>, tierClicked: (Tier) -> Unit) {
|
||||
val itemsScroll = rememberLazyListState(initialFirstVisibleItemIndex = 1)
|
||||
LazyRow(
|
||||
state = itemsScroll,
|
||||
|
@ -152,17 +164,19 @@ fun TiersList(state: PaymentsState.Success) {
|
|||
contentPadding = PaddingValues(start = 20.dp, end = 20.dp),
|
||||
flingBehavior = rememberSnapFlingBehavior(lazyListState = itemsScroll)
|
||||
) {
|
||||
itemsIndexed(state.tiers) { _, tier ->
|
||||
itemsIndexed(tiers) { _, tier ->
|
||||
val resources = mapTierToResources(tier)
|
||||
TierView(
|
||||
title = resources.title,
|
||||
subTitle = resources.subtitle,
|
||||
colorGradient = resources.colorGradient,
|
||||
radialGradient = resources.radialGradient,
|
||||
icon = resources.smallIcon,
|
||||
buttonText = stringResource(id = R.string.payments_button_learn),
|
||||
onClick = { /*TODO*/ }
|
||||
)
|
||||
if (resources != null) {
|
||||
TierView(
|
||||
title = resources.title,
|
||||
subTitle = resources.subtitle,
|
||||
colorGradient = resources.colorGradient,
|
||||
radialGradient = resources.radialGradient,
|
||||
icon = resources.smallIcon,
|
||||
buttonText = stringResource(id = R.string.payments_button_learn),
|
||||
onClick = { tierClicked.invoke(tier) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +286,7 @@ fun MainPaymentsScreenPreview() {
|
|||
Tier.CoCreator("999", isCurrent = false),
|
||||
Tier.Custom("999", isCurrent = false)
|
||||
)
|
||||
MainPaymentsScreen(PaymentsState.Success(tiers))
|
||||
MainPaymentsScreen(PaymentsState.PaymentSuccess(tiers), {})
|
||||
}
|
||||
|
||||
val headerTextStyle = TextStyle(
|
||||
|
|
|
@ -1,108 +1,102 @@
|
|||
package com.anytypeio.anytype.screens
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.FractionalThreshold
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.rememberSwipeableState
|
||||
import androidx.compose.material.swipeable
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.BodyBold
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCallout
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonPrimary
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineTitle
|
||||
import com.anytypeio.anytype.core_ui.widgets.DragStates
|
||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||
import com.anytypeio.anytype.core_ui.views.Relations2
|
||||
import com.anytypeio.anytype.models.Tier
|
||||
import com.anytypeio.anytype.peyments.R
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MembershipLevels(tier: Tier) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
fun TierScreen(tier: Tier?, onDismiss: () -> Unit, actionPay: () -> Unit) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
ModalBottomSheet(
|
||||
modifier = Modifier.padding(top = 30.dp),
|
||||
sheetState = sheetState,
|
||||
containerColor = Color.Transparent,
|
||||
dragHandle = null,
|
||||
onDismissRequest = { onDismiss() },
|
||||
content = {
|
||||
MembershipLevels(tier = tier, actionPay = actionPay)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MembershipLevels(tier: Tier?, actionPay: () -> Unit) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = colorResource(id = R.color.shape_tertiary)),
|
||||
contentAlignment = Alignment.BottomStart,
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_tertiary),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
),
|
||||
) {
|
||||
|
||||
val swipeableState = rememberSwipeableState(DragStates.VISIBLE)
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
if (swipeableState.isAnimationRunning && swipeableState.targetValue == DragStates.DISMISSED) {
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
keyboardController?.hide()
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sizePx =
|
||||
with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
|
||||
|
||||
val tierResources = mapTierToResources(tier)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = true,
|
||||
enter = slideInVertically { it },
|
||||
exit = slideOutVertically { it },
|
||||
modifier = Modifier
|
||||
.swipeable(state = swipeableState,
|
||||
orientation = Orientation.Vertical,
|
||||
anchors = mapOf(
|
||||
0f to DragStates.VISIBLE, sizePx to DragStates.DISMISSED
|
||||
),
|
||||
thresholds = { _, _ -> FractionalThreshold(0.3f) })
|
||||
.offset { IntOffset(0, swipeableState.offset.value.roundToInt()) }
|
||||
) {
|
||||
if (tierResources != null) {
|
||||
val brush = Brush.verticalGradient(
|
||||
listOf(
|
||||
tierResources.colorGradient,
|
||||
Color.Transparent
|
||||
)
|
||||
)
|
||||
|
||||
Column {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
@ -153,27 +147,70 @@ fun MembershipLevels(tier: Tier) {
|
|||
Spacer(modifier = Modifier.height(6.dp))
|
||||
}
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
NamePicker()
|
||||
if (tier is Tier.Explorer) {
|
||||
SubmitEmail(tier = tier, updateEmail = { email ->
|
||||
//viewModel.updateEmail(email)
|
||||
})
|
||||
}
|
||||
if (tier is Tier.Builder) {
|
||||
NamePickerAndButton(
|
||||
name = tier.name,
|
||||
nameIsTaken = tier.nameIsTaken,
|
||||
nameIsFree = tier.nameIsFree,
|
||||
price = tier.price,
|
||||
interval = tier.interval,
|
||||
actionPay = actionPay
|
||||
)
|
||||
}
|
||||
if (tier is Tier.CoCreator) {
|
||||
NamePickerAndButton(
|
||||
name = tier.name,
|
||||
nameIsTaken = tier.nameIsTaken,
|
||||
nameIsFree = tier.nameIsFree,
|
||||
price = tier.price,
|
||||
interval = tier.interval,
|
||||
actionPay = actionPay
|
||||
)
|
||||
Price(tier.price, tier.interval)
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
ButtonPay(enabled = true, actionPay = {
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NamePicker() {
|
||||
fun NamePickerAndButton(
|
||||
name: String,
|
||||
nameIsTaken: Boolean,
|
||||
nameIsFree: Boolean,
|
||||
price: String,
|
||||
interval: String,
|
||||
actionPay: () -> Unit
|
||||
) {
|
||||
var innerValue by remember(name) { mutableStateOf(name) }
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.fillMaxHeight()
|
||||
.background(
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = colorResource(id = R.color.background_primary)
|
||||
)
|
||||
.padding(start = 20.dp, end = 20.dp)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp, top = 26.dp),
|
||||
.padding(top = 26.dp),
|
||||
text = stringResource(id = R.string.payments_details_name_title),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
style = BodyBold,
|
||||
|
@ -182,17 +219,117 @@ fun NamePicker() {
|
|||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp, top = 6.dp),
|
||||
.padding(top = 6.dp),
|
||||
text = stringResource(id = R.string.payments_details_name_subtitle),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
style = BodyCallout,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
BasicTextField(
|
||||
value = innerValue,
|
||||
onValueChange = { innerValue = it },
|
||||
textStyle = BodyRegular.copy(color = colorResource(id = com.anytypeio.anytype.core_ui.R.color.text_primary)),
|
||||
singleLine = true,
|
||||
enabled = true,
|
||||
cursorBrush = SolidColor(colorResource(id = com.anytypeio.anytype.core_ui.R.color.text_primary)),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.wrapContentHeight()
|
||||
.padding(start = 0.dp, top = 2.dp)
|
||||
.focusRequester(focusRequester)
|
||||
.onFocusChanged { focusState ->
|
||||
if (focusState.isFocused) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text
|
||||
),
|
||||
keyboardActions = KeyboardActions {
|
||||
keyboardController?.hide()
|
||||
focusManager.clearFocus()
|
||||
},
|
||||
decorationBox = { innerTextField ->
|
||||
if (innerValue.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(id = com.anytypeio.anytype.localization.R.string.payments_details_name_hint),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = com.anytypeio.anytype.core_ui.R.color.text_tertiary),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
)
|
||||
}
|
||||
innerTextField()
|
||||
}
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.payments_details_name_domain),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
|
||||
val (messageTextColor, messageText) = when {
|
||||
nameIsTaken ->
|
||||
colorResource(id = R.color.palette_system_red) to stringResource(id = R.string.payments_details_name_error)
|
||||
|
||||
nameIsFree ->
|
||||
colorResource(id = R.color.palette_dark_lime) to stringResource(id = R.string.payments_details_name_success)
|
||||
|
||||
else ->
|
||||
colorResource(id = R.color.text_secondary) to stringResource(id = R.string.payments_details_name_min)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp),
|
||||
text = messageText,
|
||||
color = messageTextColor,
|
||||
style = Relations2,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Price(price = price, interval = interval)
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
ButtonPay(enabled = true, actionPay = {
|
||||
actionPay()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Price(price: String, interval: String) {
|
||||
Row() {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.padding(start = 20.dp),
|
||||
text = price,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
style = HeadlineTitle,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.align(Alignment.Bottom)
|
||||
.padding(bottom = 4.dp, start = 6.dp),
|
||||
text = interval,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
style = Relations1,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Benefit(benefit: String) {
|
||||
private fun Benefit(benefit: String) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
|
@ -218,9 +355,141 @@ fun Benefit(benefit: String) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubmitEmail(tier: Tier.Explorer, updateEmail: (String) -> Unit) {
|
||||
|
||||
var innerValue by remember(tier.email) { mutableStateOf(tier.email) }
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
var isChecked by remember(tier.isChecked) { mutableStateOf(tier.isChecked) }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
.background(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = colorResource(id = R.color.background_primary)
|
||||
)
|
||||
.padding(start = 20.dp, end = 20.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(26.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.payments_email_title),
|
||||
style = BodyBold,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.payments_email_subtitle),
|
||||
style = BodyCallout,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
BasicTextField(
|
||||
value = innerValue,
|
||||
onValueChange = { innerValue = it },
|
||||
textStyle = BodyRegular.copy(color = colorResource(id = com.anytypeio.anytype.core_ui.R.color.text_primary)),
|
||||
singleLine = true,
|
||||
enabled = true,
|
||||
cursorBrush = SolidColor(colorResource(id = com.anytypeio.anytype.core_ui.R.color.text_primary)),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(start = 0.dp, top = 2.dp)
|
||||
.focusRequester(focusRequester)
|
||||
.onFocusChanged { focusState ->
|
||||
if (focusState.isFocused) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Email
|
||||
),
|
||||
keyboardActions = KeyboardActions {
|
||||
keyboardController?.hide()
|
||||
focusManager.clearFocus()
|
||||
},
|
||||
decorationBox = { innerTextField ->
|
||||
if (innerValue.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(id = com.anytypeio.anytype.localization.R.string.payments_email_hint),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = com.anytypeio.anytype.core_ui.R.color.text_tertiary),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
)
|
||||
}
|
||||
innerTextField()
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
|
||||
val icon = if (isChecked) {
|
||||
R.drawable.ic_system_checkbox
|
||||
} else {
|
||||
R.drawable.ic_system_checkbox_empty
|
||||
}
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
Row {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(top = 3.dp)
|
||||
.size(16.dp)
|
||||
.noRippleClickable { isChecked = !isChecked },
|
||||
painter = painterResource(id = icon),
|
||||
contentDescription = "checkbox"
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 12.dp),
|
||||
text = stringResource(id = R.string.payments_email_checkbox_text),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(31.dp))
|
||||
val enabled = innerValue.isNotEmpty()
|
||||
ButtonPrimary(
|
||||
enabled = enabled,
|
||||
text = stringResource(id = R.string.payments_detials_button_submit),
|
||||
onClick = { updateEmail.invoke(innerValue) },
|
||||
size = ButtonSize.Large,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ButtonPay(enabled: Boolean, actionPay: () -> Unit) {
|
||||
ButtonPrimary(
|
||||
enabled = enabled,
|
||||
text = stringResource(id = R.string.payments_detials_button_pay),
|
||||
onClick = { actionPay() },
|
||||
size = ButtonSize.Large,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Preview()
|
||||
@Composable
|
||||
fun MyLevel() {
|
||||
MembershipLevels(tier = Tier.Explorer("121", true))
|
||||
MembershipLevels(
|
||||
tier = Tier.Builder(
|
||||
id = "121",
|
||||
isCurrent = true,
|
||||
price = "$99",
|
||||
interval = "per year"
|
||||
),
|
||||
actionPay = {}
|
||||
)
|
||||
}
|
|
@ -10,8 +10,8 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
|
@ -120,7 +120,7 @@ fun PriceOrOption() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun mapTierToResources(tier: Tier): TierResources {
|
||||
fun mapTierToResources(tier: Tier?): TierResources? {
|
||||
return when (tier) {
|
||||
is Tier.Builder -> TierResources(
|
||||
title = stringResource(id = R.string.payments_tier_builder),
|
||||
|
@ -160,6 +160,8 @@ fun mapTierToResources(tier: Tier): TierResources {
|
|||
radialGradient = Color(0xFF24BFD4),
|
||||
benefits = stringArrayResource(id = R.array.payments_benefits_explorer).toList()
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,5 +4,20 @@ import com.anytypeio.anytype.models.Tier
|
|||
|
||||
sealed class PaymentsState {
|
||||
object Loading : PaymentsState()
|
||||
data class Success(val tiers: List<Tier>) : PaymentsState()
|
||||
data class Default(val tiers: List<Tier>) : PaymentsState()
|
||||
data class PaymentSuccess(val tiers: List<Tier>) : PaymentsState()
|
||||
}
|
||||
|
||||
sealed class PaymentsCodeState {
|
||||
object Empty : PaymentsCodeState()
|
||||
object Loading : PaymentsCodeState()
|
||||
object Success : PaymentsCodeState()
|
||||
data class Error(val message: String) : PaymentsCodeState()
|
||||
}
|
||||
|
||||
sealed class PaymentsNavigation(val route: String) {
|
||||
object Main : PaymentsNavigation("main")
|
||||
object Tier : PaymentsNavigation("tier")
|
||||
object Code : PaymentsNavigation("code")
|
||||
object Dismiss : PaymentsNavigation("")
|
||||
}
|
|
@ -11,10 +11,14 @@ class PaymentsViewModel(
|
|||
) : ViewModel() {
|
||||
|
||||
val viewState = MutableStateFlow<PaymentsState>(PaymentsState.Loading)
|
||||
val codeViewState = MutableStateFlow<PaymentsCodeState>(PaymentsCodeState.Empty)
|
||||
val command = MutableStateFlow<PaymentsNavigation?>(null)
|
||||
|
||||
val selectedTier = MutableStateFlow<Tier?>(null)
|
||||
|
||||
init {
|
||||
Timber.d("PaymentsViewModel created")
|
||||
viewState.value = PaymentsState.Success(
|
||||
viewState.value = PaymentsState.Default(
|
||||
listOf(
|
||||
Tier.Explorer("Free", true),
|
||||
Tier.Builder("$9.99/mo", false),
|
||||
|
@ -24,8 +28,24 @@ class PaymentsViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
interface PaymentsNavigation {
|
||||
object MembershipMain : PaymentsNavigation
|
||||
object MembershipLevel : PaymentsNavigation
|
||||
fun onTierClicked(tier: Tier) {
|
||||
selectedTier.value = tier
|
||||
command.value = PaymentsNavigation.Tier
|
||||
}
|
||||
|
||||
fun onActionCode(code: String) {
|
||||
Timber.d("onActionCode: $code")
|
||||
}
|
||||
|
||||
fun onPayButtonClicked() {
|
||||
command.value = PaymentsNavigation.Code
|
||||
}
|
||||
|
||||
fun onDismissTier() {
|
||||
command.value = PaymentsNavigation.Dismiss
|
||||
}
|
||||
|
||||
fun onDismissCode() {
|
||||
command.value = PaymentsNavigation.Dismiss
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue