diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml
index a29bb00..b294abb 100644
--- a/.idea/libraries/Flutter_Plugins.xml
+++ b/.idea/libraries/Flutter_Plugins.xml
@@ -30,6 +30,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/lib/components/app_page.dart b/app/lib/components/app_page.dart
index 8b2da12..aa2704d 100644
--- a/app/lib/components/app_page.dart
+++ b/app/lib/components/app_page.dart
@@ -92,6 +92,8 @@ class AppPage extends StatelessWidget {
: AppBar(
centerTitle: true,
backgroundColor: barBackgroundColor,
+ scrolledUnderElevation: 0.5,
+ shadowColor: context.colorScheme.textDisabled,
title: titleWidget ?? _title(context),
actions: [...?actions, const SizedBox(width: 16)],
leading: leading,
diff --git a/app/lib/ui/app.dart b/app/lib/ui/app.dart
index 102180e..b60a23b 100644
--- a/app/lib/ui/app.dart
+++ b/app/lib/ui/app.dart
@@ -44,7 +44,7 @@ class _CloudGalleryAppState extends ConsumerState {
routes: $appRoutes,
redirect: (context, state) {
if (state.uri.path.contains('/auth')) {
- return '/';
+ return AppRoutePath.accounts;
}
return null;
},
diff --git a/app/lib/ui/flow/albums/add/add_album_screen.dart b/app/lib/ui/flow/albums/add/add_album_screen.dart
index 14407f7..cd7000e 100644
--- a/app/lib/ui/flow/albums/add/add_album_screen.dart
+++ b/app/lib/ui/flow/albums/add/add_album_screen.dart
@@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:style/buttons/action_button.dart';
import 'package:style/extensions/context_extensions.dart';
+import 'package:style/indicators/circular_progress_indicator.dart';
import 'package:style/text/app_text_field.dart';
import 'package:style/text/app_text_style.dart';
import '../../../../components/app_page.dart';
@@ -66,16 +67,22 @@ class _AddAlbumScreenState extends ConsumerState {
title: context.l10n.add_album_screen_title,
body: _body(context: context, state: state),
actions: [
- ActionButton(
- onPressed: state.allowSave ? _notifier.createAlbum : null,
- icon: Icon(
- Icons.check,
- size: 24,
- color: state.allowSave
- ? context.colorScheme.textPrimary
- : context.colorScheme.textDisabled,
- ),
- ),
+ state.loading
+ ? const SizedBox(
+ height: 24,
+ width: 24,
+ child: AppCircularProgressIndicator(),
+ )
+ : ActionButton(
+ onPressed: state.allowSave ? _notifier.createAlbum : null,
+ icon: Icon(
+ Icons.check,
+ size: 24,
+ color: state.allowSave
+ ? context.colorScheme.textPrimary
+ : context.colorScheme.textDisabled,
+ ),
+ ),
],
);
}
diff --git a/app/lib/ui/flow/albums/add/add_album_state_notifier.dart b/app/lib/ui/flow/albums/add/add_album_state_notifier.dart
index 51d5730..5654556 100644
--- a/app/lib/ui/flow/albums/add/add_album_state_notifier.dart
+++ b/app/lib/ui/flow/albums/add/add_album_state_notifier.dart
@@ -63,6 +63,8 @@ class AddAlbumStateNotifier extends StateNotifier {
AddAlbumsState(
albumNameController: TextEditingController(text: editAlbum?.name),
mediaSource: editAlbum?.source ?? AppMediaSource.local,
+ googleAccount: googleAccount,
+ dropboxAccount: dropboxAccount,
),
);
diff --git a/app/lib/ui/flow/albums/albums_screen.dart b/app/lib/ui/flow/albums/albums_screen.dart
index cbef8f3..2f969dd 100644
--- a/app/lib/ui/flow/albums/albums_screen.dart
+++ b/app/lib/ui/flow/albums/albums_screen.dart
@@ -3,6 +3,7 @@ import 'package:data/models/media/media.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:style/animations/fade_in_switcher.dart';
import 'package:style/animations/on_tap_scale.dart';
@@ -18,6 +19,7 @@ import '../../../components/place_holder_screen.dart';
import '../../../components/snack_bar.dart';
import '../../../components/thumbnail_builder.dart';
import '../../../domain/extensions/context_extensions.dart';
+import '../../../gen/assets.gen.dart';
import '../../navigation/app_route.dart';
import 'albums_view_notifier.dart';
@@ -74,7 +76,7 @@ class _AlbumsScreenState extends ConsumerState {
Widget _body({required BuildContext context}) {
final state = ref.watch(albumStateNotifierProvider);
- if (state.loading) {
+ if (state.loading && state.albums.isEmpty) {
return const Center(child: AppCircularProgressIndicator());
} else if (state.error != null) {
return ErrorScreen(
@@ -94,21 +96,22 @@ class _AlbumsScreenState extends ConsumerState {
}
return GridView(
- padding: EdgeInsets.all(16),
+ padding: EdgeInsets.all(8),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.9,
- crossAxisSpacing: 16,
+ crossAxisSpacing: 8,
mainAxisSpacing: 16,
),
children: state.albums
.map(
(album) => AlbumItem(
album: album,
- onTap: () {
- AlbumMediaListRoute(
+ onTap: () async {
+ await AlbumMediaListRoute(
$extra: album,
).push(context);
+ _notifier.loadAlbums();
},
onLongTap: () {
showAppSheet(
@@ -204,10 +207,36 @@ class AlbumItem extends StatelessWidget {
),
),
const SizedBox(height: 10),
- Text(
- album.name,
- style: AppTextStyles.subtitle1.copyWith(
- color: context.colorScheme.textPrimary,
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 8.0),
+ child: Row(
+ children: [
+ if (album.source == AppMediaSource.dropbox) ...[
+ SvgPicture.asset(
+ Assets.images.icDropbox,
+ width: 18,
+ height: 18,
+ ),
+ const SizedBox(width: 4),
+ ],
+ if (album.source == AppMediaSource.googleDrive) ...[
+ SvgPicture.asset(
+ Assets.images.icGoogleDrive,
+ width: 18,
+ height: 18,
+ ),
+ const SizedBox(width: 4),
+ ],
+ Expanded(
+ child: Text(
+ album.name,
+ style: AppTextStyles.subtitle1.copyWith(
+ color: context.colorScheme.textPrimary,
+ ),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ ],
),
),
],
diff --git a/app/lib/ui/flow/albums/albums_view_notifier.dart b/app/lib/ui/flow/albums/albums_view_notifier.dart
index 9ee5c92..0f0e9f1 100644
--- a/app/lib/ui/flow/albums/albums_view_notifier.dart
+++ b/app/lib/ui/flow/albums/albums_view_notifier.dart
@@ -48,7 +48,12 @@ class AlbumStateNotifier extends StateNotifier {
this._logger,
GoogleSignInAccount? googleAccount,
DropboxAccount? dropboxAccount,
- ) : super(const AlbumsState()) {
+ ) : super(
+ AlbumsState(
+ googleAccount: googleAccount,
+ dropboxAccount: dropboxAccount,
+ ),
+ ) {
loadAlbums();
}
diff --git a/app/lib/ui/flow/albums/media_list/album_media_list_screen.dart b/app/lib/ui/flow/albums/media_list/album_media_list_screen.dart
index 75fa6b8..7bcbdb4 100644
--- a/app/lib/ui/flow/albums/media_list/album_media_list_screen.dart
+++ b/app/lib/ui/flow/albums/media_list/album_media_list_screen.dart
@@ -63,7 +63,7 @@ class _AlbumMediaListScreenState extends ConsumerState {
.push(context);
if (res != null && res is List) {
- await _notifier.addMedias(res);
+ await _notifier.updateAlbumMedias(medias: res);
}
},
icon: Icon(
@@ -76,7 +76,7 @@ class _AlbumMediaListScreenState extends ConsumerState {
title: context.l10n.common_edit,
onPressed: () async {
context.pop();
- final res = await AddAlbumRoute($extra: widget.album)
+ final res = await AddAlbumRoute($extra: state.album)
.push(context);
if (res == true) {
await _notifier.reloadAlbum();
diff --git a/app/lib/ui/flow/albums/media_list/album_media_list_state_notifier.dart b/app/lib/ui/flow/albums/media_list/album_media_list_state_notifier.dart
index ea8f614..608a60d 100644
--- a/app/lib/ui/flow/albums/media_list/album_media_list_state_notifier.dart
+++ b/app/lib/ui/flow/albums/media_list/album_media_list_state_notifier.dart
@@ -112,16 +112,16 @@ class AlbumMediaListStateNotifier extends StateNotifier {
}
}
- Future addMedias(List medias) async {
+ Future updateAlbumMedias({
+ required List medias,
+ bool append = true,
+ }) async {
try {
state = state.copyWith(actionError: null);
if (state.album.source == AppMediaSource.local) {
await _localMediaService.updateAlbum(
state.album.copyWith(
- medias: [
- ...state.album.medias,
- ...medias,
- ],
+ medias: append ? [...state.album.medias, ...medias] : medias,
),
);
} else if (state.album.source == AppMediaSource.googleDrive) {
@@ -132,19 +132,13 @@ class AlbumMediaListStateNotifier extends StateNotifier {
await _googleDriveService.updateAlbum(
folderId: _backupFolderId!,
album: state.album.copyWith(
- medias: [
- ...state.album.medias,
- ...medias,
- ],
+ medias: append ? [...state.album.medias, ...medias] : medias,
),
);
} else if (state.album.source == AppMediaSource.dropbox) {
await _dropboxService.updateAlbum(
state.album.copyWith(
- medias: [
- ...state.album.medias,
- ...medias,
- ],
+ medias: append ? [...state.album.medias, ...medias] : medias,
),
);
}
@@ -160,6 +154,7 @@ class AlbumMediaListStateNotifier extends StateNotifier {
}
Future loadMedia({bool reload = false}) async {
+ ///TODO: remove deleted media
try {
if (state.loading) return;
@@ -175,14 +170,13 @@ class AlbumMediaListStateNotifier extends StateNotifier {
final loadedMediaIds = state.medias.map((e) => e.id).toList();
final moreMediaIds = state.album.medias
.where((element) => !loadedMediaIds.contains(element))
+ .take(30)
.toList();
medias = await Future.wait(
- moreMediaIds
- .take(moreMediaIds.length > 30 ? 30 : moreMediaIds.length)
- .map(
- (id) => _localMediaService.getMedia(id: id),
- ),
+ moreMediaIds.map(
+ (id) => _localMediaService.getMedia(id: id),
+ ),
).then(
(value) => value.nonNulls.toList(),
);
@@ -191,13 +185,12 @@ class AlbumMediaListStateNotifier extends StateNotifier {
state.medias.map((e) => e.driveMediaRefId).nonNulls.toList();
final moreMediaIds = state.album.medias
.where((element) => !loadedMediaIds.contains(element))
+ .take(30)
.toList();
medias = await Future.wait(
- moreMediaIds
- .take(moreMediaIds.length > 30 ? 30 : moreMediaIds.length)
- .map(
- (id) => _googleDriveService.getMedia(id: id),
- ),
+ moreMediaIds.map(
+ (id) => _googleDriveService.getMedia(id: id),
+ ),
).then(
(value) => value.nonNulls.toList(),
);
@@ -206,13 +199,12 @@ class AlbumMediaListStateNotifier extends StateNotifier {
state.medias.map((e) => e.dropboxMediaRefId).nonNulls.toList();
final moreMediaIds = state.album.medias
.where((element) => !loadedMediaIds.contains(element))
+ .take(30)
.toList();
medias = await Future.wait(
- moreMediaIds
- .take(moreMediaIds.length > 30 ? 30 : moreMediaIds.length)
- .map(
- (id) => _dropboxService.getMedia(id: id),
- ),
+ moreMediaIds.map(
+ (id) => _dropboxService.getMedia(id: id),
+ ),
).then(
(value) => value.nonNulls.toList(),
);
diff --git a/app/lib/ui/flow/main/main_screen.dart b/app/lib/ui/flow/main/main_screen.dart
index 688fab5..a1375dc 100644
--- a/app/lib/ui/flow/main/main_screen.dart
+++ b/app/lib/ui/flow/main/main_screen.dart
@@ -44,60 +44,34 @@ class _MainScreenState extends State {
),
];
- return Column(
- children: [
- Expanded(child: widget.navigationShell),
- (!kIsWeb && Platform.isIOS)
- ? CupertinoTabBar(
- currentIndex: widget.navigationShell.currentIndex,
- activeColor: context.colorScheme.primary,
- inactiveColor: context.colorScheme.textDisabled,
- onTap: (index) => _goBranch(
- index: index,
- context: context,
- ),
- backgroundColor: context.colorScheme.surface,
- border: Border(
- top: BorderSide(
- color: context.colorScheme.outline,
- width: 1,
+ return Material(
+ color: context.colorScheme.surface,
+ child: Column(
+ children: [
+ Expanded(child: widget.navigationShell),
+ (!kIsWeb && Platform.isIOS)
+ ? CupertinoTabBar(
+ currentIndex: widget.navigationShell.currentIndex,
+ activeColor: context.colorScheme.primary,
+ inactiveColor: context.colorScheme.textDisabled,
+ onTap: (index) => _goBranch(
+ index: index,
+ context: context,
),
- ),
- items: tabs
- .map(
- (e) => BottomNavigationBarItem(
- icon: Icon(
- e.icon,
- color: context.colorScheme.textDisabled,
- size: 22,
- ),
- label: e.label,
- activeIcon: Icon(
- e.activeIcon,
- color: context.colorScheme.primary,
- size: 24,
- ),
- ),
- )
- .toList(),
- )
- : Container(
- decoration: BoxDecoration(
+ backgroundColor: context.colorScheme.surface,
border: Border(
top: BorderSide(
color: context.colorScheme.outline,
width: 1,
),
),
- ),
- child: BottomNavigationBar(
items: tabs
.map(
(e) => BottomNavigationBarItem(
icon: Icon(
e.icon,
color: context.colorScheme.textDisabled,
- size: 24,
+ size: 22,
),
label: e.label,
activeIcon: Icon(
@@ -108,21 +82,50 @@ class _MainScreenState extends State {
),
)
.toList(),
- currentIndex: widget.navigationShell.currentIndex,
- selectedItemColor: context.colorScheme.primary,
- unselectedItemColor: context.colorScheme.textDisabled,
- backgroundColor: context.colorScheme.surface,
- type: BottomNavigationBarType.fixed,
- selectedFontSize: 12,
- unselectedFontSize: 12,
- elevation: 0,
- onTap: (index) => _goBranch(
- index: index,
- context: context,
+ )
+ : Container(
+ decoration: BoxDecoration(
+ color: context.colorScheme.surface,
+ border: Border(
+ top: BorderSide(
+ color: context.colorScheme.outline,
+ ),
+ ),
+ ),
+ child: BottomNavigationBar(
+ items: tabs
+ .map(
+ (e) => BottomNavigationBarItem(
+ icon: Icon(
+ e.icon,
+ color: context.colorScheme.textDisabled,
+ size: 24,
+ ),
+ label: e.label,
+ activeIcon: Icon(
+ e.activeIcon,
+ color: context.colorScheme.primary,
+ size: 24,
+ ),
+ ),
+ )
+ .toList(),
+ currentIndex: widget.navigationShell.currentIndex,
+ selectedItemColor: context.colorScheme.primary,
+ unselectedItemColor: context.colorScheme.textDisabled,
+ backgroundColor: context.colorScheme.surface,
+ type: BottomNavigationBarType.fixed,
+ selectedFontSize: 12,
+ unselectedFontSize: 12,
+ elevation: 0,
+ onTap: (index) => _goBranch(
+ index: index,
+ context: context,
+ ),
),
),
- ),
- ],
+ ],
+ ),
);
}
diff --git a/app/lib/ui/flow/media_selection/media_selection_state_notifier.dart b/app/lib/ui/flow/media_selection/media_selection_state_notifier.dart
index ffa25b9..3caa9dd 100644
--- a/app/lib/ui/flow/media_selection/media_selection_state_notifier.dart
+++ b/app/lib/ui/flow/media_selection/media_selection_state_notifier.dart
@@ -181,12 +181,14 @@ class MediaSelectionStateNotifier extends StateNotifier {
}
void toggleMediaSelection(AppMedia media) {
- String id = media.id;
+ String id;
if (_source == AppMediaSource.googleDrive) {
id = media.driveMediaRefId!;
} else if (_source == AppMediaSource.dropbox) {
id = media.dropboxMediaRefId!;
+ } else {
+ id = media.id;
}
if (state.selectedMedias.contains(id)) {
@@ -199,7 +201,7 @@ class MediaSelectionStateNotifier extends StateNotifier {
state = state.copyWith(
selectedMedias: [
...state.selectedMedias,
- media.id,
+ id,
],
);
}
diff --git a/data/lib/services/dropbox_services.dart b/data/lib/services/dropbox_services.dart
index 4c2b5ba..11b8321 100644
--- a/data/lib/services/dropbox_services.dart
+++ b/data/lib/services/dropbox_services.dart
@@ -131,7 +131,11 @@ class DropboxService extends CloudProviderService {
nextPageToken = response.data['cursor'];
medias.addAll(
(response.data['entries'] as List)
- .where((element) => element['.tag'] == 'file')
+ .where(
+ (element) =>
+ element['.tag'] == 'file' &&
+ element['name'] != 'Albums.json',
+ )
.map((e) => AppMedia.fromDropboxJson(json: e))
.toList(),
);
@@ -177,7 +181,8 @@ class DropboxService extends CloudProviderService {
);
if (response.statusCode == 200) {
final files = (response.data['entries'] as List).where(
- (element) => element['.tag'] == 'file',
+ (element) =>
+ element['.tag'] == 'file' && element['name'] != 'Albums.json',
);
final metadataResponses = await Future.wait(
@@ -378,6 +383,8 @@ class DropboxService extends CloudProviderService {
);
}
+ // ALBUM ---------------------------------------------------------------------
+
Future> getAlbums() async {
try {
final res = await _dropboxAuthenticatedDio.req(
@@ -417,8 +424,8 @@ class DropboxService extends CloudProviderService {
mode: 'overwrite',
autoRename: false,
content: AppMediaContent(
- stream: Stream.value(utf8.encode(jsonEncode(album))),
- length: utf8.encode(jsonEncode(album)).length,
+ stream: Stream.value(utf8.encode(jsonEncode(albums))),
+ length: utf8.encode(jsonEncode(albums)).length,
contentType: 'application/octet-stream',
),
filePath: "/${ProviderConstants.backupFolderName}/Albums.json",
@@ -444,7 +451,7 @@ class DropboxService extends CloudProviderService {
content: AppMediaContent(
stream: Stream.value(utf8.encode(jsonEncode(albums))),
length: utf8.encode(jsonEncode(albums)).length,
- contentType: 'application/json',
+ contentType: 'application/octet-stream',
),
filePath: "/${ProviderConstants.backupFolderName}/Albums.json",
),
@@ -478,7 +485,7 @@ class DropboxService extends CloudProviderService {
content: AppMediaContent(
stream: Stream.value(utf8.encode(jsonEncode(albums))),
length: utf8.encode(jsonEncode(albums)).length,
- contentType: 'application/json',
+ contentType: 'application/octet-stream',
),
filePath: "/${ProviderConstants.backupFolderName}/Albums.json",
),