Support for editing date of memory

This commit is contained in:
Neeraj Gupta 2021-10-26 16:07:14 +05:30
parent eff885f696
commit aa145e0217
No known key found for this signature in database
GPG key ID: 3C5A1684DC1729E1
11 changed files with 192 additions and 37 deletions

View file

@ -1003,6 +1003,14 @@ class FilesDB {
row[columnMMdEncodedJson] = file.mMdEncodedJson ?? '{}';
row[columnMMdVisibility] =
file.magicMetadata?.visibility ?? kVisibilityVisible;
row[columnPubMMdVersion] = file.pubMmdVersion ?? 0;
row[columnPubMMdEncodedJson] = file.pubMmdEncodedJson ?? '{}';
if (file.pubMagicMetadata != null &&
file.pubMagicMetadata.editedTime != null) {
// override existing creationTime to avoid re-writing all queries related
// to loading the gallery
row[columnCreationTime] = file.pubMagicMetadata.editedTime;
}
return row;
}
@ -1036,7 +1044,7 @@ class FilesDB {
file.magicMetadata?.visibility ?? kVisibilityVisible;
row[columnPubMMdVersion] = file.pubMmdVersion ?? 0;
row[columnPubMMdEncodedJson] == file.pubMmdEncodedJson ?? '{}';
row[columnPubMMdEncodedJson] = file.pubMmdEncodedJson ?? '{}';
if (file.pubMagicMetadata != null &&
file.pubMagicMetadata.editedTime != null) {
// override existing creationTime to avoid re-writing all queries related

View file

@ -48,7 +48,7 @@ class File {
int pubMmdVersion = 0;
PubMagicMetadata _pubMmd;
PubMagicMetadata get pubMagicMetadata =>
_pubMmd ?? MagicMetadata.fromEncodedJson(pubMmdEncodedJson ?? '{}');
_pubMmd ?? PubMagicMetadata.fromEncodedJson(pubMmdEncodedJson ?? '{}');
set pubMagicMetadata(val) => _pubMmd = val;
static const kCurrentMetadataVersion = 1;

View file

@ -6,7 +6,7 @@ const kVisibilityArchive = 1;
const kMagicKeyVisibility = 'visibility';
const kPubMagicKeyEditedTime = 'et';
const kPubMagicKeyEditedTime = 'editedTime';
class MagicMetadata {
// 0 -> visible

View file

@ -30,8 +30,7 @@ class FileMagicService {
FileMagicService._privateConstructor();
Future<void> changeVisibility(List<File> files, int visibility) async {
Map<String, dynamic> update = {};
update[kMagicKeyVisibility] = visibility;
Map<String, dynamic> update = {kMagicKeyVisibility: visibility};
await _updateMagicData(files, update);
// h4ck: Remove archived elements from the UI. If this was an archival,
// ArchivePage will reload the new items anyway
@ -42,6 +41,66 @@ class FileMagicService {
}
}
Future<void> updatePublicMagicMetadata(List<File> files, Map<String, dynamic> newMetadataUpdate) async
{
final params = <String, dynamic>{};
params['metadataList'] = [];
final int ownerID = Configuration.instance.getUserID();
try {
for (final file in files) {
if (file.uploadedFileID == null) {
throw AssertionError(
"operation is only supported on backed up files");
} else if (file.ownerID != ownerID) {
throw AssertionError("cannot modify memories not owned by you");
}
// read the existing magic metadata and apply new updates to existing data
// current update is simple replace. This will be enhanced in the future,
// as required.
Map<String, dynamic> jsonToUpdate = jsonDecode(file.pubMmdEncodedJson);
newMetadataUpdate.forEach((key, value) {
jsonToUpdate[key] = value;
});
// update the local information so that it's reflected on UI
file.pubMmdEncodedJson = jsonEncode(jsonToUpdate);
file.pubMagicMetadata = PubMagicMetadata.fromJson(jsonToUpdate);
final fileKey = decryptFileKey(file);
final encryptedMMd = await CryptoUtil.encryptChaCha(
utf8.encode(jsonEncode(jsonToUpdate)), fileKey);
params['metadataList'].add(UpdateMagicMetadataRequest(
id: file.uploadedFileID,
magicMetadata: MetadataRequest(
version: file.pubMmdVersion,
count: jsonToUpdate.length,
data: Sodium.bin2base64(encryptedMMd.encryptedData),
header: Sodium.bin2base64(encryptedMMd.header),
)));
file.pubMmdVersion = file.pubMmdVersion + 1;
}
await _dio.put(
Configuration.instance.getHttpEndpoint() + "/files/public-magic-metadata",
data: params,
options: Options(
headers: {"X-Auth-Token": Configuration.instance.getToken()}),
);
// update the state of the selected file. Same file in other collection
// should be eventually synced after remote sync has completed
await _filesDB.insertMultiple(files);
RemoteSyncService.instance.sync(silently: true);
} on DioError catch (e) {
if (e.response != null && e.response.statusCode == 409) {
RemoteSyncService.instance.sync(silently: true);
}
rethrow;
} catch (e, s) {
_logger.severe("failed to sync magic metadata", e, s);
rethrow;
}
}
Future<void> _updateMagicData(
List<File> files, Map<String, dynamic> newMetadataUpdate) async {
final params = <String, dynamic>{};

View file

@ -7,6 +7,7 @@ import 'package:like_button/like_button.dart';
import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:photos/core/event_bus.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/events/local_photos_updated_event.dart';
import 'package:photos/models/file.dart';
@ -20,6 +21,7 @@ import 'package:photos/utils/date_time_util.dart';
import 'package:photos/utils/delete_file_util.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/file_util.dart';
import 'package:photos/utils/magic_util.dart';
import 'package:photos/utils/toast_util.dart';
class FadingAppBar extends StatefulWidget implements PreferredSizeWidget {
@ -118,7 +120,7 @@ class FadingAppBarState extends State<FadingAppBar> {
),
);
}
// only show delete option for files owned by the user
// options for files owned by the user
if (widget.file.ownerID == null ||
widget.file.ownerID == widget.userID) {
items.add(
@ -137,6 +139,24 @@ class FadingAppBarState extends State<FadingAppBar> {
),
),
);
if(widget.file.uploadedFileID != null) {
items.add(
PopupMenuItem(
value: 3,
child: Row(
children: [
Icon(Platform.isAndroid
? Icons.access_time_rounded
: CupertinoIcons.time),
Padding(
padding: EdgeInsets.all(8),
),
Text("edit date"),
],
),
),
);
}
}
return items;
},
@ -145,6 +165,8 @@ class FadingAppBarState extends State<FadingAppBar> {
_download(widget.file);
} else if (value == 2) {
_showDeleteSheet(widget.file);
} else if(value == 3) {
_showDatePicker(widget.file);
}
},
));
@ -219,6 +241,22 @@ class FadingAppBarState extends State<FadingAppBar> {
);
}
void _showDatePicker(File file) {
DatePicker.showDatePicker(context,
showTitleActions: true,
minTime: DateTime(1900, 1, 1),
maxTime: DateTime.now().add(Duration(days: 1)),
onConfirm: (date) async {
if (await editTime(
context, List.of([widget.file]), date.microsecondsSinceEpoch)) {
widget.file.creationTime = date.microsecondsSinceEpoch;
setState(() {});
}
},
currentTime: DateTime.fromMicrosecondsSinceEpoch(file.creationTime),
locale: LocaleType.en);
}
void _showDeleteSheet(File file) {
final List<Widget> actions = [];
if (file.uploadedFileID == null) {

View file

@ -11,7 +11,7 @@ import 'package:photos/models/selected_files.dart';
import 'package:photos/models/trash_file.dart';
import 'package:photos/ui/create_collection_page.dart';
import 'package:photos/ui/file_info_dialog.dart';
import 'package:photos/utils/archive_util.dart';
import 'package:photos/utils/magic_util.dart';
import 'package:photos/utils/delete_file_util.dart';
import 'package:photos/utils/share_util.dart';

View file

@ -15,7 +15,7 @@ import 'package:photos/services/collections_service.dart';
import 'package:photos/ui/change_collection_name_dialog.dart';
import 'package:photos/ui/create_collection_page.dart';
import 'package:photos/ui/share_collection_widget.dart';
import 'package:photos/utils/archive_util.dart';
import 'package:photos/utils/magic_util.dart';
import 'package:photos/utils/delete_file_util.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/share_util.dart';

View file

@ -1,29 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:logging/logging.dart';
import 'package:photos/models/file.dart';
import 'package:photos/models/magic_metadata.dart';
import 'package:photos/services/file_magic_service.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/toast_util.dart';
Future<void> changeVisibility(
BuildContext context, List<File> files, int newVisibility) async {
final dialog = createProgressDialog(context,
newVisibility == kVisibilityArchive ? "archiving..." : "unarchiving...");
await dialog.show();
try {
await FileMagicService.instance.changeVisibility(files, newVisibility);
showToast(
newVisibility == kVisibilityArchive
? "successfully archived"
: "successfully unarchived",
toastLength: Toast.LENGTH_SHORT);
await dialog.hide();
} catch (e, s) {
Logger("ArchiveUtil").severe("failed to update file visibility", e, s);
await dialog.hide();
rethrow;
}
}

71
lib/utils/magic_util.dart Normal file
View file

@ -0,0 +1,71 @@
import 'package:flutter/widgets.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:logging/logging.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/events/force_reload_home_gallery_event.dart';
import 'package:photos/models/file.dart';
import 'package:photos/models/magic_metadata.dart';
import 'package:photos/services/file_magic_service.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/toast_util.dart';
final _logger = Logger('MagicUtil');
Future<void> changeVisibility(
BuildContext context, List<File> files, int newVisibility) async {
final dialog = createProgressDialog(context,
newVisibility == kVisibilityArchive ? "archiving..." : "unarchiving...");
await dialog.show();
try {
await FileMagicService.instance.changeVisibility(files, newVisibility);
showToast(
newVisibility == kVisibilityArchive
? "successfully archived"
: "successfully unarchived",
toastLength: Toast.LENGTH_SHORT);
await dialog.hide();
} catch (e, s) {
_logger.severe("failed to update file visibility", e, s);
await dialog.hide();
rethrow;
}
}
Future<bool> editTime(
BuildContext context, List<File> files, int editedTime) async {
try {
await _updatePublicMetadata(
context, files, kPubMagicKeyEditedTime, editedTime);
return true;
} catch (e, s) {
showToast('something went wrong');
return false;
}
}
Future<void> _updatePublicMetadata(
BuildContext context, List<File> files, String key, dynamic value) async {
if (files.isEmpty) {
return;
}
final dialog = createProgressDialog(context, 'please wait');
await dialog.show();
try {
Map<String, dynamic> update = {key: value};
await FileMagicService.instance.updatePublicMagicMetadata(files, update);
showToast('done', toastLength: Toast.LENGTH_SHORT);
await dialog.hide();
if (_shouldReloadGallery(key)) {
Bus.instance.fire(ForceReloadHomeGalleryEvent());
}
} catch (e, s) {
_logger.severe("failed to update $key = $value", e, s);
await dialog.hide();
rethrow;
}
}
bool _shouldReloadGallery(String key) {
return key == kPubMagicKeyEditedTime;
}

View file

@ -316,6 +316,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
flutter_datetime_picker:
dependency: "direct main"
description:
name: flutter_datetime_picker
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.1"
flutter_easyloading:
dependency: "direct main"
description:

View file

@ -43,6 +43,7 @@ dependencies:
flutter_localizations:
sdk: flutter
flutter_cache_manager: ^3.0.1
flutter_datetime_picker: ^1.5.1
flutter_easyloading: ^3.0.0
flutter_email_sender: ^5.0.2
flutter_image_compress: