2020-04-18 18:46:38 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2023-01-06 04:50:25 +00:00
|
|
|
import 'package:logging/logging.dart';
|
2021-03-21 19:53:02 +00:00
|
|
|
import 'package:photos/core/configuration.dart';
|
2023-01-06 08:07:50 +00:00
|
|
|
import 'package:photos/core/constants.dart';
|
2020-05-06 17:16:30 +00:00
|
|
|
import 'package:photos/core/event_bus.dart';
|
2022-08-23 04:07:21 +00:00
|
|
|
import 'package:photos/db/device_files_db.dart';
|
2020-11-26 10:27:36 +00:00
|
|
|
import 'package:photos/db/files_db.dart';
|
2021-10-29 23:55:29 +00:00
|
|
|
import 'package:photos/events/files_updated_event.dart';
|
2020-05-06 17:16:30 +00:00
|
|
|
import 'package:photos/events/local_photos_updated_event.dart';
|
2023-04-07 05:06:43 +00:00
|
|
|
import "package:photos/generated/l10n.dart";
|
2022-09-06 16:49:02 +00:00
|
|
|
import 'package:photos/models/device_collection.dart';
|
2023-08-25 04:39:30 +00:00
|
|
|
import 'package:photos/models/file/file.dart';
|
2022-07-03 10:09:01 +00:00
|
|
|
import 'package:photos/models/gallery_type.dart';
|
2020-07-15 20:29:29 +00:00
|
|
|
import 'package:photos/models/selected_files.dart';
|
2023-01-06 08:07:50 +00:00
|
|
|
import 'package:photos/services/ignored_files_service.dart';
|
2022-09-09 09:34:09 +00:00
|
|
|
import 'package:photos/services/remote_sync_service.dart';
|
2023-01-06 04:50:25 +00:00
|
|
|
import 'package:photos/theme/ente_theme.dart';
|
|
|
|
import 'package:photos/ui/components/captioned_text_widget.dart';
|
2023-01-31 12:21:57 +00:00
|
|
|
import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart';
|
2023-01-06 04:50:25 +00:00
|
|
|
import 'package:photos/ui/components/menu_section_description_widget.dart';
|
|
|
|
import 'package:photos/ui/components/toggle_switch_widget.dart';
|
2022-12-16 09:59:48 +00:00
|
|
|
import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart';
|
2022-07-01 14:18:05 +00:00
|
|
|
import 'package:photos/ui/viewer/gallery/gallery.dart';
|
|
|
|
import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
|
2020-04-18 18:46:38 +00:00
|
|
|
|
2021-05-02 11:19:05 +00:00
|
|
|
class DeviceFolderPage extends StatelessWidget {
|
2022-08-31 18:39:39 +00:00
|
|
|
final DeviceCollection deviceCollection;
|
2020-07-15 20:29:29 +00:00
|
|
|
final _selectedFiles = SelectedFiles();
|
2020-04-18 18:46:38 +00:00
|
|
|
|
2022-12-30 12:10:17 +00:00
|
|
|
DeviceFolderPage(this.deviceCollection, {Key? key}) : super(key: key);
|
2021-05-02 11:19:05 +00:00
|
|
|
|
2020-04-18 18:46:38 +00:00
|
|
|
@override
|
|
|
|
Widget build(Object context) {
|
2023-01-30 06:19:58 +00:00
|
|
|
final int? userID = Configuration.instance.getUserID();
|
2021-03-21 19:53:02 +00:00
|
|
|
final gallery = Gallery(
|
2021-05-08 17:49:06 +00:00
|
|
|
asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) {
|
2022-08-31 18:39:39 +00:00
|
|
|
return FilesDB.instance.getFilesInDeviceCollection(
|
|
|
|
deviceCollection,
|
2023-01-30 06:19:58 +00:00
|
|
|
userID,
|
2022-06-11 08:23:52 +00:00
|
|
|
creationStartTime,
|
|
|
|
creationEndTime,
|
|
|
|
limit: limit,
|
|
|
|
asc: asc,
|
|
|
|
);
|
2021-04-20 20:11:39 +00:00
|
|
|
},
|
2020-07-15 17:17:53 +00:00
|
|
|
reloadEvent: Bus.instance.on<LocalPhotosUpdatedEvent>(),
|
2021-10-29 23:55:29 +00:00
|
|
|
removalEventTypes: const {
|
2021-10-29 23:56:27 +00:00
|
|
|
EventType.deletedFromDevice,
|
|
|
|
EventType.deletedFromEverywhere,
|
2022-11-03 08:09:05 +00:00
|
|
|
EventType.hide,
|
2021-10-29 23:55:29 +00:00
|
|
|
},
|
2022-08-31 18:39:39 +00:00
|
|
|
tagPrefix: "device_folder:" + deviceCollection.name,
|
2020-07-15 20:29:29 +00:00
|
|
|
selectedFiles: _selectedFiles,
|
2022-07-03 09:49:33 +00:00
|
|
|
header: Configuration.instance.hasConfiguredAccount()
|
2023-01-06 04:50:25 +00:00
|
|
|
? BackupHeaderWidget(deviceCollection)
|
2022-08-23 12:23:17 +00:00
|
|
|
: const SizedBox.shrink(),
|
2022-12-30 18:24:25 +00:00
|
|
|
initialFiles: [deviceCollection.thumbnail!],
|
2020-07-15 17:17:53 +00:00
|
|
|
);
|
2020-04-18 18:46:38 +00:00
|
|
|
return Scaffold(
|
2021-10-12 07:24:18 +00:00
|
|
|
appBar: PreferredSize(
|
2022-07-04 06:02:17 +00:00
|
|
|
preferredSize: const Size.fromHeight(50.0),
|
2021-10-12 07:24:18 +00:00
|
|
|
child: GalleryAppBarWidget(
|
2022-07-03 07:47:15 +00:00
|
|
|
GalleryType.localFolder,
|
2022-08-31 18:39:39 +00:00
|
|
|
deviceCollection.name,
|
2021-10-12 07:24:18 +00:00
|
|
|
_selectedFiles,
|
2022-08-31 18:39:39 +00:00
|
|
|
deviceCollection: deviceCollection,
|
2021-10-12 07:24:18 +00:00
|
|
|
),
|
2020-04-18 18:46:38 +00:00
|
|
|
),
|
2022-05-04 11:08:06 +00:00
|
|
|
body: Stack(
|
|
|
|
alignment: Alignment.bottomCenter,
|
|
|
|
children: [
|
|
|
|
gallery,
|
2022-12-16 09:59:48 +00:00
|
|
|
FileSelectionOverlayBar(
|
2022-07-03 07:47:15 +00:00
|
|
|
GalleryType.localFolder,
|
2022-05-04 11:08:06 +00:00
|
|
|
_selectedFiles,
|
2023-08-19 11:39:56 +00:00
|
|
|
),
|
2022-05-04 11:08:06 +00:00
|
|
|
],
|
|
|
|
),
|
2020-04-18 18:46:38 +00:00
|
|
|
);
|
|
|
|
}
|
2021-03-25 16:58:30 +00:00
|
|
|
}
|
|
|
|
|
2023-01-06 11:27:53 +00:00
|
|
|
class BackupHeaderWidget extends StatefulWidget {
|
2022-08-31 18:39:39 +00:00
|
|
|
final DeviceCollection deviceCollection;
|
2021-03-25 16:58:30 +00:00
|
|
|
|
2023-01-06 04:50:25 +00:00
|
|
|
const BackupHeaderWidget(this.deviceCollection, {super.key});
|
2022-08-23 12:23:17 +00:00
|
|
|
|
2023-01-06 11:27:53 +00:00
|
|
|
@override
|
|
|
|
State<BackupHeaderWidget> createState() => _BackupHeaderWidgetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _BackupHeaderWidgetState extends State<BackupHeaderWidget> {
|
2023-08-24 16:56:24 +00:00
|
|
|
late Future<List<EnteFile>> filesInDeviceCollection;
|
2023-01-06 11:50:24 +00:00
|
|
|
late ValueNotifier<bool> shouldBackup;
|
2023-05-21 18:51:54 +00:00
|
|
|
final Logger _logger = Logger("_BackupHeaderWidgetState");
|
2023-01-06 11:50:24 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
shouldBackup = ValueNotifier(widget.deviceCollection.shouldBackup);
|
|
|
|
|
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
|
2021-03-25 16:58:30 +00:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2023-01-06 11:50:24 +00:00
|
|
|
filesInDeviceCollection = _filesInDeviceCollection();
|
|
|
|
|
2023-01-06 04:50:25 +00:00
|
|
|
final colorScheme = getEnteColorScheme(context);
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 20),
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
2021-03-21 19:53:02 +00:00
|
|
|
children: [
|
2023-01-06 04:50:25 +00:00
|
|
|
Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
MenuItemWidget(
|
2023-04-07 05:06:43 +00:00
|
|
|
captionedTextWidget:
|
|
|
|
CaptionedTextWidget(title: S.of(context).backup),
|
2023-02-02 02:26:41 +00:00
|
|
|
singleBorderRadius: 8.0,
|
2023-01-06 04:50:25 +00:00
|
|
|
menuItemColor: colorScheme.fillFaint,
|
|
|
|
alignCaptionedTextToLeft: true,
|
|
|
|
trailingWidget: ToggleSwitchWidget(
|
2023-01-06 05:54:34 +00:00
|
|
|
value: () => shouldBackup.value,
|
2023-01-06 04:50:25 +00:00
|
|
|
onChanged: () async {
|
2023-05-21 18:51:54 +00:00
|
|
|
_logger.fine(
|
|
|
|
"Toggling device folder sync status to "
|
|
|
|
"${!shouldBackup.value}",
|
|
|
|
);
|
2023-05-21 18:55:03 +00:00
|
|
|
try {
|
|
|
|
await RemoteSyncService.instance
|
|
|
|
.updateDeviceFolderSyncStatus(
|
|
|
|
{widget.deviceCollection.id: !shouldBackup.value},
|
|
|
|
);
|
|
|
|
if (mounted) {
|
2023-01-06 11:50:24 +00:00
|
|
|
setState(() {
|
|
|
|
shouldBackup.value = !shouldBackup.value;
|
|
|
|
});
|
2023-05-21 18:55:03 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
_logger.severe(
|
|
|
|
"Could not update device folder sync status",
|
|
|
|
e,
|
|
|
|
);
|
|
|
|
}
|
2023-01-06 04:50:25 +00:00
|
|
|
},
|
2021-03-21 19:53:02 +00:00
|
|
|
),
|
2023-01-06 04:50:25 +00:00
|
|
|
),
|
2023-01-06 05:54:34 +00:00
|
|
|
ValueListenableBuilder(
|
|
|
|
valueListenable: shouldBackup,
|
|
|
|
builder: (BuildContext context, bool value, _) {
|
|
|
|
return MenuSectionDescriptionWidget(
|
|
|
|
content: value
|
2023-04-07 05:06:43 +00:00
|
|
|
? S.of(context).deviceFilesAutoUploading
|
|
|
|
: S.of(context).turnOnBackupForAutoUpload,
|
2023-01-06 05:54:34 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
2023-01-06 09:50:22 +00:00
|
|
|
FutureBuilder(
|
|
|
|
future: _hasIgnoredFiles(filesInDeviceCollection),
|
|
|
|
builder: (context, snapshot) {
|
2023-01-06 14:52:34 +00:00
|
|
|
bool shouldShowReset = false;
|
2023-01-06 11:50:24 +00:00
|
|
|
if (snapshot.hasData &&
|
|
|
|
snapshot.data as bool &&
|
|
|
|
shouldBackup.value) {
|
2023-01-06 14:52:34 +00:00
|
|
|
shouldShowReset = true;
|
2023-01-06 11:21:46 +00:00
|
|
|
} else if (snapshot.hasError) {
|
|
|
|
Logger("BackupHeaderWidget").severe(
|
|
|
|
"Could not check if collection has ignored files",
|
|
|
|
);
|
2023-01-06 09:50:22 +00:00
|
|
|
}
|
2023-01-06 14:52:34 +00:00
|
|
|
return AnimatedCrossFade(
|
|
|
|
firstCurve: Curves.easeInOutExpo,
|
|
|
|
secondCurve: Curves.easeInOutExpo,
|
|
|
|
sizeCurve: Curves.easeInOutExpo,
|
2023-01-06 15:04:20 +00:00
|
|
|
firstChild: ResetIgnoredFilesWidget(
|
|
|
|
filesInDeviceCollection,
|
|
|
|
() => setState(() {}),
|
|
|
|
),
|
2023-01-06 14:52:34 +00:00
|
|
|
secondChild: const SizedBox(width: double.infinity),
|
|
|
|
crossFadeState: shouldShowReset
|
|
|
|
? CrossFadeState.showFirst
|
|
|
|
: CrossFadeState.showSecond,
|
|
|
|
duration: const Duration(milliseconds: 1000),
|
2023-01-06 11:21:46 +00:00
|
|
|
);
|
2023-01-06 08:07:50 +00:00
|
|
|
},
|
2023-08-19 11:39:56 +00:00
|
|
|
),
|
2023-01-06 04:50:25 +00:00
|
|
|
],
|
2021-03-21 19:53:02 +00:00
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2023-01-06 08:07:50 +00:00
|
|
|
|
2023-08-24 16:56:24 +00:00
|
|
|
Future<List<EnteFile>> _filesInDeviceCollection() async {
|
2023-01-06 15:04:20 +00:00
|
|
|
return (await FilesDB.instance.getFilesInDeviceCollection(
|
|
|
|
widget.deviceCollection,
|
2023-01-30 06:19:58 +00:00
|
|
|
Configuration.instance.getUserID(),
|
2023-01-06 15:04:20 +00:00
|
|
|
galleryLoadStartTime,
|
|
|
|
galleryLoadEndTime,
|
|
|
|
))
|
|
|
|
.files;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> _hasIgnoredFiles(
|
2023-08-24 16:56:24 +00:00
|
|
|
Future<List<EnteFile>> filesInDeviceCollection,
|
2023-01-07 12:14:45 +00:00
|
|
|
) async {
|
2023-08-24 16:56:24 +00:00
|
|
|
final List<EnteFile> deviceCollectionFiles = await filesInDeviceCollection;
|
2023-08-23 08:56:06 +00:00
|
|
|
final allIgnoredIDs =
|
|
|
|
await IgnoredFilesService.instance.idToIgnoreReasonMap;
|
2023-08-23 08:32:00 +00:00
|
|
|
if (allIgnoredIDs.isEmpty) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-08-24 16:56:24 +00:00
|
|
|
for (EnteFile file in deviceCollectionFiles) {
|
2023-01-09 08:28:43 +00:00
|
|
|
final String? ignoreID =
|
|
|
|
IgnoredFilesService.instance.getIgnoredIDForFile(file);
|
2023-08-28 05:47:17 +00:00
|
|
|
if (ignoreID != null && allIgnoredIDs.containsKey(ignoreID)) {
|
2023-08-23 08:32:00 +00:00
|
|
|
return true;
|
2023-01-09 08:28:43 +00:00
|
|
|
}
|
2023-01-06 15:04:20 +00:00
|
|
|
}
|
2023-08-23 08:32:00 +00:00
|
|
|
return false;
|
2023-01-06 15:04:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ResetIgnoredFilesWidget extends StatefulWidget {
|
2023-08-24 16:56:24 +00:00
|
|
|
final Future<List<EnteFile>> filesInDeviceCollection;
|
2023-01-06 15:04:20 +00:00
|
|
|
final VoidCallback parentSetState;
|
|
|
|
const ResetIgnoredFilesWidget(
|
2023-01-07 12:14:45 +00:00
|
|
|
this.filesInDeviceCollection,
|
|
|
|
this.parentSetState, {
|
|
|
|
super.key,
|
|
|
|
});
|
2023-01-06 15:04:20 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
State<ResetIgnoredFilesWidget> createState() =>
|
|
|
|
_ResetIgnoredFilesWidgetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ResetIgnoredFilesWidgetState extends State<ResetIgnoredFilesWidget> {
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2023-01-06 14:52:34 +00:00
|
|
|
return Column(
|
|
|
|
children: [
|
|
|
|
const SizedBox(height: 24),
|
|
|
|
MenuItemWidget(
|
2023-04-07 05:06:43 +00:00
|
|
|
captionedTextWidget: CaptionedTextWidget(
|
|
|
|
title: S.of(context).resetIgnoredFiles,
|
2023-01-06 14:52:34 +00:00
|
|
|
),
|
2023-02-02 02:26:41 +00:00
|
|
|
singleBorderRadius: 8.0,
|
2023-01-06 14:52:34 +00:00
|
|
|
menuItemColor: getEnteColorScheme(context).fillFaint,
|
|
|
|
leadingIcon: Icons.cloud_off_outlined,
|
2023-02-01 08:31:24 +00:00
|
|
|
alwaysShowSuccessState: true,
|
2023-01-06 14:52:34 +00:00
|
|
|
onTap: () async {
|
|
|
|
await _removeFilesFromIgnoredFiles(
|
2023-01-06 15:04:20 +00:00
|
|
|
widget.filesInDeviceCollection,
|
2023-01-06 14:52:34 +00:00
|
|
|
);
|
2023-12-21 07:34:06 +00:00
|
|
|
// ignore: unawaited_futures
|
2023-01-06 14:52:34 +00:00
|
|
|
RemoteSyncService.instance.sync(silently: true).then((value) {
|
2023-08-23 08:56:06 +00:00
|
|
|
if (mounted) {
|
2023-08-21 05:50:07 +00:00
|
|
|
widget.parentSetState.call();
|
|
|
|
}
|
2023-01-06 14:52:34 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
2023-04-07 05:06:43 +00:00
|
|
|
MenuSectionDescriptionWidget(
|
|
|
|
content: S.of(context).ignoredFolderUploadReason,
|
2023-01-06 14:52:34 +00:00
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-01-06 09:50:22 +00:00
|
|
|
Future<void> _removeFilesFromIgnoredFiles(
|
2023-08-24 16:56:24 +00:00
|
|
|
Future<List<EnteFile>> filesInDeviceCollection,
|
2023-01-06 09:50:22 +00:00
|
|
|
) async {
|
2023-08-24 16:56:24 +00:00
|
|
|
final List<EnteFile> deviceCollectionFiles = await filesInDeviceCollection;
|
2023-01-06 08:07:50 +00:00
|
|
|
await IgnoredFilesService.instance
|
2023-01-06 09:50:22 +00:00
|
|
|
.removeIgnoredMappings(deviceCollectionFiles);
|
2023-01-06 08:07:50 +00:00
|
|
|
}
|
2020-04-18 18:46:38 +00:00
|
|
|
}
|