2021-10-29 04:09:38 +00:00
|
|
|
import 'dart:async';
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
import 'package:photos/db/ignored_files_db.dart';
|
2021-10-29 04:19:42 +00:00
|
|
|
import 'package:photos/models/file.dart';
|
2021-10-29 04:09:38 +00:00
|
|
|
import 'package:photos/models/ignored_file.dart';
|
|
|
|
|
|
|
|
class IgnoredFilesService {
|
|
|
|
final _logger = Logger("IgnoredFilesService");
|
|
|
|
final _db = IgnoredFilesDB.instance;
|
|
|
|
|
|
|
|
IgnoredFilesService._privateConstructor();
|
|
|
|
|
|
|
|
static final IgnoredFilesService instance =
|
|
|
|
IgnoredFilesService._privateConstructor();
|
|
|
|
|
2022-12-30 06:50:35 +00:00
|
|
|
Future<Set<String>>? _ignoredIDs;
|
2021-10-29 04:09:38 +00:00
|
|
|
|
|
|
|
Future<Set<String>> get ignoredIDs async {
|
|
|
|
// lazily instantiate the db the first time it is accessed
|
|
|
|
_ignoredIDs ??= _loadExistingIDs();
|
2022-12-30 06:50:35 +00:00
|
|
|
return _ignoredIDs!;
|
2021-10-29 04:09:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> cacheAndInsert(List<IgnoredFile> ignoredFiles) async {
|
|
|
|
final existingIDs = await ignoredIDs;
|
2022-12-30 06:50:35 +00:00
|
|
|
for (IgnoredFile iFile in ignoredFiles) {
|
|
|
|
final id = _idForIgnoredFile(iFile);
|
|
|
|
if (id != null) {
|
|
|
|
existingIDs.add(id);
|
|
|
|
}
|
|
|
|
}
|
2021-10-29 04:09:38 +00:00
|
|
|
return _db.insertMultiple(ignoredFiles);
|
|
|
|
}
|
|
|
|
|
2021-10-29 04:19:42 +00:00
|
|
|
// shouldSkipUpload takes IDs to ignore and file for which it will return
|
|
|
|
// whether either true or false. This helper method takes ignoredIDs as input
|
|
|
|
// to avoid making it async in nature.
|
2021-10-29 06:32:58 +00:00
|
|
|
// This syntax is intentional as we want to ensure that ignoredIDs are loaded
|
|
|
|
// from the DB before calling this method.
|
2021-10-29 04:19:42 +00:00
|
|
|
bool shouldSkipUpload(Set<String> ignoredIDs, File file) {
|
|
|
|
final id = _getIgnoreID(file.localID, file.deviceFolder, file.title);
|
|
|
|
if (id != null && id.isNotEmpty) {
|
|
|
|
return ignoredIDs.contains(id);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-08-30 05:18:34 +00:00
|
|
|
// removeIgnoredMappings is used to remove the ignore mapping for the given
|
|
|
|
// set of files so that they can be uploaded.
|
2022-08-05 15:53:28 +00:00
|
|
|
Future<void> removeIgnoredMappings(List<File> files) async {
|
2022-08-29 18:05:13 +00:00
|
|
|
final List<IgnoredFile> ignoredFiles = [];
|
|
|
|
final Set<String> idsToRemoveFromCache = {};
|
2022-08-30 05:18:34 +00:00
|
|
|
final Set<String> currentlyIgnoredIDs = await ignoredIDs;
|
|
|
|
for (final file in files) {
|
|
|
|
// check if upload is not skipped for file. If not, no need to remove
|
|
|
|
// any mapping
|
|
|
|
if (!shouldSkipUpload(currentlyIgnoredIDs, file)) {
|
|
|
|
continue;
|
2022-08-05 15:53:28 +00:00
|
|
|
}
|
2022-12-30 06:50:35 +00:00
|
|
|
// _shouldSkipUpload checks for null ignoreID
|
|
|
|
final id = _getIgnoreID(file.localID, file.deviceFolder, file.title)!;
|
2022-08-30 05:18:34 +00:00
|
|
|
idsToRemoveFromCache.add(id);
|
|
|
|
ignoredFiles.add(
|
|
|
|
IgnoredFile(file.localID, file.title, file.deviceFolder, ""),
|
|
|
|
);
|
2022-08-05 15:53:28 +00:00
|
|
|
}
|
2022-08-30 05:18:34 +00:00
|
|
|
|
2022-08-05 15:53:28 +00:00
|
|
|
if (ignoredFiles.isNotEmpty) {
|
|
|
|
await _db.removeIgnoredEntries(ignoredFiles);
|
2022-08-30 05:18:34 +00:00
|
|
|
currentlyIgnoredIDs.removeAll(idsToRemoveFromCache);
|
2022-08-05 15:53:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 08:33:37 +00:00
|
|
|
Future<void> reset() async {
|
|
|
|
await _db.clearTable();
|
|
|
|
_ignoredIDs = null;
|
|
|
|
}
|
|
|
|
|
2021-10-29 04:09:38 +00:00
|
|
|
Future<Set<String>> _loadExistingIDs() async {
|
2021-10-29 04:57:52 +00:00
|
|
|
_logger.fine('loading existing IDs');
|
2022-12-30 06:50:35 +00:00
|
|
|
final dbResult = await _db.getAll();
|
|
|
|
final Set<String> result = <String>{};
|
|
|
|
for (IgnoredFile iFile in dbResult) {
|
|
|
|
final id = _idForIgnoredFile(iFile);
|
|
|
|
if (id != null) {
|
|
|
|
result.add(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2021-10-29 04:09:38 +00:00
|
|
|
}
|
|
|
|
|
2022-12-30 06:50:35 +00:00
|
|
|
String? _idForIgnoredFile(IgnoredFile ignoredFile) {
|
2021-10-29 04:19:42 +00:00
|
|
|
return _getIgnoreID(
|
2022-06-11 08:23:52 +00:00
|
|
|
ignoredFile.localID,
|
|
|
|
ignoredFile.deviceFolder,
|
|
|
|
ignoredFile.title,
|
|
|
|
);
|
2021-10-29 04:09:38 +00:00
|
|
|
}
|
|
|
|
|
2022-08-30 05:18:34 +00:00
|
|
|
// _getIgnoreID will return null if don't have sufficient information
|
2021-10-29 06:32:58 +00:00
|
|
|
// to ignore the file based on the platform. Uploads from web or files shared to
|
|
|
|
// end usually don't have local id.
|
2021-10-29 04:19:42 +00:00
|
|
|
// For Android: It returns deviceFolder-title as ID for Android.
|
|
|
|
// For iOS, it returns localID as localID is uuid and the title or deviceFolder (aka
|
|
|
|
// album name) can be missing due to various reasons.
|
2022-12-30 06:50:35 +00:00
|
|
|
String? _getIgnoreID(String? localID, String? deviceFolder, String? title) {
|
2021-10-29 04:09:38 +00:00
|
|
|
// file was not uploaded from mobile device
|
|
|
|
if (localID == null || localID.isEmpty) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (Platform.isAndroid) {
|
|
|
|
if (deviceFolder == null || title == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return '$deviceFolder-$title';
|
|
|
|
} else {
|
|
|
|
return localID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|