From b17a74bb2339671cb1b2b7c5da4c93879cd5828c Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sun, 27 Jul 2025 11:47:31 +0200 Subject: [PATCH] Small improvements --- .vscode/launch.json | 2 - lib/l10n/app_en.arb | 7 +- lib/main.dart | 9 +-- lib/models/syncing/database_item.dart | 2 +- lib/models/syncing/database_item.g.dart | 2 +- lib/providers/sync_provider.dart | 1 + lib/screens/home_screen.dart | 3 +- lib/screens/shared/media/season_row.dart | 2 +- .../syncing/widgets/sync_options_button.dart | 69 +++++++++++++++++-- lib/util/refresh_state.dart | 4 +- 10 files changed, 81 insertions(+), 20 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index db1705f..ee28073 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -81,7 +81,6 @@ "args": [ "--web-port", "9090", - "--web-experimental-hot-reload" ], }, { @@ -93,7 +92,6 @@ "args": [ "--web-port", "9090", - "--web-experimental-hot-reload" ], }, ], diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e988fb8..bdb9329 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1284,5 +1284,10 @@ "type": "int" } } - } + }, + "syncPauseAll": "Pause all", + "syncResumeAll": "Resume all", + "syncStopAll": "Stop all", + "syncDeleteAll": "Delete all files", + "syncAllFiles": "Sync all files" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 9530a03..a48b71f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,6 @@ import 'package:flutter/services.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:smtc_windows/smtc_windows.dart' if (dart.library.html) 'package:fladder/stubs/web/smtc_web.dart'; @@ -69,13 +68,10 @@ void main(List args) async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - Directory isarPath = Directory(""); Directory applicationDirectory = Directory(""); if (!kIsWeb) { applicationDirectory = await getApplicationDocumentsDirectory(); - isarPath = Directory(path.joinAll([applicationDirectory.path, 'Fladder', 'Database'])); - await isarPath.create(recursive: true); } if (_isDesktop) { @@ -96,10 +92,7 @@ void main(List args) async { applicationInfoProvider.overrideWith((ref) => applicationInfo), crashLogProvider.overrideWith((ref) => crashProvider), argumentsStateProvider.overrideWith((ref) => ArgumentsModel.fromArguments(args)), - syncProvider.overrideWith((ref) => SyncNotifier( - ref, - applicationDirectory, - )) + syncProvider.overrideWith((ref) => SyncNotifier(ref, applicationDirectory)) ], child: AdaptiveLayoutBuilder( child: (context) => const Main(), diff --git a/lib/models/syncing/database_item.dart b/lib/models/syncing/database_item.dart index c3998fe..d7d3a70 100644 --- a/lib/models/syncing/database_item.dart +++ b/lib/models/syncing/database_item.dart @@ -35,7 +35,7 @@ class DatabaseItems extends Table { TextColumn get userData => text().nullable()(); @override - Set> get primaryKey => {id}; + Set> get primaryKey => {id, userId}; } @DriftDatabase(tables: [DatabaseItems]) diff --git a/lib/models/syncing/database_item.g.dart b/lib/models/syncing/database_item.g.dart index 11114f2..96fa85e 100644 --- a/lib/models/syncing/database_item.g.dart +++ b/lib/models/syncing/database_item.g.dart @@ -194,7 +194,7 @@ class $DatabaseItemsTable extends DatabaseItems } @override - Set get $primaryKey => {id}; + Set get $primaryKey => {id, userId}; @override DatabaseItem map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; diff --git a/lib/providers/sync_provider.dart b/lib/providers/sync_provider.dart index 8a5bd41..aff5414 100644 --- a/lib/providers/sync_provider.dart +++ b/lib/providers/sync_provider.dart @@ -669,6 +669,7 @@ extension SyncNotifierHelpers on SyncNotifier { for (var i = 0; i < itemsToDownload.length; i++) { final item = itemsToDownload[i]; + //No need to await file sync happens in the background syncFile(item, false); } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 3fb9fff..faff11f 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:auto_route/auto_route.dart'; @@ -91,7 +92,7 @@ class HomeScreen extends ConsumerWidget { action: () => e.navigate(context), ); case HomeTabs.sync: - if (canDownload) { + if (canDownload && !kIsWeb) { return DestinationModel( label: context.localized.navigationSync, icon: Icon(e.icon), diff --git a/lib/screens/shared/media/season_row.dart b/lib/screens/shared/media/season_row.dart index 7148f71..bf6ab79 100644 --- a/lib/screens/shared/media/season_row.dart +++ b/lib/screens/shared/media/season_row.dart @@ -154,7 +154,7 @@ class SeasonPoster extends ConsumerWidget { items: season.generateActions(context, ref).popupMenuItems(useIcons: true)); }, onTap: () => onSeasonPressed?.call(season), - onLongPress: AdaptiveLayout.of(context).inputDevice != InputDevice.touch + onLongPress: AdaptiveLayout.of(context).inputDevice == InputDevice.touch ? () { showBottomSheetPill( context: context, diff --git a/lib/screens/syncing/widgets/sync_options_button.dart b/lib/screens/syncing/widgets/sync_options_button.dart index 3481bc5..10fd615 100644 --- a/lib/screens/syncing/widgets/sync_options_button.dart +++ b/lib/screens/syncing/widgets/sync_options_button.dart @@ -7,6 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:iconsax_plus/iconsax_plus.dart'; import 'package:fladder/models/syncing/sync_item.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/util/localization_helper.dart'; @@ -33,7 +34,23 @@ class SyncOptionsButton extends ConsumerWidget { final syncedChildren = children.where((element) => element.hasVideoFile && element.videoFile.existsSync()).toList(); - return [ + + final syncTasks = children + .map((element) { + final task = ref.read(syncDownloadStatusProvider(element, [])); + if (task?.status != TaskStatus.notFound) { + return task; + } else { + return null; + } + }) + .nonNulls + .toList(); + + final runningTasks = syncTasks.where((element) => element.status == TaskStatus.running).toList(); + final enqueuedTasks = syncTasks.where((element) => element.status == TaskStatus.enqueued).toList(); + final pausedTasks = syncTasks.where((element) => element.status == TaskStatus.paused).toList(); + return [ PopupMenuItem( child: Row( spacing: 12, @@ -45,13 +62,14 @@ class SyncOptionsButton extends ConsumerWidget { onTap: () => context.refreshData(), ), if (children.isNotEmpty) ...[ + const PopupMenuDivider(), PopupMenuItem( enabled: unSyncedChildren.isNotEmpty, child: Row( spacing: 12, children: [ const Icon(IconsaxPlusLinear.cloud_add), - Text(context.localized.sync), + Text(context.localized.syncAllFiles), ], ), onTap: () async => _syncRemainingItems(context, syncedItem, unSyncedChildren, ref), @@ -62,11 +80,54 @@ class SyncOptionsButton extends ConsumerWidget { spacing: 12, children: [ const Icon(IconsaxPlusLinear.trash), - Text(context.localized.delete), + Text(context.localized.syncDeleteAll), ], ), onTap: () async => _deleteSyncedItems(context, syncedItem, syncedChildren, ref), - ) + ), + const PopupMenuDivider(), + PopupMenuItem( + enabled: pausedTasks.isNotEmpty, + child: Row( + spacing: 12, + children: [ + const Icon(IconsaxPlusLinear.play), + Text(context.localized.syncResumeAll), + ], + ), + onTap: () => ref + .read(backgroundDownloaderProvider) + .resumeAll(tasks: pausedTasks.map((e) => e.task).nonNulls.toList()), + ), + PopupMenuItem( + enabled: runningTasks.isNotEmpty, + child: Row( + spacing: 12, + children: [ + const Icon(IconsaxPlusLinear.pause), + Text(context.localized.syncPauseAll), + ], + ), + onTap: () { + ref + .read(backgroundDownloaderProvider) + .pauseAll(tasks: runningTasks.map((e) => e.task).nonNulls.toList()); + }, + ), + PopupMenuItem( + enabled: [...runningTasks, ...pausedTasks, ...enqueuedTasks].isNotEmpty, + child: Row( + spacing: 12, + children: [ + const Icon(IconsaxPlusLinear.stop), + Text(context.localized.syncStopAll), + ], + ), + onTap: () { + ref.read(backgroundDownloaderProvider).cancelAll( + tasks: [...runningTasks, ...pausedTasks, ...enqueuedTasks].map((e) => e.task).nonNulls.toList()); + }, + ), ] ]; }, diff --git a/lib/util/refresh_state.dart b/lib/util/refresh_state.dart index 97413bc..11b18e9 100644 --- a/lib/util/refresh_state.dart +++ b/lib/util/refresh_state.dart @@ -35,6 +35,8 @@ extension RefreshContextExtension on BuildContext { Future refreshData() async { //Small delay to fix server not updating response based on successful query await Future.delayed(const Duration(milliseconds: 250)); - await RefreshState.maybeOf(this)?.refresh(); + if (mounted) { + await RefreshState.maybeOf(this)?.refresh(); + } } }