Skip to content

Commit 2de3879

Browse files
authored
Merge pull request #1651 from DimensionDev/feature/m3e_list_item
migrate to m3e list item
2 parents 8e7d2f5 + 43c5041 commit 2de3879

File tree

30 files changed

+1124
-998
lines changed

30 files changed

+1124
-998
lines changed

app/build.gradle.kts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,13 @@ extensions.getByType(com.android.build.api.variant.AndroidComponentsExtension::c
247247
GenerateDeepLinkManifestTask::manifest
248248
)
249249
}
250+
251+
kotlin {
252+
sourceSets {
253+
all {
254+
languageSettings {
255+
optIn("androidx.compose.material3.ExperimentalMaterial3ExpressiveApi")
256+
}
257+
}
258+
}
259+
}

app/src/main/java/dev/dimension/flare/ui/component/NavigationSuiteScaffold2.kt

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import androidx.compose.animation.animateContentSize
99
import androidx.compose.animation.slideInVertically
1010
import androidx.compose.animation.slideOutVertically
1111
import androidx.compose.foundation.ExperimentalFoundationApi
12-
import androidx.compose.foundation.clickable
1312
import androidx.compose.foundation.interaction.Interaction
1413
import androidx.compose.foundation.interaction.MutableInteractionSource
1514
import androidx.compose.foundation.layout.Arrangement
@@ -37,12 +36,13 @@ import androidx.compose.material3.ExperimentalMaterial3Api
3736
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
3837
import androidx.compose.material3.FloatingToolbarDefaults.floatingToolbarVerticalNestedScroll
3938
import androidx.compose.material3.Icon
40-
import androidx.compose.material3.ListItem
39+
import androidx.compose.material3.ListItemDefaults
4140
import androidx.compose.material3.MaterialTheme
4241
import androidx.compose.material3.ModalWideNavigationRail
4342
import androidx.compose.material3.NavigationBarItem
4443
import androidx.compose.material3.NavigationDrawerItem
4544
import androidx.compose.material3.NavigationRailItem
45+
import androidx.compose.material3.SegmentedListItem
4646
import androidx.compose.material3.ShortNavigationBarItem
4747
import androidx.compose.material3.Surface
4848
import androidx.compose.material3.WideNavigationRailState
@@ -83,6 +83,7 @@ import dev.dimension.flare.R
8383
import dev.dimension.flare.data.model.BottomBarBehavior
8484
import dev.dimension.flare.data.model.BottomBarStyle
8585
import dev.dimension.flare.data.model.LocalAppearanceSettings
86+
import dev.dimension.flare.ui.theme.segmentedShapes2
8687
import soup.compose.material.motion.animation.materialElevationScaleIn
8788
import soup.compose.material.motion.animation.materialElevationScaleOut
8889

@@ -244,26 +245,26 @@ fun NavigationSuiteScaffold2(
244245
}
245246
if (layoutType == NavigationSuiteType.NavigationBar) {
246247
Column(
247-
verticalArrangement = Arrangement.spacedBy(2.dp),
248+
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
248249
) {
249250
secondaryScope.itemList.forEachIndexed { index, it ->
250-
ListItem(
251-
headlineContent = {
251+
SegmentedListItem(
252+
onClick = {
253+
it.onClick()
254+
},
255+
shapes =
256+
ListItemDefaults.segmentedShapes2(
257+
index,
258+
secondaryScope.itemsCount,
259+
),
260+
content = {
252261
it.label?.invoke()
253262
},
254263
leadingContent = it.icon,
255264
trailingContent = it.badge,
256265
modifier =
257266
it.modifier
258-
.padding(horizontal = 16.dp)
259-
.listCard(
260-
index = index,
261-
totalCount = secondaryScope.itemsCount,
262-
).clickable(
263-
enabled = it.enabled,
264-
onClick = it.onClick,
265-
interactionSource = it.interactionSource,
266-
),
267+
.padding(horizontal = 16.dp),
267268
)
268269
}
269270
}
@@ -286,26 +287,26 @@ fun NavigationSuiteScaffold2(
286287
Spacer(modifier = Modifier.weight(1f))
287288
if (layoutType == NavigationSuiteType.NavigationBar) {
288289
Column(
289-
verticalArrangement = Arrangement.spacedBy(2.dp),
290+
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
290291
) {
291292
footerScope.itemList.forEachIndexed { index, it ->
292-
ListItem(
293-
headlineContent = {
293+
SegmentedListItem(
294+
content = {
294295
it.label?.invoke()
295296
},
297+
onClick = {
298+
it.onClick()
299+
},
300+
shapes =
301+
ListItemDefaults.segmentedShapes2(
302+
index,
303+
footerScope.itemsCount,
304+
),
296305
leadingContent = it.icon,
297306
trailingContent = it.badge,
298307
modifier =
299308
it.modifier
300-
.padding(horizontal = 16.dp)
301-
.listCard(
302-
index = index,
303-
totalCount = footerScope.itemsCount,
304-
).clickable(
305-
enabled = it.enabled,
306-
onClick = it.onClick,
307-
interactionSource = it.interactionSource,
308-
),
309+
.padding(horizontal = 16.dp),
309310
)
310311
}
311312
}

app/src/main/java/dev/dimension/flare/ui/screen/dm/DMListScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.padding
77
import androidx.compose.foundation.layout.size
88
import androidx.compose.foundation.lazy.LazyColumn
99
import androidx.compose.material3.ExperimentalMaterial3Api
10+
import androidx.compose.material3.ListItemDefaults
1011
import androidx.compose.material3.Text
1112
import androidx.compose.material3.TopAppBarDefaults
1213
import androidx.compose.runtime.Composable
@@ -97,7 +98,7 @@ internal fun DMListScreen(
9798
modifier =
9899
Modifier
99100
.padding(horizontal = screenHorizontalPadding),
100-
verticalArrangement = Arrangement.spacedBy(2.dp),
101+
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
101102
) {
102103
dmList(
103104
data = state.items,

app/src/main/java/dev/dimension/flare/ui/screen/home/AccountSelectionModal.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column
55
import androidx.compose.foundation.layout.fillMaxWidth
66
import androidx.compose.foundation.layout.padding
77
import androidx.compose.material3.Button
8+
import androidx.compose.material3.ListItemDefaults
89
import androidx.compose.material3.RadioButton
910
import androidx.compose.material3.Text
1011
import androidx.compose.runtime.Composable
@@ -14,13 +15,13 @@ import androidx.compose.ui.Modifier
1415
import androidx.compose.ui.res.stringResource
1516
import androidx.compose.ui.unit.dp
1617
import dev.dimension.flare.R
17-
import dev.dimension.flare.ui.component.listCard
1818
import dev.dimension.flare.ui.model.onSuccess
1919
import dev.dimension.flare.ui.presenter.invoke
2020
import dev.dimension.flare.ui.presenter.settings.AccountsPresenter
2121
import dev.dimension.flare.ui.route.Route
2222
import dev.dimension.flare.ui.screen.settings.AccountItem
2323
import dev.dimension.flare.ui.theme.screenHorizontalPadding
24+
import dev.dimension.flare.ui.theme.segmentedShapes2
2425
import moe.tlaster.precompose.molecule.producePresenter
2526

2627
@Composable
@@ -37,18 +38,13 @@ internal fun AccountSelectionModal(
3738
val state by producePresenter { presenter() }
3839
state.accounts.onSuccess {
3940
Column(
40-
verticalArrangement = Arrangement.spacedBy(2.dp),
41+
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
4142
) {
4243
for (index in 0 until it.size) {
4344
val (accountKey, data) = it[index]
4445
AccountItem(
45-
modifier =
46-
Modifier
47-
.listCard(
48-
index = index,
49-
totalCount = it.size,
50-
),
5146
userState = data,
47+
shapes = ListItemDefaults.segmentedShapes2(index, it.size),
5248
onClick = {
5349
state.setActiveAccount(it)
5450
onBack.invoke()

app/src/main/java/dev/dimension/flare/ui/screen/home/HomeScreen.kt

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ import androidx.compose.runtime.Composable
3838
import androidx.compose.runtime.CompositionLocalProvider
3939
import androidx.compose.runtime.DisposableEffect
4040
import androidx.compose.runtime.LaunchedEffect
41-
import androidx.compose.runtime.derivedStateOf
4241
import androidx.compose.runtime.getValue
4342
import androidx.compose.runtime.mutableStateOf
4443
import androidx.compose.runtime.remember
4544
import androidx.compose.runtime.rememberCoroutineScope
4645
import androidx.compose.runtime.rememberUpdatedState
46+
import androidx.compose.runtime.retain.retain
4747
import androidx.compose.ui.Alignment
4848
import androidx.compose.ui.Modifier
4949
import androidx.compose.ui.draw.clip
@@ -114,32 +114,30 @@ internal fun HomeScreen(afterInit: () -> Unit) {
114114
val wideNavigationRailState = rememberWideNavigationRailState()
115115
state.tabs
116116
.onSuccess { tabs ->
117-
val topLevelBackStack by producePresenter(
118-
key = "home_top_level_back_stack_${tabs.all.first().key}",
119-
useImmediateClock = true,
120-
) {
121-
TopLevelBackStack<Route>(
122-
getDirection(tabs.all.first()),
123-
)
124-
}
125-
126-
fun navigate(route: Route) {
127-
topLevelBackStack.addTopLevel(route)
128-
scope.launch {
129-
wideNavigationRailState.collapse()
117+
val topLevelBackStack =
118+
retain(
119+
"home_top_level_back_stack_${tabs.all.first().key}",
120+
) {
121+
TopLevelBackStack(
122+
getDirection(tabs.all.first()),
123+
)
130124
}
131-
}
132125

133-
val currentRoute by remember {
134-
derivedStateOf {
135-
topLevelBackStack.topLevelKey
136-
}
137-
}
138-
val accountType by remember {
139-
derivedStateOf {
140-
currentRoute.accountTypeOr(AccountType.Active)
126+
val topLevelBackStackState by rememberUpdatedState(topLevelBackStack)
127+
128+
val navigate =
129+
remember(topLevelBackStack, wideNavigationRailState) {
130+
{ route: Route ->
131+
topLevelBackStack.addTopLevel(route)
132+
scope.launch {
133+
wideNavigationRailState.collapse()
134+
}
135+
Unit
136+
}
141137
}
142-
}
138+
139+
val currentRoute = topLevelBackStack.topLevelKey
140+
val accountType = currentRoute.accountTypeOr(AccountType.Active)
143141
val userState by producePresenter(key = "home_account_type_$accountType") {
144142
userPresenter(accountType)
145143
}
@@ -177,7 +175,7 @@ internal fun HomeScreen(afterInit: () -> Unit) {
177175
userState,
178176
layoutType,
179177
currentRoute,
180-
::navigate,
178+
navigate,
181179
)
182180
},
183181
navigationSuiteItems = {
@@ -300,10 +298,10 @@ internal fun HomeScreen(afterInit: () -> Unit) {
300298
}
301299
},
302300
navigate = {
303-
topLevelBackStack.add(it)
301+
topLevelBackStackState.add(it)
304302
},
305303
onBack = {
306-
topLevelBackStack.removeLast()
304+
topLevelBackStackState.removeLast()
307305
},
308306
)
309307
}

app/src/main/java/dev/dimension/flare/ui/screen/home/TabSettingScreen.kt

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
1212
import androidx.compose.material3.ExperimentalMaterial3Api
1313
import androidx.compose.material3.IconButton
1414
import androidx.compose.material3.ListItem
15+
import androidx.compose.material3.ListItemDefaults
1516
import androidx.compose.material3.Switch
1617
import androidx.compose.material3.Text
1718
import androidx.compose.material3.TopAppBarDefaults
@@ -51,6 +52,7 @@ import dev.dimension.flare.ui.screen.settings.EditTabDialog
5152
import dev.dimension.flare.ui.screen.settings.TabAddBottomSheet
5253
import dev.dimension.flare.ui.screen.settings.TabCustomItem
5354
import dev.dimension.flare.ui.theme.screenHorizontalPadding
55+
import dev.dimension.flare.ui.theme.segmentedShapes2
5456
import kotlinx.collections.immutable.toImmutableList
5557
import kotlinx.coroutines.CoroutineScope
5658
import kotlinx.coroutines.flow.map
@@ -129,7 +131,7 @@ internal fun TabSettingScreen(
129131
modifier =
130132
Modifier
131133
.padding(horizontal = screenHorizontalPadding),
132-
verticalArrangement = Arrangement.spacedBy(2.dp),
134+
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
133135
) {
134136
state.enableMixedTimeline.onSuccess { enabled ->
135137
if (state.currentTabs.size > 1) {
@@ -166,6 +168,7 @@ internal fun TabSettingScreen(
166168
itemsIndexed(state.currentTabs, key = { _, item -> item.key }) { index, item ->
167169
TabCustomItem(
168170
item = item,
171+
shapes = ListItemDefaults.segmentedShapes2(index, state.currentTabs.size),
169172
deleteTab = {
170173
if (it is TimelineTabItem) {
171174
state.deleteTab(item)
@@ -178,12 +181,6 @@ internal fun TabSettingScreen(
178181
},
179182
reorderableLazyColumnState = reorderableLazyColumnState,
180183
canSwipeToDelete = state.canSwipeToDelete,
181-
modifier =
182-
Modifier
183-
.listCard(
184-
index = index,
185-
totalCount = state.currentTabs.size,
186-
),
187184
)
188185
}
189186
}
@@ -243,7 +240,7 @@ private fun presenter(
243240
object {
244241
val currentTabs = cacheTabs
245242
val allTabsState = allTabsState
246-
val canSwipeToDelete = cacheTabs.size > 1
243+
val canSwipeToDelete = true
247244
val showAddTab = showAddTab
248245
val selectedEditTab = selectedEditTab
249246
val enableMixedTimeline = enableMixedTimeline
@@ -285,16 +282,10 @@ private fun presenter(
285282
}
286283

287284
fun deleteTab(tab: TimelineTabItem) {
288-
if (cacheTabs.size <= 1) {
289-
return
290-
}
291285
cacheTabs.removeIf { it.key == tab.key }
292286
}
293287

294288
fun deleteTab(key: String) {
295-
if (cacheTabs.size <= 1) {
296-
return
297-
}
298289
cacheTabs.removeIf { it.key == key }
299290
}
300291

app/src/main/java/dev/dimension/flare/ui/screen/list/ListScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.size
88
import androidx.compose.foundation.lazy.LazyColumn
99
import androidx.compose.material3.ExperimentalMaterial3Api
1010
import androidx.compose.material3.IconButton
11+
import androidx.compose.material3.ListItemDefaults
1112
import androidx.compose.material3.Text
1213
import androidx.compose.material3.TopAppBarDefaults
1314
import androidx.compose.runtime.Composable
@@ -109,7 +110,7 @@ internal fun ListScreen(
109110
modifier =
110111
Modifier
111112
.padding(horizontal = screenHorizontalPadding),
112-
verticalArrangement = Arrangement.spacedBy(2.dp),
113+
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
113114
) {
114115
uiListWithTabs(
115116
state = state,

app/src/main/java/dev/dimension/flare/ui/screen/rss/RssSourcesScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding
99
import androidx.compose.foundation.lazy.LazyColumn
1010
import androidx.compose.material3.ExperimentalMaterial3Api
1111
import androidx.compose.material3.IconButton
12+
import androidx.compose.material3.ListItemDefaults
1213
import androidx.compose.material3.Text
1314
import androidx.compose.material3.TopAppBarDefaults
1415
import androidx.compose.runtime.Composable
@@ -21,7 +22,6 @@ import androidx.compose.ui.Modifier
2122
import androidx.compose.ui.input.nestedscroll.nestedScroll
2223
import androidx.compose.ui.platform.LocalContext
2324
import androidx.compose.ui.res.stringResource
24-
import androidx.compose.ui.unit.dp
2525
import compose.icons.FontAwesomeIcons
2626
import compose.icons.fontawesomeicons.Solid
2727
import compose.icons.fontawesomeicons.solid.FileExport
@@ -105,7 +105,7 @@ internal fun RssSourcesScreen(
105105
modifier =
106106
Modifier
107107
.padding(horizontal = screenHorizontalPadding),
108-
verticalArrangement = Arrangement.spacedBy(2.dp),
108+
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
109109
contentPadding = contentPadding,
110110
) {
111111
rssListWithTabs(

0 commit comments

Comments
 (0)