@@ -43,6 +43,8 @@ import androidx.compose.material3.Text
4343import androidx.compose.material3.rememberModalBottomSheetState
4444import androidx.compose.runtime.Composable
4545import androidx.compose.runtime.DisposableEffect
46+ import androidx.compose.runtime.collectAsState
47+ import androidx.compose.runtime.derivedStateOf
4648import androidx.compose.runtime.getValue
4749import androidx.compose.runtime.mutableStateOf
4850import androidx.compose.runtime.remember
@@ -70,10 +72,10 @@ import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
7072import androidx.compose.ui.unit.dp
7173import androidx.compose.ui.window.Dialog
7274import androidx.compose.ui.window.DialogProperties
75+ import androidx.lifecycle.viewmodel.compose.viewModel
7376import coil3.compose.AsyncImage
7477import org.schabi.newpipe.R
7578import org.schabi.newpipe.extractor.stream.StreamType
76- import org.schabi.newpipe.ktx.popFirst
7779import org.schabi.newpipe.ui.components.common.ScaffoldWithToolbar
7880import org.schabi.newpipe.ui.components.menu.LongPressAction.Type.EnqueueNext
7981import org.schabi.newpipe.ui.components.menu.LongPressAction.Type.ShowChannelDetails
@@ -123,6 +125,9 @@ fun LongPressMenu(
123125 longPressActions : List <LongPressAction >,
124126 onDismissRequest : () -> Unit ,
125127) {
128+ val viewModel: LongPressMenuViewModel = viewModel()
129+ val isHeaderEnabled by viewModel.isHeaderEnabled.collectAsState()
130+ val actionArrangement by viewModel.actionArrangement.collectAsState()
126131 var showEditor by rememberSaveable { mutableStateOf(false ) }
127132 val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true )
128133
@@ -140,14 +145,39 @@ fun LongPressMenu(
140145 }
141146 }
142147 } else {
148+ val enabledLongPressActions by remember {
149+ derivedStateOf {
150+ actionArrangement.mapNotNull { type ->
151+ longPressActions.firstOrNull { it.type == type }
152+ }
153+ }
154+ }
155+
156+ // show a clickable uploader in the header if an uploader action is available and the
157+ // "show channel details" action is not enabled as a standalone action
158+ val ctx = LocalContext .current
159+ val onUploaderClick by remember {
160+ derivedStateOf {
161+ longPressActions.firstOrNull { it.type == ShowChannelDetails }
162+ ?.takeIf { ! actionArrangement.contains(ShowChannelDetails ) }
163+ ?.let { showChannelDetailsAction ->
164+ {
165+ showChannelDetailsAction.action(ctx)
166+ onDismissRequest()
167+ }
168+ }
169+ }
170+ }
171+
143172 ModalBottomSheet (
144173 sheetState = sheetState,
145174 onDismissRequest = onDismissRequest,
146175 dragHandle = { LongPressMenuDragHandle (onEditActions = { showEditor = true }) },
147176 ) {
148177 LongPressMenuContent (
149- longPressable = longPressable,
150- longPressActions = longPressActions,
178+ header = longPressable.takeIf { isHeaderEnabled },
179+ onUploaderClick = onUploaderClick,
180+ actions = enabledLongPressActions,
151181 onDismissRequest = onDismissRequest,
152182 )
153183 }
@@ -156,8 +186,9 @@ fun LongPressMenu(
156186
157187@Composable
158188private fun LongPressMenuContent (
159- longPressable : LongPressable ,
160- longPressActions : List <LongPressAction >,
189+ header : LongPressable ? ,
190+ onUploaderClick : (() -> Unit )? ,
191+ actions : List <LongPressAction >,
161192 onDismissRequest : () -> Unit ,
162193) {
163194 BoxWithConstraints (
@@ -172,20 +203,10 @@ private fun LongPressMenuContent(
172203 // width for the landscape/reduced header, measured in button widths
173204 val headerWidthInButtonsReducedSpan = 4
174205 val buttonsPerRow = (this .maxWidth / MinButtonWidth ).toInt()
175-
176- // the channel icon goes in the menu header, so do not show a button for it
177- val actions = longPressActions.toMutableList()
178206 val ctx = LocalContext .current
179- val onUploaderClick = actions.popFirst { it.type == ShowChannelDetails }
180- ?.let { showChannelDetailsAction ->
181- {
182- showChannelDetailsAction.action(ctx)
183- onDismissRequest()
184- }
185- }
186207
187208 Column {
188- var actionIndex = - 1 // -1 indicates the header
209+ var actionIndex = if (header != null ) - 1 else 0 // -1 indicates the header
189210 while (actionIndex < actions.size) {
190211 Row (
191212 verticalAlignment = Alignment .CenterVertically ,
@@ -224,7 +245,7 @@ private fun LongPressMenuContent(
224245 // this branch is taken if the full-span header is going to fit on one
225246 // line (i.e. on phones in portrait)
226247 LongPressMenuHeader (
227- item = longPressable,
248+ item = header !! , // surely not null since actionIndex < 0
228249 onUploaderClick = onUploaderClick,
229250 modifier = Modifier
230251 .padding(start = 6 .dp, end = 6 .dp, bottom = 6 .dp)
@@ -241,7 +262,7 @@ private fun LongPressMenuContent(
241262 // branch is taken, at least two buttons will be on the right side of
242263 // the header (just one button would look off).
243264 LongPressMenuHeader (
244- item = longPressable,
265+ item = header !! , // surely not null since actionIndex < 0
245266 onUploaderClick = onUploaderClick,
246267 modifier = Modifier
247268 .padding(start = 8 .dp, top = 11 .dp, bottom = 11 .dp)
@@ -667,8 +688,9 @@ private fun LongPressMenuPreview(
667688 // longPressable is null when running the preview in an emulator for some reason...
668689 @Suppress(" USELESS_ELVIS" )
669690 LongPressMenuContent (
670- longPressable = longPressable ? : LongPressablePreviews ().values.first(),
671- longPressActions = LongPressAction .Type .entries
691+ header = longPressable ? : LongPressablePreviews ().values.first(),
692+ onUploaderClick = {},
693+ actions = LongPressAction .Type .entries
672694 // disable Enqueue actions just to show it off
673695 .map { t -> t.buildAction({ t != EnqueueNext }) { } },
674696 onDismissRequest = {},
0 commit comments