Skip to content

Commit 9de42e8

Browse files
committedMar 22, 2025··
Fix new search page window issues:
- crash - background color - window insets
1 parent 9e0ed35 commit 9de42e8

File tree

4 files changed

+237
-212
lines changed

4 files changed

+237
-212
lines changed
 

‎app/shared/src/commonMain/kotlin/ui/search/SearchScreen.kt

-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99

1010
package me.him188.ani.app.ui.search
1111

12-
import androidx.compose.foundation.layout.WindowInsets
1312
import androidx.compose.foundation.layout.fillMaxSize
14-
import androidx.compose.foundation.layout.safeDrawing
1513
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
1614
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
1715
import androidx.compose.runtime.Composable
@@ -76,7 +74,6 @@ fun SearchScreen(
7674
}
7775
},
7876
navigator = listDetailNavigator,
79-
contentWindowInsets = WindowInsets.safeDrawing, // 不包含 macos 标题栏, 因为左侧有 navigation rail
8077
navigationIcon = {
8178
BackNavigationIconButton(onNavigateBack)
8279
},

‎app/shared/ui-exploration/src/androidMain/kotlin/ui/exploration/search/SearchPage.android.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ fun PreviewSearchPageResultColumn() = ProvideCompositionLocalsForPreview {
100100
selectedItemIndex = { 1 },
101101
onSelect = {},
102102
onPlay = {},
103-
searchBar = {},
103+
headers = {},
104104
)
105105
}
106106
}

‎app/shared/ui-exploration/src/commonMain/kotlin/ui/exploration/search/SearchPage.kt

+46-208
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,14 @@
1010
package me.him188.ani.app.ui.exploration.search
1111

1212
import androidx.compose.animation.AnimatedVisibility
13-
import androidx.compose.foundation.focusGroup
14-
import androidx.compose.foundation.gestures.animateScrollBy
15-
import androidx.compose.foundation.layout.Arrangement
16-
import androidx.compose.foundation.layout.Box
13+
import androidx.compose.foundation.background
1714
import androidx.compose.foundation.layout.Column
1815
import androidx.compose.foundation.layout.WindowInsets
1916
import androidx.compose.foundation.layout.WindowInsetsSides
2017
import androidx.compose.foundation.layout.fillMaxWidth
2118
import androidx.compose.foundation.layout.only
2219
import androidx.compose.foundation.layout.padding
23-
import androidx.compose.foundation.layout.size
24-
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
2520
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
26-
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
27-
import androidx.compose.foundation.relocation.BringIntoViewRequester
28-
import androidx.compose.foundation.relocation.bringIntoViewRequester
2921
import androidx.compose.material.icons.Icons
3022
import androidx.compose.material.icons.rounded.KeyboardArrowUp
3123
import androidx.compose.material3.Icon
@@ -34,54 +26,30 @@ import androidx.compose.material3.Scaffold
3426
import androidx.compose.material3.SmallFloatingActionButton
3527
import androidx.compose.material3.Text
3628
import androidx.compose.material3.TopAppBarDefaults
29+
import androidx.compose.material3.TopAppBarScrollBehavior
3730
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
3831
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
3932
import androidx.compose.runtime.Composable
40-
import androidx.compose.runtime.DisposableEffect
41-
import androidx.compose.runtime.LaunchedEffect
4233
import androidx.compose.runtime.getValue
43-
import androidx.compose.runtime.mutableIntStateOf
44-
import androidx.compose.runtime.mutableStateMapOf
45-
import androidx.compose.runtime.mutableStateOf
46-
import androidx.compose.runtime.remember
4734
import androidx.compose.runtime.rememberCoroutineScope
48-
import androidx.compose.runtime.saveable.rememberSaveable
49-
import androidx.compose.runtime.setValue
50-
import androidx.compose.runtime.snapshotFlow
5135
import androidx.compose.ui.Modifier
5236
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
5337
import androidx.compose.ui.input.nestedscroll.nestedScroll
54-
import androidx.compose.ui.layout.onSizeChanged
55-
import androidx.compose.ui.unit.Dp
5638
import androidx.compose.ui.unit.dp
5739
import androidx.lifecycle.compose.collectAsStateWithLifecycle
58-
import androidx.paging.compose.LazyPagingItems
59-
import androidx.paging.compose.itemContentType
60-
import androidx.paging.compose.itemKey
6140
import kotlinx.coroutines.CoroutineStart
62-
import kotlinx.coroutines.flow.collectLatest
6341
import kotlinx.coroutines.launch
64-
import me.him188.ani.app.data.models.preference.NsfwMode
6542
import me.him188.ani.app.navigation.LocalNavigator
6643
import me.him188.ani.app.ui.adaptive.AniListDetailPaneScaffold
6744
import me.him188.ani.app.ui.adaptive.AniTopAppBar
6845
import me.him188.ani.app.ui.adaptive.PaneScope
6946
import me.him188.ani.app.ui.foundation.LocalPlatform
70-
import me.him188.ani.app.ui.foundation.animation.LocalAniMotionScheme
7147
import me.him188.ani.app.ui.foundation.ifThen
72-
import me.him188.ani.app.ui.foundation.interaction.keyboardDirectionToSelectItem
73-
import me.him188.ani.app.ui.foundation.interaction.keyboardPageToScroll
7448
import me.him188.ani.app.ui.foundation.layout.AniWindowInsets
7549
import me.him188.ani.app.ui.foundation.layout.currentWindowAdaptiveInfo1
7650
import me.him188.ani.app.ui.foundation.layout.isWidthAtLeastMedium
77-
import me.him188.ani.app.ui.foundation.layout.paneHorizontalPadding
78-
import me.him188.ani.app.ui.foundation.layout.paneVerticalPadding
7951
import me.him188.ani.app.ui.foundation.navigation.BackHandler
8052
import me.him188.ani.app.ui.foundation.widgets.BackNavigationIconButton
81-
import me.him188.ani.app.ui.foundation.widgets.NsfwMask
82-
import me.him188.ani.app.ui.search.LoadErrorCard
83-
import me.him188.ani.app.ui.search.SearchDefaults
84-
import me.him188.ani.app.ui.search.SearchResultLazyVerticalStaggeredGrid
8553
import me.him188.ani.app.ui.search.collectHasQueryAsState
8654
import me.him188.ani.utils.platform.isDesktop
8755

@@ -104,34 +72,22 @@ fun SearchPage(
10472
val scope = rememberCoroutineScope()
10573

10674
val items = state.items
107-
val searchBar = @Composable {
108-
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
75+
SearchPageListDetailScaffold(
76+
navigator,
77+
searchBar = {
10978
SuggestionSearchBar(
11079
state.suggestionSearchBarState,
11180
Modifier
11281
.ifThen(
11382
currentWindowAdaptiveInfo1().windowSizeClass.isWidthAtLeastMedium
114-
|| !state.suggestionSearchBarState.expanded,
115-
) { Modifier.padding(horizontal = 8.dp) },
83+
&& !state.suggestionSearchBarState.expanded,
84+
) { Modifier.padding(horizontal = 8.dp) } // from 16 to 24
85+
.padding(bottom = 16.dp),
11686
placeholder = { Text("搜索") },
87+
windowInsets = contentWindowInsets.only(WindowInsetsSides.Horizontal),
11788
)
118-
119-
val filterState by state.searchFilterStateFlow.collectAsStateWithLifecycle()
120-
SearchFilterChipsRow(
121-
filterState,
122-
onClickItemText = { chip, value ->
123-
state.toggleTagSelection(chip, value, unselectOthersOfSameKind = true)
124-
},
125-
onCheckedChange = { chip, value ->
126-
state.toggleTagSelection(chip, value, unselectOthersOfSameKind = false)
127-
},
128-
Modifier.fillMaxWidth(),
129-
)
130-
}
131-
}
132-
SearchPageLayout(
133-
navigator,
134-
searchResultList = { nestedScrollConnection ->
89+
},
90+
searchResultColumn = { nestedScrollConnection ->
13591
val aniNavigator = LocalNavigator.current
13692

13793
val hasQuery by state.searchState.collectHasQueryAsState()
@@ -152,17 +108,25 @@ fun SearchPage(
152108
}
153109
}
154110
}, // collect only once
155-
searchBar = searchBar,
111+
headers = {
112+
item(span = StaggeredGridItemSpan.FullLine) {
113+
val filterState by state.searchFilterStateFlow.collectAsStateWithLifecycle()
114+
SearchFilterChipsRow(
115+
filterState,
116+
onClickItemText = { chip, value ->
117+
state.toggleTagSelection(chip, value, unselectOthersOfSameKind = true)
118+
},
119+
onCheckedChange = { chip, value ->
120+
state.toggleTagSelection(chip, value, unselectOthersOfSameKind = false)
121+
},
122+
Modifier.fillMaxWidth(),
123+
)
124+
}
125+
},
156126
state = state.gridState,
157127
)
158128
},
159129
detailContent = {
160-
// AnimatedContent(
161-
// state.selectedItemIndex,
162-
// transitionSpec = AniThemeDefaults.emphasizedAnimatedContentTransition,
163-
// ) { index ->
164-
//
165-
// }
166130
items.itemSnapshotList.getOrNull(state.selectedItemIndex)?.let {
167131
detailContent(it.subjectId)
168132
}
@@ -200,157 +164,29 @@ fun SearchPage(
200164
)
201165
}
202166

203-
@Composable
204-
internal fun SearchPageResultColumn(
205-
items: LazyPagingItems<SubjectPreviewItemInfo>,
206-
showSummary: () -> Boolean, // 可在还没发起任何搜索时不展示
207-
selectedItemIndex: () -> Int,
208-
onSelect: (index: Int) -> Unit,
209-
onPlay: (info: SubjectPreviewItemInfo) -> Unit,
210-
searchBar: @Composable () -> Unit,
211-
modifier: Modifier = Modifier,
212-
state: LazyStaggeredGridState = rememberLazyStaggeredGridState()
213-
) {
214-
var height by rememberSaveable { mutableIntStateOf(0) }
215-
val bringIntoViewRequesters = remember { mutableStateMapOf<Int, BringIntoViewRequester>() }
216-
val nsfwBlurShape = SubjectItemLayoutParameters.calculate(currentWindowAdaptiveInfo1().windowSizeClass).shape
217-
val aniMotionScheme = LocalAniMotionScheme.current
218-
219-
SearchResultLazyVerticalStaggeredGrid(
220-
items,
221-
error = {
222-
LoadErrorCard(
223-
error = it,
224-
onRetry = { items.retry() },
225-
modifier = Modifier.fillMaxWidth(), // noop
226-
)
227-
},
228-
modifier
229-
.focusGroup()
230-
.onSizeChanged { height = it.height }
231-
.keyboardDirectionToSelectItem(
232-
selectedItemIndex,
233-
) {
234-
onSelect(it)
235-
state.animateScrollToItem(it)
236-
}
237-
.keyboardPageToScroll({ height.toFloat() }) {
238-
state.animateScrollBy(it)
239-
},
240-
lazyStaggeredGridState = state,
241-
horizontalArrangement = Arrangement.spacedBy(currentWindowAdaptiveInfo1().windowSizeClass.paneHorizontalPadding),
242-
) {
243-
item(span = StaggeredGridItemSpan.FullLine) {
244-
searchBar()
245-
}
246-
247-
if (showSummary()) {
248-
item(span = StaggeredGridItemSpan.FullLine) {
249-
SearchDefaults.SearchSummaryItem(
250-
items,
251-
Modifier.animateItem(
252-
fadeInSpec = aniMotionScheme.feedItemFadeInSpec,
253-
placementSpec = aniMotionScheme.feedItemPlacementSpec,
254-
fadeOutSpec = aniMotionScheme.feedItemFadeOutSpec,
255-
),
256-
containerColor = MaterialTheme.colorScheme.surfaceContainerLowest,
257-
)
258-
}
259-
}
260-
261-
items(
262-
items.itemCount,
263-
key = items.itemKey { it.subjectId },
264-
contentType = items.itemContentType { 1 },
265-
) { index ->
266-
val info = items[index]
267-
val requester = remember { BringIntoViewRequester() }
268-
// 记录 item 对应的 requester
269-
if (info != null) {
270-
DisposableEffect(requester) {
271-
bringIntoViewRequesters[info.subjectId] = requester
272-
onDispose {
273-
bringIntoViewRequesters.remove(info.subjectId)
274-
}
275-
}
276-
277-
var nsfwMaskState: NsfwMode by rememberSaveable(info) {
278-
mutableStateOf(info.nsfwMode)
279-
}
280-
NsfwMask(
281-
mode = nsfwMaskState,
282-
onTemporarilyDisplay = { nsfwMaskState = NsfwMode.DISPLAY },
283-
shape = nsfwBlurShape,
284-
) {
285-
SubjectPreviewItem(
286-
selected = index == selectedItemIndex(),
287-
onClick = { onSelect(index) },
288-
onPlay = { onPlay(info) },
289-
info = info,
290-
Modifier
291-
// .sharedElement(
292-
// rememberSharedContentState(SharedTransitionKeys.subjectBounds(info.subjectId)),
293-
// animatedVisibilityScope,
294-
// )
295-
.animateItem(
296-
fadeInSpec = aniMotionScheme.feedItemFadeInSpec,
297-
placementSpec = aniMotionScheme.feedItemPlacementSpec,
298-
fadeOutSpec = aniMotionScheme.feedItemFadeOutSpec,
299-
)
300-
.fillMaxWidth()
301-
.bringIntoViewRequester(requester)
302-
.padding(vertical = currentWindowAdaptiveInfo1().windowSizeClass.paneVerticalPadding / 2),
303-
image = {
304-
SubjectItemDefaults.Image(
305-
info.imageUrl,
306-
// Modifier.sharedElement(
307-
// rememberSharedContentState(SharedTransitionKeys.subjectCoverImage(subjectId = info.subjectId)),
308-
// animatedVisibilityScope,
309-
// ),
310-
)
311-
},
312-
title = { maxLines ->
313-
Text(
314-
info.title,
315-
// Modifier.sharedElement(
316-
// rememberSharedContentState(SharedTransitionKeys.subjectTitle(subjectId = info.subjectId)),
317-
// animatedVisibilityScope,
318-
// ),
319-
maxLines = maxLines,
320-
)
321-
},
322-
)
323-
}
324-
} else {
325-
Box(Modifier.size(Dp.Hairline))
326-
// placeholder
327-
}
328-
}
329-
}
330-
331-
LaunchedEffect(Unit) {
332-
snapshotFlow(selectedItemIndex)
333-
.collectLatest {
334-
bringIntoViewRequesters[items.itemSnapshotList.getOrNull(it)?.subjectId]?.bringIntoView()
335-
}
336-
}
337-
}
338-
339167
/**
340168
* @param searchBar contentPadding: 页面的左右 24.dp 边距
341169
*/
342170
@Composable
343-
internal fun SearchPageLayout(
171+
internal fun SearchPageListDetailScaffold(
344172
navigator: ThreePaneScaffoldNavigator<*>,
345-
searchResultList: @Composable (PaneScope.(NestedScrollConnection) -> Unit),
173+
searchBar: @Composable (PaneScope.() -> Unit),
174+
searchResultColumn: @Composable (PaneScope.(NestedScrollConnection?) -> Unit),
346175
detailContent: @Composable (PaneScope.() -> Unit),
347176
navigateToTopButton: @Composable PaneScope.() -> Unit,
348177
modifier: Modifier = Modifier,
349178
navigationIcon: @Composable () -> Unit = {},
350179
contentWindowInsets: WindowInsets = AniWindowInsets.forPageContent(),
351180
) {
352181
val coroutineScope = rememberCoroutineScope()
353-
val topAppBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
182+
183+
val topAppBarScrollBehavior: TopAppBarScrollBehavior? = if (LocalPlatform.current.isDesktop()) {
184+
// Workaround for Compose bug: scrolling to the top does not work correctly.
185+
null
186+
} else {
187+
TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
188+
}
189+
354190
AniListDetailPaneScaffold(
355191
navigator,
356192
listPaneTopAppBar = {
@@ -371,9 +207,7 @@ internal fun SearchPageLayout(
371207
}
372208
},
373209
windowInsets = paneContentWindowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
374-
scrollBehavior =
375-
if (LocalPlatform.current.isDesktop()) null // Workaround for Compose bug: scrolling to the top does not work correctly.
376-
else topAppBarScrollBehavior,
210+
scrollBehavior = topAppBarScrollBehavior,
377211
)
378212
},
379213
listPaneContent = {
@@ -385,18 +219,22 @@ internal fun SearchPageLayout(
385219
Modifier
386220
.paneContentPadding()
387221
.paneWindowInsetsPadding()
388-
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
222+
.run {
223+
if (topAppBarScrollBehavior == null) this
224+
else nestedScroll(topAppBarScrollBehavior.nestedScrollConnection)
225+
},
389226
) {
390-
searchResultList(topAppBarScrollBehavior.nestedScrollConnection)
227+
searchBar()
228+
searchResultColumn(topAppBarScrollBehavior?.nestedScrollConnection)
391229
}
392230
}
393231
},
394232
detailPane = {
395233
detailContent()
396234
},
397-
modifier,
235+
modifier.background(MaterialTheme.colorScheme.surfaceContainerLowest),
398236
useSharedTransition = false,
399-
listPanePreferredWidth = 480.dp,
237+
listPanePreferredWidth = 400.dp,
400238
contentWindowInsets = contentWindowInsets,
401239
)
402240
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*
2+
* Copyright (C) 2024-2025 OpenAni and contributors.
3+
*
4+
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
5+
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
6+
*
7+
* https://github.com/open-ani/ani/blob/main/LICENSE
8+
*/
9+
10+
package me.him188.ani.app.ui.exploration.search
11+
12+
import androidx.compose.foundation.focusGroup
13+
import androidx.compose.foundation.gestures.animateScrollBy
14+
import androidx.compose.foundation.layout.Arrangement
15+
import androidx.compose.foundation.layout.Box
16+
import androidx.compose.foundation.layout.fillMaxWidth
17+
import androidx.compose.foundation.layout.padding
18+
import androidx.compose.foundation.layout.size
19+
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope
20+
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
21+
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
22+
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
23+
import androidx.compose.foundation.relocation.BringIntoViewRequester
24+
import androidx.compose.foundation.relocation.bringIntoViewRequester
25+
import androidx.compose.material3.MaterialTheme
26+
import androidx.compose.material3.Text
27+
import androidx.compose.runtime.Composable
28+
import androidx.compose.runtime.DisposableEffect
29+
import androidx.compose.runtime.LaunchedEffect
30+
import androidx.compose.runtime.getValue
31+
import androidx.compose.runtime.mutableIntStateOf
32+
import androidx.compose.runtime.mutableStateMapOf
33+
import androidx.compose.runtime.mutableStateOf
34+
import androidx.compose.runtime.remember
35+
import androidx.compose.runtime.saveable.rememberSaveable
36+
import androidx.compose.runtime.setValue
37+
import androidx.compose.runtime.snapshotFlow
38+
import androidx.compose.ui.Modifier
39+
import androidx.compose.ui.layout.onSizeChanged
40+
import androidx.compose.ui.unit.Dp
41+
import androidx.paging.compose.LazyPagingItems
42+
import androidx.paging.compose.itemContentType
43+
import androidx.paging.compose.itemKey
44+
import kotlinx.coroutines.flow.collectLatest
45+
import me.him188.ani.app.data.models.preference.NsfwMode
46+
import me.him188.ani.app.ui.foundation.animation.LocalAniMotionScheme
47+
import me.him188.ani.app.ui.foundation.interaction.keyboardDirectionToSelectItem
48+
import me.him188.ani.app.ui.foundation.interaction.keyboardPageToScroll
49+
import me.him188.ani.app.ui.foundation.layout.currentWindowAdaptiveInfo1
50+
import me.him188.ani.app.ui.foundation.layout.paneHorizontalPadding
51+
import me.him188.ani.app.ui.foundation.layout.paneVerticalPadding
52+
import me.him188.ani.app.ui.foundation.widgets.NsfwMask
53+
import me.him188.ani.app.ui.search.LoadErrorCard
54+
import me.him188.ani.app.ui.search.SearchDefaults
55+
import me.him188.ani.app.ui.search.SearchResultLazyVerticalStaggeredGrid
56+
57+
58+
@Composable
59+
internal fun SearchPageResultColumn(
60+
items: LazyPagingItems<SubjectPreviewItemInfo>,
61+
showSummary: () -> Boolean, // 可在还没发起任何搜索时不展示
62+
selectedItemIndex: () -> Int,
63+
onSelect: (index: Int) -> Unit,
64+
onPlay: (info: SubjectPreviewItemInfo) -> Unit,
65+
modifier: Modifier = Modifier,
66+
headers: LazyStaggeredGridScope.() -> Unit = {},
67+
state: LazyStaggeredGridState = rememberLazyStaggeredGridState()
68+
) {
69+
var height by rememberSaveable { mutableIntStateOf(0) }
70+
val bringIntoViewRequesters = remember { mutableStateMapOf<Int, BringIntoViewRequester>() }
71+
val nsfwBlurShape = SubjectItemLayoutParameters.calculate(currentWindowAdaptiveInfo1().windowSizeClass).shape
72+
val aniMotionScheme = LocalAniMotionScheme.current
73+
74+
SearchResultLazyVerticalStaggeredGrid(
75+
items,
76+
error = {
77+
LoadErrorCard(
78+
error = it,
79+
onRetry = { items.retry() },
80+
modifier = Modifier.fillMaxWidth(), // noop
81+
)
82+
},
83+
modifier
84+
.focusGroup()
85+
.onSizeChanged { height = it.height }
86+
.keyboardDirectionToSelectItem(
87+
selectedItemIndex,
88+
) {
89+
state.animateScrollToItem(it)
90+
onSelect(it)
91+
}
92+
.keyboardPageToScroll({ height.toFloat() }) {
93+
state.animateScrollBy(it)
94+
},
95+
lazyStaggeredGridState = state,
96+
horizontalArrangement = Arrangement.spacedBy(currentWindowAdaptiveInfo1().windowSizeClass.paneHorizontalPadding),
97+
) {
98+
headers()
99+
100+
if (showSummary()) {
101+
item(span = StaggeredGridItemSpan.FullLine) {
102+
SearchDefaults.SearchSummaryItem(
103+
items,
104+
Modifier.animateItem(
105+
fadeInSpec = aniMotionScheme.feedItemFadeInSpec,
106+
placementSpec = aniMotionScheme.feedItemPlacementSpec,
107+
fadeOutSpec = aniMotionScheme.feedItemFadeOutSpec,
108+
),
109+
containerColor = MaterialTheme.colorScheme.surfaceContainerLowest,
110+
)
111+
}
112+
}
113+
114+
items(
115+
items.itemCount,
116+
key = items.itemKey { it.subjectId },
117+
contentType = items.itemContentType { 1 },
118+
) { index ->
119+
val info = items[index]
120+
val requester = remember { BringIntoViewRequester() }
121+
// 记录 item 对应的 requester
122+
if (info != null) {
123+
DisposableEffect(requester) {
124+
bringIntoViewRequesters[info.subjectId] = requester
125+
onDispose {
126+
bringIntoViewRequesters.remove(info.subjectId)
127+
}
128+
}
129+
130+
var nsfwMaskState: NsfwMode by rememberSaveable(info) {
131+
mutableStateOf(info.nsfwMode)
132+
}
133+
NsfwMask(
134+
mode = nsfwMaskState,
135+
onTemporarilyDisplay = { nsfwMaskState = NsfwMode.DISPLAY },
136+
shape = nsfwBlurShape,
137+
) {
138+
SubjectPreviewItem(
139+
selected = index == selectedItemIndex(),
140+
onClick = { onSelect(index) },
141+
onPlay = { onPlay(info) },
142+
info = info,
143+
Modifier
144+
// .sharedElement(
145+
// rememberSharedContentState(SharedTransitionKeys.subjectBounds(info.subjectId)),
146+
// animatedVisibilityScope,
147+
// )
148+
.animateItem(
149+
fadeInSpec = aniMotionScheme.feedItemFadeInSpec,
150+
placementSpec = aniMotionScheme.feedItemPlacementSpec,
151+
fadeOutSpec = aniMotionScheme.feedItemFadeOutSpec,
152+
)
153+
.fillMaxWidth()
154+
.bringIntoViewRequester(requester)
155+
.padding(vertical = currentWindowAdaptiveInfo1().windowSizeClass.paneVerticalPadding / 2),
156+
image = {
157+
SubjectItemDefaults.Image(
158+
info.imageUrl,
159+
// Modifier.sharedElement(
160+
// rememberSharedContentState(SharedTransitionKeys.subjectCoverImage(subjectId = info.subjectId)),
161+
// animatedVisibilityScope,
162+
// ),
163+
)
164+
},
165+
title = { maxLines ->
166+
Text(
167+
info.title,
168+
// Modifier.sharedElement(
169+
// rememberSharedContentState(SharedTransitionKeys.subjectTitle(subjectId = info.subjectId)),
170+
// animatedVisibilityScope,
171+
// ),
172+
maxLines = maxLines,
173+
)
174+
},
175+
)
176+
}
177+
} else {
178+
Box(Modifier.size(Dp.Hairline))
179+
// placeholder
180+
}
181+
}
182+
}
183+
184+
LaunchedEffect(Unit) {
185+
snapshotFlow(selectedItemIndex)
186+
.collectLatest {
187+
bringIntoViewRequesters[items.itemSnapshotList.getOrNull(it)?.subjectId]?.bringIntoView()
188+
}
189+
}
190+
}

0 commit comments

Comments
 (0)
Please sign in to comment.