mirror of
https://github.com/Ashinch/ReadYou.git
synced 2025-01-31 11:35:10 +01:00
feat(ui): separate toolbars from the content below with dividers (#747)
This commit is contained in:
parent
ed7c272193
commit
99e1d894e4
@ -1,6 +1,13 @@
|
||||
package me.ash.reader.ui.page.home.reading
|
||||
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.VisibilityThreshold
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@ -18,6 +25,7 @@ import androidx.compose.material.icons.outlined.Headphones
|
||||
import androidx.compose.material.icons.rounded.ExpandMore
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.rounded.StarOutline
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -25,6 +33,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import me.ash.reader.R
|
||||
@ -59,113 +68,120 @@ fun BottomBar(
|
||||
.zIndex(1f),
|
||||
contentAlignment = Alignment.BottomCenter
|
||||
) {
|
||||
RYExtensibleVisibility(visible = isShow) {
|
||||
AnimatedVisibility(
|
||||
visible = isShow,
|
||||
enter = expandVertically(),
|
||||
exit = shrinkVertically()
|
||||
) {
|
||||
val view = LocalView.current
|
||||
|
||||
Surface(
|
||||
tonalElevation = tonalElevation.value.dp,
|
||||
) {
|
||||
// TODO: Component styles await refactoring
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.navigationBarsPadding()
|
||||
.fillMaxWidth()
|
||||
.height(60.dp),
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
CanBeDisabledIconButton(
|
||||
modifier = Modifier.size(40.dp),
|
||||
disabled = false,
|
||||
imageVector = if (isUnread) {
|
||||
Icons.Filled.FiberManualRecord
|
||||
} else {
|
||||
Icons.Outlined.FiberManualRecord
|
||||
},
|
||||
contentDescription = stringResource(if (isUnread) R.string.mark_as_read else R.string.mark_as_unread),
|
||||
tint = if (isUnread) {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
},
|
||||
Column {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
thickness = 0.5f.dp
|
||||
)
|
||||
Surface() {
|
||||
// TODO: Component styles await refactoring
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.navigationBarsPadding()
|
||||
.fillMaxWidth()
|
||||
.height(60.dp),
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
onUnread(!isUnread)
|
||||
}
|
||||
CanBeDisabledIconButton(
|
||||
modifier = Modifier.size(40.dp),
|
||||
disabled = false,
|
||||
imageVector = if (isStarred) {
|
||||
Icons.Rounded.Star
|
||||
} else {
|
||||
Icons.Rounded.StarOutline
|
||||
},
|
||||
contentDescription = stringResource(if (isStarred) R.string.mark_as_unstar else R.string.mark_as_starred),
|
||||
tint = if (isStarred) {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
},
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
onStarred(!isStarred)
|
||||
}
|
||||
CanBeDisabledIconButton(
|
||||
disabled = !isNextArticleAvailable,
|
||||
modifier = Modifier.size(40.dp),
|
||||
imageVector = Icons.Rounded.ExpandMore,
|
||||
contentDescription = "Next Article",
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
onNextArticle()
|
||||
}
|
||||
CanBeDisabledIconButton(
|
||||
modifier = Modifier.size(36.dp),
|
||||
disabled = false,
|
||||
imageVector = if (renderer == ReadingRendererPreference.WebView) null else Icons.Outlined.Headphones,
|
||||
contentDescription = if (renderer == ReadingRendererPreference.WebView) {
|
||||
stringResource(R.string.bionic_reading)
|
||||
} else {
|
||||
stringResource(R.string.read_aloud)
|
||||
},
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
icon = {
|
||||
BionicReadingIcon(
|
||||
filled = isBionicReading,
|
||||
size = 24.dp,
|
||||
tint = if (renderer == ReadingRendererPreference.WebView) {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
}
|
||||
)
|
||||
},
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
if (renderer == ReadingRendererPreference.WebView) {
|
||||
onBionicReading()
|
||||
} else {
|
||||
onReadAloud()
|
||||
CanBeDisabledIconButton(
|
||||
modifier = Modifier.size(40.dp),
|
||||
disabled = false,
|
||||
imageVector = if (isUnread) {
|
||||
Icons.Filled.FiberManualRecord
|
||||
} else {
|
||||
Icons.Outlined.FiberManualRecord
|
||||
},
|
||||
contentDescription = stringResource(if (isUnread) R.string.mark_as_read else R.string.mark_as_unread),
|
||||
tint = if (isUnread) {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
},
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
onUnread(!isUnread)
|
||||
}
|
||||
CanBeDisabledIconButton(
|
||||
modifier = Modifier.size(40.dp),
|
||||
disabled = false,
|
||||
imageVector = if (isStarred) {
|
||||
Icons.Rounded.Star
|
||||
} else {
|
||||
Icons.Rounded.StarOutline
|
||||
},
|
||||
contentDescription = stringResource(if (isStarred) R.string.mark_as_unstar else R.string.mark_as_starred),
|
||||
tint = if (isStarred) {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
},
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
onStarred(!isStarred)
|
||||
}
|
||||
CanBeDisabledIconButton(
|
||||
disabled = !isNextArticleAvailable,
|
||||
modifier = Modifier.size(40.dp),
|
||||
imageVector = Icons.Rounded.ExpandMore,
|
||||
contentDescription = "Next Article",
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
onNextArticle()
|
||||
}
|
||||
CanBeDisabledIconButton(
|
||||
modifier = Modifier.size(36.dp),
|
||||
disabled = false,
|
||||
imageVector = if (renderer == ReadingRendererPreference.WebView) null else Icons.Outlined.Headphones,
|
||||
contentDescription = if (renderer == ReadingRendererPreference.WebView) {
|
||||
stringResource(R.string.bionic_reading)
|
||||
} else {
|
||||
stringResource(R.string.read_aloud)
|
||||
},
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
icon = {
|
||||
BionicReadingIcon(
|
||||
filled = isBionicReading,
|
||||
size = 24.dp,
|
||||
tint = if (renderer == ReadingRendererPreference.WebView) {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
}
|
||||
)
|
||||
},
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
if (renderer == ReadingRendererPreference.WebView) {
|
||||
onBionicReading()
|
||||
} else {
|
||||
onReadAloud()
|
||||
}
|
||||
}
|
||||
CanBeDisabledIconButton(
|
||||
disabled = false,
|
||||
modifier = Modifier.size(40.dp),
|
||||
imageVector = if (isFullContent) {
|
||||
Icons.AutoMirrored.Rounded.Article
|
||||
} else {
|
||||
Icons.AutoMirrored.Outlined.Article
|
||||
},
|
||||
contentDescription = stringResource(R.string.parse_full_content),
|
||||
tint = if (isFullContent) {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
},
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
onFullContent(!isFullContent)
|
||||
}
|
||||
}
|
||||
CanBeDisabledIconButton(
|
||||
disabled = false,
|
||||
modifier = Modifier.size(40.dp),
|
||||
imageVector = if (isFullContent) {
|
||||
Icons.AutoMirrored.Rounded.Article
|
||||
} else {
|
||||
Icons.AutoMirrored.Outlined.Article
|
||||
},
|
||||
contentDescription = stringResource(R.string.parse_full_content),
|
||||
tint = if (isFullContent) {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
},
|
||||
) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
onFullContent(!isFullContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ fun Content(
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
when (renderer) {
|
||||
ReadingRendererPreference.WebView -> {
|
||||
Column(
|
||||
@ -81,8 +82,7 @@ fun Content(
|
||||
Spacer(modifier = Modifier.height(64.dp))
|
||||
// padding
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
modifier = Modifier.padding(horizontal = 12.dp)
|
||||
) {
|
||||
DisableSelection {
|
||||
Metadata(
|
||||
|
@ -21,6 +21,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@ -73,6 +74,10 @@ fun ReadingPage(
|
||||
true
|
||||
}
|
||||
|
||||
var showTopDivider by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val pagingItems = homeUiState.pagingData.collectAsLazyPagingItems().itemSnapshotList
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
@ -97,23 +102,22 @@ fun ReadingPage(
|
||||
|
||||
Scaffold(
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
// topBarTonalElevation = tonalElevation.value.dp,
|
||||
// containerTonalElevation = tonalElevation.value.dp,
|
||||
content = { paddings ->
|
||||
Log.i("RLog", "TopBar: recomposition")
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
// Top Bar
|
||||
TopBar(
|
||||
navController = navController,
|
||||
isShow = isShowToolBar,
|
||||
windowInsets = WindowInsets(top = paddings.calculateTopPadding()),
|
||||
title = readerState.title,
|
||||
link = readerState.link,
|
||||
onClose = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
)
|
||||
if (readerState.articleId != null) {
|
||||
TopBar(
|
||||
navController = navController,
|
||||
isShow = isShowToolBar,
|
||||
showDivider = showTopDivider,
|
||||
title = readerState.title,
|
||||
link = readerState.link,
|
||||
onClose = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val isNextArticleAvailable = !readerState.nextArticleId.isNullOrEmpty()
|
||||
val isPreviousArticleAvailable = !readerState.previousArticleId.isNullOrEmpty()
|
||||
@ -163,6 +167,9 @@ fun ReadingPage(
|
||||
saver = LazyListState.Saver
|
||||
) { LazyListState() }
|
||||
|
||||
showTopDivider = snapshotFlow {
|
||||
listState.firstVisibleItemIndex != 0
|
||||
}.collectAsStateValue(initial = false)
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalOverscrollConfiguration provides
|
||||
|
@ -1,15 +1,27 @@
|
||||
package me.ash.reader.ui.page.home.reading
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Palette
|
||||
import androidx.compose.material.icons.outlined.Share
|
||||
import androidx.compose.material.icons.rounded.Close
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -17,6 +29,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.navigation.NavHostController
|
||||
@ -24,7 +37,6 @@ import me.ash.reader.R
|
||||
import me.ash.reader.infrastructure.preference.LocalReadingPageTonalElevation
|
||||
import me.ash.reader.infrastructure.preference.LocalSharedContent
|
||||
import me.ash.reader.ui.component.base.FeedbackIconButton
|
||||
import me.ash.reader.ui.component.base.RYExtensibleVisibility
|
||||
import me.ash.reader.ui.ext.surfaceColorAtElevation
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
|
||||
@ -33,7 +45,7 @@ import me.ash.reader.ui.page.common.RouteName
|
||||
fun TopBar(
|
||||
navController: NavHostController,
|
||||
isShow: Boolean,
|
||||
windowInsets: WindowInsets = WindowInsets(0.dp),
|
||||
showDivider: Boolean = false,
|
||||
title: String? = "",
|
||||
link: String? = "",
|
||||
onClose: () -> Unit = {},
|
||||
@ -48,43 +60,65 @@ fun TopBar(
|
||||
.zIndex(1f),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
RYExtensibleVisibility(visible = isShow) {
|
||||
TopAppBar(
|
||||
title = {},
|
||||
modifier = Modifier,
|
||||
windowInsets = windowInsets,
|
||||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
imageVector = Icons.Rounded.Close,
|
||||
contentDescription = stringResource(R.string.close),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
) {
|
||||
onClose()
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
FeedbackIconButton(
|
||||
modifier = Modifier.size(22.dp),
|
||||
imageVector = Icons.Outlined.Palette,
|
||||
contentDescription = stringResource(R.string.style),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
) {
|
||||
navController.navigate(RouteName.READING_PAGE_STYLE) {
|
||||
launchSingleTop = true
|
||||
Column {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(
|
||||
WindowInsets.statusBars
|
||||
.asPaddingValues()
|
||||
.calculateTopPadding()
|
||||
)
|
||||
) {}
|
||||
AnimatedVisibility(
|
||||
visible = isShow,
|
||||
enter = expandVertically(expandFrom = Alignment.Top),
|
||||
exit = shrinkVertically(shrinkTowards = Alignment.Top)
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {},
|
||||
modifier = Modifier,
|
||||
windowInsets = WindowInsets(0.dp),
|
||||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
imageVector = Icons.Rounded.Close,
|
||||
contentDescription = stringResource(R.string.close),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
FeedbackIconButton(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.Share,
|
||||
contentDescription = stringResource(R.string.share),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
sharedContent.share(context, title, link)
|
||||
}
|
||||
}, colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(tonalElevation.value.dp),
|
||||
}, actions = {
|
||||
FeedbackIconButton(
|
||||
modifier = Modifier.size(22.dp),
|
||||
imageVector = Icons.Outlined.Palette,
|
||||
contentDescription = stringResource(R.string.style),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
) {
|
||||
navController.navigate(RouteName.READING_PAGE_STYLE) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
FeedbackIconButton(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.Share,
|
||||
contentDescription = stringResource(R.string.share),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
sharedContent.share(context, title, link)
|
||||
}
|
||||
}, colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||
tonalElevation.value.dp
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
if (showDivider) {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
thickness = 0.5f.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user