Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
- Align tab colors, Show details button, notifications bell figure, and home activity count with iOS #907

## [2.2.0] - 2026-04-07

### Fixed
Expand Down
11 changes: 8 additions & 3 deletions app/src/main/java/to/bitkit/ui/ContentView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,11 @@ fun ContentView(

TimedSheetType.NOTIFICATIONS -> {
BackgroundPaymentsIntroSheet(
onContinue = {
onLater = {
appViewModel.dismissTimedSheet()
settingsViewModel.setBgPaymentsIntroSeen(true)
},
onEnable = {
appViewModel.dismissTimedSheet()
navController.navigateTo(Routes.BackgroundPaymentsSettings)
settingsViewModel.setBgPaymentsIntroSeen(true)
Expand Down Expand Up @@ -1133,9 +1137,10 @@ private fun NavGraphBuilder.generalSettingsSubScreens(navController: NavHostCont
composableWithDefaultTransitions<Routes.BackgroundPaymentsIntro> {
BackgroundPaymentsIntroScreen(
onBack = { navController.popBackStack() },
onContinue = {
onLater = { navController.popBackStack() },
onEnable = {
navController.navigateTo(Routes.BackgroundPaymentsSettings)
}
},
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package to.bitkit.ui.screens.shop.shopDiscover
import android.annotation.SuppressLint
import android.view.ViewGroup
import android.webkit.WebView
import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
Expand All @@ -15,102 +16,85 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRowDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import kotlinx.collections.immutable.toImmutableList
import to.bitkit.R
import to.bitkit.env.Env
import to.bitkit.ext.configureForBasicWebContent
import to.bitkit.models.BitrefillCategory
import to.bitkit.ui.components.BodyM
import to.bitkit.ui.components.CaptionB
import to.bitkit.ui.components.SuggestionCard
import to.bitkit.ui.components.Text13Up
import to.bitkit.ui.components.VerticalSpacer
import to.bitkit.ui.scaffold.AppTopBar
import to.bitkit.ui.scaffold.DrawerNavIcon
import to.bitkit.ui.scaffold.ScreenColumn
import to.bitkit.ui.screens.wallets.activity.components.CustomTabRowWithSpacing
import to.bitkit.ui.screens.wallets.activity.components.TabItem
import to.bitkit.ui.shared.modifiers.clickableAlpha
import to.bitkit.ui.shared.util.gradientBackground
import to.bitkit.ui.theme.AppThemeSurface
import to.bitkit.ui.theme.Colors
import to.bitkit.ui.theme.Shapes

@OptIn(ExperimentalMaterial3Api::class)
private enum class ShopDiscoverTab(@StringRes private val titleRes: Int) : TabItem {
Shop(R.string.other__shop__discover__tabs__shop),
Map(R.string.other__shop__discover__tabs__map);

override val uiText @Composable get() = stringResource(titleRes)
}

@Composable
fun ShopDiscoverScreen(
onBack: () -> Unit,
navigateWebView: (String, String) -> Unit, // Page, Title
modifier: Modifier = Modifier,
) {
var selectedTabIndex by remember { mutableIntStateOf(0) }
val tabTitles = listOf(
stringResource(R.string.other__shop__discover__tabs__shop),
stringResource(R.string.other__shop__discover__tabs__map),
)
val tabs = remember { ShopDiscoverTab.entries.toImmutableList() }
var selectedTab by remember { mutableStateOf(ShopDiscoverTab.Shop) }

ScreenColumn(
modifier = Modifier.gradientBackground(),
) {
ScreenColumn(modifier = modifier) {
AppTopBar(
titleText = stringResource(R.string.other__shop__discover__nav_title),
onBackClick = onBack,
actions = { DrawerNavIcon() },
)

PrimaryTabRow(
selectedTabIndex = selectedTabIndex,
containerColor = Color.Transparent,
indicator = {
TabRowDefaults.PrimaryIndicator(
modifier = Modifier.tabIndicatorOffset(selectedTabIndex),
color = Colors.Yellow,
width = Dp.Unspecified,
)
},
modifier = Modifier.padding(horizontal = 16.dp),
) {
tabTitles.forEachIndexed { index, title ->
Tab(
selected = selectedTabIndex == index,
onClick = { selectedTabIndex = index },
unselectedContentColor = Colors.White64,
text = { CaptionB(title) },
)
}
}
CustomTabRowWithSpacing(
tabs = tabs,
currentTabIndex = tabs.indexOf(selectedTab),
selectedColor = Colors.White,
onTabChange = { selectedTab = it },
modifier = Modifier.padding(horizontal = 16.dp)
)

when (selectedTabIndex) {
0 -> ShopTabContent(navigateWebView = navigateWebView)
1 -> MapTabContent()
when (selectedTab) {
ShopDiscoverTab.Shop -> ShopTabContent(navigateWebView = navigateWebView)
ShopDiscoverTab.Map -> MapTabContent()
}
}
}

@Composable
private fun ShopTabContent(
navigateWebView: (String, String) -> Unit,
modifier: Modifier = Modifier,
) {
LazyColumn(
modifier = Modifier.padding(horizontal = 16.dp),
modifier = modifier.padding(horizontal = 16.dp)
) {
item {
VerticalSpacer(16.dp)
Expand All @@ -120,31 +104,29 @@ private fun ShopTabContent(
) {
val title = stringResource(R.string.other__shop__discover__gift_cards__title)
SuggestionCard(
modifier = Modifier.weight(1f),
gradientColor = Colors.Green24,
title = title,
description = stringResource(R.string.other__shop__discover__gift_cards__description),
icon = R.drawable.gift,
captionColor = Colors.Gray1,

disableGlow = true,
onClick = {
navigateWebView("gift-cards", title)
},
modifier = Modifier.weight(1f)
)
val title2 = stringResource(R.string.other__shop__discover__esims__title)
SuggestionCard(
modifier = Modifier.weight(1f),
gradientColor = Colors.Yellow24,
title = title2,
description = stringResource(R.string.other__shop__discover__esims__description),
icon = R.drawable.globe,
captionColor = Colors.Gray1,

disableGlow = true,
onClick = {
navigateWebView("esims", title2)
},
modifier = Modifier.weight(1f)
)
}

Expand All @@ -155,31 +137,29 @@ private fun ShopTabContent(
) {
val title = stringResource(R.string.other__shop__discover__refill__title)
SuggestionCard(
modifier = Modifier.weight(1f),
gradientColor = Colors.Purple24,
title = title,
description = stringResource(R.string.other__shop__discover__refill__description),
icon = R.drawable.phone,
captionColor = Colors.Gray1,

disableGlow = true,
onClick = {
navigateWebView("refill", title)
},
modifier = Modifier.weight(1f)
)
val title2 = stringResource(R.string.other__shop__discover__travel__title)
SuggestionCard(
modifier = Modifier.weight(1f),
gradientColor = Colors.Red24,
title = title2,
description = stringResource(R.string.other__shop__discover__travel__description),
icon = R.drawable.rocket_2,

disableGlow = true,
captionColor = Colors.Gray1,
onClick = {
navigateWebView("buy/travel", title2)
},
modifier = Modifier.weight(1f)
)
}

Expand All @@ -193,38 +173,38 @@ private fun ShopTabContent(
items(items = BitrefillCategory.entries.toList(), key = { it.name }) { item ->
Column {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickableAlpha {
navigateWebView(item.route, item.title)
}
.padding(top = 8.5.dp, bottom = 10.5.dp),
verticalAlignment = Alignment.CenterVertically,
.padding(top = 8.5.dp, bottom = 10.5.dp)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.clip(CircleShape)
.size(32.dp)
.background(Colors.White10),
contentAlignment = Alignment.Center,
.background(Colors.White10)
) {
Icon(
imageVector = item.icon,
contentDescription = null,
tint = Colors.White64,
modifier = Modifier.size(16.dp),
modifier = Modifier.size(16.dp)
)
}
BodyM(
text = item.title,
modifier = Modifier
.weight(1f)
.padding(horizontal = 8.dp),
.padding(horizontal = 8.dp)
)
Icon(
painter = painterResource(R.drawable.ic_chevron_right),
contentDescription = null,
tint = Colors.White64,
modifier = Modifier.size(24.dp),
modifier = Modifier.size(24.dp)
)
}
HorizontalDivider()
Expand All @@ -235,7 +215,9 @@ private fun ShopTabContent(

@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun MapTabContent() {
private fun MapTabContent(
modifier: Modifier = Modifier,
) {
var isLoading by remember { mutableStateOf(true) }

val webViewClient = remember {
Expand All @@ -245,13 +227,12 @@ private fun MapTabContent() {
}

Box(
modifier = Modifier
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
.clip(Shapes.medium),
contentAlignment = Alignment.Center,
modifier = modifier
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
.clip(Shapes.medium)
) {
AndroidView(
modifier = Modifier.fillMaxWidth(),
factory = { context ->
WebView(context).apply {
layoutParams = ViewGroup.LayoutParams(
Expand All @@ -264,6 +245,7 @@ private fun MapTabContent() {
loadUrl(Env.BTC_MAP_URL)
}
},
modifier = Modifier.fillMaxWidth()
)

if (isLoading) {
Expand Down
18 changes: 13 additions & 5 deletions app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ import to.bitkit.viewmodels.SettingsViewModel
import to.bitkit.viewmodels.WalletViewModel

private const val SMALL_SCREEN_HEIGHT_DP = 700
private const val SMALL_SCREEN_ACTIVITY_COUNT = 2
private const val LARGE_SCREEN_ACTIVITY_COUNT = 3
private const val SMALL_SCREEN_SLOT_CAPACITY = 3
private const val LARGE_SCREEN_SLOT_CAPACITY = 4
private val BOTTOM_SPACER_HEIGHT = (TAB_BAR_HEIGHT + TAB_BAR_PADDING_BOTTOM + 36).dp

@Suppress("CyclomaticComplexMethod")
Expand Down Expand Up @@ -370,11 +370,13 @@ private fun Content(

val density = LocalDensity.current
val screenHeightDp = with(density) { LocalWindowInfo.current.containerSize.height.toDp().value.toInt() }
val activityCount = if (screenHeightDp < SMALL_SCREEN_HEIGHT_DP) {
SMALL_SCREEN_ACTIVITY_COUNT
val slotCapacity = if (screenHeightDp < SMALL_SCREEN_HEIGHT_DP) {
SMALL_SCREEN_SLOT_CAPACITY
} else {
LARGE_SCREEN_ACTIVITY_COUNT
LARGE_SCREEN_SLOT_CAPACITY
}
val nonItemSlots = countNonItemSlots(homeUiState)
val activityCount = (slotCapacity - nonItemSlots).coerceAtLeast(0)

val paginatedActivities = remember(latestActivities, activityCount) {
latestActivities?.take(activityCount)?.toImmutableList()
Expand Down Expand Up @@ -439,6 +441,12 @@ private fun Content(
}
}

private fun countNonItemSlots(homeUiState: HomeUiState): Int {
val bannerSlot = if (homeUiState.banners.isNotEmpty()) 1 else 0
val widgetsHintSlot = if (homeUiState.showWidgetsOnboardingHint) 1 else 0
return bannerSlot + widgetsHintSlot
}

@Suppress("MagicNumber")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand Down
Loading
Loading