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
88 changes: 88 additions & 0 deletions app/src/main/java/to/bitkit/ui/scaffold/PinnedTabsScaffold.kt
Comment thread
jvsena42 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package to.bitkit.ui.scaffold

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.SubcomposeLayout
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import dev.chrisbanes.haze.HazeStyle
import dev.chrisbanes.haze.HazeTint
import dev.chrisbanes.haze.hazeEffect
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.rememberHazeState
import to.bitkit.ui.theme.Colors

private val PinnedTabsShadowHeight = 32.dp
private val PinnedTabsBlurRadius = 24.dp

private enum class PinnedTabsSlot { Header, Content, Shadow }

@Composable
fun PinnedTabsScaffold(
header: @Composable ColumnScope.() -> Unit,
modifier: Modifier = Modifier,
content: @Composable (topPadding: Dp) -> Unit,
) {
val hazeState = rememberHazeState()
val shadowBrush = remember {
Brush.verticalGradient(colors = listOf(Colors.Black, Color.Transparent))
}
val hazeStyle = remember {
HazeStyle(
backgroundColor = Colors.Black,
tint = HazeTint(Colors.Black70),
blurRadius = PinnedTabsBlurRadius,
)
}

SubcomposeLayout(modifier = modifier.fillMaxSize()) { constraints ->
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)

val headerPlaceables = subcompose(PinnedTabsSlot.Header) {
Column(
modifier = Modifier
.fillMaxWidth()
.hazeEffect(state = hazeState, style = hazeStyle),
content = { header() },
)
}.map { it.measure(looseConstraints) }

val headerHeightPx = headerPlaceables.maxOfOrNull { it.height } ?: 0
val headerHeightDp = headerHeightPx.toDp()

val contentPlaceables = subcompose(PinnedTabsSlot.Content) {
Box(
modifier = Modifier
.fillMaxSize()
.hazeSource(hazeState)
) {
content(headerHeightDp)
}
}.map { it.measure(constraints) }

val shadowPlaceables = subcompose(PinnedTabsSlot.Shadow) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(PinnedTabsShadowHeight)
.background(shadowBrush)
)
}.map { it.measure(looseConstraints) }

layout(constraints.maxWidth, constraints.maxHeight) {
contentPlaceables.forEach { it.placeRelative(0, 0) }
shadowPlaceables.forEach { it.placeRelative(0, headerHeightPx) }
headerPlaceables.forEach { it.placeRelative(0, 0) }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package to.bitkit.ui.screens.shop.shopDiscover

import android.annotation.SuppressLint
import android.view.MotionEvent
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
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
Expand All @@ -22,16 +26,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.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 kotlinx.coroutines.launch
import to.bitkit.R
import to.bitkit.env.Env
import to.bitkit.ext.configureForBasicWebContent
Expand All @@ -42,6 +49,7 @@ 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.PinnedTabsScaffold
import to.bitkit.ui.scaffold.ScreenColumn
import to.bitkit.ui.screens.wallets.activity.components.CustomTabRowWithSpacing
import to.bitkit.ui.screens.wallets.activity.components.TabItem
Expand All @@ -64,26 +72,39 @@ fun ShopDiscoverScreen(
modifier: Modifier = Modifier,
) {
val tabs = remember { ShopDiscoverTab.entries.toImmutableList() }
var selectedTab by remember { mutableStateOf(ShopDiscoverTab.Shop) }
val pagerState = rememberPagerState(pageCount = { tabs.size })
val scope = rememberCoroutineScope()

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

CustomTabRowWithSpacing(
tabs = tabs,
currentTabIndex = tabs.indexOf(selectedTab),
selectedColor = Colors.White,
onTabChange = { selectedTab = it },
modifier = Modifier.padding(horizontal = 16.dp)
)
CustomTabRowWithSpacing(
tabs = tabs,
currentTabIndex = pagerState.currentPage,
selectedColor = Colors.White,
onTabChange = { scope.launch { pagerState.animateScrollToPage(tabs.indexOf(it)) } },
modifier = Modifier.padding(horizontal = 16.dp)
)
}
}
) { topPadding ->
HorizontalPager(state = pagerState) { page ->
when (tabs[page]) {
ShopDiscoverTab.Shop -> ShopTabContent(
navigateWebView = navigateWebView,
contentPadding = PaddingValues(top = topPadding, bottom = 42.dp),
)

when (selectedTab) {
ShopDiscoverTab.Shop -> ShopTabContent(navigateWebView = navigateWebView)
ShopDiscoverTab.Map -> MapTabContent()
ShopDiscoverTab.Map -> MapTabContent(topPadding = topPadding)
}
}
}
}
}
Expand All @@ -92,8 +113,10 @@ fun ShopDiscoverScreen(
private fun ShopTabContent(
navigateWebView: (String, String) -> Unit,
modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(0.dp),
) {
LazyColumn(
contentPadding = contentPadding,
modifier = modifier.padding(horizontal = 16.dp)
) {
item {
Expand Down Expand Up @@ -213,10 +236,11 @@ private fun ShopTabContent(
}
}

@SuppressLint("SetJavaScriptEnabled")
@SuppressLint("SetJavaScriptEnabled", "ClickableViewAccessibility")
@Composable
private fun MapTabContent(
modifier: Modifier = Modifier,
topPadding: Dp = 0.dp,
) {
var isLoading by remember { mutableStateOf(true) }

Expand All @@ -229,7 +253,7 @@ private fun MapTabContent(
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
.padding(start = 16.dp, end = 16.dp, top = topPadding + 16.dp)
.clip(Shapes.medium)
) {
AndroidView(
Expand All @@ -242,6 +266,20 @@ private fun MapTabContent(

this.webViewClient = webViewClient
configureForBasicWebContent()
// Keep the parent HorizontalPager from intercepting horizontal pans while the user is
// interacting with the map. Outside the WebView bounds the pager still swipes normally.
setOnTouchListener { view, event ->
when (event.actionMasked) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_MOVE,
-> view.parent?.requestDisallowInterceptTouchEvent(true)

MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL,
-> view.parent?.requestDisallowInterceptTouchEvent(false)
}
false
}
loadUrl(Env.BTC_MAP_URL)
}
},
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,13 @@ private fun WidgetsPage(
text = stringResource(R.string.widgets__add),
onClick = onClickAddWidget,
enabled = !isCalculatorInputActive,
icon = {
Icon(
painter = painterResource(R.drawable.ic_plus),
contentDescription = null,
modifier = Modifier.size(16.dp)
)
},
modifier = Modifier
.alpha(footerAlpha)
.testTag("WidgetsAdd")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package to.bitkit.ui.screens.wallets.activity

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
Expand All @@ -17,7 +15,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.synonym.bitkitcore.Activity
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentListOf
Expand All @@ -28,6 +25,7 @@ import to.bitkit.ui.appViewModel
import to.bitkit.ui.components.Sheet
import to.bitkit.ui.scaffold.AppTopBar
import to.bitkit.ui.scaffold.DrawerNavIcon
import to.bitkit.ui.scaffold.PinnedTabsScaffold
import to.bitkit.ui.screens.wallets.activity.components.ActivityListFilter
import to.bitkit.ui.screens.wallets.activity.components.ActivityListGrouped
import to.bitkit.ui.screens.wallets.activity.components.ActivityTab
Expand Down Expand Up @@ -75,7 +73,6 @@ fun AllActivityScreen(
}

@Composable
@OptIn(ExperimentalHazeMaterialsApi::class)
private fun AllActivityScreenContent(
filteredActivities: ImmutableList<Activity>?,
searchText: String,
Expand All @@ -93,6 +90,12 @@ private fun AllActivityScreenContent(
onActivityItemClick: (String) -> Unit,
onEmptyActivityRowClick: () -> Unit,
) {
val listState = rememberLazyListState()

LaunchedEffect(currentTabIndex) {
listState.scrollToItem(0)
}

Column(
modifier = Modifier.screen()
) {
Expand All @@ -104,33 +107,30 @@ private fun AllActivityScreenContent(
},
)

ActivityListFilter(
searchText = searchText,
onSearchTextChange = onSearchTextChange,
hasTagFilter = hasTagFilter,
hasDateRangeFilter = hasDateRangeFilter,
onTagClick = onTagClick,
selectedTags = selectedTags,
onRemoveTag = onRemoveTag,
onDateRangeClick = onDateRangeClick,
tabs = tabs,
currentTabIndex = currentTabIndex,
onTabChange = { onTabChange(tabs.indexOf(it)) },
modifier = Modifier.padding(horizontal = 16.dp)

)
Spacer(modifier = Modifier.height(16.dp))

// List
Box(
modifier = Modifier
.fillMaxSize()
) {
PinnedTabsScaffold(
header = {
ActivityListFilter(
searchText = searchText,
onSearchTextChange = onSearchTextChange,
hasTagFilter = hasTagFilter,
hasDateRangeFilter = hasDateRangeFilter,
onTagClick = onTagClick,
selectedTags = selectedTags,
onRemoveTag = onRemoveTag,
onDateRangeClick = onDateRangeClick,
tabs = tabs,
currentTabIndex = currentTabIndex,
onTabChange = { onTabChange(tabs.indexOf(it)) },
modifier = Modifier.padding(horizontal = 16.dp)
)
}
) { topPadding ->
ActivityListGrouped(
items = filteredActivities,
onActivityItemClick = onActivityItemClick,
onEmptyActivityRowClick = onEmptyActivityRowClick,
contentPadding = PaddingValues(top = 0.dp),
listState = listState,
contentPadding = PaddingValues(top = topPadding + 16.dp),
Comment thread
jvsena42 marked this conversation as resolved.
modifier = Modifier
.swipeToChangeTab(
currentTabIndex = currentTabIndex,
Expand Down
Loading
Loading