mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
Implement album feature on mobile (#420)
* Refactor sharing to album * Added library page in the bottom navigation bar * Refactor SharedAlbumService to album service * Refactor apiProvider to its file * Added image grid * render album thumbnail * Using the wrap to render thumbnail and album info better * Navigate to album viewer * After deletion, navigate to the respective page of the shared and non-shared album * Correctly remove album in local state * Refactor create album page * Implemented create non-shared album
This commit is contained in:
parent
0e85b0fd8f
commit
e8d1f89a47
42 changed files with 521 additions and 154 deletions
|
@ -47,6 +47,7 @@
|
|||
"backup_info_card_assets": "assets",
|
||||
"control_bottom_app_bar_delete": "Delete",
|
||||
"create_shared_album_page_share": "Share",
|
||||
"create_shared_album_page_create": "Create",
|
||||
"create_shared_album_page_share_add_assets": "ADD ASSETS",
|
||||
"create_shared_album_page_share_select_photos": "Select Photos",
|
||||
"daily_title_text_date": "E, MMM dd",
|
||||
|
@ -97,10 +98,11 @@
|
|||
"tab_controller_nav_photos": "Photos",
|
||||
"tab_controller_nav_search": "Search",
|
||||
"tab_controller_nav_sharing": "Sharing",
|
||||
"tab_controller_nav_library": "Library",
|
||||
"version_announcement_overlay_ack": "Acknowledge",
|
||||
"version_announcement_overlay_release_notes": "release notes",
|
||||
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
||||
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
||||
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89"
|
||||
}
|
||||
}
|
||||
|
|
38
mobile/lib/modules/album/providers/album.provider.dart
Normal file
38
mobile/lib/modules/album/providers/album.provider.dart
Normal file
|
@ -0,0 +1,38 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class AlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
||||
AlbumNotifier(this._albumService) : super([]);
|
||||
final AlbumService _albumService;
|
||||
|
||||
getAllAlbums() async {
|
||||
List<AlbumResponseDto>? albums =
|
||||
await _albumService.getAlbums(isShared: false);
|
||||
|
||||
if (albums != null) {
|
||||
state = albums;
|
||||
}
|
||||
}
|
||||
|
||||
deleteAlbum(String albumId) {
|
||||
state = state.where((album) => album.id != albumId).toList();
|
||||
}
|
||||
|
||||
Future<AlbumResponseDto?> createAlbum(
|
||||
String albumTitle, Set<AssetResponseDto> assets) async {
|
||||
AlbumResponseDto? album =
|
||||
await _albumService.createAlbum(albumTitle, assets, []);
|
||||
|
||||
if (album != null) {
|
||||
state = [...state, album];
|
||||
return album;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final albumProvider =
|
||||
StateNotifierProvider<AlbumNotifier, List<AlbumResponseDto>>((ref) {
|
||||
return AlbumNotifier(ref.watch(albumServiceProvider));
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/models/album_viewer_page_state.model.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
|
||||
import 'package:immich_mobile/modules/album/models/album_viewer_page_state.model.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
|
||||
class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
||||
AlbumViewerNotifier(this.ref)
|
||||
|
@ -34,7 +34,7 @@ class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
|||
String ownerId,
|
||||
String newAlbumTitle,
|
||||
) async {
|
||||
SharedAlbumService service = ref.watch(sharedAlbumServiceProvider);
|
||||
AlbumService service = ref.watch(albumServiceProvider);
|
||||
|
||||
bool isSuccess =
|
||||
await service.changeTitleAlbum(albumId, ownerId, newAlbumTitle);
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/models/asset_selection_state.model.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_state.model.dart';
|
||||
|
||||
import 'package:openapi/api.dart';
|
||||
|
|
@ -1,30 +1,48 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
||||
SharedAlbumNotifier(this._sharedAlbumService) : super([]);
|
||||
|
||||
final SharedAlbumService _sharedAlbumService;
|
||||
final AlbumService _sharedAlbumService;
|
||||
|
||||
Future<AlbumResponseDto?> createSharedAlbum(
|
||||
String albumName,
|
||||
Set<AssetResponseDto> assets,
|
||||
List<String> sharedUserIds,
|
||||
) async {
|
||||
try {
|
||||
var newAlbum = await _sharedAlbumService.createAlbum(
|
||||
albumName,
|
||||
assets,
|
||||
sharedUserIds,
|
||||
);
|
||||
|
||||
if (newAlbum != null) {
|
||||
state = [...state, newAlbum];
|
||||
}
|
||||
|
||||
return newAlbum;
|
||||
} catch (e) {
|
||||
debugPrint("Error createSharedAlbum ${e.toString()}");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getAllSharedAlbums() async {
|
||||
List<AlbumResponseDto>? sharedAlbums =
|
||||
await _sharedAlbumService.getAllSharedAlbum();
|
||||
await _sharedAlbumService.getAlbums(isShared: true);
|
||||
|
||||
if (sharedAlbums != null) {
|
||||
state = sharedAlbums;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteAlbum(String albumId) async {
|
||||
var res = await _sharedAlbumService.deleteAlbum(albumId);
|
||||
|
||||
if (res) {
|
||||
state = state.where((album) => album.id != albumId).toList();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
deleteAlbum(String albumId) async {
|
||||
state = state.where((album) => album.id != albumId).toList();
|
||||
}
|
||||
|
||||
Future<bool> leaveAlbum(String albumId) async {
|
||||
|
@ -54,13 +72,12 @@ class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
|||
|
||||
final sharedAlbumProvider =
|
||||
StateNotifierProvider<SharedAlbumNotifier, List<AlbumResponseDto>>((ref) {
|
||||
return SharedAlbumNotifier(ref.watch(sharedAlbumServiceProvider));
|
||||
return SharedAlbumNotifier(ref.watch(albumServiceProvider));
|
||||
});
|
||||
|
||||
final sharedAlbumDetailProvider = FutureProvider.autoDispose
|
||||
.family<AlbumResponseDto?, String>((ref, albumId) async {
|
||||
final SharedAlbumService sharedAlbumService =
|
||||
ref.watch(sharedAlbumServiceProvider);
|
||||
final AlbumService sharedAlbumService = ref.watch(albumServiceProvider);
|
||||
|
||||
return await sharedAlbumService.getAlbumDetail(albumId);
|
||||
});
|
|
@ -2,46 +2,47 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final sharedAlbumServiceProvider = Provider(
|
||||
(ref) => SharedAlbumService(
|
||||
final albumServiceProvider = Provider(
|
||||
(ref) => AlbumService(
|
||||
ref.watch(apiServiceProvider),
|
||||
),
|
||||
);
|
||||
|
||||
class SharedAlbumService {
|
||||
class AlbumService {
|
||||
final ApiService _apiService;
|
||||
SharedAlbumService(this._apiService);
|
||||
|
||||
Future<List<AlbumResponseDto>?> getAllSharedAlbum() async {
|
||||
AlbumService(this._apiService);
|
||||
|
||||
Future<List<AlbumResponseDto>?> getAlbums({required bool isShared}) async {
|
||||
try {
|
||||
return await _apiService.albumApi.getAllAlbums(shared: true);
|
||||
return await _apiService.albumApi
|
||||
.getAllAlbums(shared: isShared ? isShared : null);
|
||||
} catch (e) {
|
||||
debugPrint("Error getAllSharedAlbum ${e.toString()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> createSharedAlbum(
|
||||
Future<AlbumResponseDto?> createAlbum(
|
||||
String albumName,
|
||||
Set<AssetResponseDto> assets,
|
||||
List<String> sharedUserIds,
|
||||
) async {
|
||||
try {
|
||||
_apiService.albumApi.createAlbum(
|
||||
return await _apiService.albumApi.createAlbum(
|
||||
CreateAlbumDto(
|
||||
albumName: albumName,
|
||||
assetIds: assets.map((asset) => asset.id).toList(),
|
||||
sharedWithUserIds: sharedUserIds,
|
||||
),
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
debugPrint("Error createSharedAlbum ${e.toString()}");
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
77
mobile/lib/modules/album/ui/album_thumbnail_card.dart
Normal file
77
mobile/lib/modules/album/ui/album_thumbnail_card.dart
Normal file
|
@ -0,0 +1,77 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:transparent_image/transparent_image.dart';
|
||||
|
||||
class AlbumThumbnailCard extends StatelessWidget {
|
||||
const AlbumThumbnailCard({Key? key, required this.album}) : super(key: key);
|
||||
|
||||
final AlbumResponseDto album;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var box = Hive.box(userInfoBox);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
AutoRouter.of(context).push(AlbumViewerRoute(albumId: album.id));
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: FadeInImage(
|
||||
width: MediaQuery.of(context).size.width / 2 - 18,
|
||||
height: MediaQuery.of(context).size.width / 2 - 18,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: MemoryImage(kTransparentImage),
|
||||
image: NetworkImage(
|
||||
'${box.get(serverEndpointKey)}/asset/thumbnail/${album.albumThumbnailAssetId}?format=JPEG',
|
||||
headers: {
|
||||
"Authorization": "Bearer ${box.get(accessTokenKey)}"
|
||||
},
|
||||
),
|
||||
fadeInDuration: const Duration(milliseconds: 200),
|
||||
fadeOutDuration: const Duration(milliseconds: 200),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
album.albumName,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'${album.assets.length} item${album.assets.length > 1 ? 's' : ''}',
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
if (album.shared)
|
||||
const Text(
|
||||
' · Shared',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/album_title.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_title.provider.dart';
|
||||
|
||||
class AlbumTitleTextField extends ConsumerWidget {
|
||||
const AlbumTitleTextField({
|
|
@ -4,9 +4,11 @@ import 'package:flutter/material.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_viewer.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
|
@ -15,13 +17,12 @@ import 'package:openapi/api.dart';
|
|||
class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
||||
const AlbumViewerAppbar({
|
||||
Key? key,
|
||||
required AsyncValue<AlbumResponseDto?> albumInfo,
|
||||
required this.albumInfo,
|
||||
required this.userId,
|
||||
required this.albumId,
|
||||
}) : _albumInfo = albumInfo,
|
||||
super(key: key);
|
||||
}) : super(key: key);
|
||||
|
||||
final AsyncValue<AlbumResponseDto?> _albumInfo;
|
||||
final AlbumResponseDto albumInfo;
|
||||
final String userId;
|
||||
final String albumId;
|
||||
|
||||
|
@ -38,11 +39,18 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
bool isSuccess =
|
||||
await ref.watch(sharedAlbumProvider.notifier).deleteAlbum(albumId);
|
||||
await ref.watch(albumServiceProvider).deleteAlbum(albumId);
|
||||
|
||||
if (isSuccess) {
|
||||
AutoRouter.of(context)
|
||||
.navigate(const TabControllerRoute(children: [SharingRoute()]));
|
||||
if (albumInfo.shared) {
|
||||
ref.watch(sharedAlbumProvider.notifier).deleteAlbum(albumId);
|
||||
AutoRouter.of(context)
|
||||
.navigate(const TabControllerRoute(children: [SharingRoute()]));
|
||||
} else {
|
||||
ref.watch(albumProvider.notifier).deleteAlbum(albumId);
|
||||
AutoRouter.of(context)
|
||||
.navigate(const TabControllerRoute(children: [LibraryRoute()]));
|
||||
}
|
||||
} else {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
|
@ -105,7 +113,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
|
||||
_buildBottomSheetActionButton() {
|
||||
if (isMultiSelectionEnable) {
|
||||
if (_albumInfo.asData?.value?.ownerId == userId) {
|
||||
if (albumInfo.ownerId == userId) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.delete_sweep_rounded),
|
||||
title: const Text(
|
||||
|
@ -118,7 +126,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
if (_albumInfo.asData?.value?.ownerId == userId) {
|
||||
if (albumInfo.ownerId == userId) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.delete_forever_rounded),
|
||||
title: const Text(
|
|
@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_viewer.provider.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class AlbumViewerEditableTitle extends HookConsumerWidget {
|
|
@ -6,7 +6,7 @@ import 'package:hive_flutter/hive_flutter.dart';
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/selection_thumbnail_image.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/selection_thumbnail_image.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class AssetGridByMonth extends HookConsumerWidget {
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class MonthGroupTitle extends HookConsumerWidget {
|
|
@ -4,7 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class SelectionThumbnailImage extends HookConsumerWidget {
|
|
@ -16,8 +16,6 @@ class SharingSliverAppBar extends StatelessWidget {
|
|||
pinned: true,
|
||||
snap: false,
|
||||
automaticallyImplyLeading: false,
|
||||
// leading: Container(),
|
||||
// elevation: 0,
|
||||
title: Text(
|
||||
'IMMICH',
|
||||
style: TextStyle(
|
||||
|
@ -46,7 +44,7 @@ class SharingSliverAppBar extends StatelessWidget {
|
|||
),
|
||||
onPressed: () {
|
||||
AutoRouter.of(context)
|
||||
.push(const CreateSharedAlbumRoute());
|
||||
.push(CreateAlbumRoute(isSharedAlbum: true));
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.photo_album_outlined,
|
|
@ -6,14 +6,14 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/draggable_scrollbar.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/album_action_outlined_button.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/album_viewer_appbar.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/album_viewer_editable_title.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/album_viewer_thumbnail.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_action_outlined_button.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_viewer_appbar.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_viewer_editable_title.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_viewer_thumbnail.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_sliver_persistent_app_bar_delegate.dart';
|
||||
|
@ -29,6 +29,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
FocusNode titleFocusNode = useFocusNode();
|
||||
ScrollController scrollController = useScrollController();
|
||||
|
||||
AsyncValue<AlbumResponseDto?> albumInfo =
|
||||
ref.watch(sharedAlbumDetailProvider(albumId));
|
||||
|
||||
|
@ -53,12 +54,11 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
if (returnPayload.selectedAdditionalAsset.isNotEmpty) {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
var isSuccess = await ref
|
||||
.watch(sharedAlbumServiceProvider)
|
||||
.addAdditionalAssetToAlbum(
|
||||
returnPayload.selectedAdditionalAsset,
|
||||
albumId,
|
||||
);
|
||||
var isSuccess =
|
||||
await ref.watch(albumServiceProvider).addAdditionalAssetToAlbum(
|
||||
returnPayload.selectedAdditionalAsset,
|
||||
albumId,
|
||||
);
|
||||
|
||||
if (isSuccess) {
|
||||
ref.refresh(sharedAlbumDetailProvider(albumId));
|
||||
|
@ -83,7 +83,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
var isSuccess = await ref
|
||||
.watch(sharedAlbumServiceProvider)
|
||||
.watch(albumServiceProvider)
|
||||
.addAdditionalUserToAlbum(sharedUserIds, albumId);
|
||||
|
||||
if (isSuccess) {
|
||||
|
@ -132,7 +132,11 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
String endDate = DateFormat('LLL d, y').format(parsedEndDate);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 8),
|
||||
padding: EdgeInsets.only(
|
||||
left: 16.0,
|
||||
top: 8.0,
|
||||
bottom: albumInfo.shared ? 0.0 : 8.0,
|
||||
),
|
||||
child: Text(
|
||||
"$startDate-$endDate",
|
||||
style: const TextStyle(
|
||||
|
@ -152,31 +156,33 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
_buildTitle(albumInfo),
|
||||
if (albumInfo.assets.isNotEmpty == true)
|
||||
_buildAlbumDateRange(albumInfo),
|
||||
SizedBox(
|
||||
height: 60,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: ((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: Colors.grey[300],
|
||||
radius: 18,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
child:
|
||||
Image.asset('assets/immich-logo-no-outline.png'),
|
||||
if (albumInfo.shared)
|
||||
SizedBox(
|
||||
height: 60,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: ((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: Colors.grey[300],
|
||||
radius: 18,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
child: Image.asset(
|
||||
'assets/immich-logo-no-outline.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
itemCount: albumInfo.sharedUsers.length,
|
||||
),
|
||||
)
|
||||
);
|
||||
}),
|
||||
itemCount: albumInfo.sharedUsers.length,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -261,10 +267,19 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AlbumViewerAppbar(
|
||||
albumInfo: albumInfo,
|
||||
userId: userId,
|
||||
albumId: albumId,
|
||||
appBar: albumInfo.when(
|
||||
data: (AlbumResponseDto? data) {
|
||||
if (data != null) {
|
||||
return AlbumViewerAppbar(
|
||||
albumInfo: data,
|
||||
userId: userId,
|
||||
albumId: albumId,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
error: (e, _) => null,
|
||||
loading: () => null,
|
||||
),
|
||||
body: albumInfo.when(
|
||||
data: (albumInfo) => albumInfo != null
|
|
@ -3,15 +3,16 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/asset_grid_by_month.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/month_group_title.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/asset_grid_by_month.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/month_group_title.dart';
|
||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/draggable_scrollbar.dart';
|
||||
|
||||
class AssetSelectionPage extends HookConsumerWidget {
|
||||
const AssetSelectionPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ScrollController scrollController = useScrollController();
|
|
@ -3,16 +3,20 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/album_title.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/album_action_outlined_button.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/album_title_text_field.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/shared_album_thumbnail_image.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_title.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_action_outlined_button.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_title_text_field.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/shared_album_thumbnail_image.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
|
||||
class CreateSharedAlbumPage extends HookConsumerWidget {
|
||||
const CreateSharedAlbumPage({Key? key}) : super(key: key);
|
||||
// ignore: must_be_immutable
|
||||
class CreateAlbumPage extends HookConsumerWidget {
|
||||
bool isSharedAlbum;
|
||||
|
||||
CreateAlbumPage({Key? key, required this.isSharedAlbum}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
@ -165,6 +169,21 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
|
|||
return const SliverToBoxAdapter();
|
||||
}
|
||||
|
||||
_createNonSharedAlbum() async {
|
||||
var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(
|
||||
ref.watch(albumTitleProvider),
|
||||
ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum,
|
||||
);
|
||||
|
||||
if (newAlbum != null) {
|
||||
ref.watch(albumProvider.notifier).getAllAlbums();
|
||||
ref.watch(assetSelectionProvider.notifier).removeAll();
|
||||
ref.watch(albumTitleProvider.notifier).clearAlbumTitle();
|
||||
|
||||
AutoRouter.of(context).replace(AlbumViewerRoute(albumId: newAlbum.id));
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
|
@ -181,17 +200,31 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
|
|||
style: TextStyle(color: Colors.black),
|
||||
).tr(),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: albumTitleController.text.isNotEmpty
|
||||
? _showSelectUserPage
|
||||
: null,
|
||||
child: Text(
|
||||
'create_shared_album_page_share'.tr(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
if (isSharedAlbum)
|
||||
TextButton(
|
||||
onPressed: albumTitleController.text.isNotEmpty
|
||||
? _showSelectUserPage
|
||||
: null,
|
||||
child: Text(
|
||||
'create_shared_album_page_share'.tr(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isSharedAlbum)
|
||||
TextButton(
|
||||
onPressed: albumTitleController.text.isNotEmpty &&
|
||||
selectedAssets.isNotEmpty
|
||||
? _createNonSharedAlbum
|
||||
: null,
|
||||
child: Text(
|
||||
'create_shared_album_page_create'.tr(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: GestureDetector(
|
116
mobile/lib/modules/album/views/library_page.dart
Normal file
116
mobile/lib/modules/album/views/library_page.dart
Normal file
|
@ -0,0 +1,116 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_thumbnail_card.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
|
||||
class LibraryPage extends HookConsumerWidget {
|
||||
const LibraryPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final albums = ref.watch(albumProvider);
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
ref.read(albumProvider.notifier).getAllAlbums();
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
Widget _buildAppBar() {
|
||||
return SliverAppBar(
|
||||
centerTitle: true,
|
||||
floating: true,
|
||||
pinned: false,
|
||||
snap: false,
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(
|
||||
'IMMICH',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SnowburstOne',
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCreateAlbumButton() {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
AutoRouter.of(context).push(CreateAlbumRoute(isSharedAlbum: false));
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width / 2 - 18,
|
||||
height: MediaQuery.of(context).size.width / 2 - 18,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Colors.grey,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.add_rounded,
|
||||
size: 28,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
"New album",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
_buildAppBar(),
|
||||
const SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(12.0),
|
||||
child: Text(
|
||||
"Albums",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(left: 12.0, right: 12, bottom: 50),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Wrap(
|
||||
spacing: 12,
|
||||
children: [
|
||||
_buildCreateAlbumButton(),
|
||||
for (var album in albums)
|
||||
AlbumThumbnailCard(
|
||||
album: album,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/suggested_shared_users.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
|
@ -3,11 +3,10 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/album_title.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_title.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/suggested_shared_users.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
@ -22,14 +21,14 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
ref.watch(suggestedSharedUsersProvider);
|
||||
|
||||
_createSharedAlbum() async {
|
||||
var isSuccess =
|
||||
await ref.watch(sharedAlbumServiceProvider).createSharedAlbum(
|
||||
var newAlbum =
|
||||
await ref.watch(sharedAlbumProvider.notifier).createSharedAlbum(
|
||||
ref.watch(albumTitleProvider),
|
||||
ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum,
|
||||
sharedUsersList.value.map((userInfo) => userInfo.id).toList(),
|
||||
);
|
||||
|
||||
if (isSuccess) {
|
||||
if (newAlbum != null) {
|
||||
await ref.watch(sharedAlbumProvider.notifier).getAllSharedAlbums();
|
||||
ref.watch(assetSelectionProvider.notifier).removeAll();
|
||||
ref.watch(albumTitleProvider.notifier).clearAlbumTitle();
|
|
@ -5,8 +5,8 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/ui/sharing_sliver_appbar.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/sharing_sliver_appbar.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:transparent_image/transparent_image.dart';
|
||||
|
@ -23,7 +23,6 @@ class SharingPage extends HookConsumerWidget {
|
|||
useEffect(
|
||||
() {
|
||||
ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
|
||||
|
||||
return null;
|
||||
},
|
||||
[],
|
|
@ -2,6 +2,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
@ -14,6 +15,7 @@ final imageViewerServiceProvider =
|
|||
|
||||
class ImageViewerService {
|
||||
final ApiService _apiService;
|
||||
|
||||
ImageViewerService(this._apiService);
|
||||
|
||||
Future<bool> downloadAssetToDevice(AssetResponseDto asset) async {
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/files_helper.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
@ -24,6 +25,7 @@ final backupServiceProvider = Provider(
|
|||
|
||||
class BackupService {
|
||||
final ApiService _apiService;
|
||||
|
||||
BackupService(this._apiService);
|
||||
|
||||
Future<List<String>?> getDeviceBackupAsset() async {
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:immich_mobile/constants/hive_box.dart';
|
|||
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
|
||||
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:immich_mobile/shared/services/device_info.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
|
@ -11,6 +12,7 @@ final searchServiceProvider = Provider(
|
|||
|
||||
class SearchService {
|
||||
final ApiService _apiService;
|
||||
|
||||
SearchService(this._apiService);
|
||||
|
||||
Future<List<String>?> getUserSuggestedSearchTerms() async {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/views/library_page.dart';
|
||||
import 'package:immich_mobile/modules/backup/views/album_preview_page.dart';
|
||||
import 'package:immich_mobile/modules/backup/views/backup_album_selection_page.dart';
|
||||
import 'package:immich_mobile/modules/backup/views/failed_backup_status_page.dart';
|
||||
|
@ -9,16 +10,17 @@ import 'package:immich_mobile/modules/login/views/login_page.dart';
|
|||
import 'package:immich_mobile/modules/home/views/home_page.dart';
|
||||
import 'package:immich_mobile/modules/search/views/search_page.dart';
|
||||
import 'package:immich_mobile/modules/search/views/search_result_page.dart';
|
||||
import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/sharing/views/album_viewer_page.dart';
|
||||
import 'package:immich_mobile/modules/sharing/views/asset_selection_page.dart';
|
||||
import 'package:immich_mobile/modules/sharing/views/create_shared_album_page.dart';
|
||||
import 'package:immich_mobile/modules/sharing/views/select_additional_user_for_sharing_page.dart';
|
||||
import 'package:immich_mobile/modules/sharing/views/select_user_for_sharing_page.dart';
|
||||
import 'package:immich_mobile/modules/sharing/views/sharing_page.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/album/views/album_viewer_page.dart';
|
||||
import 'package:immich_mobile/modules/album/views/asset_selection_page.dart';
|
||||
import 'package:immich_mobile/modules/album/views/create_album_page.dart';
|
||||
import 'package:immich_mobile/modules/album/views/select_additional_user_for_sharing_page.dart';
|
||||
import 'package:immich_mobile/modules/album/views/select_user_for_sharing_page.dart';
|
||||
import 'package:immich_mobile/modules/album/views/sharing_page.dart';
|
||||
import 'package:immich_mobile/routing/auth_guard.dart';
|
||||
import 'package:immich_mobile/modules/backup/views/backup_controller_page.dart';
|
||||
import 'package:immich_mobile/modules/asset_viewer/views/image_viewer_page.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:immich_mobile/shared/views/splash_screen.dart';
|
||||
import 'package:immich_mobile/shared/views/tab_controller_page.dart';
|
||||
|
@ -40,7 +42,8 @@ part 'router.gr.dart';
|
|||
children: [
|
||||
AutoRoute(page: HomePage, guards: [AuthGuard]),
|
||||
AutoRoute(page: SearchPage, guards: [AuthGuard]),
|
||||
AutoRoute(page: SharingPage, guards: [AuthGuard])
|
||||
AutoRoute(page: SharingPage, guards: [AuthGuard]),
|
||||
AutoRoute(page: LibraryPage, guards: [AuthGuard])
|
||||
],
|
||||
transitionsBuilder: TransitionsBuilders.fadeIn,
|
||||
),
|
||||
|
@ -48,7 +51,7 @@ part 'router.gr.dart';
|
|||
AutoRoute(page: VideoViewerPage, guards: [AuthGuard]),
|
||||
AutoRoute(page: BackupControllerPage, guards: [AuthGuard]),
|
||||
AutoRoute(page: SearchResultPage, guards: [AuthGuard]),
|
||||
AutoRoute(page: CreateSharedAlbumPage, guards: [AuthGuard]),
|
||||
AutoRoute(page: CreateAlbumPage, guards: [AuthGuard]),
|
||||
CustomRoute<AssetSelectionPageResult?>(
|
||||
page: AssetSelectionPage,
|
||||
guards: [AuthGuard],
|
||||
|
@ -76,6 +79,7 @@ part 'router.gr.dart';
|
|||
)
|
||||
class AppRouter extends _$AppRouter {
|
||||
final ApiService _apiService;
|
||||
|
||||
AppRouter(this._apiService) : super(authGuard: AuthGuard(_apiService));
|
||||
}
|
||||
|
||||
|
|
|
@ -69,9 +69,12 @@ class _$AppRouter extends RootStackRouter {
|
|||
routeData: routeData,
|
||||
child: SearchResultPage(key: args.key, searchTerm: args.searchTerm));
|
||||
},
|
||||
CreateSharedAlbumRoute.name: (routeData) {
|
||||
CreateAlbumRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<CreateAlbumRouteArgs>();
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData, child: const CreateSharedAlbumPage());
|
||||
routeData: routeData,
|
||||
child: CreateAlbumPage(
|
||||
key: args.key, isSharedAlbum: args.isSharedAlbum));
|
||||
},
|
||||
AssetSelectionRoute.name: (routeData) {
|
||||
return CustomPage<AssetSelectionPageResult?>(
|
||||
|
@ -136,6 +139,10 @@ class _$AppRouter extends RootStackRouter {
|
|||
SharingRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData, child: const SharingPage());
|
||||
},
|
||||
LibraryRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData, child: const LibraryPage());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -161,6 +168,10 @@ class _$AppRouter extends RootStackRouter {
|
|||
RouteConfig(SharingRoute.name,
|
||||
path: 'sharing-page',
|
||||
parent: TabControllerRoute.name,
|
||||
guards: [authGuard]),
|
||||
RouteConfig(LibraryRoute.name,
|
||||
path: 'library-page',
|
||||
parent: TabControllerRoute.name,
|
||||
guards: [authGuard])
|
||||
]),
|
||||
RouteConfig(ImageViewerRoute.name,
|
||||
|
@ -171,8 +182,8 @@ class _$AppRouter extends RootStackRouter {
|
|||
path: '/backup-controller-page', guards: [authGuard]),
|
||||
RouteConfig(SearchResultRoute.name,
|
||||
path: '/search-result-page', guards: [authGuard]),
|
||||
RouteConfig(CreateSharedAlbumRoute.name,
|
||||
path: '/create-shared-album-page', guards: [authGuard]),
|
||||
RouteConfig(CreateAlbumRoute.name,
|
||||
path: '/create-album-page', guards: [authGuard]),
|
||||
RouteConfig(AssetSelectionRoute.name,
|
||||
path: '/asset-selection-page', guards: [authGuard]),
|
||||
RouteConfig(SelectUserForSharingRoute.name,
|
||||
|
@ -334,12 +345,27 @@ class SearchResultRouteArgs {
|
|||
}
|
||||
|
||||
/// generated route for
|
||||
/// [CreateSharedAlbumPage]
|
||||
class CreateSharedAlbumRoute extends PageRouteInfo<void> {
|
||||
const CreateSharedAlbumRoute()
|
||||
: super(CreateSharedAlbumRoute.name, path: '/create-shared-album-page');
|
||||
/// [CreateAlbumPage]
|
||||
class CreateAlbumRoute extends PageRouteInfo<CreateAlbumRouteArgs> {
|
||||
CreateAlbumRoute({Key? key, required bool isSharedAlbum})
|
||||
: super(CreateAlbumRoute.name,
|
||||
path: '/create-album-page',
|
||||
args: CreateAlbumRouteArgs(key: key, isSharedAlbum: isSharedAlbum));
|
||||
|
||||
static const String name = 'CreateSharedAlbumRoute';
|
||||
static const String name = 'CreateAlbumRoute';
|
||||
}
|
||||
|
||||
class CreateAlbumRouteArgs {
|
||||
const CreateAlbumRouteArgs({this.key, required this.isSharedAlbum});
|
||||
|
||||
final Key? key;
|
||||
|
||||
final bool isSharedAlbum;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateAlbumRouteArgs{key: $key, isSharedAlbum: $isSharedAlbum}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
|
@ -492,3 +518,11 @@ class SharingRoute extends PageRouteInfo<void> {
|
|||
|
||||
static const String name = 'SharingRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [LibraryPage]
|
||||
class LibraryRoute extends PageRouteInfo<void> {
|
||||
const LibraryRoute() : super(LibraryRoute.name, path: 'library-page');
|
||||
|
||||
static const String name = 'LibraryRoute';
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
|
||||
|
||||
import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
|
||||
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
|
||||
|
||||
class TabNavigationObserver extends AutoRouterObserver {
|
||||
|
@ -37,6 +38,9 @@ class TabNavigationObserver extends AutoRouterObserver {
|
|||
ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
|
||||
}
|
||||
|
||||
if (route.name == 'LibraryRoute') {
|
||||
ref.read(albumProvider.notifier).getAllAlbums();
|
||||
}
|
||||
ref.watch(serverInfoProvider.notifier).getServerVersion();
|
||||
}
|
||||
}
|
||||
|
|
4
mobile/lib/shared/providers/api.provider.dart
Normal file
4
mobile/lib/shared/providers/api.provider.dart
Normal file
|
@ -0,0 +1,4 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
|
||||
final apiServiceProvider = Provider((ref) => ApiService());
|
|
@ -1,8 +1,5 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final apiServiceProvider = Provider((ref) => ApiService());
|
||||
|
||||
class ApiService {
|
||||
late ApiClient _apiClient;
|
||||
|
||||
|
@ -15,7 +12,6 @@ class ApiService {
|
|||
|
||||
setEndpoint(String endpoint) {
|
||||
_apiClient = ApiClient(basePath: endpoint);
|
||||
|
||||
userApi = UserApi(_apiClient);
|
||||
authenticationApi = AuthenticationApi(_apiClient);
|
||||
albumApi = AlbumApi(_apiClient);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
|
@ -11,6 +12,7 @@ final serverInfoServiceProvider = Provider(
|
|||
|
||||
class ServerInfoService {
|
||||
final ApiService _apiService;
|
||||
|
||||
ServerInfoService(this._apiService);
|
||||
|
||||
Future<ServerInfoResponseDto?> getServerInfo() async {
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:http/http.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/files_helper.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
|
||||
|
@ -18,6 +19,7 @@ class TabControllerPage extends ConsumerWidget {
|
|||
const HomeRoute(),
|
||||
SearchRoute(),
|
||||
const SharingRoute(),
|
||||
const LibraryRoute()
|
||||
],
|
||||
builder: (context, child, animation) {
|
||||
final tabsRouter = AutoTabsRouter.of(context);
|
||||
|
@ -34,12 +36,14 @@ class TabControllerPage extends ConsumerWidget {
|
|||
bottomNavigationBar: isMultiSelectEnable
|
||||
? null
|
||||
: BottomNavigationBar(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
backgroundColor: immichBackgroundColor,
|
||||
selectedLabelStyle: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
unselectedLabelStyle: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
currentIndex: tabsRouter.activeIndex,
|
||||
|
@ -59,6 +63,12 @@ class TabControllerPage extends ConsumerWidget {
|
|||
label: 'tab_controller_nav_sharing'.tr(),
|
||||
icon: const Icon(Icons.group_outlined),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
label: 'tab_controller_nav_library'.tr(),
|
||||
icon: const Icon(
|
||||
Icons.photo_album_outlined,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue