Add create album flow

This commit is contained in:
Vishnu Mohandas 2020-10-28 17:33:28 +05:30
parent 585079b380
commit 673e5e9442
9 changed files with 221 additions and 24 deletions

View file

@ -0,0 +1,7 @@
import 'package:photos/events/event.dart';
class CollectionUpdatedEvent extends Event {
final int collectionID;
CollectionUpdatedEvent(this.collectionID);
}

View file

@ -1,5 +1,3 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:photos/models/file.dart';

View file

@ -7,7 +7,9 @@ import 'package:flutter_sodium/flutter_sodium.dart';
import 'package:logging/logging.dart';
import 'package:photos/core/configuration.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/db/collections_db.dart';
import 'package:photos/events/collection_updated_event.dart';
import 'package:photos/models/collection.dart';
import 'package:photos/models/collection_file_item.dart';
import 'package:photos/models/file.dart';
@ -178,6 +180,22 @@ class CollectionsService {
return _collectionIDToOwnedCollections[collectionID];
}
Future<Collection> createAlbum(String albumName) async {
final key = CryptoUtil.generateKey();
final encryptedKeyData = CryptoUtil.encryptSync(key, _config.getKey());
final collection = await createAndCacheCollection(Collection(
null,
null,
Sodium.bin2base64(encryptedKeyData.encryptedData),
Sodium.bin2base64(encryptedKeyData.nonce),
albumName,
CollectionType.album,
CollectionAttributes(),
null,
));
return collection;
}
Future<Collection> getOrCreateForPath(String path) async {
if (_localCollections.containsKey(path)) {
return _localCollections[path];
@ -217,12 +235,15 @@ class CollectionsService {
Sodium.bin2base64(encryptedKeyData.nonce),
).toMap());
}
return Dio().post(
Configuration.instance.getHttpEndpoint() + "/collections/add-files",
data: params,
options:
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
);
return Dio()
.post(
Configuration.instance.getHttpEndpoint() + "/collections/add-files",
data: params,
options: Options(
headers: {"X-Auth-Token": Configuration.instance.getToken()}),
)
.then(
(value) => Bus.instance.fire(CollectionUpdatedEvent(collectionID)));
}
Future<void> removeFromCollection(int collectionID, List<File> files) {
@ -234,12 +255,17 @@ class CollectionsService {
}
params["fileIDs"].add(file.uploadedFileID);
}
return Dio().post(
Configuration.instance.getHttpEndpoint() + "/collections/remove-files",
data: params,
options:
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
);
return Dio()
.post(
Configuration.instance.getHttpEndpoint() +
"/collections/remove-files",
data: params,
options: Options(
headers: {"X-Auth-Token": Configuration.instance.getToken()}),
)
.then(
(value) => Bus.instance.fire(CollectionUpdatedEvent(collectionID)));
;
}
Future<Collection> createAndCacheCollection(Collection collection) async {

View file

@ -1,8 +1,6 @@
import 'package:flutter_sodium/flutter_sodium.dart';
import 'package:photos/core/configuration.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/events/local_photos_updated_event.dart';
import 'package:photos/models/collection.dart';
import 'package:photos/models/file.dart';
import 'package:photos/services/collections_service.dart';
@ -55,7 +53,6 @@ class FavoritesService {
} else {
await _collectionsService.addToCollection(collectionID, [file]);
_cachedFavoriteFiles.add(file);
Bus.instance.fire(LocalPhotosUpdatedEvent());
}
}
@ -67,7 +64,6 @@ class FavoritesService {
} else {
await _collectionsService.removeFromCollection(collectionID, [file]);
_cachedFavoriteFiles.remove(file);
Bus.instance.fire(LocalPhotosUpdatedEvent());
}
}

View file

@ -1,11 +1,11 @@
import 'dart:async';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/events/collection_updated_event.dart';
import 'package:photos/events/local_photos_updated_event.dart';
import 'package:photos/events/tab_changed_event.dart';
import 'package:photos/models/collection.dart';
@ -29,11 +29,17 @@ class CollectionsGalleryWidget extends StatefulWidget {
}
class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget> {
StreamSubscription<LocalPhotosUpdatedEvent> _subscription;
StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
StreamSubscription<CollectionUpdatedEvent> _collectionUpdatesSubscription;
@override
void initState() {
_subscription = Bus.instance.on<LocalPhotosUpdatedEvent>().listen((event) {
_localFilesSubscription =
Bus.instance.on<LocalPhotosUpdatedEvent>().listen((event) {
setState(() {});
});
_collectionUpdatesSubscription =
Bus.instance.on<CollectionUpdatedEvent>().listen((event) {
setState(() {});
});
super.initState();
@ -232,7 +238,8 @@ class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget> {
@override
void dispose() {
_subscription.cancel();
_localFilesSubscription.cancel();
_collectionUpdatesSubscription.cancel();
super.dispose();
}
}

View file

@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:logging/logging.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/models/collection.dart';
import 'package:photos/models/file.dart';
import 'package:photos/services/collections_service.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/file_uploader.dart';
import 'package:photos/utils/toast_util.dart';
class CreateCollectionPage extends StatefulWidget {
final List<File> files;
const CreateCollectionPage(this.files, {Key key}) : super(key: key);
@override
_CreateCollectionPageState createState() => _CreateCollectionPageState();
}
class _CreateCollectionPageState extends State<CreateCollectionPage> {
final _logger = Logger("CreateCollectionPage");
String _albumName;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Create album"),
),
body: _getBody(context),
);
}
Widget _getBody(BuildContext context) {
return Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: OutlineButton(
child: Text(
"Create a new album",
style: Theme.of(context).textTheme.bodyText1,
),
onPressed: () {
_showNameAlbumDialog();
},
),
),
),
],
);
}
void _showNameAlbumDialog() async {
AlertDialog alert = AlertDialog(
title: Text("Album title"),
content: TextFormField(
decoration: InputDecoration(
hintText: "Christmas 21 / Dinner at Bob's",
contentPadding: EdgeInsets.all(8),
),
onChanged: (value) {
setState(() {
_albumName = value;
});
},
autofocus: true,
keyboardType: TextInputType.text,
),
actions: [
FlatButton(
child: Text("OK"),
onPressed: () async {
final collection = await _createAlbum(_albumName);
if (collection != null) {
await _addToCollection(collection.id);
}
},
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
Future<void> _addToCollection(int collectionID) async {
final dialog = createProgressDialog(context, "Uploading files to album...");
await dialog.show();
final files = List<File>();
for (final file in widget.files) {
if (file.uploadedFileID == null) {
file.collectionID = collectionID;
final uploadedFile =
(await FileUploader.instance.encryptAndUploadFile(file));
await FilesDB.instance.update(uploadedFile);
files.add(uploadedFile);
} else {
files.add(file);
}
}
try {
await CollectionsService.instance.addToCollection(collectionID, files);
Navigator.pop(context);
Navigator.pop(context);
showToast("Album '" + _albumName + "' created.");
} catch (e, s) {
_logger.severe(e, s);
await dialog.hide();
showGenericErrorDialog(context);
} finally {
await dialog.hide();
}
}
Future<Collection> _createAlbum(String albumName) async {
var collection;
final dialog = createProgressDialog(context, "Creating album...");
await dialog.show();
try {
collection = await CollectionsService.instance.createAlbum(albumName);
} catch (e, s) {
_logger.severe(e, s);
await dialog.hide();
showGenericErrorDialog(context);
} finally {
await dialog.hide();
}
return collection;
}
}

View file

@ -1,11 +1,13 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:page_transition/page_transition.dart';
import 'package:photos/core/configuration.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/events/user_authenticated_event.dart';
import 'package:photos/models/selected_files.dart';
import 'package:photos/services/collections_service.dart';
import 'package:photos/ui/create_collection_page.dart';
import 'package:photos/ui/email_entry_page.dart';
import 'package:photos/ui/passphrase_entry_page.dart';
import 'package:photos/ui/passphrase_reentry_page.dart';
@ -153,11 +155,22 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
);
}
Future<void> _createAlbum() async {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.bottomToTop,
child: CreateCollectionPage(
widget.selectedFiles.files.toList(),
)));
}
List<Widget> _getActions(BuildContext context) {
List<Widget> actions = List<Widget>();
if (widget.selectedFiles.files.isNotEmpty) {
if (widget.type != GalleryAppBarType.shared_collection &&
widget.type != GalleryAppBarType.search_results) {
if (widget.type == GalleryAppBarType.homepage ||
widget.type == GalleryAppBarType.local_folder ||
widget.type == GalleryAppBarType.collection) {
actions.add(IconButton(
icon: Icon(Icons.delete),
onPressed: () {
@ -165,6 +178,12 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
},
));
}
actions.add(IconButton(
icon: Icon(Icons.add),
onPressed: () {
_createAlbum();
},
));
actions.add(IconButton(
icon: Icon(Icons.share),
onPressed: () {

View file

@ -415,6 +415,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
page_transition:
dependency: "direct main"
description:
name: page_transition
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.7+2"
path:
dependency: transitive
description:

View file

@ -61,6 +61,7 @@ dependencies:
crisp: ^0.1.3
flutter_sodium: ^0.1.8
pedantic: ^1.9.2
page_transition: "^1.1.7+2"
dev_dependencies:
flutter_test: