fix: Sync leaving left over temp files (#73)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2024-10-25 18:58:44 +02:00 committed by GitHub
parent c5e39db9ec
commit 8d15e319d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 644 additions and 406 deletions

View file

@ -267,8 +267,8 @@
"@hideEmpty": {},
"home": "Home",
"@home": {},
"homeBannerBanner": "Banner",
"@homeBannerBanner": {},
"homeBannerSlideshow": "Slideshow",
"@homeBannerSlideshow": {},
"homeBannerCarousel": "Carousel",
"@homeBannerCarousel": {},
"identify": "Identify",
@ -947,5 +947,44 @@
"example": "1"
}
}
},
"syncStatusEnqueued": "Enqueued",
"syncStatusRunning": "Running",
"syncStatusComplete": "Complete",
"syncStatusNotFound": "Not Found",
"syncStatusFailed": "Failed",
"syncStatusCanceled": "Canceled",
"syncStatusWaitingToRetry": "Waiting to retry",
"syncStatusPaused": "Paused",
"syncStatusSynced": "Synced",
"syncStatusPartially": "Partially",
"syncOverlayDeleting": "Removing synced item",
"syncOverlaySyncing": "Syncing item details",
"syncSelectDownloadsFolder": "Select downloads folder",
"syncNoFolderSetup": "No sync folder setup",
"syncRemoveUnableToDeleteItem": "Unable to remove synced item, somethin went wrong",
"syncAddItemForSyncing": "Added {item} for syncing",
"@syncAddItemForSyncing":{
"placeholders": {
"item":{
"type": "String"
}
}
} ,
"startedSyncingItem": "Started syncing {item}",
"@startedSyncingItem": {
"placeholders": {
"item":{
"type": "String"
}
}
},
"unableToSyncItem": "Unable to sync {item}, something went wrong",
"@unableToSyncItem": {
"placeholders": {
"item":{
"type": "String"
}
}
}
}

View file

@ -28,7 +28,7 @@ enum HomeBanner {
String label(BuildContext context) => switch (this) {
HomeBanner.hide => context.localized.hide,
HomeBanner.carousel => context.localized.homeBannerCarousel,
HomeBanner.banner => context.localized.homeBannerBanner,
HomeBanner.banner => context.localized.homeBannerSlideshow,
};
}

View file

@ -28,6 +28,7 @@ part 'i_synced_item.g.dart';
class ISyncedItem {
String? userId;
String id;
bool syncing;
String? sortName;
String? parentId;
String? path;
@ -42,6 +43,7 @@ class ISyncedItem {
ISyncedItem({
this.userId,
required this.id,
required this.syncing,
this.sortName,
this.parentId,
this.path,
@ -59,6 +61,7 @@ class ISyncedItem {
return ISyncedItem(
id: syncedItem.id,
parentId: syncedItem.parentId,
syncing: syncedItem.syncing,
userId: syncedItem.userId,
path: syncedItem.path?.replaceAll(path ?? "", '').substring(1),
fileSize: syncedItem.fileSize,

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@ import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/models/syncing/i_synced_item.dart';
import 'package:fladder/providers/sync/sync_provider_helpers.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/util/localization_helper.dart';
part 'sync_item.freezed.dart';
@ -29,6 +30,7 @@ class SyncedItem with _$SyncedItem {
factory SyncedItem({
required String id,
@Default(false) bool syncing,
String? parentId,
required String userId,
String? path,
@ -72,7 +74,7 @@ class SyncedItem with _$SyncedItem {
_ => SyncStatus.partially,
};
String? get taskId => null;
String? get taskId => task?.taskId;
bool get childHasTask => false;
@ -126,6 +128,7 @@ class SyncedItem with _$SyncedItem {
parentId: isarSyncedItem.parentId,
userId: isarSyncedItem.userId ?? "",
sortName: isarSyncedItem.sortName,
syncing: isarSyncedItem.syncing,
path: joinAll([savePath, isarSyncedItem.path ?? ""]),
fileSize: isarSyncedItem.fileSize,
videoFileName: isarSyncedItem.videoFileName,
@ -155,21 +158,25 @@ class SyncedItem with _$SyncedItem {
enum SyncStatus {
complete(
"Synced",
Color.fromARGB(255, 141, 214, 58),
IconsaxOutline.tick_circle,
),
partially(
"Partially",
Color.fromARGB(255, 221, 135, 23),
IconsaxOutline.more_circle,
),
;
const SyncStatus(this.label, this.color, this.icon);
const SyncStatus(this.color, this.icon);
final Color color;
final String label;
String label(BuildContext context) {
return switch (this) {
SyncStatus.partially => context.localized.syncStatusPartially,
SyncStatus.complete => context.localized.syncStatusSynced,
};
}
final IconData icon;
}
@ -183,14 +190,14 @@ extension StatusExtension on TaskStatus {
TaskStatus.paused => Colors.orangeAccent,
};
String get name => switch (this) {
TaskStatus.enqueued => 'Enqueued',
TaskStatus.running => 'Running',
TaskStatus.complete => 'Complete',
TaskStatus.notFound => 'Not Found',
TaskStatus.failed => 'Failed',
TaskStatus.canceled => 'Canceled',
TaskStatus.waitingToRetry => 'Waiting To Retry',
TaskStatus.paused => 'Paused',
String name(BuildContext context) => switch (this) {
TaskStatus.enqueued => context.localized.syncStatusEnqueued,
TaskStatus.running => context.localized.syncStatusRunning,
TaskStatus.complete => context.localized.syncStatusComplete,
TaskStatus.notFound => context.localized.syncStatusNotFound,
TaskStatus.failed => context.localized.syncStatusFailed,
TaskStatus.canceled => context.localized.syncStatusCanceled,
TaskStatus.waitingToRetry => context.localized.syncStatusWaitingToRetry,
TaskStatus.paused => context.localized.syncStatusPaused,
};
}

View file

@ -17,6 +17,7 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc
mixin _$SyncedItem {
String get id => throw _privateConstructorUsedError;
bool get syncing => throw _privateConstructorUsedError;
String? get parentId => throw _privateConstructorUsedError;
String get userId => throw _privateConstructorUsedError;
String? get path => throw _privateConstructorUsedError;
@ -48,6 +49,7 @@ abstract class $SyncedItemCopyWith<$Res> {
@useResult
$Res call(
{String id,
bool syncing,
String? parentId,
String userId,
String? path,
@ -82,6 +84,7 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
@override
$Res call({
Object? id = null,
Object? syncing = null,
Object? parentId = freezed,
Object? userId = null,
Object? path = freezed,
@ -101,6 +104,10 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
syncing: null == syncing
? _value.syncing
: syncing // ignore: cast_nullable_to_non_nullable
as bool,
parentId: freezed == parentId
? _value.parentId
: parentId // ignore: cast_nullable_to_non_nullable
@ -195,6 +202,7 @@ abstract class _$$SyncItemImplCopyWith<$Res>
@useResult
$Res call(
{String id,
bool syncing,
String? parentId,
String userId,
String? path,
@ -229,6 +237,7 @@ class __$$SyncItemImplCopyWithImpl<$Res>
@override
$Res call({
Object? id = null,
Object? syncing = null,
Object? parentId = freezed,
Object? userId = null,
Object? path = freezed,
@ -248,6 +257,10 @@ class __$$SyncItemImplCopyWithImpl<$Res>
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
syncing: null == syncing
? _value.syncing
: syncing // ignore: cast_nullable_to_non_nullable
as bool,
parentId: freezed == parentId
? _value.parentId
: parentId // ignore: cast_nullable_to_non_nullable
@ -309,6 +322,7 @@ class __$$SyncItemImplCopyWithImpl<$Res>
class _$SyncItemImpl extends _SyncItem {
_$SyncItemImpl(
{required this.id,
this.syncing = false,
this.parentId,
required this.userId,
this.path,
@ -329,6 +343,9 @@ class _$SyncItemImpl extends _SyncItem {
@override
final String id;
@override
@JsonKey()
final bool syncing;
@override
final String? parentId;
@override
final String userId;
@ -373,7 +390,7 @@ class _$SyncItemImpl extends _SyncItem {
@override
String toString() {
return 'SyncedItem(id: $id, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortName: $sortName, fileSize: $fileSize, videoFileName: $videoFileName, introOutSkipModel: $introOutSkipModel, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, userData: $userData)';
return 'SyncedItem(id: $id, syncing: $syncing, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortName: $sortName, fileSize: $fileSize, videoFileName: $videoFileName, introOutSkipModel: $introOutSkipModel, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, userData: $userData)';
}
@override
@ -382,6 +399,7 @@ class _$SyncItemImpl extends _SyncItem {
(other.runtimeType == runtimeType &&
other is _$SyncItemImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.syncing, syncing) || other.syncing == syncing) &&
(identical(other.parentId, parentId) ||
other.parentId == parentId) &&
(identical(other.userId, userId) || other.userId == userId) &&
@ -411,6 +429,7 @@ class _$SyncItemImpl extends _SyncItem {
int get hashCode => Object.hash(
runtimeType,
id,
syncing,
parentId,
userId,
path,
@ -437,6 +456,7 @@ class _$SyncItemImpl extends _SyncItem {
abstract class _SyncItem extends SyncedItem {
factory _SyncItem(
{required final String id,
final bool syncing,
final String? parentId,
required final String userId,
final String? path,
@ -455,6 +475,8 @@ abstract class _SyncItem extends SyncedItem {
@override
String get id;
@override
bool get syncing;
@override
String? get parentId;
@override
String get userId;

View file

@ -13,6 +13,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:isar/isar.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/item_base_model.dart';
@ -35,6 +36,7 @@ import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/providers/sync/background_download_provider.dart';
import 'package:fladder/providers/user_provider.dart';
import 'package:fladder/screens/shared/fladder_snackbar.dart';
import 'package:fladder/util/localization_helper.dart';
final syncProvider = StateNotifierProvider<SyncNotifier, SyncSettingsModel>((ref) => throw UnimplementedError());
@ -46,6 +48,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
}
void _init() {
cleanupTemporaryFiles();
ref.listen(
userProvider,
(previous, next) {
@ -84,6 +87,35 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
});
}
Future<void> cleanupTemporaryFiles() async {
// List of directories to check
final directories = [
//Desktop directory
await getTemporaryDirectory(),
//Mobile directory
await getApplicationSupportDirectory(),
];
for (final dir in directories) {
final List<FileSystemEntity> files = dir.listSync();
for (var file in files) {
if (file is File) {
final fileName = file.path.split(Platform.pathSeparator).last;
final fileSize = await file.length();
if (fileName.startsWith('com.bbflight.background_downloader') && fileSize != 0) {
try {
await file.delete();
log('Deleted temporary file: $fileName from ${dir.path}');
} catch (e) {
log('Failed to delete file $fileName: $e');
}
}
}
}
}
}
final Ref ref;
final Isar? isar;
final Directory mobileDirectory;
@ -190,19 +222,20 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
return syncedItem.createItemModel(ref);
}
Future<SyncedItem?> addSyncItem(BuildContext? context, ItemBaseModel item) async {
if (context == null) return null;
Future<void> addSyncItem(BuildContext? context, ItemBaseModel item) async {
if (context == null) return;
if (saveDirectory == null) {
String? selectedDirectory = await FilePicker.platform.getDirectoryPath(dialogTitle: 'Select downloads folder');
String? selectedDirectory =
await FilePicker.platform.getDirectoryPath(dialogTitle: context.localized.syncSelectDownloadsFolder);
if (selectedDirectory?.isEmpty == true) {
fladderSnackbar(context, title: "No sync folder setup");
return null;
fladderSnackbar(context, title: context.localized.syncNoFolderSetup);
return;
}
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
}
fladderSnackbar(context, title: "Added ${item.detailedName(context)} for syncing");
fladderSnackbar(context, title: context.localized.syncAddItemForSyncing(item.detailedName(context) ?? "Unknown"));
final newSync = switch (item) {
EpisodeModel episode => await syncSeries(item.parentBaseModel, episode: episode),
SeriesModel series => await syncSeries(series),
@ -211,12 +244,12 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
};
fladderSnackbar(context,
title: newSync != null
? "Started syncing ${item.detailedName(context)}"
: "Unable to sync ${item.detailedName(context)}, type not supported?");
return newSync;
? context.localized.startedSyncingItem(item.detailedName(context) ?? "Unknown")
: context.localized.unableToSyncItem(item.detailedName(context) ?? "Unknown"));
return;
}
Future<bool> removeSync(SyncedItem? item) async {
Future<bool> removeSync(BuildContext context, SyncedItem? item) async {
try {
if (item == null) return false;
@ -259,6 +292,8 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
} catch (e) {
log('Error deleting synced item');
log(e.toString());
state = state.copyWith(items: state.items.map((e) => e.copyWith(markedForDelete: false)).toList());
fladderSnackbar(context, title: context.localized.syncRemoveUnableToDeleteItem);
return false;
}
}
@ -366,14 +401,23 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncedItem, syncPath ?? "")));
}
Future<SyncedItem> deleteFullSyncFiles(SyncedItem syncedItem) async {
Future<SyncedItem> deleteFullSyncFiles(SyncedItem syncedItem, DownloadTask? task) async {
await syncedItem.deleteDatFiles(ref);
ref.read(downloadTasksProvider(syncedItem.id).notifier).update((state) => DownloadStream.empty());
final taskId = task?.taskId;
if (taskId != null) {
ref.read(backgroundDownloaderProvider).cancelTaskWithId(taskId);
}
cleanupTemporaryFiles();
refresh();
return syncedItem;
}
Future<DownloadStream?> syncVideoFile(SyncedItem syncItem, bool skipDownload) async {
cleanupTemporaryFiles();
final playbackResponse = await api.itemsItemIdPlaybackInfoPost(
itemId: syncItem.id,
body: const PlaybackInfoDto(
@ -480,27 +524,37 @@ extension SyncNotifierHelpers on SyncNotifier {
final Directory? parentDirectory = parent?.directory;
SyncedItem syncItem = SyncedItem(id: item.id, userId: ref.read(userProvider)?.id ?? "");
final directory = Directory(path.joinAll([(parentDirectory ?? saveDirectory)?.path ?? "", item.id]));
await directory.create(recursive: true);
File dataFile = File(path.joinAll([directory.path, 'data.json']));
await dataFile.writeAsString(jsonEncode(response.toJson()));
final imageData = await saveImageData(item.images, directory);
final origChapters = Chapter.chaptersFromInfo(item.id, response.chapters ?? [], ref);
return syncItem.copyWith(
SyncedItem syncItem = SyncedItem(
syncing: true,
id: item.id,
parentId: parent?.id,
sortName: response.sortName,
fImages: imageData,
userId: ref.read(userProvider)?.id ?? "",
path: directory.path,
userData: item.userData,
);
//Save item if parent so the user is aware.
if (parent == null) {
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncItem, syncPath)));
}
final origChapters = Chapter.chaptersFromInfo(item.id, response.chapters ?? [], ref);
return syncItem.copyWith(
fChapters: await saveChapterImages(origChapters, directory) ?? [],
fileSize: response.mediaSources?.firstOrNull?.size ?? 0,
fImages: imageData,
syncing: false,
videoFileName: response.path?.split('/').lastOrNull ?? "",
userData: item.userData,
);
}
@ -528,7 +582,7 @@ extension SyncNotifierHelpers on SyncNotifier {
await syncVideoFile(syncItem, skipDownload);
await isar?.writeAsync((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncItem, syncPath)));
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncItem, syncPath)));
return syncItem;
}

View file

@ -15,8 +15,8 @@ import 'package:fladder/screens/shared/default_alert_dialog.dart';
import 'package:fladder/screens/shared/media/poster_widget.dart';
import 'package:fladder/screens/syncing/sync_child_item.dart';
import 'package:fladder/screens/syncing/sync_widgets.dart';
import 'package:fladder/screens/syncing/widgets/sync_markedfordelete.dart';
import 'package:fladder/screens/syncing/widgets/sync_progress_builder.dart';
import 'package:fladder/screens/syncing/widgets/sync_status_overlay.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/localization_helper.dart';
@ -55,7 +55,7 @@ class _SyncItemDetailsState extends ConsumerState<SyncItemDetails> {
final syncChildren = ref.read(syncProvider.notifier).getChildren(syncedItem);
final downloadTask = ref.read(downloadTasksProvider(syncedItem.id));
return SyncMarkedForDelete(
return SyncStatusOverlay(
syncedItem: syncedItem,
child: ActionContent(
title: Row(
@ -135,7 +135,9 @@ class _SyncItemDetailsState extends ConsumerState<SyncItemDetails> {
icon: const Icon(IconsaxBold.play),
),
IconButton(
onPressed: () => ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem),
onPressed: () => ref
.read(syncProvider.notifier)
.deleteFullSyncFiles(syncedItem, combinedStream?.task),
icon: const Icon(IconsaxBold.stop),
),
],
@ -177,7 +179,7 @@ class _SyncItemDetailsState extends ConsumerState<SyncItemDetails> {
context.localized.syncRemoveDataTitle,
context.localized.syncRemoveDataDesc,
(context) {
ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem);
ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem, downloadTask.task);
Navigator.of(context).pop();
},
context.localized.delete,
@ -217,7 +219,7 @@ class _SyncItemDetailsState extends ConsumerState<SyncItemDetails> {
context.localized.syncDeleteItemTitle,
context.localized.syncDeleteItemDesc(baseItem?.detailedName(context) ?? ""),
(context) async {
await ref.read(syncProvider.notifier).removeSync(syncedItem);
await ref.read(syncProvider.notifier).removeSync(context, syncedItem);
Navigator.pop(context);
Navigator.pop(context);
},

View file

@ -1,18 +1,20 @@
import 'package:flutter/material.dart';
import 'package:ficonsax/ficonsax.dart';
import 'package:flutter_riverpod/flutter_riverpod.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:fladder/screens/syncing/sync_widgets.dart';
import 'package:fladder/screens/syncing/widgets/sync_markedfordelete.dart';
import 'package:fladder/screens/syncing/widgets/sync_progress_builder.dart';
import 'package:fladder/screens/syncing/widgets/sync_status_overlay.dart';
import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/size_formatting.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SyncListItem extends ConsumerStatefulWidget {
final SyncedItem syncedItem;
@ -29,7 +31,7 @@ class SyncListItemState extends ConsumerState<SyncListItem> {
final baseItem = ref.read(syncProvider.notifier).getItem(syncedItem);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: SyncMarkedForDelete(
child: SyncStatusOverlay(
syncedItem: syncedItem,
child: Card(
elevation: 1,
@ -53,7 +55,7 @@ class SyncListItemState extends ConsumerState<SyncListItem> {
context.localized.deleteItem(baseItem?.detailedName(context) ?? ""),
context.localized.syncDeletePopupPermanent,
(context) async {
ref.read(syncProvider.notifier).removeSync(syncedItem);
ref.read(syncProvider.notifier).removeSync(context, syncedItem);
Navigator.of(context).pop();
return true;
},

View file

@ -1,5 +1,9 @@
import 'package:flutter/material.dart';
import 'package:background_downloader/background_downloader.dart';
import 'package:ficonsax/ficonsax.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/items/series_model.dart';
@ -10,8 +14,6 @@ import 'package:fladder/providers/sync/sync_provider_helpers.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SyncLabel extends ConsumerWidget {
final String? label;
@ -28,7 +30,7 @@ class SyncLabel extends ConsumerWidget {
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
child: Text(
label ?? status.label,
label ?? status.label(context),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold,
color: status.color,
@ -55,7 +57,7 @@ class SyncProgressBar extends ConsumerWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(downloadStatus.name),
Text(downloadStatus.name(context)),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
@ -82,7 +84,7 @@ class SyncProgressBar extends ConsumerWidget {
icon: const Icon(IconsaxBold.play),
),
IconButton(
onPressed: () => ref.read(syncProvider.notifier).deleteFullSyncFiles(item),
onPressed: () => ref.read(syncProvider.notifier).deleteFullSyncFiles(item, downloadTask),
icon: const Icon(IconsaxBold.stop),
)
],
@ -129,7 +131,7 @@ class SyncSubtitle extends ConsumerWidget {
);
},
),
_ => Text(syncStatus.label),
_ => Text(syncStatus.label(context)),
},
),
),

View file

@ -5,12 +5,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/syncing/sync_item.dart';
import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/localization_helper.dart';
///This is a wrapper widget for marking a synced item as deleted (while it is being deleted)
class SyncMarkedForDelete extends ConsumerWidget {
class SyncStatusOverlay extends ConsumerWidget {
final SyncedItem syncedItem;
final Widget child;
const SyncMarkedForDelete({required this.syncedItem, required this.child, super.key});
const SyncStatusOverlay({required this.syncedItem, required this.child, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -32,12 +32,35 @@ class SyncMarkedForDelete extends ConsumerWidget {
strokeCap: StrokeCap.round,
valueColor: AlwaysStoppedAnimation(Theme.of(context).colorScheme.error),
),
const Text("Deleting"),
Text(context.localized.syncOverlayDeleting),
const Icon(IconsaxOutline.trash)
].addPadding(const EdgeInsets.symmetric(horizontal: 16)),
),
),
)
),
if (syncedItem.syncing)
Positioned.fill(
child: IgnorePointer(
child: Card(
elevation: 0,
semanticContainer: false,
color: Colors.black.withOpacity(0.6),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircularProgressIndicator.adaptive(
strokeCap: StrokeCap.round,
valueColor: AlwaysStoppedAnimation(Theme.of(context).colorScheme.error),
),
Text(context.localized.syncOverlaySyncing),
const Icon(IconsaxOutline.cloud_notif)
].addPadding(const EdgeInsets.symmetric(horizontal: 16)),
),
),
),
),
],
);
}

View file

@ -1,4 +1,8 @@
import 'package:flutter/material.dart';
import 'package:ficonsax/ficonsax.dart';
import 'package:flutter_riverpod/flutter_riverpod.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';
@ -10,8 +14,6 @@ import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/size_formatting.dart';
import 'package:fladder/widgets/shared/icon_button_await.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SyncedEpisodeItem extends ConsumerStatefulWidget {
const SyncedEpisodeItem({
@ -106,7 +108,7 @@ class _SyncedEpisodeItemState extends ConsumerState<SyncedEpisodeItem> {
context.localized.syncRemoveDataTitle,
context.localized.syncRemoveDataDesc,
(context) async {
await ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem);
await ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem, downloadTask.task);
Navigator.pop(context);
},
context.localized.delete,

View file

@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "Fladder")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.fladder")
set(APPLICATION_ID "nl.jknaapen.fladder")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.

View file

@ -11,4 +11,4 @@ PRODUCT_NAME = Fladder
PRODUCT_BUNDLE_IDENTIFIER = nl.jknaapen.fladder
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved.
PRODUCT_COPYRIGHT = Copyright © 2023 Donutware. All rights reserved.

View file

@ -89,11 +89,11 @@ BEGIN
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "CompanyName", "DonutWare" "\0"
VALUE "FileDescription", "fladder" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "nl.jknaapen.fladder" "\0"
VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0"
VALUE "LegalCopyright", "Copyright (C) 2023 DonutWare. All rights reserved." "\0"
VALUE "OriginalFilename", "fladder.exe" "\0"
VALUE "ProductName", "fladder" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"