Add FeedOptionDrawer

This commit is contained in:
Ash 2022-03-24 17:13:05 +08:00
parent ada579377b
commit eda76f4445
10 changed files with 406 additions and 168 deletions

View File

@ -50,6 +50,7 @@ android {
} }
dependencies { dependencies {
implementation("androidx.compose.animation:animation-graphics:$compose_version")
implementation("com.google.accompanist:accompanist-flowlayout:0.24.3-alpha") implementation("com.google.accompanist:accompanist-flowlayout:0.24.3-alpha")
implementation("com.google.accompanist:accompanist-navigation-animation:0.24.3-alpha") implementation("com.google.accompanist:accompanist-navigation-animation:0.24.3-alpha")
implementation "androidx.datastore:datastore-preferences:1.0.0" implementation "androidx.datastore:datastore-preferences:1.0.0"
@ -83,7 +84,7 @@ dependencies {
implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha01" implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha01"
implementation "androidx.compose.material:material:1.2.0-alpha04" implementation "androidx.compose.material:material:1.2.0-alpha04"
implementation "androidx.compose.material3:material3:1.0.0-alpha06" implementation "androidx.compose.material3:material3:1.0.0-alpha07"
implementation "androidx.compose.material:material-icons-extended:$compose_version" implementation "androidx.compose.material:material-icons-extended:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'

View File

@ -6,13 +6,17 @@ import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Row import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.foundation.layout.Spacer import androidx.compose.material.ModalBottomSheetState
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.navigationBarsHeight import com.google.accompanist.insets.navigationBarsHeight
import com.google.accompanist.insets.statusBarsPadding import com.google.accompanist.insets.statusBarsPadding
@ -20,11 +24,17 @@ import com.google.accompanist.navigation.animation.AnimatedNavHost
import com.google.accompanist.navigation.animation.composable import com.google.accompanist.navigation.animation.composable
import com.google.accompanist.navigation.animation.rememberAnimatedNavController import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import me.ash.reader.ui.page.home.FeedOptionDrawer
import me.ash.reader.ui.page.home.HomePage import me.ash.reader.ui.page.home.HomePage
import me.ash.reader.ui.page.settings.SettingsPage import me.ash.reader.ui.page.settings.SettingsPage
import me.ash.reader.ui.theme.AppTheme import me.ash.reader.ui.theme.AppTheme
@OptIn(ExperimentalAnimationApi::class) @OptIn(ExperimentalMaterialApi::class)
val LocalDrawerState = staticCompositionLocalOf<ModalBottomSheetState> {
error("CompositionLocal LocalDrawerState not present")
}
@OptIn(ExperimentalAnimationApi::class, androidx.compose.material.ExperimentalMaterialApi::class)
@Composable @Composable
fun HomeEntry() { fun HomeEntry() {
val navController = rememberAnimatedNavController() val navController = rememberAnimatedNavController()
@ -32,110 +42,117 @@ fun HomeEntry() {
AppTheme { AppTheme {
ProvideWindowInsets { ProvideWindowInsets {
rememberSystemUiController().run { rememberSystemUiController().run {
setStatusBarColor(MaterialTheme.colorScheme.surface, !isSystemInDarkTheme()) setStatusBarColor(Color.Transparent, !isSystemInDarkTheme())
setSystemBarsColor(MaterialTheme.colorScheme.surface, !isSystemInDarkTheme()) setSystemBarsColor(Color.Transparent, !isSystemInDarkTheme())
setNavigationBarColor(MaterialTheme.colorScheme.surface, !isSystemInDarkTheme()) setNavigationBarColor(MaterialTheme.colorScheme.surface, !isSystemInDarkTheme())
} }
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) { Box {
Row( CompositionLocalProvider(
modifier = Modifier LocalDrawerState provides rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
.weight(1f)
.statusBarsPadding()
) { ) {
AnimatedNavHost( Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
modifier = Modifier.background(MaterialTheme.colorScheme.surface), Row(
navController = navController, modifier = Modifier
startDestination = RouteName.HOME, .weight(1f)
) { .statusBarsPadding()
composable(
route = RouteName.HOME,
enterTransition = {
slideInHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
initialOffsetX = { -it }
) + fadeIn(animationSpec = tween(220, delayMillis = 90))
},
exitTransition = {
slideOutHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
targetOffsetX = { it }
) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
popEnterTransition = {
slideInHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
initialOffsetX = { -it }
) + fadeIn(animationSpec = tween(220, delayMillis = 90))
},
popExitTransition = {
slideOutHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
targetOffsetX = { it }
) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
) { ) {
HomePage(navController) AnimatedNavHost(
} modifier = Modifier.background(MaterialTheme.colorScheme.surface),
composable( navController = navController,
route = RouteName.SETTINGS, startDestination = RouteName.HOME,
enterTransition = { ) {
slideInHorizontally( composable(
animationSpec = spring( route = RouteName.HOME,
stiffness = Spring.StiffnessLow, enterTransition = {
dampingRatio = Spring.DampingRatioNoBouncy slideInHorizontally(
), animationSpec = spring(
initialOffsetX = { -it } stiffness = Spring.StiffnessLow,
) + fadeIn(animationSpec = tween(220, delayMillis = 90)) dampingRatio = Spring.DampingRatioNoBouncy
}, ),
exitTransition = { initialOffsetX = { -it }
slideOutHorizontally( ) + fadeIn(animationSpec = tween(220, delayMillis = 90))
animationSpec = spring( },
stiffness = Spring.StiffnessLow, exitTransition = {
dampingRatio = Spring.DampingRatioNoBouncy slideOutHorizontally(
), animationSpec = spring(
targetOffsetX = { -it } stiffness = Spring.StiffnessLow,
) + fadeOut(animationSpec = tween(220, delayMillis = 90)) dampingRatio = Spring.DampingRatioNoBouncy
}, ),
popEnterTransition = { targetOffsetX = { it }
slideInHorizontally( ) + fadeOut(animationSpec = tween(220, delayMillis = 90))
animationSpec = spring( },
stiffness = Spring.StiffnessLow, popEnterTransition = {
dampingRatio = Spring.DampingRatioNoBouncy slideInHorizontally(
), animationSpec = spring(
initialOffsetX = { -it } stiffness = Spring.StiffnessLow,
) + fadeIn(animationSpec = tween(220, delayMillis = 90)) dampingRatio = Spring.DampingRatioNoBouncy
}, ),
popExitTransition = { initialOffsetX = { -it }
slideOutHorizontally( ) + fadeIn(animationSpec = tween(220, delayMillis = 90))
animationSpec = spring( },
stiffness = Spring.StiffnessLow, popExitTransition = {
dampingRatio = Spring.DampingRatioNoBouncy slideOutHorizontally(
), animationSpec = spring(
targetOffsetX = { -it } stiffness = Spring.StiffnessLow,
) + fadeOut(animationSpec = tween(220, delayMillis = 90)) dampingRatio = Spring.DampingRatioNoBouncy
}, ),
) { targetOffsetX = { it }
SettingsPage(navController) ) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
) {
HomePage(navController)
}
composable(
route = RouteName.SETTINGS,
enterTransition = {
slideInHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
initialOffsetX = { -it }
) + fadeIn(animationSpec = tween(220, delayMillis = 90))
},
exitTransition = {
slideOutHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
targetOffsetX = { -it }
) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
popEnterTransition = {
slideInHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
initialOffsetX = { -it }
) + fadeIn(animationSpec = tween(220, delayMillis = 90))
},
popExitTransition = {
slideOutHorizontally(
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioNoBouncy
),
targetOffsetX = { -it }
) + fadeOut(animationSpec = tween(220, delayMillis = 90))
},
) {
SettingsPage(navController)
}
}
} }
Spacer(
modifier = Modifier
.navigationBarsHeight()
.fillMaxWidth()
)
} }
FeedOptionDrawer(drawerState = LocalDrawerState.current)
} }
Spacer(
modifier = Modifier
.navigationBarsHeight()
.fillMaxWidth()
)
} }
} }
} }

View File

@ -0,0 +1,116 @@
package me.ash.reader.ui.page.home
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ChipDefaults
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.RssFeed
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.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.text.style.TextAlign
import androidx.compose.ui.unit.dp
import me.ash.reader.R
import me.ash.reader.ui.page.home.feeds.subscribe.ResultViewPage
import me.ash.reader.ui.widget.BottomDrawer
import me.ash.reader.ui.widget.Subtitle
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun FeedOptionDrawer(
modifier: Modifier = Modifier,
drawerState: ModalBottomSheetState = androidx.compose.material.rememberModalBottomSheetState(
ModalBottomSheetValue.Hidden
),
) {
BottomDrawer(
drawerState = drawerState,
) {
Column {
Icon(
modifier = modifier
.fillMaxWidth()
.align(Alignment.CenterHorizontally),
imageVector = Icons.Rounded.RssFeed,
contentDescription = stringResource(R.string.subscribe),
)
Spacer(modifier = modifier.height(16.dp))
Text(
modifier = Modifier.fillMaxWidth(),
text = "Feed",
style = MaterialTheme.typography.headlineSmall,
textAlign = TextAlign.Center,
)
Spacer(modifier = modifier.height(16.dp))
ResultViewPage(
link = "https://joeycz.github.io/weekly/rss.xml",
groups = emptyList(),
selectedAllowNotificationPreset = true,
selectedParseFullContentPreset = true,
selectedGroupId = "selectedGroupId",
newGroupContent = "",
onNewGroupValueChange = { },
newGroupSelected = false,
changeNewGroupSelected = { },
allowNotificationPresetOnClick = { },
parseFullContentPresetOnClick = { },
groupOnClick = { },
onKeyboardAction = { },
)
Spacer(modifier = Modifier.height(20.dp))
Subtitle(text = "More")
Spacer(modifier = Modifier.height(10.dp))
androidx.compose.material.FilterChip(
modifier = modifier,
colors = ChipDefaults.filterChipColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.error,
leadingIconColor = MaterialTheme.colorScheme.error,
disabledBackgroundColor = MaterialTheme.colorScheme.outline.copy(
alpha = 0.7f
),
disabledContentColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
disabledLeadingIconColor = MaterialTheme.colorScheme.outline.copy(
alpha = 0.7f
),
selectedBackgroundColor = Color.Transparent,
selectedContentColor = MaterialTheme.colorScheme.error,
selectedLeadingIconColor = MaterialTheme.colorScheme.error
),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.error),
selected = false,
shape = CircleShape,
onClick = {
// focusManager.clearFocus()
// onClick()
},
content = {
Text(
modifier = modifier.padding(
start = if (false) 0.dp else 8.dp,
top = 8.dp,
end = 8.dp,
bottom = 8.dp
),
text = "Delete",
style = MaterialTheme.typography.titleSmall,
color = if (true) {
MaterialTheme.colorScheme.error
} else {
MaterialTheme.colorScheme.error
},
)
},
)
}
}
}

View File

@ -42,6 +42,7 @@ import com.google.accompanist.pager.PagerState
import me.ash.reader.R import me.ash.reader.R
import me.ash.reader.data.constant.Filter import me.ash.reader.data.constant.Filter
import me.ash.reader.ui.extension.getName import me.ash.reader.ui.extension.getName
import me.ash.reader.ui.theme.LocalLightThemeColors
import me.ash.reader.ui.widget.CanBeDisabledIconButton import me.ash.reader.ui.widget.CanBeDisabledIconButton
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -192,6 +193,9 @@ fun Item(
onClick: () -> Unit = {}, onClick: () -> Unit = {},
) { ) {
val view = LocalView.current val view = LocalView.current
val lightThemeColors = LocalLightThemeColors.current
val lightPrimaryContainer = lightThemeColors.primaryContainer
val lightOnSurface = lightThemeColors.onSurface
FilterChip( FilterChip(
modifier = Modifier modifier = Modifier
@ -204,9 +208,9 @@ fun Item(
disabledBackgroundColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), disabledBackgroundColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
disabledContentColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), disabledContentColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
disabledLeadingIconColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), disabledLeadingIconColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
selectedBackgroundColor = MaterialTheme.colorScheme.primaryContainer, selectedBackgroundColor = lightPrimaryContainer,
selectedContentColor = MaterialTheme.colorScheme.onSurface, selectedContentColor = lightOnSurface,
selectedLeadingIconColor = MaterialTheme.colorScheme.onSurface selectedLeadingIconColor = lightOnSurface
), ),
selected = selected, selected = selected,
selectedIcon = { selectedIcon = {
@ -216,7 +220,7 @@ fun Item(
modifier = Modifier modifier = Modifier
.padding(start = 8.dp) .padding(start = 8.dp)
.size(20.dp), .size(20.dp),
tint = MaterialTheme.colorScheme.onSurface, tint = lightOnSurface,
) )
}, },
onClick = { onClick = {
@ -235,7 +239,7 @@ fun Item(
text = if (selected) name.uppercase() else "", text = if (selected) name.uppercase() else "",
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
color = if (selected) { color = if (selected) {
MaterialTheme.colorScheme.onSurface lightOnSurface
} else { } else {
MaterialTheme.colorScheme.outline MaterialTheme.colorScheme.outline
}, },

View File

@ -2,9 +2,12 @@ package me.ash.reader.ui.page.home
import android.util.Log import android.util.Log
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -26,7 +29,7 @@ import me.ash.reader.ui.page.home.read.ReadViewAction
import me.ash.reader.ui.page.home.read.ReadViewModel import me.ash.reader.ui.page.home.read.ReadViewModel
import me.ash.reader.ui.widget.ViewPager import me.ash.reader.ui.widget.ViewPager
@OptIn(ExperimentalPagerApi::class) @OptIn(ExperimentalPagerApi::class, androidx.compose.material.ExperimentalMaterialApi::class)
@Composable @Composable
fun HomePage( fun HomePage(
navController: NavHostController, navController: NavHostController,
@ -38,6 +41,7 @@ fun HomePage(
val filterState = viewModel.filterState.collectAsStateValue() val filterState = viewModel.filterState.collectAsStateValue()
val readState = readViewModel.viewState.collectAsStateValue() val readState = readViewModel.viewState.collectAsStateValue()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val drawerState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
context.findActivity()?.let { activity -> context.findActivity()?.let { activity ->
@ -92,66 +96,69 @@ fun HomePage(
} }
} }
Column { Box {
ViewPager( Column {
modifier = Modifier.weight(1f), ViewPager(
state = viewState.pagerState, modifier = Modifier.weight(1f),
composableList = listOf( state = viewState.pagerState,
{ composableList = listOf(
FeedsPage(navController = navController) {
}, FeedsPage(navController = navController)
{ },
FlowPage(navController = navController) {
}, FlowPage(navController = navController)
{ },
ReadPage( {
navController = navController, ReadPage(
btnBackOnClickListener = { navController = navController,
viewModel.dispatch( btnBackOnClickListener = {
HomeViewAction.ScrollToPage( viewModel.dispatch(
scope = scope, HomeViewAction.ScrollToPage(
targetPage = 1, scope = scope,
callback = { targetPage = 1,
readViewModel.dispatch(ReadViewAction.ClearArticle) callback = {
} readViewModel.dispatch(ReadViewAction.ClearArticle)
}
)
) )
) },
}, )
) },
),
)
HomeBottomNavBar(
modifier = Modifier
.height(60.dp)
.fillMaxWidth(),
pagerState = viewState.pagerState,
disabled = readState.articleWithFeed == null,
isUnread = readState.articleWithFeed?.article?.isUnread ?: false,
isStarred = readState.articleWithFeed?.article?.isStarred ?: false,
isFullContent = readState.articleWithFeed?.feed?.isFullContent ?: false,
unreadOnClick = {
readViewModel.dispatch(ReadViewAction.MarkUnread(it))
}, },
), starredOnClick = {
) readViewModel.dispatch(ReadViewAction.MarkStarred(it))
HomeBottomNavBar( },
modifier = Modifier fullContentOnClick = { afterIsFullContent ->
.height(60.dp) readState.articleWithFeed?.let {
.fillMaxWidth(), if (afterIsFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
pagerState = viewState.pagerState, else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
disabled = readState.articleWithFeed == null, }
isUnread = readState.articleWithFeed?.article?.isUnread ?: false, },
isStarred = readState.articleWithFeed?.article?.isStarred ?: false, filter = filterState.filter,
isFullContent = readState.articleWithFeed?.feed?.isFullContent ?: false, filterOnClick = {
unreadOnClick = { viewModel.dispatch(
readViewModel.dispatch(ReadViewAction.MarkUnread(it)) HomeViewAction.ChangeFilter(
}, filterState.copy(
starredOnClick = { filter = it
readViewModel.dispatch(ReadViewAction.MarkStarred(it)) )
},
fullContentOnClick = { afterIsFullContent ->
readState.articleWithFeed?.let {
if (afterIsFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
}
},
filter = filterState.filter,
filterOnClick = {
viewModel.dispatch(
HomeViewAction.ChangeFilter(
filterState.copy(
filter = it
) )
) )
) },
}, )
) }
FeedOptionDrawer(drawerState = drawerState)
} }
} }

View File

@ -3,9 +3,11 @@ package me.ash.reader.ui.page.home.feeds
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ExpandLess import androidx.compose.material.icons.rounded.ExpandLess
import androidx.compose.material.icons.rounded.ExpandMore import androidx.compose.material.icons.rounded.ExpandMore
@ -18,9 +20,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import me.ash.reader.R import me.ash.reader.R
import me.ash.reader.data.feed.Feed import me.ash.reader.data.feed.Feed
import me.ash.reader.ui.page.common.LocalDrawerState
@OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
@Composable @Composable
fun GroupItem( fun GroupItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -31,6 +36,8 @@ fun GroupItem(
feedOnClick: (feed: Feed) -> Unit = {}, feedOnClick: (feed: Feed) -> Unit = {},
) { ) {
var expanded by remember { mutableStateOf(isExpanded) } var expanded by remember { mutableStateOf(isExpanded) }
val scope = rememberCoroutineScope()
val drawerState = LocalDrawerState.current
Column( Column(
modifier = Modifier modifier = Modifier
@ -38,7 +45,16 @@ fun GroupItem(
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.clip(RoundedCornerShape(32.dp)) .clip(RoundedCornerShape(32.dp))
.background(MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.14f)) .background(MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.14f))
.clickable { groupOnClick() } .combinedClickable(
onClick = {
groupOnClick()
},
onLongClick = {
scope.launch {
drawerState.show()
}
}
)
.padding(top = 22.dp) .padding(top = 22.dp)
) { ) {
Row( Row(

View File

@ -82,6 +82,7 @@ private fun Link(
Text( Text(
text = text, text = text,
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
style = MaterialTheme.typography.bodyMedium,
) )
} }
} }

View File

@ -0,0 +1,73 @@
package me.ash.reader.ui.widget
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetDefaults
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
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.unit.dp
import androidx.compose.ui.zIndex
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun BottomDrawer(
modifier: Modifier = Modifier,
drawerState: ModalBottomSheetState = androidx.compose.material.rememberModalBottomSheetState(
ModalBottomSheetValue.Hidden
),
content: @Composable () -> Unit = {},
) {
androidx.compose.material.ModalBottomSheetLayout(
sheetState = drawerState,
sheetBackgroundColor = Color.Transparent,
sheetElevation = if (drawerState.isVisible) ModalBottomSheetDefaults.Elevation else 0.dp,
sheetContent = {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(
RoundedCornerShape(
topStart = 28.0.dp,
topEnd = 28.0.dp,
bottomEnd = 0.0.dp,
bottomStart = 0.0.dp
)
)
.background(MaterialTheme.colorScheme.surface)
.padding(horizontal = 28.dp)
.navigationBarsPadding()
) {
Box {
Row(
modifier = modifier
.padding(top = 8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
Row(
modifier = modifier
.size(38.dp, 4.dp)
.background(MaterialTheme.colorScheme.outline.copy(alpha = 0.2f))
.zIndex(1f)
) {}
}
Column {
Spacer(modifier = Modifier.height(40.dp))
content()
Spacer(modifier = Modifier.height(28.dp))
}
}
}
},
content = {}
)
}

View File

@ -1,5 +1,6 @@
package me.ash.reader.ui.widget package me.ash.reader.ui.widget
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -39,6 +40,7 @@ fun SelectionChip(
enabled: Boolean = true, enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = CircleShape, shape: Shape = CircleShape,
border: BorderStroke? = null,
selectedIcon: @Composable () -> Unit = { selectedIcon: @Composable () -> Unit = {
Icon( Icon(
imageVector = Icons.Filled.CheckCircle, imageVector = Icons.Filled.CheckCircle,
@ -66,6 +68,7 @@ fun SelectionChip(
selectedContentColor = MaterialTheme.colorScheme.onSurface, selectedContentColor = MaterialTheme.colorScheme.onSurface,
selectedLeadingIconColor = MaterialTheme.colorScheme.onSurface selectedLeadingIconColor = MaterialTheme.colorScheme.onSurface
), ),
border = border,
interactionSource = interactionSource, interactionSource = interactionSource,
enabled = enabled, enabled = enabled,
selected = selected, selected = selected,

View File

@ -1,6 +1,6 @@
buildscript { buildscript {
ext { ext {
compose_version = '1.2.0-alpha02' compose_version = '1.2.0-alpha06'
} }
dependencies { dependencies {