Introduce favorites
This commit is contained in:
parent
9abe5335b2
commit
3fafd47fdd
40
lib/favorite_photos_repository.dart
Normal file
40
lib/favorite_photos_repository.dart
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import 'package:photos/models/photo.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class FavoritePhotosRepository {
|
||||||
|
static final _favoritePhotoIdsKey = "favorite_photo_ids";
|
||||||
|
FavoritePhotosRepository._privateConstructor();
|
||||||
|
static FavoritePhotosRepository instance =
|
||||||
|
FavoritePhotosRepository._privateConstructor();
|
||||||
|
|
||||||
|
SharedPreferences _preferences;
|
||||||
|
|
||||||
|
Future<void> init() async {
|
||||||
|
_preferences = await SharedPreferences.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLiked(Photo photo) {
|
||||||
|
return _getLiked().contains(photo.generatedId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> setLiked(Photo photo, bool isLiked) {
|
||||||
|
final liked = _getLiked();
|
||||||
|
if (isLiked) {
|
||||||
|
liked.add(photo.generatedId.toString());
|
||||||
|
} else {
|
||||||
|
liked.remove(photo.generatedId.toString());
|
||||||
|
}
|
||||||
|
return _preferences
|
||||||
|
.setStringList(_favoritePhotoIdsKey, liked.toList())
|
||||||
|
.then((_) => isLiked);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> _getLiked() {
|
||||||
|
final value = _preferences.getStringList(_favoritePhotoIdsKey);
|
||||||
|
if (value == null) {
|
||||||
|
return Set<String>();
|
||||||
|
} else {
|
||||||
|
return value.toSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:photos/core/constants.dart';
|
import 'package:photos/core/constants.dart';
|
||||||
import 'package:photos/core/configuration.dart';
|
import 'package:photos/core/configuration.dart';
|
||||||
|
import 'package:photos/favorite_photos_repository.dart';
|
||||||
import 'package:photos/photo_sync_manager.dart';
|
import 'package:photos/photo_sync_manager.dart';
|
||||||
import 'package:photos/ui/home_widget.dart';
|
import 'package:photos/ui/home_widget.dart';
|
||||||
import 'package:sentry/sentry.dart';
|
import 'package:sentry/sentry.dart';
|
||||||
|
@ -26,6 +27,7 @@ void _main() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
Configuration.instance.init();
|
Configuration.instance.init();
|
||||||
|
FavoritePhotosRepository.instance.init();
|
||||||
PhotoSyncManager.instance.sync();
|
PhotoSyncManager.instance.sync();
|
||||||
|
|
||||||
final SentryClient sentry = new SentryClient(dsn: SENTRY_DSN);
|
final SentryClient sentry = new SentryClient(dsn: SENTRY_DSN);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:collection';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:photos/favorite_photos_repository.dart';
|
||||||
import 'package:photos/models/album.dart';
|
import 'package:photos/models/album.dart';
|
||||||
import 'package:photos/models/photo.dart';
|
import 'package:photos/models/photo.dart';
|
||||||
import 'package:photos/ui/album_widget.dart';
|
import 'package:photos/ui/album_widget.dart';
|
||||||
|
@ -41,14 +42,22 @@ class _AlbumListWidgetState extends State<AlbumListWidget> {
|
||||||
|
|
||||||
List<Album> _getAlbums(List<Photo> photos) {
|
List<Album> _getAlbums(List<Photo> photos) {
|
||||||
final albumMap = new LinkedHashMap<String, List<Photo>>();
|
final albumMap = new LinkedHashMap<String, List<Photo>>();
|
||||||
|
final favorites = Album("Favorites", List<Photo>());
|
||||||
for (Photo photo in photos) {
|
for (Photo photo in photos) {
|
||||||
final folder = path.basename(photo.pathName);
|
final folder = path.basename(photo.pathName);
|
||||||
if (!albumMap.containsKey(folder)) {
|
if (!albumMap.containsKey(folder)) {
|
||||||
albumMap[folder] = new List<Photo>();
|
albumMap[folder] = new List<Photo>();
|
||||||
}
|
}
|
||||||
albumMap[folder].add(photo);
|
albumMap[folder].add(photo);
|
||||||
|
|
||||||
|
if (FavoritePhotosRepository.instance.isLiked(photo)) {
|
||||||
|
favorites.photos.add(photo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
List<Album> albums = new List<Album>();
|
List<Album> albums = new List<Album>();
|
||||||
|
if (favorites.photos.isNotEmpty) {
|
||||||
|
albums.add(favorites);
|
||||||
|
}
|
||||||
for (String albumName in albumMap.keys) {
|
for (String albumName in albumMap.keys) {
|
||||||
albums.add(Album(albumName, albumMap[albumName]));
|
albums.add(Album(albumName, albumMap[albumName]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:like_button/like_button.dart';
|
||||||
import 'package:photos/core/cache/lru_map.dart';
|
import 'package:photos/core/cache/lru_map.dart';
|
||||||
|
import 'package:photos/favorite_photos_repository.dart';
|
||||||
import 'package:photos/models/photo.dart';
|
import 'package:photos/models/photo.dart';
|
||||||
import 'package:photos/ui/extents_page_view.dart';
|
import 'package:photos/ui/extents_page_view.dart';
|
||||||
import 'package:photos/ui/zoomable_image.dart';
|
import 'package:photos/ui/zoomable_image.dart';
|
||||||
|
@ -18,7 +20,7 @@ class DetailPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DetailPageState extends State<DetailPage> {
|
class _DetailPageState extends State<DetailPage> {
|
||||||
final logger = Logger("DetailPageState");
|
final _logger = Logger("DetailPageState");
|
||||||
bool _shouldDisableScroll = false;
|
bool _shouldDisableScroll = false;
|
||||||
List<Photo> _photos;
|
List<Photo> _photos;
|
||||||
int _selectedIndex = 0;
|
int _selectedIndex = 0;
|
||||||
|
@ -35,7 +37,7 @@ class _DetailPageState extends State<DetailPage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
logger.info("Opening " +
|
_logger.info("Opening " +
|
||||||
_photos[_selectedIndex].toString() +
|
_photos[_selectedIndex].toString() +
|
||||||
". " +
|
". " +
|
||||||
_selectedIndex.toString() +
|
_selectedIndex.toString() +
|
||||||
|
@ -73,8 +75,9 @@ class _DetailPageState extends State<DetailPage> {
|
||||||
},
|
},
|
||||||
extents: 1,
|
extents: 1,
|
||||||
onPageChanged: (int index) {
|
onPageChanged: (int index) {
|
||||||
logger.info("onPageChanged to " + index.toString());
|
setState(() {
|
||||||
_selectedIndex = index;
|
_selectedIndex = index;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
physics: _shouldDisableScroll
|
physics: _shouldDisableScroll
|
||||||
? NeverScrollableScrollPhysics()
|
? NeverScrollableScrollPhysics()
|
||||||
|
@ -87,6 +90,7 @@ class _DetailPageState extends State<DetailPage> {
|
||||||
AppBar _buildAppBar() {
|
AppBar _buildAppBar() {
|
||||||
return AppBar(
|
return AppBar(
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
|
_getFavoriteButton(),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.share),
|
icon: Icon(Icons.share),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -96,4 +100,14 @@ class _DetailPageState extends State<DetailPage> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _getFavoriteButton() {
|
||||||
|
final photo = _photos[_selectedIndex];
|
||||||
|
return LikeButton(
|
||||||
|
isLiked: FavoritePhotosRepository.instance.isLiked(photo),
|
||||||
|
onTap: (oldValue) {
|
||||||
|
return FavoritePhotosRepository.instance.setLiked(photo, !oldValue);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
44
pubspec.lock
44
pubspec.lock
|
@ -21,35 +21,28 @@ packages:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "1.0.5"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "1.1.2"
|
||||||
clock:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: clock
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.14.12"
|
version: "1.14.11"
|
||||||
connectivity:
|
connectivity:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -127,13 +120,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
fake_async:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: fake_async
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -212,6 +198,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
like_button:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: like_button
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
local_image_provider:
|
local_image_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -253,7 +246,7 @@ packages:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.6.4"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -281,7 +274,7 @@ packages:
|
||||||
name: pedantic
|
name: pedantic
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.8.0+1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -324,6 +317,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.0"
|
||||||
|
quiver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quiver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
sensors:
|
sensors:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -384,7 +384,7 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.5.5"
|
||||||
sqflite:
|
sqflite:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -447,7 +447,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.15"
|
version: "0.2.11"
|
||||||
toast:
|
toast:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -45,6 +45,7 @@ dependencies:
|
||||||
shake: ^0.1.0
|
shake: ^0.1.0
|
||||||
archive: ^2.0.11
|
archive: ^2.0.11
|
||||||
flutter_email_sender: ^3.0.1
|
flutter_email_sender: ^3.0.1
|
||||||
|
like_button: ^0.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue