Add create album flow
This commit is contained in:
parent
585079b380
commit
673e5e9442
9 changed files with 221 additions and 24 deletions
7
lib/events/collection_updated_event.dart
Normal file
7
lib/events/collection_updated_event.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
import 'package:photos/events/event.dart';
|
||||
|
||||
class CollectionUpdatedEvent extends Event {
|
||||
final int collectionID;
|
||||
|
||||
CollectionUpdatedEvent(this.collectionID);
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
136
lib/ui/create_collection_page.dart
Normal file
136
lib/ui/create_collection_page.dart
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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: () {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue