118 - Implement shared album feature (#124)

* New features 
  - Share album. Users can now create albums to share with existing people on the network.
  - Owner can delete the album.
  - Owner can invite the additional users to the album.
  - Shared users and the owner can add additional assets to the album.
* In the asset viewer, the user can swipe up to see detailed information and swip down to dismiss.
* Several UI enhancements.
This commit is contained in:
Alex
2022-04-23 21:08:45 -05:00
committed by GitHub
parent a3b84b3ca7
commit 4309104925
87 changed files with 3717 additions and 199 deletions

View File

@@ -1,9 +1,17 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
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/models/shared_album.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/routing/auth_guard.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:immich_mobile/shared/views/backup_controller_page.dart';
@@ -22,13 +30,31 @@ part 'router.gr.dart';
guards: [AuthGuard],
children: [
AutoRoute(page: HomePage, guards: [AuthGuard]),
AutoRoute(page: SearchPage, guards: [AuthGuard])
AutoRoute(page: SearchPage, guards: [AuthGuard]),
AutoRoute(page: SharingPage, guards: [AuthGuard])
],
),
AutoRoute(page: ImageViewerPage, guards: [AuthGuard]),
AutoRoute(page: VideoViewerPage, guards: [AuthGuard]),
AutoRoute(page: BackupControllerPage, guards: [AuthGuard]),
AutoRoute(page: SearchResultPage, guards: [AuthGuard]),
AutoRoute(page: CreateSharedAlbumPage, guards: [AuthGuard]),
CustomRoute<AssetSelectionPageResult?>(
page: AssetSelectionPage,
guards: [AuthGuard],
transitionsBuilder: TransitionsBuilders.slideBottom,
),
CustomRoute<List<String>>(
page: SelectUserForSharingPage,
guards: [AuthGuard],
transitionsBuilder: TransitionsBuilders.slideBottom,
),
AutoRoute(page: AlbumViewerPage, guards: [AuthGuard]),
CustomRoute<List<String>?>(
page: SelectAdditionalUserForSharingPage,
guards: [AuthGuard],
transitionsBuilder: TransitionsBuilders.slideBottom,
),
],
)
class AppRouter extends _$AppRouter {

View File

@@ -57,6 +57,42 @@ class _$AppRouter extends RootStackRouter {
routeData: routeData,
child: SearchResultPage(key: args.key, searchTerm: args.searchTerm));
},
CreateSharedAlbumRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData, child: const CreateSharedAlbumPage());
},
AssetSelectionRoute.name: (routeData) {
return CustomPage<AssetSelectionPageResult?>(
routeData: routeData,
child: const AssetSelectionPage(),
transitionsBuilder: TransitionsBuilders.slideBottom,
opaque: true,
barrierDismissible: false);
},
SelectUserForSharingRoute.name: (routeData) {
return CustomPage<List<String>>(
routeData: routeData,
child: const SelectUserForSharingPage(),
transitionsBuilder: TransitionsBuilders.slideBottom,
opaque: true,
barrierDismissible: false);
},
AlbumViewerRoute.name: (routeData) {
final args = routeData.argsAs<AlbumViewerRouteArgs>();
return MaterialPageX<dynamic>(
routeData: routeData,
child: AlbumViewerPage(key: args.key, albumId: args.albumId));
},
SelectAdditionalUserForSharingRoute.name: (routeData) {
final args = routeData.argsAs<SelectAdditionalUserForSharingRouteArgs>();
return CustomPage<List<String>?>(
routeData: routeData,
child: SelectAdditionalUserForSharingPage(
key: args.key, albumInfo: args.albumInfo),
transitionsBuilder: TransitionsBuilders.slideBottom,
opaque: true,
barrierDismissible: false);
},
HomeRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData, child: const HomePage());
@@ -66,6 +102,10 @@ class _$AppRouter extends RootStackRouter {
orElse: () => const SearchRouteArgs());
return MaterialPageX<dynamic>(
routeData: routeData, child: SearchPage(key: args.key));
},
SharingRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData, child: const SharingPage());
}
};
@@ -85,6 +125,10 @@ class _$AppRouter extends RootStackRouter {
RouteConfig(SearchRoute.name,
path: 'search-page',
parent: TabControllerRoute.name,
guards: [authGuard]),
RouteConfig(SharingRoute.name,
path: 'sharing-page',
parent: TabControllerRoute.name,
guards: [authGuard])
]),
RouteConfig(ImageViewerRoute.name,
@@ -94,7 +138,18 @@ class _$AppRouter extends RootStackRouter {
RouteConfig(BackupControllerRoute.name,
path: '/backup-controller-page', guards: [authGuard]),
RouteConfig(SearchResultRoute.name,
path: '/search-result-page', guards: [authGuard])
path: '/search-result-page', guards: [authGuard]),
RouteConfig(CreateSharedAlbumRoute.name,
path: '/create-shared-album-page', guards: [authGuard]),
RouteConfig(AssetSelectionRoute.name,
path: '/asset-selection-page', guards: [authGuard]),
RouteConfig(SelectUserForSharingRoute.name,
path: '/select-user-for-sharing-page', guards: [authGuard]),
RouteConfig(AlbumViewerRoute.name,
path: '/album-viewer-page', guards: [authGuard]),
RouteConfig(SelectAdditionalUserForSharingRoute.name,
path: '/select-additional-user-for-sharing-page',
guards: [authGuard])
];
}
@@ -223,6 +278,86 @@ class SearchResultRouteArgs {
}
}
/// generated route for
/// [CreateSharedAlbumPage]
class CreateSharedAlbumRoute extends PageRouteInfo<void> {
const CreateSharedAlbumRoute()
: super(CreateSharedAlbumRoute.name, path: '/create-shared-album-page');
static const String name = 'CreateSharedAlbumRoute';
}
/// generated route for
/// [AssetSelectionPage]
class AssetSelectionRoute extends PageRouteInfo<void> {
const AssetSelectionRoute()
: super(AssetSelectionRoute.name, path: '/asset-selection-page');
static const String name = 'AssetSelectionRoute';
}
/// generated route for
/// [SelectUserForSharingPage]
class SelectUserForSharingRoute extends PageRouteInfo<void> {
const SelectUserForSharingRoute()
: super(SelectUserForSharingRoute.name,
path: '/select-user-for-sharing-page');
static const String name = 'SelectUserForSharingRoute';
}
/// generated route for
/// [AlbumViewerPage]
class AlbumViewerRoute extends PageRouteInfo<AlbumViewerRouteArgs> {
AlbumViewerRoute({Key? key, required String albumId})
: super(AlbumViewerRoute.name,
path: '/album-viewer-page',
args: AlbumViewerRouteArgs(key: key, albumId: albumId));
static const String name = 'AlbumViewerRoute';
}
class AlbumViewerRouteArgs {
const AlbumViewerRouteArgs({this.key, required this.albumId});
final Key? key;
final String albumId;
@override
String toString() {
return 'AlbumViewerRouteArgs{key: $key, albumId: $albumId}';
}
}
/// generated route for
/// [SelectAdditionalUserForSharingPage]
class SelectAdditionalUserForSharingRoute
extends PageRouteInfo<SelectAdditionalUserForSharingRouteArgs> {
SelectAdditionalUserForSharingRoute(
{Key? key, required SharedAlbum albumInfo})
: super(SelectAdditionalUserForSharingRoute.name,
path: '/select-additional-user-for-sharing-page',
args: SelectAdditionalUserForSharingRouteArgs(
key: key, albumInfo: albumInfo));
static const String name = 'SelectAdditionalUserForSharingRoute';
}
class SelectAdditionalUserForSharingRouteArgs {
const SelectAdditionalUserForSharingRouteArgs(
{this.key, required this.albumInfo});
final Key? key;
final SharedAlbum albumInfo;
@override
String toString() {
return 'SelectAdditionalUserForSharingRouteArgs{key: $key, albumInfo: $albumInfo}';
}
}
/// generated route for
/// [HomePage]
class HomeRoute extends PageRouteInfo<void> {
@@ -251,3 +386,11 @@ class SearchRouteArgs {
return 'SearchRouteArgs{key: $key}';
}
}
/// generated route for
/// [SharingPage]
class SharingRoute extends PageRouteInfo<void> {
const SharingRoute() : super(SharingRoute.name, path: 'sharing-page');
static const String name = 'SharingRoute';
}

View File

@@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/shared/providers/server_info.provider.dart';
class TabNavigationObserver extends AutoRouterObserver {
@@ -21,7 +22,8 @@ class TabNavigationObserver extends AutoRouterObserver {
}
@override
Future<void> didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) async {
Future<void> didChangeTabRoute(
TabPageRoute route, TabPageRoute previousRoute) async {
// Perform tasks on re-visit to SearchRoute
if (route.name == 'SearchRoute') {
// Refresh Location State
@@ -29,6 +31,10 @@ class TabNavigationObserver extends AutoRouterObserver {
ref.refresh(getCuratedObjectProvider);
}
if (route.name == 'SharingRoute') {
ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
}
ref.watch(serverInfoProvider.notifier).getServerVersion();
}
}