@@ -33,6 +33,7 @@ import androidx.compose.foundation.lazy.items
33
33
import androidx.compose.foundation.lazy.rememberLazyListState
34
34
import androidx.compose.foundation.rememberScrollState
35
35
import androidx.compose.foundation.shape.RoundedCornerShape
36
+ import androidx.compose.foundation.verticalScroll
36
37
import androidx.compose.material.ExperimentalMaterialApi
37
38
import androidx.compose.material.ModalBottomSheetValue
38
39
import androidx.compose.material.icons.Icons
@@ -42,6 +43,7 @@ import androidx.compose.material.icons.filled.SettingsSuggest
42
43
import androidx.compose.material.icons.filled.VideoFile
43
44
import androidx.compose.material.icons.outlined.Cancel
44
45
import androidx.compose.material.icons.outlined.DoneAll
46
+ import androidx.compose.material.icons.outlined.ErrorOutline
45
47
import androidx.compose.material.icons.outlined.ExpandMore
46
48
import androidx.compose.material.icons.outlined.FileDownload
47
49
import androidx.compose.material.icons.outlined.MoreVert
@@ -51,6 +53,7 @@ import androidx.compose.material3.Button
51
53
import androidx.compose.material3.ButtonDefaults
52
54
import androidx.compose.material3.CircularProgressIndicator
53
55
import androidx.compose.material3.ExperimentalMaterial3Api
56
+ import androidx.compose.material3.FilledTonalButton
54
57
import androidx.compose.material3.HorizontalDivider
55
58
import androidx.compose.material3.Icon
56
59
import androidx.compose.material3.MaterialTheme
@@ -72,15 +75,21 @@ import androidx.compose.runtime.setValue
72
75
import androidx.compose.ui.Alignment
73
76
import androidx.compose.ui.Modifier
74
77
import androidx.compose.ui.draw.alpha
78
+ import androidx.compose.ui.platform.LocalClipboardManager
75
79
import androidx.compose.ui.platform.LocalDensity
80
+ import androidx.compose.ui.platform.LocalView
76
81
import androidx.compose.ui.res.stringResource
82
+ import androidx.compose.ui.text.AnnotatedString
83
+ import androidx.compose.ui.text.font.FontFamily
77
84
import androidx.compose.ui.text.style.TextAlign
78
85
import androidx.compose.ui.text.style.TextOverflow
79
86
import androidx.compose.ui.tooling.preview.Preview
80
87
import androidx.compose.ui.unit.Dp
81
88
import androidx.compose.ui.unit.dp
82
89
import androidx.lifecycle.compose.collectAsStateWithLifecycle
90
+ import com.junkfood.seal.App
83
91
import com.junkfood.seal.R
92
+ import com.junkfood.seal.ui.common.HapticFeedback.longPressHapticFeedback
84
93
import com.junkfood.seal.ui.common.motion.materialSharedAxisX
85
94
import com.junkfood.seal.ui.component.DrawerSheetSubtitle
86
95
import com.junkfood.seal.ui.component.OutlinedButtonWithIcon
@@ -122,6 +131,7 @@ import com.junkfood.seal.util.PreferenceUtil.updateBoolean
122
131
import com.junkfood.seal.util.PreferenceUtil.updateInt
123
132
import com.junkfood.seal.util.SUBTITLE
124
133
import com.junkfood.seal.util.THUMBNAIL
134
+ import com.junkfood.seal.util.ToastUtil
125
135
import com.junkfood.seal.util.USE_CUSTOM_AUDIO_PRESET
126
136
import com.junkfood.seal.util.VIDEO_FORMAT
127
137
import com.junkfood.seal.util.VIDEO_QUALITY
@@ -220,7 +230,7 @@ fun ConfigureDialog(
220
230
preferences : DownloadUtil .DownloadPreferences ,
221
231
onPreferencesUpdate : (DownloadUtil .DownloadPreferences ) -> Unit ,
222
232
state : DownloadDialogViewModel .SheetState = Configure ,
223
- onActionPosted : (Action ) -> Unit = {}
233
+ onActionPost : (Action ) -> Unit = {}
224
234
) {
225
235
226
236
var showVideoPresetDialog by remember { mutableStateOf(false ) }
@@ -276,56 +286,152 @@ fun ConfigureDialog(
276
286
SealModalBottomSheet (
277
287
sheetState = sheetState,
278
288
contentPadding = PaddingValues (),
279
- onDismissRequest = { onActionPosted(Action .HideSheet ) }) {
280
- AnimatedContent (
281
- targetState = state,
282
- label = " " ,
283
- transitionSpec = {
284
- materialSharedAxisX(initialOffsetX = { it / 4 }, targetOffsetX = { - it / 4 })
285
- }) { state ->
286
- when (state) {
287
- Configure -> {
288
- ConfigurePageImpl (
289
- url = url,
290
- config = config,
291
- preferences = preferences,
292
- onPresetEdit = { type ->
293
- when (type) {
294
- Audio -> showAudioPresetDialog = true
295
-
296
- Video -> showVideoPresetDialog = true
297
-
298
- else -> {}
299
- }
300
- },
301
- onConfigSave = { Config .updatePreferences(it) },
302
- settingChips = {
303
- AdditionalSettings (
304
- modifier = Modifier .padding(horizontal = 16 .dp),
305
- isQuickDownload = false ,
306
- preference = preferences,
307
- selectedType = Audio ,
308
- onPreferenceUpdate = {
309
- onPreferencesUpdate(
310
- DownloadUtil .DownloadPreferences
311
- .createFromPreferences())
312
- })
313
- },
314
- onActionPost = { onActionPosted(it) })
315
- }
289
+ onDismissRequest = { onActionPost(Action .HideSheet ) },
290
+ ) {
291
+ ConfigureDialogContent (
292
+ modifier = modifier,
293
+ state = state,
294
+ url = url,
295
+ config = config,
296
+ preferences = preferences,
297
+ onPreferencesUpdate = onPreferencesUpdate,
298
+ onPresetEdit = { type ->
299
+ when (type) {
300
+ Audio -> showAudioPresetDialog = true
316
301
317
- is Error -> {
318
- Text (state.throwable.stackTrace.contentToString())
319
- }
302
+ Video -> showVideoPresetDialog = true
320
303
321
- else -> {
322
- Column (modifier = Modifier .fillMaxWidth().padding(vertical = 120 .dp)) {
323
- CircularProgressIndicator (
324
- modifier = Modifier .align(Alignment .CenterHorizontally ))
325
- }
326
- }
304
+ else -> {}
305
+ }
306
+ },
307
+ onActionPost = onActionPost)
308
+ }
309
+ }
310
+
311
+ @Composable
312
+ private fun ErrorPage (modifier : Modifier = Modifier , state : Error , onActionPost : (Action ) -> Unit ) {
313
+ val view = LocalView .current
314
+ val clipboardManager = LocalClipboardManager .current
315
+ val url =
316
+ state.action.run {
317
+ when (this ) {
318
+ is Action .FetchFormats -> url
319
+ is Action .FetchPlaylist -> url
320
+ else -> {
321
+ throw IllegalArgumentException ()
322
+ }
323
+ }
324
+ }
325
+ Column (modifier = modifier.fillMaxWidth(), horizontalAlignment = Alignment .CenterHorizontally ) {
326
+ Icon (
327
+ imageVector = Icons .Outlined .ErrorOutline ,
328
+ contentDescription = null ,
329
+ modifier = Modifier .size(40 .dp))
330
+ Text (
331
+ text = stringResource(R .string.fetch_info_error_msg),
332
+ style = MaterialTheme .typography.titleMedium,
333
+ modifier = Modifier .padding(top = 12 .dp))
334
+ Text (
335
+ text = state.throwable.message.toString(),
336
+ style = MaterialTheme .typography.bodySmall.copy(fontFamily = FontFamily .Monospace ),
337
+ color = MaterialTheme .colorScheme.onSurfaceVariant,
338
+ modifier =
339
+ Modifier .padding(vertical = 16 .dp, horizontal = 20 .dp)
340
+ .fillMaxWidth()
341
+ .verticalScroll(rememberScrollState()),
342
+ maxLines = 20 ,
343
+ overflow = TextOverflow .Clip )
344
+
345
+ Row {
346
+ Button (
347
+ onClick = {
348
+ view.longPressHapticFeedback()
349
+ clipboardManager.setText(
350
+ AnnotatedString (
351
+ App .getVersionReport() + " \n URL: ${url} \n ${state.throwable.message} " ))
352
+ ToastUtil .makeToast(R .string.error_copied)
353
+ }) {
354
+ Text (stringResource(R .string.copy_error_report))
355
+ }
356
+ Spacer (Modifier .width(8 .dp))
357
+ FilledTonalButton (onClick = { onActionPost(state.action) }) { Text (" Retry" ) }
358
+ }
359
+ }
360
+ }
361
+
362
+ @Composable
363
+ private fun ConfigureDialogContent (
364
+ modifier : Modifier = Modifier ,
365
+ url : String ,
366
+ state : DownloadDialogViewModel .SheetState ,
367
+ config : Config ,
368
+ preferences : DownloadUtil .DownloadPreferences ,
369
+ onPreferencesUpdate : (DownloadUtil .DownloadPreferences ) -> Unit ,
370
+ onPresetEdit : (DownloadType ? ) -> Unit ,
371
+ onActionPost : (Action ) -> Unit ,
372
+ ) {
373
+ AnimatedContent (
374
+ modifier = modifier,
375
+ targetState = state,
376
+ label = " " ,
377
+ transitionSpec = {
378
+ materialSharedAxisX(initialOffsetX = { it / 4 }, targetOffsetX = { - it / 4 })
379
+ }) { state ->
380
+ when (state) {
381
+ Configure -> {
382
+ ConfigurePageImpl (
383
+ url = url,
384
+ config = config,
385
+ preferences = preferences,
386
+ onPresetEdit = onPresetEdit,
387
+ onConfigSave = { Config .updatePreferences(it) },
388
+ settingChips = {
389
+ AdditionalSettings (
390
+ modifier = Modifier .padding(horizontal = 16 .dp),
391
+ isQuickDownload = false ,
392
+ preference = preferences,
393
+ selectedType = Audio ,
394
+ onPreferenceUpdate = {
395
+ onPreferencesUpdate(
396
+ DownloadUtil .DownloadPreferences .createFromPreferences())
397
+ })
398
+ },
399
+ onActionPost = { onActionPost(it) })
400
+ }
401
+
402
+ is Error -> {
403
+ ErrorPage (state = state, onActionPost = onActionPost)
404
+ }
405
+
406
+ else -> {
407
+ Column (modifier = Modifier .fillMaxWidth().padding(vertical = 120 .dp)) {
408
+ CircularProgressIndicator (
409
+ modifier = Modifier .align(Alignment .CenterHorizontally ))
327
410
}
328
411
}
412
+ }
413
+ }
414
+ }
415
+
416
+ @OptIn(ExperimentalMaterial3Api ::class )
417
+ @Preview
418
+ @Composable
419
+ private fun ErrorPreview () {
420
+ SealModalBottomSheet (
421
+ onDismissRequest = {},
422
+ sheetState =
423
+ SheetState (
424
+ skipPartiallyExpanded = true ,
425
+ initialValue = SheetValue .Expanded ,
426
+ density = LocalDensity .current)) {
427
+ ErrorPage (
428
+ state =
429
+ Error (
430
+ action =
431
+ Action .FetchFormats (
432
+ url = " " , audioOnly = true , preferences = PreferencesMock ),
433
+ throwable = Exception (" Not good" )),
434
+ onActionPost = {})
329
435
}
330
436
}
331
437
0 commit comments