Skip to content

Commit

Permalink
Add screenshots
Browse files Browse the repository at this point in the history
  • Loading branch information
toluo-stripe committed Jan 3, 2025
1 parent a25a089 commit bf11f9a
Show file tree
Hide file tree
Showing 13 changed files with 234 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ internal class LinkActivity : ComponentActivity() {
var bottomSheetContent by remember { mutableStateOf<BottomSheetContent?>(null) }
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
val appBarState by vm.linkState.collectAsState()
val appBarState by vm.linkAppBarState.collectAsState()

if (bottomSheetContent != null) {
DisposableEffect(bottomSheetContent) {
Expand Down Expand Up @@ -81,7 +81,8 @@ internal class LinkActivity : ComponentActivity() {
onUpdateSheetContent = {
bottomSheetContent = it
},
onBackPressed = onBackPressedDispatcher::onBackPressed
onBackPressed = onBackPressedDispatcher::onBackPressed,
onLogout = vm::logout
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import androidx.navigation.NavHostController
import com.stripe.android.core.Logger
import com.stripe.android.link.LinkActivity.Companion.getArgs
import com.stripe.android.link.account.LinkAccountManager
import com.stripe.android.link.injection.DaggerNativeLinkComponent
Expand All @@ -22,36 +23,76 @@ import com.stripe.android.link.ui.LinkAppBarState
import com.stripe.android.paymentsheet.R
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

internal class LinkActivityViewModel @Inject constructor(
val activityRetainedComponent: NativeLinkComponent,
private val linkAccountManager: LinkAccountManager
private val linkAccountManager: LinkAccountManager,
private val logger: Logger
) : ViewModel(), DefaultLifecycleObserver {
private val _linkState = MutableStateFlow(
private val _linkAppBarState = MutableStateFlow(
value = LinkAppBarState(
navigationIcon = R.drawable.stripe_link_close,
showHeader = true,
showOverflowMenu = false,
email = null,
)
)
val linkState: StateFlow<LinkAppBarState> = _linkState
val linkAppBarState: StateFlow<LinkAppBarState> = _linkAppBarState

val linkAccount: LinkAccount?
get() = linkAccountManager.linkAccount.value

var navController: NavHostController? = null
var dismissWithResult: ((LinkActivityResult) -> Unit)? = null

init {
listenForAppBarState()
}

private fun listenForAppBarState() {
viewModelScope.launch {
linkAccountManager.linkAccount.collectLatest { account ->
val showOverflowMenu = account?.email != null && account.accountStatus == AccountStatus.Verified
_linkAppBarState.update {
it.copy(showOverflowMenu = showOverflowMenu)
}
}
}
}

fun handleViewAction(action: LinkAction) {
when (action) {
LinkAction.BackPressed -> handleBackPressed()
}
}

fun logout() {
viewModelScope.launch {
linkAccountManager.logOut()
.fold(
onSuccess = {
dismissWithResult?.invoke(
LinkActivityResult.Canceled(LinkActivityResult.Canceled.Reason.LoggedOut)
)
},
onFailure = { e ->
logger.error(
msg = "failed to log out",
t = e
)
dismissWithResult?.invoke(
LinkActivityResult.Canceled(LinkActivityResult.Canceled.Reason.LoggedOut)
)
}
)
}
}

private fun handleBackPressed() {
navController?.let { navController ->
if (!navController.popBackStack()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.stripe.android.ui.core.R as StripeUiCoreR
internal fun LinkAppBar(
state: LinkAppBarState,
onBackPressed: () -> Unit,
onLogout: () -> Unit,
showBottomSheetContent: (BottomSheetContent?) -> Unit
) {
Row(
Expand Down Expand Up @@ -65,6 +66,7 @@ internal fun LinkAppBar(

LinkAppBarAction(
showOverflowMenu = state.showOverflowMenu,
onLogout = onLogout,
showBottomSheetContent = showBottomSheetContent
)
}
Expand Down Expand Up @@ -113,6 +115,7 @@ private fun RowScope.LinkAppBarTitle(
@Composable
private fun LinkAppBarAction(
showOverflowMenu: Boolean,
onLogout: () -> Unit,
showBottomSheetContent: (BottomSheetContent?) -> Unit
) {
val overflowIconAlpha by animateFloatAsState(
Expand All @@ -122,7 +125,17 @@ private fun LinkAppBarAction(

IconButton(
onClick = {
showBottomSheetContent {}
showBottomSheetContent {
LinkLogoutSheet(
onLogoutClick = {
showBottomSheetContent(null)
onLogout()
},
onCancelClick = {
showBottomSheetContent(null)
}
)
}
},
enabled = showOverflowMenu,
modifier = Modifier
Expand Down Expand Up @@ -150,6 +163,7 @@ private fun LinkAppBarPreview() {
email = "[email protected]",
),
onBackPressed = {},
onLogout = {},
showBottomSheetContent = {}
)
}
Expand All @@ -169,6 +183,7 @@ private fun LinkAppBarNoEmail() {
email = null,
),
onBackPressed = {},
onLogout = {},
showBottomSheetContent = {}
)
}
Expand All @@ -188,6 +203,7 @@ private fun LinkAppBarChildScreen() {
email = "[email protected]",
),
onBackPressed = {},
onLogout = {},
showBottomSheetContent = {}
)
}
Expand All @@ -207,6 +223,7 @@ private fun LinkAppBarChildScreenNoEmail() {
email = null,
),
onBackPressed = {},
onLogout = {},
showBottomSheetContent = {}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ internal fun LinkContent(
sheetState: ModalBottomSheetState,
bottomSheetContent: BottomSheetContent?,
onUpdateSheetContent: (BottomSheetContent?) -> Unit,
onLogout: () -> Unit,
onBackPressed: () -> Unit
) {
val coroutineScope = rememberCoroutineScope()
Expand Down Expand Up @@ -79,6 +80,7 @@ internal fun LinkContent(
LinkAppBar(
state = appBarState,
onBackPressed = onBackPressed,
onLogout = onLogout,
showBottomSheetContent = {
if (it == null) {
coroutineScope.launch {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.stripe.android.link.ui

import androidx.compose.runtime.Composable
import com.stripe.android.link.ui.menus.LinkMenu
import com.stripe.android.link.ui.menus.LinkMenuItem
import com.stripe.android.paymentsheet.R
import com.stripe.android.R as StripeR

internal sealed class LinkLogoutMenuItem(
override val textResId: Int,
override val isDestructive: Boolean = false
) : LinkMenuItem {
data object Logout : LinkLogoutMenuItem(textResId = R.string.stripe_log_out, isDestructive = true)
data object Cancel : LinkLogoutMenuItem(textResId = StripeR.string.stripe_cancel)
}

@Composable
internal fun LinkLogoutSheet(
onLogoutClick: () -> Unit,
onCancelClick: () -> Unit
) {
val items = listOf(
LinkLogoutMenuItem.Logout,
LinkLogoutMenuItem.Cancel
)

LinkMenu(
items = items,
onItemPress = { item ->
when (item) {
LinkLogoutMenuItem.Logout -> onLogoutClick()
LinkLogoutMenuItem.Cancel -> onCancelClick()
}
}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.stripe.android.link.ui.menus

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.stripe.android.link.theme.HorizontalPadding
import com.stripe.android.link.theme.MinimumTouchTargetSize
import com.stripe.android.link.theme.linkColors

/**
* An item to be displayed in a [LinkMenu].
*
* @property textResId The resource ID of the text of the item
* @property isDestructive Whether this item should be rendered with the error text color
*/
internal interface LinkMenuItem {
val textResId: Int
val isDestructive: Boolean
}

/**
* Displays a generic bottom sheet with the provided [items].
*
* @param items The list of items that implement [LinkMenuItem]
* @param onItemPress Called when an item in the list is pressed
*/
@Composable
internal fun <T : LinkMenuItem> LinkMenu(
items: List<T>,
onItemPress: (T) -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp)
) {
for (item in items) {
LinkBottomSheetRow(
item = item,
modifier = Modifier.clickable {
onItemPress(item)
}
)
}
}
}

@Composable
private fun <T : LinkMenuItem> LinkBottomSheetRow(
item: T,
modifier: Modifier = Modifier
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.height(MinimumTouchTargetSize)
.fillMaxWidth()
) {
Text(
text = stringResource(item.textResId),
color = if (item.isDestructive) {
MaterialTheme.linkColors.errorText
} else {
Color.Unspecified
},
modifier = Modifier.padding(horizontal = HorizontalPadding)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.savedstate.SavedStateRegistryOwner
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import com.stripe.android.link.account.FakeLinkAccountManager
import com.stripe.android.testing.FakeLogger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
Expand All @@ -35,7 +36,12 @@ import kotlin.test.assertFailsWith
internal class LinkActivityViewModelTest {
private val dispatcher = UnconfinedTestDispatcher()
private val linkAccountManager = FakeLinkAccountManager()
private val vm = LinkActivityViewModel(mock(), linkAccountManager)
private val logger = FakeLogger()
private val vm = LinkActivityViewModel(
activityRetainedComponent = mock(),
linkAccountManager = linkAccountManager,
logger = logger
)
private val navController: NavHostController = mock()
private val dismissWithResult: (LinkActivityResult) -> Unit = mock()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ internal class LinkAppBarScreenshotTest(
LinkAppBar(
state = testCase.state,
onBackPressed = {},
onLogout = {},
showBottomSheetContent = {}
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.stripe.android.link.ui

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.stripe.android.screenshottesting.FontSize
import com.stripe.android.screenshottesting.Locale
import com.stripe.android.screenshottesting.PaparazziRule
import com.stripe.android.screenshottesting.SystemAppearance
import org.junit.Rule
import org.junit.Test

internal class LinkLogoutSheetScreenshotTest {
@get:Rule
val paparazziRule = PaparazziRule(
SystemAppearance.entries,
FontSize.entries,
boxModifier = Modifier
.padding(0.dp)
.fillMaxWidth(),
)

@get:Rule
val localesPaparazziRule = PaparazziRule(
SystemAppearance.entries,
FontSize.entries,
Locale.entries,
boxModifier = Modifier
.padding(0.dp)
.fillMaxWidth(),
)

@Test
fun testSheet() {
paparazziRule.snapshot {
LinkLogoutSheet(
onCancelClick = {},
onLogoutClick = {}
)
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit bf11f9a

Please sign in to comment.