mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-08 23:18:16 -07:00
feature: Improved sync capability
This commit is contained in:
parent
f3e920ac79
commit
c5c7f71b84
31 changed files with 500 additions and 344 deletions
|
|
@ -1,12 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/providers/sync/sync_provider_helpers.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
import 'package:fladder/screens/shared/default_alert_dialog.dart';
|
||||
import 'package:fladder/screens/syncing/sync_item_details.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class SyncButton extends ConsumerStatefulWidget {
|
||||
final ItemBaseModel item;
|
||||
|
|
@ -21,37 +20,21 @@ class _SyncButtonState extends ConsumerState<SyncButton> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final syncedItem = widget.syncedItem;
|
||||
final status = syncedItem != null ? ref.watch(syncStatusesProvider(syncedItem)).value : null;
|
||||
final progress = syncedItem != null ? ref.watch(syncDownloadStatusProvider(syncedItem)) : null;
|
||||
final status = syncedItem != null ? ref.watch(syncStatusesProvider(syncedItem, null)).value : null;
|
||||
final progress = syncedItem != null ? ref.watch(syncDownloadStatusProvider(syncedItem, [])) : null;
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: syncedItem != null
|
||||
? () => showSyncItemDetails(context, syncedItem, ref)
|
||||
: () => showDefaultActionDialog(
|
||||
context,
|
||||
'Sync ${widget.item.detailedName}?',
|
||||
null,
|
||||
(context) async {
|
||||
await ref.read(syncProvider.notifier).addSyncItem(context, widget.item);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
"Sync",
|
||||
(context) => Navigator.of(context).pop(),
|
||||
"Cancel",
|
||||
),
|
||||
child: Icon(
|
||||
syncedItem != null
|
||||
? status == SyncStatus.partially
|
||||
? (progress?.progress ?? 0) > 0
|
||||
? IconsaxPlusLinear.arrow_down
|
||||
: IconsaxPlusLinear.more_circle
|
||||
: IconsaxPlusLinear.tick_circle
|
||||
: IconsaxPlusLinear.arrow_down_2,
|
||||
color: status?.color,
|
||||
size: (progress?.progress ?? 0) > 0 ? 16 : null,
|
||||
),
|
||||
Icon(
|
||||
syncedItem != null
|
||||
? status == SyncStatus.partially
|
||||
? (progress?.progress ?? 0) > 0
|
||||
? IconsaxPlusLinear.arrow_down
|
||||
: IconsaxPlusLinear.more_circle
|
||||
: IconsaxPlusLinear.tick_circle
|
||||
: IconsaxPlusLinear.arrow_down_2,
|
||||
color: status?.color,
|
||||
size: (progress?.progress ?? 0) > 0 ? 16 : null,
|
||||
),
|
||||
if ((progress?.progress ?? 0) > 0)
|
||||
IgnorePointer(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import 'package:fladder/models/items/season_model.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/screens/syncing/widgets/synced_season_poster.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/items/season_model.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
import 'package:fladder/screens/syncing/widgets/synced_season_poster.dart';
|
||||
|
||||
import 'widgets/synced_episode_item.dart';
|
||||
|
||||
|
|
@ -33,31 +34,25 @@ class _ChildSyncWidgetState extends ConsumerState<ChildSyncWidget> {
|
|||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: Card(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
baseItem.navigateTo(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: switch (baseItem) {
|
||||
SeasonModel season => SyncedSeasonPoster(
|
||||
syncedItem: syncedItem,
|
||||
season: season,
|
||||
),
|
||||
EpisodeModel episode => SyncedEpisodeItem(
|
||||
episode: episode,
|
||||
syncedItem: syncedItem,
|
||||
hasFile: hasFile,
|
||||
),
|
||||
_ => Container(),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: switch (baseItem) {
|
||||
SeasonModel season => SyncedSeasonPoster(
|
||||
syncedItem: syncedItem,
|
||||
season: season,
|
||||
),
|
||||
EpisodeModel episode => SyncedEpisodeItem(
|
||||
episode: episode,
|
||||
syncedItem: syncedItem,
|
||||
hasFile: hasFile,
|
||||
),
|
||||
_ => Container(),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||
import 'package:fladder/providers/sync/background_download_provider.dart';
|
||||
import 'package:fladder/providers/sync/sync_provider_helpers.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
import 'package:fladder/screens/shared/adaptive_dialog.dart';
|
||||
|
|
@ -101,6 +99,7 @@ class _SyncItemDetailsState extends ConsumerState<SyncItemDetails> {
|
|||
Expanded(
|
||||
child: SyncProgressBuilder(
|
||||
item: syncedItem,
|
||||
children: syncChildren,
|
||||
builder: (context, combinedStream) {
|
||||
return Row(
|
||||
children: [
|
||||
|
|
@ -114,52 +113,17 @@ class _SyncItemDetailsState extends ConsumerState<SyncItemDetails> {
|
|||
),
|
||||
SyncSubtitle(syncItem: syncedItem),
|
||||
SyncLabel(
|
||||
label: context.localized
|
||||
.totalSize(ref.watch(syncSizeProvider(syncedItem)).byteFormat ?? '--'),
|
||||
status: ref.watch(syncStatusesProvider(syncedItem)).value ?? SyncStatus.partially,
|
||||
label: context.localized.totalSize(
|
||||
ref.watch(syncSizeProvider(syncedItem, syncChildren)).byteFormat ?? '--'),
|
||||
status: ref.watch(syncStatusesProvider(syncedItem, syncChildren)).value ??
|
||||
SyncStatus.partially,
|
||||
),
|
||||
if (combinedStream?.task != null && combinedStream != null) ...{
|
||||
SyncProgressBar(item: syncedItem, task: combinedStream)
|
||||
},
|
||||
].addInBetween(const SizedBox(height: 8)),
|
||||
),
|
||||
),
|
||||
if (combinedStream?.task != null) ...{
|
||||
if (combinedStream?.status != TaskStatus.paused)
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
ref.read(backgroundDownloaderProvider).pause(combinedStream!.task!),
|
||||
icon: const Icon(IconsaxPlusBold.pause),
|
||||
),
|
||||
if (combinedStream?.status == TaskStatus.paused) ...[
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
ref.read(backgroundDownloaderProvider).resume(combinedStream!.task!),
|
||||
icon: const Icon(IconsaxPlusBold.play),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => ref
|
||||
.read(syncProvider.notifier)
|
||||
.deleteFullSyncFiles(syncedItem, combinedStream?.task),
|
||||
icon: const Icon(IconsaxPlusBold.stop),
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 16)
|
||||
},
|
||||
if (combinedStream != null && combinedStream.hasDownload)
|
||||
SizedBox.fromSize(
|
||||
size: const Size.fromRadius(35),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(
|
||||
value: combinedStream.progress,
|
||||
strokeWidth: 8,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface.withValues(alpha: 0.5),
|
||||
strokeCap: StrokeCap.round,
|
||||
color: combinedStream.status.color(context),
|
||||
),
|
||||
Center(child: Text("${((combinedStream.progress) * 100).toStringAsFixed(0)}%"))
|
||||
],
|
||||
)),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
|
@ -167,7 +131,7 @@ class _SyncItemDetailsState extends ConsumerState<SyncItemDetails> {
|
|||
),
|
||||
if (!hasFile && !downloadTask.hasDownload && syncedItem.hasVideoFile)
|
||||
IconButtonAwait(
|
||||
onPressed: () async => await ref.read(syncProvider.notifier).syncVideoFile(syncedItem, false),
|
||||
onPressed: () async => await ref.read(syncProvider.notifier).syncFile(syncedItem, false),
|
||||
icon: const Icon(IconsaxPlusLinear.cloud_change),
|
||||
)
|
||||
else if (hasFile)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class SyncListItemState extends ConsumerState<SyncListItem> {
|
|||
Widget build(BuildContext context) {
|
||||
final syncedItem = widget.syncedItem;
|
||||
final baseItem = ref.read(syncProvider.notifier).getItem(syncedItem);
|
||||
final children = ref.watch(syncChildrenProvider(syncedItem));
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: SyncStatusOverlay(
|
||||
|
|
@ -89,6 +90,7 @@ class SyncListItemState extends ConsumerState<SyncListItem> {
|
|||
Expanded(
|
||||
child: SyncProgressBuilder(
|
||||
item: syncedItem,
|
||||
children: children,
|
||||
builder: (context, combinedStream) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -103,13 +105,17 @@ class SyncListItemState extends ConsumerState<SyncListItem> {
|
|||
),
|
||||
),
|
||||
Flexible(
|
||||
child: SyncSubtitle(syncItem: syncedItem),
|
||||
child: SyncSubtitle(
|
||||
syncItem: syncedItem,
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: SyncLabel(
|
||||
label: context.localized
|
||||
.totalSize(ref.watch(syncSizeProvider(syncedItem)).byteFormat ?? '--'),
|
||||
status: ref.watch(syncStatusesProvider(syncedItem)).value ?? SyncStatus.partially,
|
||||
label: context.localized.totalSize(
|
||||
ref.watch(syncSizeProvider(syncedItem, children)).byteFormat ?? '--'),
|
||||
status: ref.watch(syncStatusesProvider(syncedItem, children)).value ??
|
||||
SyncStatus.partially,
|
||||
),
|
||||
),
|
||||
if (combinedStream != null && combinedStream.hasDownload == true)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/items/season_model.dart';
|
||||
|
|
@ -15,6 +15,13 @@ import 'package:fladder/providers/sync_provider.dart';
|
|||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
const _cancellableStatuses = {
|
||||
TaskStatus.canceled,
|
||||
TaskStatus.failed,
|
||||
TaskStatus.enqueued,
|
||||
TaskStatus.waitingToRetry,
|
||||
};
|
||||
|
||||
class SyncLabel extends ConsumerWidget {
|
||||
final String? label;
|
||||
final SyncStatus status;
|
||||
|
|
@ -76,18 +83,24 @@ class SyncProgressBar extends ConsumerWidget {
|
|||
IconButton(
|
||||
onPressed: () => ref.read(backgroundDownloaderProvider).pause(downloadTask),
|
||||
icon: const Icon(IconsaxPlusBold.pause),
|
||||
),
|
||||
if (downloadStatus == TaskStatus.paused) ...[
|
||||
IconButton(
|
||||
onPressed: () => ref.read(backgroundDownloaderProvider).resume(downloadTask),
|
||||
icon: const Icon(IconsaxPlusBold.play),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => ref.read(syncProvider.notifier).deleteFullSyncFiles(item, downloadTask),
|
||||
icon: const Icon(IconsaxPlusBold.stop),
|
||||
)
|
||||
],
|
||||
if (_cancellableStatuses.contains(downloadStatus)) ...[
|
||||
IconButton(
|
||||
onPressed: () => ref.read(syncProvider.notifier).deleteFullSyncFiles(item, downloadTask),
|
||||
icon: const Icon(IconsaxPlusBold.stop),
|
||||
),
|
||||
],
|
||||
},
|
||||
if (downloadStatus == TaskStatus.paused && downloadTask != null) ...[
|
||||
IconButton(
|
||||
onPressed: () => ref.read(backgroundDownloaderProvider).resume(downloadTask),
|
||||
icon: const Icon(IconsaxPlusBold.play),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => ref.read(syncProvider.notifier).deleteFullSyncFiles(item, downloadTask),
|
||||
icon: const Icon(IconsaxPlusBold.stop),
|
||||
)
|
||||
],
|
||||
].addInBetween(const SizedBox(width: 8)),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
|
|
@ -98,16 +111,17 @@ class SyncProgressBar extends ConsumerWidget {
|
|||
|
||||
class SyncSubtitle extends ConsumerWidget {
|
||||
final SyncedItem syncItem;
|
||||
final List<SyncedItem> children;
|
||||
const SyncSubtitle({
|
||||
required this.syncItem,
|
||||
this.children = const [],
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final baseItem = ref.read(syncProvider.notifier).getItem(syncItem);
|
||||
final children = syncItem.nestedChildren(ref);
|
||||
final syncStatus = ref.watch(syncStatusesProvider(syncItem)).value ?? SyncStatus.partially;
|
||||
final syncStatus = ref.watch(syncStatusesProvider(syncItem, children)).value ?? SyncStatus.partially;
|
||||
return Container(
|
||||
decoration:
|
||||
BoxDecoration(color: syncStatus.color.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10)),
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/syncing/download_stream.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/providers/sync/sync_provider_helpers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class SyncProgressBuilder extends ConsumerWidget {
|
||||
final SyncedItem item;
|
||||
final List<SyncedItem> children;
|
||||
final Widget Function(BuildContext context, DownloadStream? combinedStream) builder;
|
||||
const SyncProgressBuilder({required this.item, required this.builder, super.key});
|
||||
const SyncProgressBuilder({required this.item, this.children = const [], required this.builder, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final syncStatus = ref.watch(syncDownloadStatusProvider(item));
|
||||
final syncStatus = ref.watch(syncDownloadStatusProvider(item, children));
|
||||
return builder(context, syncStatus);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/providers/sync/sync_provider_helpers.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
import 'package:fladder/screens/shared/default_alert_dialog.dart';
|
||||
import 'package:fladder/screens/shared/flat_button.dart';
|
||||
import 'package:fladder/screens/shared/media/episode_posters.dart';
|
||||
import 'package:fladder/screens/syncing/sync_widgets.dart';
|
||||
import 'package:fladder/util/list_padding.dart';
|
||||
|
|
@ -40,11 +42,15 @@ class _SyncedEpisodeItemState extends ConsumerState<SyncedEpisodeItem> {
|
|||
|
||||
return Row(
|
||||
children: [
|
||||
IgnorePointer(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.3),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.3),
|
||||
child: FlatButton(
|
||||
onTap: () {
|
||||
widget.episode.navigateTo(context);
|
||||
return context.maybePop();
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 250,
|
||||
width: 175,
|
||||
child: EpisodePoster(
|
||||
episode: widget.episode,
|
||||
syncedItem: syncedItem,
|
||||
|
|
@ -87,8 +93,8 @@ class _SyncedEpisodeItemState extends ConsumerState<SyncedEpisodeItem> {
|
|||
else
|
||||
Flexible(
|
||||
child: SyncLabel(
|
||||
label: context.localized.totalSize(ref.watch(syncSizeProvider(syncedItem)).byteFormat ?? '--'),
|
||||
status: ref.watch(syncStatusesProvider(syncedItem)).value ?? SyncStatus.partially,
|
||||
label: context.localized.totalSize(ref.watch(syncSizeProvider(syncedItem, [])).byteFormat ?? '--'),
|
||||
status: ref.watch(syncStatusesProvider(syncedItem, [])).value ?? SyncStatus.partially,
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
@ -96,7 +102,7 @@ class _SyncedEpisodeItemState extends ConsumerState<SyncedEpisodeItem> {
|
|||
),
|
||||
if (!hasFile && !downloadTask.hasDownload)
|
||||
IconButtonAwait(
|
||||
onPressed: () async => await ref.read(syncProvider.notifier).syncVideoFile(syncedItem, false),
|
||||
onPressed: () async => await ref.read(syncProvider.notifier).syncFile(syncedItem, false),
|
||||
icon: const Icon(IconsaxPlusLinear.cloud_change),
|
||||
)
|
||||
else if (hasFile)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/items/season_model.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||
import 'package:fladder/screens/shared/flat_button.dart';
|
||||
import 'package:fladder/screens/syncing/widgets/synced_episode_item.dart';
|
||||
import 'package:fladder/util/fladder_image.dart';
|
||||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:fladder/widgets/shared/icon_button_await.dart';
|
||||
|
||||
class SyncedSeasonPoster extends ConsumerStatefulWidget {
|
||||
const SyncedSeasonPoster({
|
||||
|
|
@ -29,14 +33,21 @@ class _SyncedSeasonPosterState extends ConsumerState<SyncedSeasonPoster> {
|
|||
Widget build(BuildContext context) {
|
||||
final season = widget.season;
|
||||
final children = ref.read(syncProvider.notifier).getChildren(widget.syncedItem);
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 125,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 0.65,
|
||||
final unSyncedChildren = children.where((child) => child.status == SyncStatus.partially).toList();
|
||||
return ExpansionTile(
|
||||
tilePadding: EdgeInsets.zero,
|
||||
title: Row(
|
||||
spacing: 6,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 75,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 0.65,
|
||||
child: FlatButton(
|
||||
onTap: () {
|
||||
season.navigateTo(context);
|
||||
return context.maybePop();
|
||||
},
|
||||
child: Card(
|
||||
child: FladderImage(
|
||||
image: season.getPosters?.primary ??
|
||||
|
|
@ -46,50 +57,47 @@ class _SyncedSeasonPosterState extends ConsumerState<SyncedSeasonPoster> {
|
|||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
season.name,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
)
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
expanded = !expanded;
|
||||
});
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
season.name,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (unSyncedChildren.isNotEmpty)
|
||||
IconButtonAwait(
|
||||
onPressed: () async {
|
||||
for (var i = 0; i < unSyncedChildren.length; i++) {
|
||||
final childSyncedItem = unSyncedChildren[i];
|
||||
await ref.read(syncProvider.notifier).syncFile(childSyncedItem, false);
|
||||
}
|
||||
},
|
||||
icon: Icon(!expanded ? Icons.keyboard_arrow_down_rounded : Icons.keyboard_arrow_up_rounded),
|
||||
)
|
||||
].addPadding(const EdgeInsets.symmetric(horizontal: 6)),
|
||||
),
|
||||
AnimatedFadeSize(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
child: expanded && children.isNotEmpty
|
||||
? ListView(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: <Widget>[
|
||||
const Divider(),
|
||||
...children.map(
|
||||
(item) {
|
||||
final baseItem = ref.read(syncProvider.notifier).getItem(item);
|
||||
return IntrinsicHeight(
|
||||
child: SyncedEpisodeItem(
|
||||
episode: baseItem as EpisodeModel,
|
||||
syncedItem: item,
|
||||
hasFile: item.videoFile.existsSync(),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
].addPadding(const EdgeInsets.symmetric(vertical: 10)),
|
||||
)
|
||||
: Container(),
|
||||
)
|
||||
].addPadding(EdgeInsets.only(top: 10, bottom: expanded ? 10 : 0)),
|
||||
icon: const Icon(IconsaxPlusLinear.cloud_change),
|
||||
),
|
||||
],
|
||||
),
|
||||
children: children.map(
|
||||
(item) {
|
||||
final baseItem = ref.read(syncProvider.notifier).getItem(item);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: IntrinsicHeight(
|
||||
child: SyncedEpisodeItem(
|
||||
episode: baseItem as EpisodeModel,
|
||||
syncedItem: item,
|
||||
hasFile: item.videoFile.existsSync(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue