From bb0b2a1f536996b079a0a7cf88e5d1df276d51d5 Mon Sep 17 00:00:00 2001 From: Matthias Rupp Date: Sun, 5 Feb 2023 04:25:11 +0100 Subject: [PATCH 01/25] feat(mobile): Library page rework (album sorting, favorites) (#1501) * Add album sorting * Change AppBar to match photos page behaviour * Add buttons * First crude implementation of the favorites page * Clean up * Add favorite button * i18n * Add star indicator to thumbnail * Add favorite logic to separate provider and fix favorite behavior in album * Review feedback (Add isFavorite variable) * dev: style buttons * dev: styled drop down button --------- Co-authored-by: Alex Tran --- mobile/assets/i18n/en-US.json | 5 + .../album/ui/album_viewer_thumbnail.dart | 15 ++ .../lib/modules/album/views/library_page.dart | 166 ++++++++++++++++-- .../asset_viewer/ui/top_control_app_bar.dart | 19 ++ .../asset_viewer/views/gallery_viewer.dart | 13 +- .../favorite/providers/favorite_provider.dart | 52 ++++++ .../modules/favorite/ui/favorite_image.dart | 36 ++++ .../favorite/views/favorites_page.dart | 68 +++++++ .../home/ui/asset_grid/thumbnail_image.dart | 11 ++ mobile/lib/routing/router.dart | 2 + mobile/lib/routing/router.gr.dart | 14 ++ mobile/lib/shared/models/asset.dart | 9 +- .../lib/shared/providers/asset.provider.dart | 20 +++ mobile/lib/shared/services/asset.service.dart | 9 + .../openapi/lib/model/album_response_dto.dart | 94 +++++----- .../test/asset_grid_data_structure_test.dart | 1 + 16 files changed, 478 insertions(+), 56 deletions(-) create mode 100644 mobile/lib/modules/favorite/providers/favorite_provider.dart create mode 100644 mobile/lib/modules/favorite/ui/favorite_image.dart create mode 100644 mobile/lib/modules/favorite/views/favorites_page.dart diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 60a6e4ff50..bc62a48a61 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -108,12 +108,17 @@ "experimental_settings_new_asset_list_title": "Enable experimental photo grid", "experimental_settings_subtitle": "Use at your own risk!", "experimental_settings_title": "Experimental", + "favorites_page_title": "Favorites", "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", "home_page_add_to_album_success": "Added {added} assets to album {album}.", "home_page_building_timeline": "Building the timeline", "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Albums", "library_page_new_album": "New album", + "library_page_favorites": "Favorites", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "Login", "login_form_email_hint": "youremail@email.com", "login_form_endpoint_hint": "http://your-server-ip:port/api", diff --git a/mobile/lib/modules/album/ui/album_viewer_thumbnail.dart b/mobile/lib/modules/album/ui/album_viewer_thumbnail.dart index ff4062aae6..df3fc5ac0a 100644 --- a/mobile/lib/modules/album/ui/album_viewer_thumbnail.dart +++ b/mobile/lib/modules/album/ui/album_viewer_thumbnail.dart @@ -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/favorite/providers/favorite_provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -26,6 +27,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget { ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer; final isMultiSelectionEnable = ref.watch(assetSelectionProvider).isMultiselectEnable; + final isFavorite = ref.watch(favoriteProvider).contains(asset.id); viewAsset() { AutoRouter.of(context).push( @@ -96,6 +98,18 @@ class AlbumViewerThumbnail extends HookConsumerWidget { ); } + buildAssetFavoriteIcon() { + return const Positioned( + left: 10, + bottom: 5, + child: Icon( + Icons.star, + color: Colors.white, + size: 18, + ), + ); + } + buildAssetSelectionIcon() { bool isSelected = selectedAssetsInAlbumViewer.contains(asset); @@ -143,6 +157,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget { child: Stack( children: [ buildThumbnailImage(), + if (isFavorite) buildAssetFavoriteIcon(), if (showStorageIndicator) buildAssetStoreLocationIcon(), if (!asset.isImage) buildVideoLabel(), if (isMultiSelectionEnable) buildAssetSelectionIcon(), diff --git a/mobile/lib/modules/album/views/library_page.dart b/mobile/lib/modules/album/views/library_page.dart index 8b40fbf7bc..1dc8ab64ab 100644 --- a/mobile/lib/modules/album/views/library_page.dart +++ b/mobile/lib/modules/album/views/library_page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -6,6 +7,7 @@ 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'; +import 'package:openapi/api.dart'; class LibraryPage extends HookConsumerWidget { const LibraryPage({Key? key}) : super(key: key); @@ -22,14 +24,11 @@ class LibraryPage extends HookConsumerWidget { [], ); - Widget buildAppBar() { - return const SliverAppBar( + AppBar buildAppBar() { + return AppBar( centerTitle: true, - floating: true, - pinned: false, - snap: false, automaticallyImplyLeading: false, - title: Text( + title: const Text( 'IMMICH', style: TextStyle( fontFamily: 'SnowburstOne', @@ -40,6 +39,74 @@ class LibraryPage extends HookConsumerWidget { ); } + final selectedAlbumSortOrder = useState(0); + + List sortedAlbums() { + if (selectedAlbumSortOrder.value == 0) { + return albums.sortedBy((album) => album.createdAt).reversed.toList(); + } + return albums.sortedBy((album) => album.albumName); + } + + Widget buildSortButton() { + final options = [ + "library_page_sort_created".tr(), + "library_page_sort_title".tr() + ]; + + return PopupMenuButton( + position: PopupMenuPosition.over, + itemBuilder: (BuildContext context) { + return options.mapIndexed>((index, option) { + final selected = selectedAlbumSortOrder.value == index; + return PopupMenuItem( + value: index, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 12.0), + child: Icon( + Icons.check, + color: selected + ? Theme.of(context).primaryColor + : Colors.transparent, + ), + ), + Text( + option, + style: TextStyle( + color: selected ? Theme.of(context).primaryColor : null, + fontSize: 12.0, + ), + ) + ], + ), + ); + }).toList(); + }, + onSelected: (int value) { + selectedAlbumSortOrder.value = value; + }, + child: Row( + children: [ + Icon( + Icons.swap_vert_rounded, + size: 18, + color: Theme.of(context).primaryColor, + ), + Text( + options[selectedAlbumSortOrder.value], + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor, + fontSize: 12.0, + ), + ), + ], + ), + ); + } + Widget buildCreateAlbumButton() { return GestureDetector( onTap: () { @@ -80,17 +147,90 @@ class LibraryPage extends HookConsumerWidget { ); } + Widget buildLibraryNavButton( + String label, + IconData icon, + Function() onClick, + ) { + return Expanded( + child: OutlinedButton.icon( + onPressed: onClick, + label: Padding( + padding: const EdgeInsets.only(left: 8.0), + child: Text( + label, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 12.0, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white + : Colors.black, + ), + ), + ), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.all(12), + side: BorderSide( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[600]! + : Colors.grey[300]!, + ), + alignment: Alignment.centerLeft, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6.0), + ), + ), + icon: Icon(icon, color: Theme.of(context).primaryColor), + ), + ); + } + return Scaffold( + appBar: buildAppBar(), body: CustomScrollView( slivers: [ - buildAppBar(), SliverToBoxAdapter( child: Padding( - padding: const EdgeInsets.all(12.0), - child: const Text( - 'library_page_albums', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + padding: const EdgeInsets.only( + left: 12.0, + right: 12.0, + top: 24.0, + bottom: 12.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + buildLibraryNavButton( + "library_page_favorites".tr(), Icons.star_border, () { + AutoRouter.of(context).navigate(const FavoritesRoute()); + }), + const SizedBox(width: 12.0), + buildLibraryNavButton( + "library_page_sharing".tr(), Icons.group_outlined, () { + AutoRouter.of(context).navigate(const SharingRoute()); + }), + ], + ), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only( + top: 12.0, + left: 12.0, + right: 12.0, + bottom: 20.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'library_page_albums', + style: TextStyle(fontWeight: FontWeight.bold), + ).tr(), + buildSortButton(), + ], + ), ), ), SliverPadding( @@ -100,7 +240,7 @@ class LibraryPage extends HookConsumerWidget { spacing: 12, children: [ buildCreateAlbumButton(), - for (var album in albums) + for (var album in sortedAlbums()) AlbumThumbnailCard( album: album, ), diff --git a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart index 2ae0c131cc..61a9b64d62 100644 --- a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart +++ b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart @@ -14,6 +14,8 @@ class TopControlAppBar extends HookConsumerWidget { required this.onAddToAlbumPressed, required this.onToggleMotionVideo, required this.isPlayingMotionVideo, + required this.onFavorite, + required this.isFavorite, }) : super(key: key); final Asset asset; @@ -22,13 +24,29 @@ class TopControlAppBar extends HookConsumerWidget { final VoidCallback onToggleMotionVideo; final VoidCallback onDeletePressed; final VoidCallback onAddToAlbumPressed; + final VoidCallback onFavorite; final Function onSharePressed; final bool isPlayingMotionVideo; + final bool isFavorite; @override Widget build(BuildContext context, WidgetRef ref) { double iconSize = 18.0; + Widget buildFavoriteButton() { + return IconButton( + iconSize: iconSize, + splashRadius: iconSize, + onPressed: () { + onFavorite(); + }, + icon: Icon( + isFavorite ? Icons.star : Icons.star_border, + color: Colors.grey[200], + ), + ); + } + return AppBar( foregroundColor: Colors.grey[100], backgroundColor: Colors.transparent, @@ -43,6 +61,7 @@ class TopControlAppBar extends HookConsumerWidget { ), ), actions: [ + if (asset.isRemote) buildFavoriteButton(), if (asset.livePhotoVideoId != null) IconButton( iconSize: iconSize, diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index 44e2f4cea0..0a9097dd2d 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_s import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart'; import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart'; +import 'package:immich_mobile/modules/favorite/providers/favorite_provider.dart'; import 'package:immich_mobile/shared/services/asset.service.dart'; import 'package:immich_mobile/modules/home/ui/delete_diaglog.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; @@ -69,7 +70,11 @@ class GalleryViewerPage extends HookConsumerWidget { [], ); - void getAssetExif() async { + void toggleFavorite(Asset asset) { + ref.watch(favoriteProvider.notifier).toggleFavorite(asset); + } + + getAssetExif() async { if (assetList[indexOfAsset.value].isRemote) { assetDetail = await ref .watch(assetServiceProvider) @@ -236,9 +241,15 @@ class GalleryViewerPage extends HookConsumerWidget { child: TopControlAppBar( isPlayingMotionVideo: isPlayingMotionVideo.value, asset: assetList[indexOfAsset.value], + isFavorite: ref.watch(favoriteProvider).contains( + assetList[indexOfAsset.value].id, + ), onMoreInfoPressed: () { showInfo(); }, + onFavorite: () { + toggleFavorite(assetList[indexOfAsset.value]); + }, onDownloadPressed: assetList[indexOfAsset.value].isLocal ? null : () { diff --git a/mobile/lib/modules/favorite/providers/favorite_provider.dart b/mobile/lib/modules/favorite/providers/favorite_provider.dart new file mode 100644 index 0000000000..49176cd067 --- /dev/null +++ b/mobile/lib/modules/favorite/providers/favorite_provider.dart @@ -0,0 +1,52 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/providers/asset.provider.dart'; + +class FavoriteSelectionNotifier extends StateNotifier> { + FavoriteSelectionNotifier(this.ref) : super({}) { + state = ref.watch(assetProvider).allAssets + .where((asset) => asset.isFavorite) + .map((asset) => asset.id) + .toSet(); + } + + final Ref ref; + + void _setFavoriteForAssetId(String id, bool favorite) { + if (!favorite) { + state = state.difference({id}); + } else { + state = state.union({id}); + } + } + + bool _isFavorite(String id) { + return state.contains(id); + } + + Future toggleFavorite(Asset asset) async { + if (!asset.isRemote) return; // TODO support local favorite assets + + _setFavoriteForAssetId(asset.id, !_isFavorite(asset.id)); + + await ref.watch(assetProvider.notifier).toggleFavorite( + asset, + state.contains(asset.id), + ); + } +} + +final favoriteProvider = + StateNotifierProvider>((ref) { + return FavoriteSelectionNotifier(ref); +}); + +final favoriteAssetProvider = StateProvider((ref) { + final favorites = ref.watch(favoriteProvider); + + return ref + .watch(assetProvider) + .allAssets + .where((element) => favorites.contains(element.id)) + .toList(); +}); diff --git a/mobile/lib/modules/favorite/ui/favorite_image.dart b/mobile/lib/modules/favorite/ui/favorite_image.dart new file mode 100644 index 0000000000..26bef32f96 --- /dev/null +++ b/mobile/lib/modules/favorite/ui/favorite_image.dart @@ -0,0 +1,36 @@ + +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/ui/immich_image.dart'; + +class FavoriteImage extends HookConsumerWidget { + final Asset asset; + final List assets; + + const FavoriteImage(this.asset, this.assets, {super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + void viewAsset() { + AutoRouter.of(context).push( + GalleryViewerRoute( + asset: asset, + assetList: assets, + ), + ); + } + + return GestureDetector( + onTap: viewAsset, + child: ImmichImage( + asset, + width: 300, + height: 300, + ), + ); + } + +} \ No newline at end of file diff --git a/mobile/lib/modules/favorite/views/favorites_page.dart b/mobile/lib/modules/favorite/views/favorites_page.dart new file mode 100644 index 0000000000..657bfe7939 --- /dev/null +++ b/mobile/lib/modules/favorite/views/favorites_page.dart @@ -0,0 +1,68 @@ +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/modules/favorite/providers/favorite_provider.dart'; +import 'package:immich_mobile/modules/favorite/ui/favorite_image.dart'; +import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; +import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; + +class FavoritesPage extends HookConsumerWidget { + const FavoritesPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + AppBar buildAppBar() { + return AppBar( + leading: IconButton( + onPressed: () => AutoRouter.of(context).pop(), + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + centerTitle: true, + automaticallyImplyLeading: false, + title: const Text( + 'favorites_page_title', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ).tr(), + ); + } + + Widget buildImageGrid() { + final appSettingService = ref.watch(appSettingsServiceProvider); + + if (ref.watch(favoriteAssetProvider).isNotEmpty) { + return SliverPadding( + padding: const EdgeInsets.only(top: 10.0), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: + appSettingService.getSetting(AppSettingsEnum.tilesPerRow), + crossAxisSpacing: 5.0, + mainAxisSpacing: 5, + ), + delegate: SliverChildBuilderDelegate( + ( + BuildContext context, + int index, + ) { + return FavoriteImage( + ref.watch(favoriteAssetProvider)[index], + ref.watch(favoriteAssetProvider), + ); + }, + childCount: ref.watch(favoriteAssetProvider).length, + ), + ), + ); + } + return const SliverToBoxAdapter(); + } + + return Scaffold( + appBar: buildAppBar(), + body: CustomScrollView( + slivers: [buildImageGrid()], + ), + ); + } +} diff --git a/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart b/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart index 3dd5aca99c..e2d6f54b7d 100644 --- a/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart +++ b/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/modules/favorite/providers/favorite_provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/models/asset.dart'; @@ -110,6 +111,16 @@ class ThumbnailImage extends HookConsumerWidget { size: 18, ), ), + if (ref.watch(favoriteProvider).contains(asset.id)) + const Positioned( + left: 10, + bottom: 5, + child: Icon( + Icons.star, + color: Colors.white, + size: 18, + ), + ), if (!asset.isImage) Positioned( top: 5, diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 873873524f..ace3629288 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -15,6 +15,7 @@ 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/backup_controller_page.dart'; import 'package:immich_mobile/modules/backup/views/failed_backup_status_page.dart'; +import 'package:immich_mobile/modules/favorite/views/favorites_page.dart'; import 'package:immich_mobile/modules/home/views/home_page.dart'; import 'package:immich_mobile/modules/login/views/change_password_page.dart'; import 'package:immich_mobile/modules/login/views/login_page.dart'; @@ -55,6 +56,7 @@ part 'router.gr.dart'; AutoRoute(page: BackupControllerPage, guards: [AuthGuard]), AutoRoute(page: SearchResultPage, guards: [AuthGuard]), AutoRoute(page: CreateAlbumPage, guards: [AuthGuard]), + AutoRoute(page: FavoritesPage, guards: [AuthGuard]), CustomRoute( page: AssetSelectionPage, guards: [AuthGuard], diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index b307fc6cb5..0a1311460d 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -77,6 +77,10 @@ class _$AppRouter extends RootStackRouter { isSharedAlbum: args.isSharedAlbum, initialAssets: args.initialAssets)); }, + FavoritesRoute.name: (routeData) { + return MaterialPageX( + routeData: routeData, child: const FavoritesPage()); + }, AssetSelectionRoute.name: (routeData) { return CustomPage( routeData: routeData, @@ -197,6 +201,8 @@ class _$AppRouter extends RootStackRouter { path: '/search-result-page', guards: [authGuard]), RouteConfig(CreateAlbumRoute.name, path: '/create-album-page', guards: [authGuard]), + RouteConfig(FavoritesRoute.name, + path: '/favorites-page', guards: [authGuard]), RouteConfig(AssetSelectionRoute.name, path: '/asset-selection-page', guards: [authGuard]), RouteConfig(SelectUserForSharingRoute.name, @@ -386,6 +392,14 @@ class CreateAlbumRouteArgs { } } +/// generated route for +/// [FavoritesPage] +class FavoritesRoute extends PageRouteInfo { + const FavoritesRoute() : super(FavoritesRoute.name, path: '/favorites-page'); + + static const String name = 'FavoritesRoute'; +} + /// generated route for /// [AssetSelectionPage] class AssetSelectionRoute extends PageRouteInfo { diff --git a/mobile/lib/shared/models/asset.dart b/mobile/lib/shared/models/asset.dart index e3e8afbed7..567e42a1a0 100644 --- a/mobile/lib/shared/models/asset.dart +++ b/mobile/lib/shared/models/asset.dart @@ -23,7 +23,8 @@ class Asset { latitude = remote.exifInfo?.latitude?.toDouble(), longitude = remote.exifInfo?.longitude?.toDouble(), exifInfo = - remote.exifInfo != null ? ExifInfo.fromDto(remote.exifInfo!) : null; + remote.exifInfo != null ? ExifInfo.fromDto(remote.exifInfo!) : null, + isFavorite = remote.isFavorite; Asset.local(AssetEntity local, String owner) : localId = local.id, @@ -37,6 +38,7 @@ class Asset { deviceId = Hive.box(userInfoBox).get(deviceIdKey), ownerId = owner, modifiedAt = local.modifiedDateTime.toUtc(), + isFavorite = local.isFavorite, createdAt = local.createDateTime.toUtc() { if (createdAt.year == 1970) { createdAt = modifiedAt; @@ -59,6 +61,7 @@ class Asset { required this.fileName, this.livePhotoVideoId, this.exifInfo, + required this.isFavorite, }); AssetEntity? _local; @@ -111,6 +114,8 @@ class Asset { ExifInfo? exifInfo; + bool isFavorite; + String get id => isLocal ? localId.toString() : remoteId!; String get name => p.withoutExtension(fileName); @@ -150,6 +155,7 @@ class Asset { json["height"] = height; json["fileName"] = fileName; json["livePhotoVideoId"] = livePhotoVideoId; + json["isFavorite"] = isFavorite; if (exifInfo != null) { json["exifInfo"] = exifInfo!.toJson(); } @@ -179,6 +185,7 @@ class Asset { fileName: json["fileName"], livePhotoVideoId: json["livePhotoVideoId"], exifInfo: ExifInfo.fromJson(json["exifInfo"]), + isFavorite: json["isFavorite"], ); } return null; diff --git a/mobile/lib/shared/providers/asset.provider.dart b/mobile/lib/shared/providers/asset.provider.dart index 4376cf36ab..6655955850 100644 --- a/mobile/lib/shared/providers/asset.provider.dart +++ b/mobile/lib/shared/providers/asset.provider.dart @@ -268,6 +268,26 @@ class AssetNotifier extends StateNotifier { .where((a) => a.status == DeleteAssetStatus.SUCCESS) .map((a) => a.id); } + + Future toggleFavorite(Asset asset, bool status) async { + final newAsset = await _assetService.changeFavoriteStatus(asset, status); + + if (newAsset == null) { + log.severe("Change favorite status failed for asset ${asset.id}"); + return asset.isFavorite; + } + + await _updateAssetsState( + state.allAssets.map((a) { + if (asset.id == a.id) { + return Asset.remote(newAsset); + } + return a; + }).toList(), + ); + + return newAsset.isFavorite; + } } final assetProvider = StateNotifierProvider((ref) { diff --git a/mobile/lib/shared/services/asset.service.dart b/mobile/lib/shared/services/asset.service.dart index ca33c5fb8c..0cc04936fb 100644 --- a/mobile/lib/shared/services/asset.service.dart +++ b/mobile/lib/shared/services/asset.service.dart @@ -104,4 +104,13 @@ class AssetService { return null; } } + + Future updateAsset(Asset asset, UpdateAssetDto updateAssetDto) async { + return await _apiService.assetApi.updateAsset(asset.id, updateAssetDto); + } + + Future changeFavoriteStatus(Asset asset, bool isFavorite) { + return updateAsset(asset, UpdateAssetDto(isFavorite: isFavorite)); + } + } diff --git a/mobile/openapi/lib/model/album_response_dto.dart b/mobile/openapi/lib/model/album_response_dto.dart index 282344a16f..7858e857d6 100644 --- a/mobile/openapi/lib/model/album_response_dto.dart +++ b/mobile/openapi/lib/model/album_response_dto.dart @@ -43,48 +43,51 @@ class AlbumResponseDto { List assets; @override - bool operator ==(Object other) => identical(this, other) || other is AlbumResponseDto && - other.assetCount == assetCount && - other.id == id && - other.ownerId == ownerId && - other.albumName == albumName && - other.createdAt == createdAt && - other.albumThumbnailAssetId == albumThumbnailAssetId && - other.shared == shared && - other.sharedUsers == sharedUsers && - other.assets == assets; + bool operator ==(Object other) => + identical(this, other) || + other is AlbumResponseDto && + other.assetCount == assetCount && + other.id == id && + other.ownerId == ownerId && + other.albumName == albumName && + other.createdAt == createdAt && + other.albumThumbnailAssetId == albumThumbnailAssetId && + other.shared == shared && + other.sharedUsers == sharedUsers && + other.assets == assets; @override int get hashCode => - // ignore: unnecessary_parenthesis - (assetCount.hashCode) + - (id.hashCode) + - (ownerId.hashCode) + - (albumName.hashCode) + - (createdAt.hashCode) + - (albumThumbnailAssetId == null ? 0 : albumThumbnailAssetId!.hashCode) + - (shared.hashCode) + - (sharedUsers.hashCode) + - (assets.hashCode); + // ignore: unnecessary_parenthesis + (assetCount.hashCode) + + (id.hashCode) + + (ownerId.hashCode) + + (albumName.hashCode) + + (createdAt.hashCode) + + (albumThumbnailAssetId == null ? 0 : albumThumbnailAssetId!.hashCode) + + (shared.hashCode) + + (sharedUsers.hashCode) + + (assets.hashCode); @override - String toString() => 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]'; + String toString() => + 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]'; Map toJson() { final json = {}; - json[r'assetCount'] = this.assetCount; - json[r'id'] = this.id; - json[r'ownerId'] = this.ownerId; - json[r'albumName'] = this.albumName; - json[r'createdAt'] = this.createdAt; + json[r'assetCount'] = this.assetCount; + json[r'id'] = this.id; + json[r'ownerId'] = this.ownerId; + json[r'albumName'] = this.albumName; + json[r'createdAt'] = this.createdAt; if (this.albumThumbnailAssetId != null) { json[r'albumThumbnailAssetId'] = this.albumThumbnailAssetId; } else { // json[r'albumThumbnailAssetId'] = null; } - json[r'shared'] = this.shared; - json[r'sharedUsers'] = this.sharedUsers; - json[r'assets'] = this.assets; + json[r'shared'] = this.shared; + json[r'sharedUsers'] = this.sharedUsers; + json[r'assets'] = this.assets; return json; } @@ -98,13 +101,13 @@ class AlbumResponseDto { // Ensure that the map contains the required keys. // Note 1: the values aren't checked for validity beyond being non-null. // Note 2: this code is stripped in release mode! - assert(() { - requiredKeys.forEach((key) { - assert(json.containsKey(key), 'Required key "AlbumResponseDto[$key]" is missing from JSON.'); - assert(json[key] != null, 'Required key "AlbumResponseDto[$key]" has a null value in JSON.'); - }); - return true; - }()); + // assert(() { + // requiredKeys.forEach((key) { + // assert(json.containsKey(key), 'Required key "AlbumResponseDto[$key]" is missing from JSON.'); + // assert(json[key] != null, 'Required key "AlbumResponseDto[$key]" has a null value in JSON.'); + // }); + // return true; + // }()); return AlbumResponseDto( assetCount: mapValueOfType(json, r'assetCount')!, @@ -112,7 +115,8 @@ class AlbumResponseDto { ownerId: mapValueOfType(json, r'ownerId')!, albumName: mapValueOfType(json, r'albumName')!, createdAt: mapValueOfType(json, r'createdAt')!, - albumThumbnailAssetId: mapValueOfType(json, r'albumThumbnailAssetId'), + albumThumbnailAssetId: + mapValueOfType(json, r'albumThumbnailAssetId'), shared: mapValueOfType(json, r'shared')!, sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers'])!, assets: AssetResponseDto.listFromJson(json[r'assets'])!, @@ -121,7 +125,10 @@ class AlbumResponseDto { return null; } - static List? listFromJson(dynamic json, {bool growable = false,}) { + static List? listFromJson( + dynamic json, { + bool growable = false, + }) { final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { @@ -149,12 +156,18 @@ class AlbumResponseDto { } // maps a json object with a list of AlbumResponseDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + static Map> mapListFromJson( + dynamic json, { + bool growable = false, + }) { final map = >{}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = AlbumResponseDto.listFromJson(entry.value, growable: growable,); + final value = AlbumResponseDto.listFromJson( + entry.value, + growable: growable, + ); if (value != null) { map[entry.key] = value; } @@ -176,4 +189,3 @@ class AlbumResponseDto { 'assets', }; } - diff --git a/mobile/test/asset_grid_data_structure_test.dart b/mobile/test/asset_grid_data_structure_test.dart index e957796de6..20aa2b64d3 100644 --- a/mobile/test/asset_grid_data_structure_test.dart +++ b/mobile/test/asset_grid_data_structure_test.dart @@ -20,6 +20,7 @@ void main() { modifiedAt: date, durationInSeconds: 0, fileName: '', + isFavorite: false, ), ); } From f38c7a4b7e8b08caa0f9d8e03ec87b0786dca09d Mon Sep 17 00:00:00 2001 From: martyfuhry Date: Sun, 5 Feb 2023 07:57:07 -0500 Subject: [PATCH 02/25] feat(mobile): Tap to enter immersive mode on gallery viewer (#1546) --- .../asset_viewer/views/gallery_viewer.dart | 21 ++++++++++++++----- .../asset_viewer/views/video_viewer_page.dart | 16 ++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index 0a9097dd2d..be3d2c2c35 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -52,6 +52,7 @@ class GalleryViewerPage extends HookConsumerWidget { final showAppBar = useState(true); final indexOfAsset = useState(assetList.indexOf(asset)); final isPlayingMotionVideo = useState(false); + final isPlayingVideo = useState(false); late Offset localPosition; final authToken = 'Bearer ${box.get(accessTokenKey)}'; @@ -233,9 +234,13 @@ class GalleryViewerPage extends HookConsumerWidget { } buildAppBar() { + final show = (showAppBar.value || // onTap has the final say + (showAppBar.value && !isZoomed.value)) && + !isPlayingVideo.value; + return AnimatedOpacity( duration: const Duration(milliseconds: 100), - opacity: (showAppBar.value || !isZoomed.value) ? 1.0 : 0.0, + opacity: show ? 1.0 : 0.0, child: Container( color: Colors.black.withOpacity(0.4), child: TopControlAppBar( @@ -312,8 +317,14 @@ class GalleryViewerPage extends HookConsumerWidget { // Use the WEBP Thumbnail as a placeholder for the JPEG thumbnail to acheive // Three-Stage Loading (WEBP -> JPEG -> Original) final webPThumbnail = CachedNetworkImage( - imageUrl: getThumbnailUrl(asset), - cacheKey: getThumbnailCacheKey(asset), + imageUrl: getThumbnailUrl( + asset, + type: api.ThumbnailFormat.WEBP, + ), + cacheKey: getThumbnailCacheKey( + asset, + type: api.ThumbnailFormat.WEBP, + ), httpHeaders: {'Authorization': authToken}, progressIndicatorBuilder: (_, __, ___) => const Center( child: ImmichLoadingIndicator(), @@ -377,14 +388,14 @@ class GalleryViewerPage extends HookConsumerWidget { onDragStart: (_, details, __) => localPosition = details.localPosition, onDragUpdate: (_, details, __) => handleSwipeUpDown(details), - onTapDown: (_, __, ___) => - showAppBar.value = !showAppBar.value, heroAttributes: PhotoViewHeroAttributes(tag: assetList[index].id), maxScale: 1.0, minScale: 1.0, child: SafeArea( child: VideoViewerPage( + onPlaying: () => isPlayingVideo.value = true, + onPaused: () => isPlayingVideo.value = false, asset: assetList[index], isMotionVideo: isPlayingMotionVideo.value, onVideoEnded: () { diff --git a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart index adf2cfa324..0dfa7ea802 100644 --- a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart +++ b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart @@ -17,12 +17,16 @@ class VideoViewerPage extends HookConsumerWidget { final Asset asset; final bool isMotionVideo; final VoidCallback onVideoEnded; + final VoidCallback? onPlaying; + final VoidCallback? onPaused; const VideoViewerPage({ Key? key, required this.asset, required this.isMotionVideo, required this.onVideoEnded, + this.onPlaying, + this.onPaused, }) : super(key: key); @override @@ -63,6 +67,8 @@ class VideoViewerPage extends HookConsumerWidget { jwtToken: jwtToken, isMotionVideo: isMotionVideo, onVideoEnded: onVideoEnded, + onPaused: onPaused, + onPlaying: onPlaying, ), if (downloadAssetStatus == DownloadAssetStatus.loading) const Center( @@ -89,6 +95,9 @@ class VideoThumbnailPlayer extends StatefulWidget { final bool isMotionVideo; final VoidCallback onVideoEnded; + final Function()? onPlaying; + final Function()? onPaused; + const VideoThumbnailPlayer({ Key? key, this.url, @@ -96,6 +105,8 @@ class VideoThumbnailPlayer extends StatefulWidget { this.file, required this.onVideoEnded, required this.isMotionVideo, + this.onPlaying, + this.onPaused, }) : super(key: key); @override @@ -112,6 +123,11 @@ class _VideoThumbnailPlayerState extends State { initializePlayer(); videoPlayerController.addListener(() { + if (videoPlayerController.value.isPlaying) { + widget.onPlaying?.call(); + } else if (!videoPlayerController.value.isPlaying) { + widget.onPaused?.call(); + } if (videoPlayerController.value.position == videoPlayerController.value.duration) { widget.onVideoEnded(); From 16183791f3a5f7d389e8fe1da24085fdf4790b1c Mon Sep 17 00:00:00 2001 From: martyfuhry Date: Sun, 5 Feb 2023 09:07:02 -0500 Subject: [PATCH 03/25] feat(mobile): Removed stay logged in checkbox and made it enabled by default (#1550) * removed stay logged in checkbox and made it enabled by default * adds padding to login button * removed all isSaveLogin * fix: logout would re-login with previous credential upon app restart --------- Co-authored-by: Alex --- .../models/hive_saved_login_info.model.dart | 4 -- .../models/hive_saved_login_info.model.g.dart | 5 +-- .../providers/authentication.provider.dart | 43 +++++-------------- mobile/lib/modules/login/ui/login_form.dart | 36 +--------------- mobile/lib/shared/views/splash_screen.dart | 3 +- 5 files changed, 14 insertions(+), 77 deletions(-) diff --git a/mobile/lib/modules/login/models/hive_saved_login_info.model.dart b/mobile/lib/modules/login/models/hive_saved_login_info.model.dart index e807fc4780..456dbadbf2 100644 --- a/mobile/lib/modules/login/models/hive_saved_login_info.model.dart +++ b/mobile/lib/modules/login/models/hive_saved_login_info.model.dart @@ -13,9 +13,6 @@ class HiveSavedLoginInfo { @HiveField(2) String serverUrl; - @HiveField(3, defaultValue: false) - bool isSaveLogin; - @HiveField(4, defaultValue: "") String accessToken; @@ -23,7 +20,6 @@ class HiveSavedLoginInfo { required this.email, required this.password, required this.serverUrl, - required this.isSaveLogin, required this.accessToken, }); } diff --git a/mobile/lib/modules/login/models/hive_saved_login_info.model.g.dart b/mobile/lib/modules/login/models/hive_saved_login_info.model.g.dart index 27c1d19672..c86ccdfa4d 100644 --- a/mobile/lib/modules/login/models/hive_saved_login_info.model.g.dart +++ b/mobile/lib/modules/login/models/hive_saved_login_info.model.g.dart @@ -20,7 +20,6 @@ class HiveSavedLoginInfoAdapter extends TypeAdapter { email: fields[0] as String, password: fields[1] as String, serverUrl: fields[2] as String, - isSaveLogin: fields[3] == null ? false : fields[3] as bool, accessToken: fields[4] == null ? '' : fields[4] as String, ); } @@ -28,15 +27,13 @@ class HiveSavedLoginInfoAdapter extends TypeAdapter { @override void write(BinaryWriter writer, HiveSavedLoginInfo obj) { writer - ..writeByte(5) + ..writeByte(4) ..writeByte(0) ..write(obj.email) ..writeByte(1) ..write(obj.password) ..writeByte(2) ..write(obj.serverUrl) - ..writeByte(3) - ..write(obj.isSaveLogin) ..writeByte(4) ..write(obj.accessToken); } diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index 79416149a4..5b47b1c632 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -55,7 +55,6 @@ class AuthenticationNotifier extends StateNotifier { String email, String password, String serverUrl, - bool isSavedLoginInfo, ) async { try { // Resolve API server endpoint from user provided serverUrl @@ -83,7 +82,6 @@ class AuthenticationNotifier extends StateNotifier { return setSuccessLoginInfo( accessToken: loginResponse.accessToken, serverUrl: serverUrl, - isSavedLoginInfo: isSavedLoginInfo, ); } catch (e) { HapticFeedback.vibrate(); @@ -100,21 +98,9 @@ class AuthenticationNotifier extends StateNotifier { _assetCacheService.invalidate(), _albumCacheService.invalidate(), _sharedAlbumCacheService.invalidate(), + Hive.box(hiveLoginInfoBox).delete(savedLoginInfoKey) ]); - // Remove login info from local storage - var loginInfo = - Hive.box(hiveLoginInfoBox).get(savedLoginInfoKey); - if (loginInfo != null) { - loginInfo.email = ""; - loginInfo.password = ""; - loginInfo.isSaveLogin = false; - - await Hive.box(hiveLoginInfoBox).put( - savedLoginInfoKey, - loginInfo, - ); - } return true; } @@ -156,7 +142,6 @@ class AuthenticationNotifier extends StateNotifier { Future setSuccessLoginInfo({ required String accessToken, required String serverUrl, - required bool isSavedLoginInfo, }) async { _apiService.setAccessToken(accessToken); var userResponseDto = await _apiService.userApi.getMyUserInfo(); @@ -181,22 +166,16 @@ class AuthenticationNotifier extends StateNotifier { deviceType: deviceInfo["deviceType"], ); - if (isSavedLoginInfo) { - // Save login info to local storage - Hive.box(hiveLoginInfoBox).put( - savedLoginInfoKey, - HiveSavedLoginInfo( - email: "", - password: "", - isSaveLogin: true, - serverUrl: serverUrl, - accessToken: accessToken, - ), - ); - } else { - Hive.box(hiveLoginInfoBox) - .delete(savedLoginInfoKey); - } + // Save login info to local storage + Hive.box(hiveLoginInfoBox).put( + savedLoginInfoKey, + HiveSavedLoginInfo( + email: "", + password: "", + serverUrl: serverUrl, + accessToken: accessToken, + ), + ); } // Register device info diff --git a/mobile/lib/modules/login/ui/login_form.dart b/mobile/lib/modules/login/ui/login_form.dart index b47e64bd0d..c834fc1543 100644 --- a/mobile/lib/modules/login/ui/login_form.dart +++ b/mobile/lib/modules/login/ui/login_form.dart @@ -29,7 +29,6 @@ class LoginForm extends HookConsumerWidget { useTextEditingController.fromValue(TextEditingValue.empty); final apiService = ref.watch(apiServiceProvider); final serverEndpointFocusNode = useFocusNode(); - final isSaveLoginInfo = useState(false); final isLoading = useState(false); final isOauthEnable = useState(false); final oAuthButtonLabel = useState('OAuth'); @@ -75,7 +74,6 @@ class LoginForm extends HookConsumerWidget { usernameController.text = loginInfo.email; passwordController.text = loginInfo.password; serverEndpointController.text = loginInfo.serverUrl; - isSaveLoginInfo.value = loginInfo.isSaveLogin; } getServeLoginConfig(); @@ -88,7 +86,6 @@ class LoginForm extends HookConsumerWidget { usernameController.text = 'testuser@email.com'; passwordController.text = 'password'; serverEndpointController.text = 'http://10.1.15.216:2283/api'; - isSaveLoginInfo.value = true; } return Center( @@ -124,30 +121,6 @@ class LoginForm extends HookConsumerWidget { controller: serverEndpointController, focusNode: serverEndpointFocusNode, ), - CheckboxListTile( - activeColor: Theme.of(context).primaryColor, - contentPadding: const EdgeInsets.symmetric(horizontal: 8), - dense: true, - side: const BorderSide(color: Colors.grey, width: 1.5), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5), - ), - enableFeedback: true, - title: const Text( - "login_form_save_login", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.grey, - ), - ).tr(), - value: isSaveLoginInfo.value, - onChanged: (switchValue) { - if (switchValue != null) { - isSaveLoginInfo.value = switchValue; - } - }, - ), if (isLoading.value) const SizedBox( width: 24, @@ -161,11 +134,11 @@ class LoginForm extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ + const SizedBox(height: 18), LoginButton( emailController: usernameController, passwordController: passwordController, serverEndpointController: serverEndpointController, - isSavedLoginInfo: isSaveLoginInfo.value, ), if (isOauthEnable.value) ...[ Padding( @@ -181,7 +154,6 @@ class LoginForm extends HookConsumerWidget { ), OAuthLoginButton( serverEndpointController: serverEndpointController, - isSavedLoginInfo: isSaveLoginInfo.value, buttonLabel: oAuthButtonLabel.value, isLoading: isLoading, onLoginSuccess: () { @@ -304,14 +276,12 @@ class LoginButton extends ConsumerWidget { final TextEditingController emailController; final TextEditingController passwordController; final TextEditingController serverEndpointController; - final bool isSavedLoginInfo; const LoginButton({ Key? key, required this.emailController, required this.passwordController, required this.serverEndpointController, - required this.isSavedLoginInfo, }) : super(key: key); @override @@ -329,7 +299,6 @@ class LoginButton extends ConsumerWidget { emailController.text, passwordController.text, serverEndpointController.text, - isSavedLoginInfo, ); if (isAuthenticated) { @@ -361,7 +330,6 @@ class LoginButton extends ConsumerWidget { class OAuthLoginButton extends ConsumerWidget { final TextEditingController serverEndpointController; - final bool isSavedLoginInfo; final ValueNotifier isLoading; final VoidCallback onLoginSuccess; final String buttonLabel; @@ -369,7 +337,6 @@ class OAuthLoginButton extends ConsumerWidget { const OAuthLoginButton({ Key? key, required this.serverEndpointController, - required this.isSavedLoginInfo, required this.isLoading, required this.onLoginSuccess, required this.buttonLabel, @@ -407,7 +374,6 @@ class OAuthLoginButton extends ConsumerWidget { .watch(authenticationProvider.notifier) .setSuccessLoginInfo( accessToken: loginResponseDto.accessToken, - isSavedLoginInfo: isSavedLoginInfo, serverUrl: serverEndpointController.text, ); diff --git a/mobile/lib/shared/views/splash_screen.dart b/mobile/lib/shared/views/splash_screen.dart index 7aa973fcf6..f5ad5bb384 100644 --- a/mobile/lib/shared/views/splash_screen.dart +++ b/mobile/lib/shared/views/splash_screen.dart @@ -29,7 +29,6 @@ class SplashScreenPage extends HookConsumerWidget { .read(authenticationProvider.notifier) .setSuccessLoginInfo( accessToken: loginInfo.accessToken, - isSavedLoginInfo: true, serverUrl: loginInfo.serverUrl, ); if (isSuccess) { @@ -47,7 +46,7 @@ class SplashScreenPage extends HookConsumerWidget { useEffect( () { - if (loginInfo?.isSaveLogin == true) { + if (loginInfo != null) { performLoggingIn(); } else { AutoRouter.of(context).replace(const LoginRoute()); From 7dbddba7573da524b540360411465f7039053b39 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 5 Feb 2023 23:31:16 -0600 Subject: [PATCH 04/25] chore(server): remove token when logged out (#1560) * chore(mobile): invoke logout() on mobile app * feat: add mechanism to delete token from logging out endpoint * fix: set state after login sequence success * fix: not removing token when logging out from OAuth * fix: prettier * refactor: using accessTokenId to delete * chore: pr comments * fix: test * fix: test threshold --- .../providers/authentication.provider.dart | 4 +++- .../immich/src/controllers/auth.controller.ts | 9 +++++++-- .../libs/domain/src/auth/auth.service.spec.ts | 10 +++++++--- server/libs/domain/src/auth/auth.service.ts | 6 +++++- .../libs/domain/src/auth/dto/auth-user.dto.ts | 1 + .../domain/src/user-token/user-token.core.ts | 20 +++++++++---------- server/libs/domain/test/fixtures.ts | 2 ++ .../db/repository/user-token.repository.ts | 4 ++-- server/package.json | 2 +- 9 files changed, 37 insertions(+), 21 deletions(-) diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index 5b47b1c632..f5f8481c59 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -91,8 +91,8 @@ class AuthenticationNotifier extends StateNotifier { } Future logout() async { - state = state.copyWith(isAuthenticated: false); await Future.wait([ + _apiService.authenticationApi.logout(), Hive.box(userInfoBox).delete(accessTokenKey), Hive.box(userInfoBox).delete(assetEtagKey), _assetCacheService.invalidate(), @@ -101,6 +101,8 @@ class AuthenticationNotifier extends StateNotifier { Hive.box(hiveLoginInfoBox).delete(savedLoginInfoKey) ]); + state = state.copyWith(isAuthenticated: false); + return true; } diff --git a/server/apps/immich/src/controllers/auth.controller.ts b/server/apps/immich/src/controllers/auth.controller.ts index b7ab6c9519..5ba2716b54 100644 --- a/server/apps/immich/src/controllers/auth.controller.ts +++ b/server/apps/immich/src/controllers/auth.controller.ts @@ -59,13 +59,18 @@ export class AuthController { return this.authService.changePassword(authUser, dto); } + @Authenticated() @Post('logout') - async logout(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise { + async logout( + @Req() req: Request, + @Res({ passthrough: true }) res: Response, + @GetAuthUser() authUser: AuthUserDto, + ): Promise { const authType: AuthType = req.cookies[IMMICH_AUTH_TYPE_COOKIE]; res.clearCookie(IMMICH_ACCESS_COOKIE); res.clearCookie(IMMICH_AUTH_TYPE_COOKIE); - return this.authService.logout(authType); + return this.authService.logout(authUser, authType); } } diff --git a/server/libs/domain/src/auth/auth.service.spec.ts b/server/libs/domain/src/auth/auth.service.spec.ts index b4268d4eab..2d874f485d 100644 --- a/server/libs/domain/src/auth/auth.service.spec.ts +++ b/server/libs/domain/src/auth/auth.service.spec.ts @@ -26,7 +26,7 @@ import { IUserRepository } from '../user'; import { IUserTokenRepository } from '../user-token'; import { AuthType } from './auth.constant'; import { AuthService } from './auth.service'; -import { SignUpDto } from './dto'; +import { AuthUserDto, SignUpDto } from './dto'; // const token = Buffer.from('my-api-key', 'utf8').toString('base64'); @@ -192,14 +192,18 @@ describe('AuthService', () => { describe('logout', () => { it('should return the end session endpoint', async () => { - await expect(sut.logout(AuthType.OAUTH)).resolves.toEqual({ + const authUser = { id: '123' } as AuthUserDto; + + await expect(sut.logout(authUser, AuthType.OAUTH)).resolves.toEqual({ successful: true, redirectUri: 'http://end-session-endpoint', }); }); it('should return the default redirect', async () => { - await expect(sut.logout(AuthType.PASSWORD)).resolves.toEqual({ + const authUser = { id: '123' } as AuthUserDto; + + await expect(sut.logout(authUser, AuthType.PASSWORD)).resolves.toEqual({ successful: true, redirectUri: '/auth/login?autoLaunch=0', }); diff --git a/server/libs/domain/src/auth/auth.service.ts b/server/libs/domain/src/auth/auth.service.ts index 5cd7e13032..d5e8e668a3 100644 --- a/server/libs/domain/src/auth/auth.service.ts +++ b/server/libs/domain/src/auth/auth.service.ts @@ -76,7 +76,11 @@ export class AuthService { return this.authCore.createLoginResponse(user, AuthType.PASSWORD, isSecure); } - public async logout(authType: AuthType): Promise { + public async logout(authUser: AuthUserDto, authType: AuthType): Promise { + if (authUser.accessTokenId) { + await this.userTokenCore.deleteToken(authUser.accessTokenId); + } + if (authType === AuthType.OAUTH) { const url = await this.oauthCore.getLogoutEndpoint(); if (url) { diff --git a/server/libs/domain/src/auth/dto/auth-user.dto.ts b/server/libs/domain/src/auth/dto/auth-user.dto.ts index 25d1cae1d1..9af777e7b0 100644 --- a/server/libs/domain/src/auth/dto/auth-user.dto.ts +++ b/server/libs/domain/src/auth/dto/auth-user.dto.ts @@ -7,4 +7,5 @@ export class AuthUserDto { isAllowUpload?: boolean; isAllowDownload?: boolean; isShowExif?: boolean; + accessTokenId?: string; } diff --git a/server/libs/domain/src/user-token/user-token.core.ts b/server/libs/domain/src/user-token/user-token.core.ts index 4bc2cddb49..80743b9c32 100644 --- a/server/libs/domain/src/user-token/user-token.core.ts +++ b/server/libs/domain/src/user-token/user-token.core.ts @@ -9,28 +9,22 @@ export class UserTokenCore { async validate(tokenValue: string) { const hashedToken = this.crypto.hashSha256(tokenValue); - const user = await this.getUserByToken(hashedToken); - if (user) { + const token = await this.repository.get(hashedToken); + + if (token?.user) { return { - ...user, + ...token.user, isPublicUser: false, isAllowUpload: true, isAllowDownload: true, isShowExif: true, + accessTokenId: token.id, }; } throw new UnauthorizedException('Invalid user token'); } - public async getUserByToken(tokenValue: string): Promise { - const token = await this.repository.get(tokenValue); - if (token?.user) { - return token.user; - } - return null; - } - public async createToken(user: UserEntity): Promise { const key = this.crypto.randomBytes(32).toString('base64').replace(/\W/g, ''); const token = this.crypto.hashSha256(key); @@ -41,4 +35,8 @@ export class UserTokenCore { return key; } + + public async deleteToken(id: string): Promise { + await this.repository.delete(id); + } } diff --git a/server/libs/domain/test/fixtures.ts b/server/libs/domain/test/fixtures.ts index 7a2b078b22..3cc9a17f32 100644 --- a/server/libs/domain/test/fixtures.ts +++ b/server/libs/domain/test/fixtures.ts @@ -91,6 +91,7 @@ export const authStub = { isAllowUpload: true, isAllowDownload: true, isShowExif: true, + accessTokenId: 'token-id', }), adminSharedLink: Object.freeze({ id: 'admin_id', @@ -111,6 +112,7 @@ export const authStub = { isPublicUser: true, isShowExif: true, sharedLinkId: '123', + accessTokenId: 'token-id', }), }; diff --git a/server/libs/infra/src/db/repository/user-token.repository.ts b/server/libs/infra/src/db/repository/user-token.repository.ts index 1fd9d03639..eca4ded9d4 100644 --- a/server/libs/infra/src/db/repository/user-token.repository.ts +++ b/server/libs/infra/src/db/repository/user-token.repository.ts @@ -19,7 +19,7 @@ export class UserTokenRepository implements IUserTokenRepository { return this.userTokenRepository.save(userToken); } - async delete(userToken: string): Promise { - await this.userTokenRepository.delete(userToken); + async delete(id: string): Promise { + await this.userTokenRepository.delete(id); } } diff --git a/server/package.json b/server/package.json index 3cb3d298d4..b82c210ebc 100644 --- a/server/package.json +++ b/server/package.json @@ -140,7 +140,7 @@ }, "./libs/domain/": { "branches": 80, - "functions": 90, + "functions": 89, "lines": 95, "statements": 95 } From 4261fc8a04b72d5104423b802f70acd31c24f791 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 6 Feb 2023 00:38:06 -0500 Subject: [PATCH 05/25] feat(deployment): support docker secrets (#1254) * Support secrets * Rewrite to support sh * Remove JWT_SECRET --- server/start-microservices.sh | 21 +++++++++++++++++++++ server/start-server.sh | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/server/start-microservices.sh b/server/start-microservices.sh index 153fb97cd3..f2802af4ca 100644 --- a/server/start-microservices.sh +++ b/server/start-microservices.sh @@ -1,2 +1,23 @@ #! /bin/sh + +if [ "$DB_HOSTNAME_FILE" ]; then + export DB_HOSTNAME=$(cat $DB_HOSTNAME_FILE) + unset DB_HOSTNAME_FILE +fi + +if [ "$DB_DATABASE_NAME_FILE" ]; then + export DB_DATABASE_NAME=$(cat $DB_DATABASE_NAME_FILE) + unset DB_DATABASE_NAME_FILE +fi + +if [ "$DB_USERNAME_FILE" ]; then + export DB_USERNAME=$(cat $DB_USERNAME_FILE) + unset DB_USERNAME_FILE +fi + +if [ "$DB_PASSWORD_FILE" ]; then + export DB_PASSWORD=$(cat $DB_PASSWORD_FILE) + unset DB_PASSWORD_FILE +fi + exec node dist/apps/microservices/apps/microservices/src/main diff --git a/server/start-server.sh b/server/start-server.sh index 0b539f3069..c808439a94 100644 --- a/server/start-server.sh +++ b/server/start-server.sh @@ -1,2 +1,23 @@ #! /bin/sh + +if [ "$DB_HOSTNAME_FILE" ]; then + export DB_HOSTNAME=$(cat $DB_HOSTNAME_FILE) + unset DB_HOSTNAME_FILE +fi + +if [ "$DB_DATABASE_NAME_FILE" ]; then + export DB_DATABASE_NAME=$(cat $DB_DATABASE_NAME_FILE) + unset DB_DATABASE_NAME_FILE +fi + +if [ "$DB_USERNAME_FILE" ]; then + export DB_USERNAME=$(cat $DB_USERNAME_FILE) + unset DB_USERNAME_FILE +fi + +if [ "$DB_PASSWORD_FILE" ]; then + export DB_PASSWORD=$(cat $DB_PASSWORD_FILE) + unset DB_PASSWORD_FILE +fi + exec node dist/apps/immich/apps/immich/src/main From 527aa61a87e1f27984d963e7c07888f5c3fa5456 Mon Sep 17 00:00:00 2001 From: martyfuhry Date: Mon, 6 Feb 2023 01:41:07 -0500 Subject: [PATCH 06/25] fix(mobile): Added flutter native splash and splash screens (#1520) * rebasing * added launch background image to repository --------- Co-authored-by: Marty Fuhry --- .../res/drawable-hdpi/android12splash.png | Bin 0 -> 12557 bytes .../app/src/main/res/drawable-hdpi/splash.png | Bin 0 -> 9762 bytes .../res/drawable-mdpi/android12splash.png | Bin 0 -> 5258 bytes .../app/src/main/res/drawable-mdpi/splash.png | Bin 0 -> 3898 bytes .../drawable-night-hdpi/android12splash.png | Bin 0 -> 12557 bytes .../drawable-night-mdpi/android12splash.png | Bin 0 -> 5258 bytes .../res/drawable-night-v21/background.png | Bin 0 -> 70 bytes .../drawable-night-v21/launch_background.xml | 9 + .../drawable-night-xhdpi/android12splash.png | Bin 0 -> 14657 bytes .../drawable-night-xxhdpi/android12splash.png | Bin 0 -> 35144 bytes .../android12splash.png | Bin 0 -> 40239 bytes .../main/res/drawable-night/background.png | Bin 0 -> 70 bytes .../res/drawable-night/launch_background.xml | 9 + .../src/main/res/drawable-v21/background.png | Bin 0 -> 70 bytes .../res/drawable-v21/launch_background.xml | 15 +- .../res/drawable-xhdpi/android12splash.png | Bin 0 -> 14657 bytes .../src/main/res/drawable-xhdpi/splash.png | Bin 0 -> 10698 bytes .../res/drawable-xxhdpi/android12splash.png | Bin 0 -> 35144 bytes .../src/main/res/drawable-xxhdpi/splash.png | Bin 0 -> 27423 bytes .../res/drawable-xxxhdpi/android12splash.png | Bin 0 -> 40239 bytes .../src/main/res/drawable-xxxhdpi/splash.png | Bin 0 -> 28738 bytes .../app/src/main/res/drawable/background.png | Bin 0 -> 70 bytes .../main/res/drawable/launch_background.xml | 15 +- .../src/main/res/values-night-v31/styles.xml | 20 ++ .../app/src/main/res/values-night/styles.xml | 3 + .../app/src/main/res/values-v31/styles.xml | 20 ++ .../app/src/main/res/values/styles.xml | 3 + mobile/assets/immich-splash-android12.png | Bin 0 -> 39200 bytes mobile/assets/immich-splash.png | Bin 0 -> 41292 bytes mobile/flutter_native_splash.yaml | 138 ++++++++++++ .../LaunchBackground.imageset/Contents.json | 22 ++ .../LaunchBackground.imageset/background.png | Bin 0 -> 70 bytes .../darkbackground.png | Bin 0 -> 70 bytes .../LaunchImage.imageset/Contents.json | 10 +- .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 3898 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 10698 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 27423 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 17 +- mobile/ios/Runner/Info.plist | 196 +++++++++--------- mobile/lib/shared/views/splash_screen.dart | 28 +-- mobile/makefile | 5 +- mobile/pubspec.lock | 18 +- mobile/pubspec.yaml | 1 + 43 files changed, 373 insertions(+), 156 deletions(-) create mode 100644 mobile/android/app/src/main/res/drawable-hdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-hdpi/splash.png create mode 100644 mobile/android/app/src/main/res/drawable-mdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-mdpi/splash.png create mode 100644 mobile/android/app/src/main/res/drawable-night-hdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-night-mdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-night-v21/background.png create mode 100644 mobile/android/app/src/main/res/drawable-night-v21/launch_background.xml create mode 100644 mobile/android/app/src/main/res/drawable-night-xhdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-night/background.png create mode 100644 mobile/android/app/src/main/res/drawable-night/launch_background.xml create mode 100644 mobile/android/app/src/main/res/drawable-v21/background.png create mode 100644 mobile/android/app/src/main/res/drawable-xhdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-xhdpi/splash.png create mode 100644 mobile/android/app/src/main/res/drawable-xxhdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-xxhdpi/splash.png create mode 100644 mobile/android/app/src/main/res/drawable-xxxhdpi/android12splash.png create mode 100644 mobile/android/app/src/main/res/drawable-xxxhdpi/splash.png create mode 100644 mobile/android/app/src/main/res/drawable/background.png create mode 100644 mobile/android/app/src/main/res/values-night-v31/styles.xml create mode 100644 mobile/android/app/src/main/res/values-v31/styles.xml create mode 100644 mobile/assets/immich-splash-android12.png create mode 100644 mobile/assets/immich-splash.png create mode 100644 mobile/flutter_native_splash.yaml create mode 100644 mobile/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json create mode 100644 mobile/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png create mode 100644 mobile/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png diff --git a/mobile/android/app/src/main/res/drawable-hdpi/android12splash.png b/mobile/android/app/src/main/res/drawable-hdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..98499dc3e42204f0b0a437fd4c8db3fec42ad764 GIT binary patch literal 12557 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 M%BsoKNSS~7KZATxa{vGU literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-hdpi/splash.png b/mobile/android/app/src/main/res/drawable-hdpi/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..bae50bb025052cb8d28b61125a881bfa76a38c84 GIT binary patch literal 9762 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 M%BsoKNSS~7KZATxa{vGU literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night-mdpi/android12splash.png b/mobile/android/app/src/main/res/drawable-night-mdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..8b7e4f9646bb7681eca32da360e4f12898277959 GIT binary patch literal 5258 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 + + + + + + + + diff --git a/mobile/android/app/src/main/res/drawable-night-xhdpi/android12splash.png b/mobile/android/app/src/main/res/drawable-night-xhdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..86e4a379f6bb3a9b288aee7dc803494bcb116d3a GIT binary patch literal 14657 zcmeIZ^+S{0|35wuWH(AoDM7QTy(lVrM$NTA_j02##tJqT~ zd|6N&3CYL6clS!3wdu+=xUgiO#uQdPsO(wqDde&KaP|=w+wYYfCwxopk*njFPmh6p z?M$B*OWmh(h~Axk<7cHIoVeP7ptw5Ins00Jgj?J}@Sqr6{kso6qzs(OQ4#zS`tK5? zc4QthjQQs7AJ8B}W2`7!Bs#u}B~glhui?0$2_2r|*NG3-{75!(uq?&VtrW=UJi)q7 zoPjd)D(&BE9<#jCV#~OCfSMY!*HGG%cn!sjrO5yGreF&E@1>D{@qM8$`k=+(jVx~^ ztk4K*Yp^;fah&45N}U!4MDf1k>Bn!=A&Zzvg{H9J%p)R6AJp5P=Zgdv2qYPxsj6%o zNV0?ZC!6(Ej^zH9rW74n1nQI+1q>DG?lMx~v zCD2d)li@&i-_8Q^U+%r+N3k*eX5IYc1;c5iqfUiwe59j@%E~*UzknxrArBITc%JB0 zZy|q;DHe@^Rm+7&leunRw$1UAbgP0$`_#;zdO$->Z=dGm&OS}}D&i6KMN)%SrhWMw zq@n~OH9R8>XRsRk$>02)3avrJZ=$%smsMK>ph-yheTOWXp#7%?2(0lhjD~d`ejNP7 zNfmsaULingtOAPAQ9FCiwB+PAi7y+`<|Y%0p*@ZQ{<@tDB5N6tz0lF3D%MM)^?fo) zPjXWm6mh#(?fTSY`b9J=2f9C)o6Oq6Pc6I?nC20*_If>I+uBe5osKJQj-@Q$_e>BO zA;@Wxo=F{Y8`~au`EI46hC-Byl=kHV;lom6>-U^#GGTP#_wSO@{(0pGJm(0ZO|oeV z?S5ZbLopHtJZFEzNn@iwfip34m_bR#g2?XChTmtzBXzIm=Bu|TL6MvwWrbWLKXTB! zx4>j)xC#MLMt2+k4qFi$^isdc^o

60n`d`O}EoKpv1i41oOiThTY1?XngaeaQ=q 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 sQgWcv74OPV!RE<2_QI^h-aOg^ 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 CN`Nl_ literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png b/mobile/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..2336e479abfeb5582a1ec22babc4e90c3da8b3f4 GIT binary patch literal 40239 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 CN`Nl_ literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-xxhdpi/splash.png b/mobile/android/app/src/main/res/drawable-xxhdpi/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..4ba88c62ad8c4dffd2c4b76c3812b0c9a010c312 GIT binary patch literal 27423 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

vqz+GM+E(7a0LfwIn*_0!cCp#11!? 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-xxxhdpi/splash.png b/mobile/android/app/src/main/res/drawable-xxxhdpi/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..11732021826b996c057bfef64e4403416eb7b62d GIT binary patch literal 28738 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

Pk&V6QRMVP8}0%=|M~MaiosQx(@&N?(ew_rB) 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable/background.png b/mobile/android/app/src/main/res/drawable/background.png new file mode 100644 index 0000000000000000000000000000000000000000..649393cb5a6abaf3949702e48f9f99d4edba69f0 GIT binary patch literal 70 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k92}1TpU9x7?Xc~dwjp1f%6LU2cvm+ QTY(Y`p00i_>zopr0O8INo&W#< literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable/launch_background.xml b/mobile/android/app/src/main/res/drawable/launch_background.xml index 304732f884..3cc4948a14 100644 --- a/mobile/android/app/src/main/res/drawable/launch_background.xml +++ b/mobile/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,9 @@ - - - - - + + + + + + diff --git a/mobile/android/app/src/main/res/values-night-v31/styles.xml b/mobile/android/app/src/main/res/values-night-v31/styles.xml new file mode 100644 index 0000000000..8e51f7679e --- /dev/null +++ b/mobile/android/app/src/main/res/values-night-v31/styles.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/mobile/android/app/src/main/res/values-night/styles.xml b/mobile/android/app/src/main/res/values-night/styles.xml index 3db14bb539..c831fec9b9 100644 --- a/mobile/android/app/src/main/res/values-night/styles.xml +++ b/mobile/android/app/src/main/res/values-night/styles.xml @@ -5,6 +5,9 @@ @drawable/launch_background + false + false + shortEdges + + + + diff --git a/mobile/android/app/src/main/res/values/styles.xml b/mobile/android/app/src/main/res/values/styles.xml index d460d1e921..0d15186673 100644 --- a/mobile/android/app/src/main/res/values/styles.xml +++ b/mobile/android/app/src/main/res/values/styles.xml @@ -5,6 +5,9 @@ @drawable/launch_background + false + false + shortEdges - - -**Describe the bug** -A clear and concise description of what the bug is. - -**Task List** - -*Please complete the task list below. We need this information to help us reproduce the bug or point out problems in your setup. You are not providing enough info may delay our effort to help you.* - -- [ ] I have read thoroughly the README setup and installation instructions. -- [ ] I have included my `docker-compose` file. -- [ ] I have included my redacted `.env` file. -- [ ] I have included information on my machine, and environment. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**System** -- Phone OS [iOS, Android]: `` -- Server Version: `` -- Mobile App Version: `` - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000000..2783375e36 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,100 @@ +name: Report an issue with Immich +description: Report an issue with Immich +labels: ["bug", "need triage"] +title: "[BUG] " +body: + - type: markdown + attributes: + value: | + This issue form is for reporting bugs only! + + If you have a feature or enhancement request, please use the [feature request][fr] section of our [GitHub Discussions][fr]. + + [fr]: https://github.com/immich-app/immich/discussions/new?category=feature-request + + - type: textarea + validations: + required: true + attributes: + label: The bug + description: >- + Describe the issue you are experiencing here, to communicate to the + maintainers. Tell us what you were trying to do and what happened. + + Provide a clear and concise description of what the problem is. + + - type: markdown + attributes: + value: | + ## Environment + + - type: input + validations: + required: true + attributes: + label: The OS that Immich Server is running on + placeholder: Ubuntu 22.10, Debian, Arch...etc + + - type: input + id: version + validations: + required: true + attributes: + label: Version of Immich Server + placeholder: v1.0.0 + + - type: input + validations: + required: true + attributes: + label: Version of Immich Mobile App + placeholder: v1.0.0 + + - type: checkboxes + validations: + required: true + attributes: + label: Platform with the issue + options: + - label: Server + - label: Web + - label: Mobile + + - type: textarea + validations: + required: true + attributes: + label: Your docker-compose.yml content + render: YAML + + - type: textarea + validations: + required: true + attributes: + label: Your .env content + description: Please provide the redacted .env content of your setup + render: Shell + + - type: textarea + id: repro + attributes: + label: Reproduction steps + description: "How do you trigger this bug? Please walk us through it step by step." + value: | + 1. + 2. + 3. + ... + render: bash + validations: + required: true + + - type: textarea + attributes: + label: Additional information + description: > + If you have any additional information for us, use the field below. + + - type: markdown + attributes: + value: Thank you for submitting the form diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3ba13e0cec..4dc46325fa 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1 +1,11 @@ blank_issues_enabled: false +contact_links: + - name: I have a question or need support + url: https://discord.gg/D8JsnBEuKb + about: We use GitHub for tracking bugs, please check out our Discord channel for freaky fast support. + - name: Feature Request + url: https://github.com/immich-app/immich/discussions/new?category=feature-request + about: Please use our GitHub Discussion for making feature requests. + - name: I'm unsure where to go + url: https://discord.gg/D8JsnBEuKb + about: If you are unsure where to go, then joining our Discord is recommended; Just ask! diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml deleted file mode 100644 index 68df56131b..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: Feature Request -description: Request a feature that you would like for the app -title: "[Feature]: " -labels: ["feature", "need triage"] -assignees: - - "" - -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this feature request! - - - type: textarea - id: feature-detail - attributes: - label: Feature detail - placeholder: Describe the feature you would like to see for the app - validations: - required: true - - - type: dropdown - id: platform - attributes: - label: Platform - description: Choose the platform for the feature - options: - - Web - - Mobile App - - Server - validations: - required: true From be8f2c01a274d664a446d2b500aa871acb80e085 Mon Sep 17 00:00:00 2001 From: Alex Tran <alex.tran1502@gmail.com> Date: Tue, 7 Feb 2023 22:18:42 -0600 Subject: [PATCH 15/25] fix(GitHub): feature request template --- .../{feature_request.yaml => feature-request.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/DISCUSSION_TEMPLATE/{feature_request.yaml => feature-request.yaml} (100%) diff --git a/.github/DISCUSSION_TEMPLATE/feature_request.yaml b/.github/DISCUSSION_TEMPLATE/feature-request.yaml similarity index 100% rename from .github/DISCUSSION_TEMPLATE/feature_request.yaml rename to .github/DISCUSSION_TEMPLATE/feature-request.yaml From 18647203cc9355f1856f1347faafa2e8619aa702 Mon Sep 17 00:00:00 2001 From: Alex Tran <alex.tran1502@gmail.com> Date: Tue, 7 Feb 2023 22:19:50 -0600 Subject: [PATCH 16/25] fix(GitHub): feature request form has wrong type for textarea --- .github/DISCUSSION_TEMPLATE/feature-request.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/feature-request.yaml b/.github/DISCUSSION_TEMPLATE/feature-request.yaml index add026f373..73d36af7c3 100644 --- a/.github/DISCUSSION_TEMPLATE/feature-request.yaml +++ b/.github/DISCUSSION_TEMPLATE/feature-request.yaml @@ -10,7 +10,6 @@ body: id: feature attributes: label: The feature - render: markdown validations: required: true From dc9da7480c3ba656e5d1689c4d3c2ca58ed847e2 Mon Sep 17 00:00:00 2001 From: martyfuhry <martyfuhry@gmail.com> Date: Wed, 8 Feb 2023 14:42:45 -0500 Subject: [PATCH 17/25] feat(mobile): Responsive layout improvements with a navigation rail and album grid (#1583) --- .../album/ui/album_thumbnail_card.dart | 152 ++++++++-------- .../lib/modules/album/views/library_page.dart | 99 +++++++---- .../home/ui/asset_grid/immich_asset_grid.dart | 40 ++--- .../lib/shared/views/tab_controller_page.dart | 166 +++++++++++++----- 4 files changed, 281 insertions(+), 176 deletions(-) diff --git a/mobile/lib/modules/album/ui/album_thumbnail_card.dart b/mobile/lib/modules/album/ui/album_thumbnail_card.dart index 4887e7d05c..36ce62676f 100644 --- a/mobile/lib/modules/album/ui/album_thumbnail_card.dart +++ b/mobile/lib/modules/album/ui/album_thumbnail_card.dart @@ -1,18 +1,19 @@ -import 'package:auto_route/auto_route.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_localization/easy_localization.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:immich_mobile/shared/models/album.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:openapi/api.dart'; class AlbumThumbnailCard extends StatelessWidget { + final Function()? onTap; + const AlbumThumbnailCard({ Key? key, required this.album, + this.onTap, }) : super(key: key); final Album album; @@ -20,89 +21,94 @@ class AlbumThumbnailCard extends StatelessWidget { @override Widget build(BuildContext context) { var box = Hive.box(userInfoBox); - var cardSize = MediaQuery.of(context).size.width / 2 - 18; var isDarkMode = Theme.of(context).brightness == Brightness.dark; + return LayoutBuilder( + builder: (context, constraints) { + var cardSize = constraints.maxWidth; - buildEmptyThumbnail() { - return Container( - decoration: BoxDecoration( - color: isDarkMode ? Colors.grey[800] : Colors.grey[200], - ), - child: SizedBox( + buildEmptyThumbnail() { + return Container( height: cardSize, width: cardSize, - child: const Center( - child: Icon(Icons.no_photography), + decoration: BoxDecoration( + color: isDarkMode ? Colors.grey[800] : Colors.grey[200], ), - ), - ); - } - - buildAlbumThumbnail() { - return CachedNetworkImage( - width: cardSize, - height: cardSize, - fit: BoxFit.cover, - fadeInDuration: const Duration(milliseconds: 200), - imageUrl: getAlbumThumbnailUrl( - album, - type: ThumbnailFormat.JPEG, - ), - httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"}, - cacheKey: getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG), - ); - } - - 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: album.albumThumbnailAssetId == null - ? buildEmptyThumbnail() - : buildAlbumThumbnail(), + child: Center( + child: Icon( + Icons.no_photography, + size: cardSize * .15, ), - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: SizedBox( - width: cardSize, - child: Text( - album.name, - style: const TextStyle( - fontWeight: FontWeight.bold, + ), + ); + } + + buildAlbumThumbnail() { + return CachedNetworkImage( + width: cardSize, + height: cardSize, + fit: BoxFit.cover, + fadeInDuration: const Duration(milliseconds: 200), + imageUrl: getAlbumThumbnailUrl( + album, + type: ThumbnailFormat.JPEG, + ), + httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"}, + cacheKey: getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG), + ); + } + + return GestureDetector( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.only(bottom: 32.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: album.albumThumbnailAssetId == null + ? buildEmptyThumbnail() + : buildAlbumThumbnail(), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: SizedBox( + width: cardSize, + child: Text( + album.name, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), ), ), ), - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - album.assetCount == 1 - ? 'album_thumbnail_card_item' - : 'album_thumbnail_card_items', - style: const TextStyle( - fontSize: 12, - ), - ).tr(args: ['${album.assetCount}']), - if (album.shared) - const Text( - 'album_thumbnail_card_shared', - style: TextStyle( + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + album.assetCount == 1 + ? 'album_thumbnail_card_item' + : 'album_thumbnail_card_items', + style: const TextStyle( fontSize: 12, ), - ).tr() - ], - ) - ], + ).tr(args: ['${album.assetCount}']), + if (album.shared) + const Text( + 'album_thumbnail_card_shared', + style: TextStyle( + fontSize: 12, + ), + ).tr() + ], + ) + ], + ), ), - ), + ); + }, ); } } diff --git a/mobile/lib/modules/album/views/library_page.dart b/mobile/lib/modules/album/views/library_page.dart index 7c9475d3a4..08e3327161 100644 --- a/mobile/lib/modules/album/views/library_page.dart +++ b/mobile/lib/modules/album/views/library_page.dart @@ -112,37 +112,43 @@ class LibraryPage extends HookConsumerWidget { 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, + child: Padding( + padding: const EdgeInsets.only(bottom: 32), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Container( + 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, + ), + ), ), ), - ), - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: const Text( - 'library_page_new_album', - style: TextStyle( - fontWeight: FontWeight.bold, + Padding( + padding: const EdgeInsets.only( + top: 8.0, + bottom: 16, ), - ).tr(), - ) - ], + child: const Text( + 'library_page_new_album', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ).tr(), + ), + ], + ), ), ); } @@ -185,6 +191,8 @@ class LibraryPage extends HookConsumerWidget { ); } + final sorted = sortedAlbums(); + return Scaffold( appBar: buildAppBar(), body: CustomScrollView( @@ -234,20 +242,33 @@ class LibraryPage extends HookConsumerWidget { ), ), SliverPadding( - padding: const EdgeInsets.only(left: 12.0, right: 12, bottom: 50), - sliver: SliverToBoxAdapter( - child: Wrap( - spacing: 12, - children: [ - buildCreateAlbumButton(), - for (var album in sortedAlbums()) - AlbumThumbnailCard( - album: album, + padding: const EdgeInsets.all(12.0), + sliver: SliverGrid( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 250, + mainAxisSpacing: 12, + crossAxisSpacing: 12, + childAspectRatio: .7, + ), + delegate: SliverChildBuilderDelegate( + childCount: sorted.length + 1, + (context, index) { + if (index == 0) { + return buildCreateAlbumButton(); + } + + return AlbumThumbnailCard( + album: sorted[index - 1], + onTap: () => AutoRouter.of(context).push( + AlbumViewerRoute( + albumId: sorted[index - 1].id, + ), ), - ], + ); + }, ), ), - ) + ), ], ), ); diff --git a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart index 863631267e..3db0da6753 100644 --- a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart @@ -66,11 +66,6 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> { assets.firstWhereOrNull((e) => !_selectedAssets.contains(e.id)) == null; } - double _getItemSize(BuildContext context) { - return MediaQuery.of(context).size.width / widget.assetsPerRow - - widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow; - } - Widget _buildThumbnailOrPlaceholder( Asset asset, bool placeholder, @@ -97,24 +92,29 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> { RenderAssetGridRow row, bool scrolling, ) { - double size = _getItemSize(context); - return Row( - key: Key("asset-row-${row.assets.first.id}"), - children: row.assets.map((Asset asset) { - bool last = asset.id == row.assets.last.id; + return LayoutBuilder( + builder: (context, constraints) { + final size = constraints.maxWidth / widget.assetsPerRow - + widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow; + return Row( + key: Key("asset-row-${row.assets.first.id}"), + children: row.assets.map((Asset asset) { + bool last = asset.id == row.assets.last.id; - return Container( - key: Key("asset-${asset.id}"), - width: size, - height: size, - margin: EdgeInsets.only( - top: widget.margin, - right: last ? 0.0 : widget.margin, - ), - child: _buildThumbnailOrPlaceholder(asset, scrolling), + return Container( + key: Key("asset-${asset.id}"), + width: size, + height: size, + margin: EdgeInsets.only( + top: widget.margin, + right: last ? 0.0 : widget.margin, + ), + child: _buildThumbnailOrPlaceholder(asset, scrolling), + ); + }).toList(), ); - }).toList(), + }, ); } diff --git a/mobile/lib/shared/views/tab_controller_page.dart b/mobile/lib/shared/views/tab_controller_page.dart index 8f41963c50..42f98eefbe 100644 --- a/mobile/lib/shared/views/tab_controller_page.dart +++ b/mobile/lib/shared/views/tab_controller_page.dart @@ -11,6 +11,96 @@ class TabControllerPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + + navigationRail(TabsRouter tabsRouter) { + return NavigationRail( + labelType: NavigationRailLabelType.all, + selectedIndex: tabsRouter.activeIndex, + onDestinationSelected: (index) { + HapticFeedback.selectionClick(); + tabsRouter.setActiveIndex(index); + }, + selectedIconTheme: IconThemeData( + color: Theme.of(context).primaryColor, + ), + selectedLabelTextStyle: TextStyle( + color: Theme.of(context).primaryColor, + ), + useIndicator: false, + destinations: [ + NavigationRailDestination( + padding: EdgeInsets.only( + top: MediaQuery.of(context).padding.top + 4, + left: 4, + right: 4, + bottom: 4, + ), + icon: const Icon(Icons.photo_outlined), + selectedIcon: const Icon(Icons.photo), + label: const Text('tab_controller_nav_photos').tr(), + ), + NavigationRailDestination( + padding: const EdgeInsets.all(4), + icon: const Icon(Icons.search_rounded), + selectedIcon: const Icon(Icons.search), + label: const Text('tab_controller_nav_search').tr(), + ), + NavigationRailDestination( + padding: const EdgeInsets.all(4), + icon: const Icon(Icons.share_rounded), + selectedIcon: const Icon(Icons.share), + label: const Text('tab_controller_nav_sharing').tr(), + ), + NavigationRailDestination( + padding: const EdgeInsets.all(4), + icon: const Icon(Icons.photo_album_outlined), + selectedIcon: const Icon(Icons.photo_album), + label: const Text('tab_controller_nav_library').tr(), + ), + ], + ); + } + + bottomNavigationBar(TabsRouter tabsRouter) { + return BottomNavigationBar( + selectedLabelStyle: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + unselectedLabelStyle: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + currentIndex: tabsRouter.activeIndex, + onTap: (index) { + HapticFeedback.selectionClick(); + tabsRouter.setActiveIndex(index); + }, + items: [ + BottomNavigationBarItem( + label: 'tab_controller_nav_photos'.tr(), + icon: const Icon(Icons.photo_outlined), + activeIcon: const Icon(Icons.photo), + ), + BottomNavigationBarItem( + label: 'tab_controller_nav_search'.tr(), + icon: const Icon(Icons.search_rounded), + activeIcon: const Icon(Icons.search), + ), + BottomNavigationBarItem( + label: 'tab_controller_nav_sharing'.tr(), + icon: const Icon(Icons.group_outlined), + activeIcon: const Icon(Icons.group), + ), + BottomNavigationBarItem( + label: 'tab_controller_nav_library'.tr(), + icon: const Icon(Icons.photo_album_outlined), + activeIcon: const Icon(Icons.photo_album_rounded), + ) + ], + ); + } + final multiselectEnabled = ref.watch(multiselectProvider); return AutoTabsRouter( routes: [ @@ -32,51 +122,39 @@ class TabControllerPage extends ConsumerWidget { } return atHomeTab; }, - child: Scaffold( - body: FadeTransition( - opacity: animation, - child: child, - ), - bottomNavigationBar: multiselectEnabled - ? null - : BottomNavigationBar( - selectedLabelStyle: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, + child: LayoutBuilder( + builder: (context, constraints) { + const medium = 600; + final Widget? bottom; + final Widget body; + if (constraints.maxWidth < medium) { + // Normal phone width + bottom = bottomNavigationBar(tabsRouter); + body = FadeTransition( + opacity: animation, + child: child, + ); + } else { + // Medium tablet width + bottom = null; + body = Row( + children: [ + navigationRail(tabsRouter), + Expanded( + child: FadeTransition( + opacity: animation, + child: child, + ), ), - unselectedLabelStyle: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - currentIndex: tabsRouter.activeIndex, - onTap: (index) { - HapticFeedback.selectionClick(); - tabsRouter.setActiveIndex(index); - }, - items: [ - BottomNavigationBarItem( - label: 'tab_controller_nav_photos'.tr(), - icon: const Icon(Icons.photo_outlined), - activeIcon: const Icon(Icons.photo), - ), - BottomNavigationBarItem( - label: 'tab_controller_nav_search'.tr(), - icon: const Icon(Icons.search_rounded), - activeIcon: const Icon(Icons.search), - ), - BottomNavigationBarItem( - label: 'tab_controller_nav_sharing'.tr(), - icon: const Icon(Icons.group_outlined), - activeIcon: const Icon(Icons.group), - ), - BottomNavigationBarItem( - label: 'tab_controller_nav_library'.tr(), - icon: const Icon(Icons.photo_album_outlined), - activeIcon: const Icon(Icons.photo_album_rounded), - ) - ], - ), - ), + ], + ); + } return Scaffold( + body: body, + bottomNavigationBar: multiselectEnabled + ? null + : bottom, + ); + },), ); }, ); From 43359f1d26cf466aed0435b2def7e9a3f3d569fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Skyler=20M=C3=A4ntysaari?= <samip5@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:53:48 +0200 Subject: [PATCH 18/25] feat(proxy): Initial IPv6 support (#1577) --- nginx/nginx.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 25f9fa1699..b1ffcd0018 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -56,6 +56,7 @@ http { client_max_body_size 50000M; listen 8080; + listen [::]:8080; access_log off; location /api { From 8c20d8cb3d74356846d4f3c6f5b7eb8c019d65b8 Mon Sep 17 00:00:00 2001 From: Alex <alex.tran1502@gmail.com> Date: Wed, 8 Feb 2023 17:15:32 -0600 Subject: [PATCH 19/25] fix(server): Create album response doesn't have owner property as required (#1704) --- server/apps/immich/src/api-v1/album/album-repository.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/apps/immich/src/api-v1/album/album-repository.ts b/server/apps/immich/src/api-v1/album/album-repository.ts index 81f72db18e..38a2b4406a 100644 --- a/server/apps/immich/src/api-v1/album/album-repository.ts +++ b/server/apps/immich/src/api-v1/album/album-repository.ts @@ -84,7 +84,11 @@ export class AlbumRepository implements IAlbumRepository { newAlbum.ownerId = ownerId; newAlbum.albumName = createAlbumDto.albumName; - const album = await transactionalEntityManager.save(newAlbum); + let album = await transactionalEntityManager.save(newAlbum); + album = await transactionalEntityManager.findOneOrFail(AlbumEntity, { + where: { id: album.id }, + relations: ['owner'], + }); // Add shared users if (createAlbumDto.sharedWithUserIds?.length) { From adb265794cc557114639e801c4f18c6023f3fa24 Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Thu, 9 Feb 2023 17:08:19 +0100 Subject: [PATCH 20/25] feat(web): allow uploading more file types (#1570) * feat(web): allow uploading more file types * fix(web): make filename extension lowercase --- .../upload-asset-preview.svelte | 69 +++++++++++++++ .../shared-components/upload-panel.svelte | 84 +------------------ web/src/lib/utils/asset-utils.ts | 35 ++++++++ web/src/lib/utils/file-uploader.ts | 24 ++++-- 4 files changed, 123 insertions(+), 89 deletions(-) create mode 100644 web/src/lib/components/shared-components/upload-asset-preview.svelte diff --git a/web/src/lib/components/shared-components/upload-asset-preview.svelte b/web/src/lib/components/shared-components/upload-asset-preview.svelte new file mode 100644 index 0000000000..21dfd304bf --- /dev/null +++ b/web/src/lib/components/shared-components/upload-asset-preview.svelte @@ -0,0 +1,69 @@ +<script lang="ts"> + import { fade } from 'svelte/transition'; + import { asByteUnitString } from '$lib/utils/byte-units'; + import { UploadAsset } from '$lib/models/upload-asset'; + + export let uploadAsset: UploadAsset; + + let showFallbackImage = false; + const previewURL = URL.createObjectURL(uploadAsset.file); +</script> + +<div + in:fade={{ duration: 250 }} + out:fade={{ duration: 100 }} + class="text-xs mt-3 rounded-lg bg-immich-bg grid grid-cols-[70px_auto] gap-2 h-[70px]" +> + <div class="relative"> + {#if showFallbackImage} + <img + in:fade={{ duration: 250 }} + src="immich-logo.svg" + alt="Immich Logo" + class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg" + draggable="false" + /> + {:else} + <img + in:fade={{ duration: 250 }} + on:load={() => { + URL.revokeObjectURL(previewURL); + }} + on:error={() => { + URL.revokeObjectURL(previewURL); + showFallbackImage = true; + }} + src={previewURL} + alt="Preview of asset" + class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg" + draggable="false" + /> + {/if} + + <div class="bottom-0 left-0 absolute w-full h-[25px] bg-immich-primary/30"> + <p + class="absolute bottom-1 right-1 object-right-bottom text-gray-50/95 font-semibold stroke-immich-primary uppercase" + > + .{uploadAsset.fileExtension} + </p> + </div> + </div> + + <div class="p-2 pr-4 flex flex-col justify-between"> + <input + disabled + class="bg-gray-100 border w-full p-1 rounded-md text-[10px] px-2" + value={`[${asByteUnitString(uploadAsset.file.size)}] ${uploadAsset.file.name}`} + /> + + <div class="w-full bg-gray-300 h-[15px] rounded-md mt-[5px] text-white relative"> + <div + class="bg-immich-primary h-[15px] rounded-md transition-all" + style={`width: ${uploadAsset.progress}%`} + /> + <p class="absolute h-full w-full text-center top-0 text-[10px] "> + {uploadAsset.progress}/100 + </p> + </div> + </div> +</div> diff --git a/web/src/lib/components/shared-components/upload-panel.svelte b/web/src/lib/components/shared-components/upload-panel.svelte index 7e24627d8d..74753437bd 100644 --- a/web/src/lib/components/shared-components/upload-panel.svelte +++ b/web/src/lib/components/shared-components/upload-panel.svelte @@ -4,55 +4,20 @@ import { uploadAssetsStore } from '$lib/stores/upload'; import CloudUploadOutline from 'svelte-material-icons/CloudUploadOutline.svelte'; import WindowMinimize from 'svelte-material-icons/WindowMinimize.svelte'; - import type { UploadAsset } from '$lib/models/upload-asset'; import { notificationController, NotificationType } from './notification/notification'; - import { asByteUnitString } from '$lib/utils/byte-units'; + import UploadAssetPreview from './upload-asset-preview.svelte'; let showDetail = true; - let uploadLength = 0; + let isUploading = false; - const showUploadImageThumbnail = async (a: UploadAsset) => { - const extension = a.fileExtension.toLowerCase(); - - if (extension == 'jpeg' || extension == 'jpg' || extension == 'png') { - try { - const imgData = await a.file.arrayBuffer(); - const arrayBufferView = new Uint8Array(imgData); - const blob = new Blob([arrayBufferView], { type: 'image/jpeg' }); - const urlCreator = window.URL || window.webkitURL; - const imageUrl = urlCreator.createObjectURL(blob); - // TODO: There is probably a cleaner way of doing this - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const img: any = document.getElementById(`${a.id}`); - img.src = imageUrl; - } catch { - // Do nothing? - } - } - }; - - // Reactive action to get thumbnail image of upload asset whenever there is a new one added to the list + // Reactive action to update asset uploadLength whenever there is a new one added to the list $: { if ($uploadAssetsStore.length != uploadLength) { - $uploadAssetsStore.map((asset) => { - showUploadImageThumbnail(asset); - }); - uploadLength = $uploadAssetsStore.length; } } - $: { - if (showDetail) { - $uploadAssetsStore.map((asset) => { - showUploadImageThumbnail(asset); - }); - } - } - - let isUploading = false; - uploadAssetsStore.isUploading.subscribe((value) => { isUploading = value; }); @@ -88,48 +53,7 @@ <div class="max-h-[400px] overflow-y-auto pr-2 rounded-lg immich-scrollbar"> {#each $uploadAssetsStore as uploadAsset} {#key uploadAsset.id} - <div - in:fade={{ duration: 250 }} - out:fade={{ duration: 100 }} - class="text-xs mt-3 rounded-lg bg-immich-bg grid grid-cols-[70px_auto] gap-2 h-[70px]" - > - <div class="relative"> - <img - in:fade={{ duration: 250 }} - id={`${uploadAsset.id}`} - src="/immich-logo.svg" - alt="" - class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg " - draggable="false" - /> - - <div class="bottom-0 left-0 absolute w-full h-[25px] bg-immich-primary/30"> - <p - class="absolute bottom-1 right-1 object-right-bottom text-gray-50/95 font-semibold stroke-immich-primary uppercase" - > - .{uploadAsset.fileExtension} - </p> - </div> - </div> - - <div class="p-2 pr-4 flex flex-col justify-between"> - <input - disabled - class="bg-gray-100 border w-full p-1 rounded-md text-[10px] px-2" - value={`[${asByteUnitString(uploadAsset.file.size)}] ${uploadAsset.file.name}`} - /> - - <div class="w-full bg-gray-300 h-[15px] rounded-md mt-[5px] text-white relative"> - <div - class="bg-immich-primary h-[15px] rounded-md transition-all" - style={`width: ${uploadAsset.progress}%`} - /> - <p class="absolute h-full w-full text-center top-0 text-[10px] "> - {uploadAsset.progress}/100 - </p> - </div> - </div> - </div> + <UploadAssetPreview {uploadAsset} /> {/key} {/each} </div> diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index a245ea03cd..06642b0a0b 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -111,3 +111,38 @@ export async function bulkDownload( }); } } + +/** + * Returns the lowercase filename extension without a dot (.) and + * an empty string when not found. + */ +export function getFilenameExtension(filename: string): string { + const lastIndex = filename.lastIndexOf('.'); + return filename.slice(lastIndex + 1).toLowerCase(); +} + +/** + * Returns the MIME type of the file and an empty string when not found. + */ +export function getFileMimeType(file: File): string { + if (file.type !== '') { + // Return the MIME type determined by the browser. + return file.type; + } + + // Return MIME type based on the file extension. + switch (getFilenameExtension(file.name)) { + case 'heic': + return 'image/heic'; + case 'heif': + return 'image/heif'; + case 'dng': + return 'image/dng'; + case '3gp': + return 'video/3gpp'; + case 'nef': + return 'image/nef'; + default: + return ''; + } +} diff --git a/web/src/lib/utils/file-uploader.ts b/web/src/lib/utils/file-uploader.ts index 00523df27a..cffcfa55b3 100644 --- a/web/src/lib/utils/file-uploader.ts +++ b/web/src/lib/utils/file-uploader.ts @@ -7,7 +7,7 @@ import * as exifr from 'exifr'; import { uploadAssetsStore } from '$lib/stores/upload'; import type { UploadAsset } from '../models/upload-asset'; import { api, AssetFileUploadResponseDto } from '@api'; -import { addAssetsToAlbum } from '$lib/utils/asset-utils'; +import { addAssetsToAlbum, getFileMimeType, getFilenameExtension } from '$lib/utils/asset-utils'; export const openFileUploadDialog = ( albumId: string | undefined = undefined, @@ -19,6 +19,9 @@ export const openFileUploadDialog = ( fileSelector.type = 'file'; fileSelector.multiple = true; + + // When adding a content type that is unsupported by browsers, make sure + // to also add it to getFileMimeType() otherwise the upload will fail. fileSelector.accept = 'image/*,video/*,.heic,.heif,.dng,.3gp,.nef'; fileSelector.onchange = async (e: Event) => { @@ -55,9 +58,10 @@ export const fileUploadHandler = async ( return; } - const acceptedFile = files.filter( - (e) => e.type.split('/')[0] === 'video' || e.type.split('/')[0] === 'image' - ); + const acceptedFile = files.filter((file) => { + const assetType = getFileMimeType(file).split('/')[0]; + return assetType === 'video' || assetType === 'image'; + }); for (const asset of acceptedFile) { await fileUploader(asset, albumId, sharedKey, onDone); @@ -71,9 +75,9 @@ async function fileUploader( sharedKey: string | undefined = undefined, onDone?: (id: string) => void ) { - const assetType = asset.type.split('/')[0].toUpperCase(); - const temp = asset.name.split('.'); - const fileExtension = temp[temp.length - 1]; + const mimeType = getFileMimeType(asset); + const assetType = mimeType.split('/')[0].toUpperCase(); + const fileExtension = getFilenameExtension(asset.name); const formData = new FormData(); try { @@ -114,8 +118,10 @@ async function fileUploader( // Get asset file extension formData.append('fileExtension', '.' + fileExtension); - // Get asset binary data. - formData.append('assetData', asset); + // Get asset binary data with a custom MIME type, because browsers will + // use application/octet-stream for unsupported MIME types, leading to + // failed uploads. + formData.append('assetData', new File([asset], asset.name, { type: mimeType })); // Check if asset upload on server before performing upload const { data, status } = await api.assetApi.checkDuplicateAsset( From 911c35a7f14c34c093804435024bc276c7ff0d99 Mon Sep 17 00:00:00 2001 From: Fynn Petersen-Frey <zoodyy@users.noreply.github.com> Date: Thu, 9 Feb 2023 18:32:08 +0100 Subject: [PATCH 21/25] refactor(mobile): add Isar DB & Store class (#1574) * refactor(mobile): add Isar DB & Store class new Store: globally accessible key-value store like Hive (but based on Isar) replace first few places of Hive usage with the new Store * reduce max. DB size to prevent errors on older iOS devices --------- Co-authored-by: Alex <alex.tran1502@gmail.com> --- .../test_utils/general_helper.dart | 8 +- mobile/lib/main.dart | 27 +- .../providers/authentication.provider.dart | 6 +- mobile/lib/shared/models/store.dart | 96 +++ mobile/lib/shared/models/store.g.dart | 574 ++++++++++++++++++ .../lib/shared/providers/asset.provider.dart | 9 +- mobile/lib/shared/providers/db.provider.dart | 5 + mobile/lib/shared/services/asset.service.dart | 19 +- mobile/lib/utils/migration.dart | 24 + mobile/pubspec.lock | 42 ++ mobile/pubspec.yaml | 4 + 11 files changed, 796 insertions(+), 18 deletions(-) create mode 100644 mobile/lib/shared/models/store.dart create mode 100644 mobile/lib/shared/models/store.g.dart create mode 100644 mobile/lib/shared/providers/db.provider.dart create mode 100644 mobile/lib/utils/migration.dart diff --git a/mobile/integration_test/test_utils/general_helper.dart b/mobile/integration_test/test_utils/general_helper.dart index 0ce776ce97..24b98c9617 100644 --- a/mobile/integration_test/test_utils/general_helper.dart +++ b/mobile/integration_test/test_utils/general_helper.dart @@ -1,7 +1,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hive/hive.dart'; +import 'package:immich_mobile/shared/models/store.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:isar/isar.dart'; // ignore: depend_on_referenced_packages import 'package:meta/meta.dart'; import 'package:immich_mobile/main.dart' as app; @@ -34,8 +36,12 @@ class ImmichTestHelper { // Clear all data from Hive await Hive.deleteFromDisk(); await app.openBoxes(); + // Clear all data from Isar (reuse existing instance if available) + final db = Isar.getInstance() ?? await app.loadDb(); + await Store.clear(); + await db.writeTxn(() => db.clear()); // Load main Widget - await tester.pumpWidget(app.getMainWidget()); + await tester.pumpWidget(app.getMainWidget(db)); // Post run tasks await tester.pumpAndSettle(); await EasyLocalization.ensureInitialized(); diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 262612e44d..fe96a5d274 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -17,8 +17,10 @@ import 'package:immich_mobile/modules/login/providers/authentication.provider.da import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/tab_navigation_observer.dart'; import 'package:immich_mobile/shared/models/immich_logger_message.model.dart'; +import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/providers/app_state.provider.dart'; import 'package:immich_mobile/shared/providers/asset.provider.dart'; +import 'package:immich_mobile/shared/providers/db.provider.dart'; import 'package:immich_mobile/shared/providers/release_info.provider.dart'; import 'package:immich_mobile/shared/providers/server_info.provider.dart'; import 'package:immich_mobile/shared/providers/websocket.provider.dart'; @@ -26,11 +28,16 @@ import 'package:immich_mobile/shared/services/immich_logger.service.dart'; import 'package:immich_mobile/shared/views/immich_loading_overlay.dart'; import 'package:immich_mobile/shared/views/version_announcement_overlay.dart'; import 'package:immich_mobile/utils/immich_app_theme.dart'; +import 'package:immich_mobile/utils/migration.dart'; +import 'package:isar/isar.dart'; +import 'package:path_provider/path_provider.dart'; import 'constants/hive_box.dart'; void main() async { await initApp(); - runApp(getMainWidget()); + final db = await loadDb(); + await migrateHiveToStoreIfNecessary(); + runApp(getMainWidget(db)); } Future<void> openBoxes() async { @@ -70,13 +77,27 @@ Future<void> initApp() async { ImmichLogger().init(); } -Widget getMainWidget() { +Future<Isar> loadDb() async { + final dir = await getApplicationDocumentsDirectory(); + Isar db = await Isar.open( + [StoreValueSchema], + directory: dir.path, + maxSizeMiB: 256, + ); + Store.init(db); + return db; +} + +Widget getMainWidget(Isar db) { return EasyLocalization( supportedLocales: locales, path: translationsPath, useFallbackTranslations: true, fallbackLocale: locales.first, - child: const ProviderScope(child: ImmichApp()), + child: ProviderScope( + overrides: [dbProvider.overrideWithValue(db)], + child: const ImmichApp(), + ), ); } diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index f5f8481c59..88b49a4b10 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -4,6 +4,7 @@ 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/album/services/album_cache.service.dart'; +import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/services/asset_cache.service.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'; @@ -94,7 +95,8 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> { await Future.wait([ _apiService.authenticationApi.logout(), Hive.box(userInfoBox).delete(accessTokenKey), - Hive.box(userInfoBox).delete(assetEtagKey), + Store.delete(StoreKey.assetETag), + Store.delete(StoreKey.userRemoteId), _assetCacheService.invalidate(), _albumCacheService.invalidate(), _sharedAlbumCacheService.invalidate(), @@ -153,7 +155,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> { var deviceInfo = await _deviceInfoService.getDeviceInfo(); userInfoHiveBox.put(deviceIdKey, deviceInfo["deviceId"]); userInfoHiveBox.put(accessTokenKey, accessToken); - userInfoHiveBox.put(userIdKey, userResponseDto.id); + Store.put(StoreKey.userRemoteId, userResponseDto.id); state = state.copyWith( isAuthenticated: true, diff --git a/mobile/lib/shared/models/store.dart b/mobile/lib/shared/models/store.dart new file mode 100644 index 0000000000..537ac5443b --- /dev/null +++ b/mobile/lib/shared/models/store.dart @@ -0,0 +1,96 @@ +import 'package:isar/isar.dart'; +import 'dart:convert'; + +part 'store.g.dart'; + +/// Key-value store for individual items enumerated in StoreKey. +/// Supports String, int and JSON-serializable Objects +/// Can be used concurrently from multiple isolates +class Store { + static late final Isar _db; + static final List<dynamic> _cache = List.filled(StoreKey.values.length, null); + + /// Initializes the store (call exactly once per app start) + static void init(Isar db) { + _db = db; + _populateCache(); + _db.storeValues.where().build().watch().listen(_onChangeListener); + } + + /// clears all values from this store (cache and DB), only for testing! + static Future<void> clear() { + _cache.fillRange(0, _cache.length, null); + return _db.writeTxn(() => _db.storeValues.clear()); + } + + /// Returns the stored value for the given key, or the default value if null + static T? get<T>(StoreKey key, [T? defaultValue]) => + _cache[key._id] ?? defaultValue; + + /// Stores the value synchronously in the cache and asynchronously in the DB + static Future<void> put<T>(StoreKey key, T value) { + _cache[key._id] = value; + return _db.writeTxn(() => _db.storeValues.put(StoreValue._of(value, key))); + } + + /// Removes the value synchronously from the cache and asynchronously from the DB + static Future<void> delete(StoreKey key) { + _cache[key._id] = null; + return _db.writeTxn(() => _db.storeValues.delete(key._id)); + } + + /// Fills the cache with the values from the DB + static _populateCache() { + for (StoreKey key in StoreKey.values) { + final StoreValue? value = _db.storeValues.getSync(key._id); + if (value != null) { + _cache[key._id] = value._extract(key); + } + } + } + + /// updates the state if a value is updated in any isolate + static void _onChangeListener(List<StoreValue>? data) { + if (data != null) { + for (StoreValue value in data) { + _cache[value.id] = value._extract(StoreKey.values[value.id]); + } + } + } +} + +/// Internal class for `Store`, do not use elsewhere. +@Collection(inheritance: false) +class StoreValue { + StoreValue(this.id, {this.intValue, this.strValue}); + Id id; + int? intValue; + String? strValue; + + T? _extract<T>(StoreKey key) => key._isInt + ? intValue + : (key._fromJson != null + ? key._fromJson!(json.decode(strValue!)) + : strValue); + static StoreValue _of(dynamic value, StoreKey key) => StoreValue( + key._id, + intValue: key._isInt ? value : null, + strValue: key._isInt + ? null + : (key._fromJson == null ? value : json.encode(value.toJson())), + ); +} + +/// Key for each possible value in the `Store`. +/// Defines the data type (int, String, JSON) for each value +enum StoreKey { + userRemoteId(0), + assetETag(1), + ; + + // ignore: unused_element + const StoreKey(this._id, [this._isInt = false, this._fromJson]); + final int _id; + final bool _isInt; + final Function(dynamic)? _fromJson; +} diff --git a/mobile/lib/shared/models/store.g.dart b/mobile/lib/shared/models/store.g.dart new file mode 100644 index 0000000000..6370573a68 --- /dev/null +++ b/mobile/lib/shared/models/store.g.dart @@ -0,0 +1,574 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'store.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetStoreValueCollection on Isar { + IsarCollection<StoreValue> get storeValues => this.collection(); +} + +const StoreValueSchema = CollectionSchema( + name: r'StoreValue', + id: 902899285492123510, + properties: { + r'intValue': PropertySchema( + id: 0, + name: r'intValue', + type: IsarType.long, + ), + r'strValue': PropertySchema( + id: 1, + name: r'strValue', + type: IsarType.string, + ) + }, + estimateSize: _storeValueEstimateSize, + serialize: _storeValueSerialize, + deserialize: _storeValueDeserialize, + deserializeProp: _storeValueDeserializeProp, + idName: r'id', + indexes: {}, + links: {}, + embeddedSchemas: {}, + getId: _storeValueGetId, + getLinks: _storeValueGetLinks, + attach: _storeValueAttach, + version: '3.0.5', +); + +int _storeValueEstimateSize( + StoreValue object, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.strValue; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + return bytesCount; +} + +void _storeValueSerialize( + StoreValue object, + IsarWriter writer, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + writer.writeLong(offsets[0], object.intValue); + writer.writeString(offsets[1], object.strValue); +} + +StoreValue _storeValueDeserialize( + Id id, + IsarReader reader, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + final object = StoreValue( + id, + intValue: reader.readLongOrNull(offsets[0]), + strValue: reader.readStringOrNull(offsets[1]), + ); + return object; +} + +P _storeValueDeserializeProp<P>( + IsarReader reader, + int propertyId, + int offset, + Map<Type, List<int>> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readLongOrNull(offset)) as P; + case 1: + return (reader.readStringOrNull(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _storeValueGetId(StoreValue object) { + return object.id; +} + +List<IsarLinkBase<dynamic>> _storeValueGetLinks(StoreValue object) { + return []; +} + +void _storeValueAttach(IsarCollection<dynamic> col, Id id, StoreValue object) { + object.id = id; +} + +extension StoreValueQueryWhereSort + on QueryBuilder<StoreValue, StoreValue, QWhere> { + QueryBuilder<StoreValue, StoreValue, QAfterWhere> anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension StoreValueQueryWhere + on QueryBuilder<StoreValue, StoreValue, QWhereClause> { + QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } +} + +extension StoreValueQueryFilter + on QueryBuilder<StoreValue, StoreValue, QFilterCondition> { + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> intValueIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'intValue', + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> + intValueIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'intValue', + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> intValueEqualTo( + int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'intValue', + value: value, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> + intValueGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'intValue', + value: value, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> intValueLessThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'intValue', + value: value, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> intValueBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'intValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'strValue', + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> + strValueIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'strValue', + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> + strValueGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'strValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> + strValueStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'strValue', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> + strValueIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'strValue', + value: '', + )); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> + strValueIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'strValue', + value: '', + )); + }); + } +} + +extension StoreValueQueryObject + on QueryBuilder<StoreValue, StoreValue, QFilterCondition> {} + +extension StoreValueQueryLinks + on QueryBuilder<StoreValue, StoreValue, QFilterCondition> {} + +extension StoreValueQuerySortBy + on QueryBuilder<StoreValue, StoreValue, QSortBy> { + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> sortByIntValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'intValue', Sort.asc); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> sortByIntValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'intValue', Sort.desc); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> sortByStrValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'strValue', Sort.asc); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> sortByStrValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'strValue', Sort.desc); + }); + } +} + +extension StoreValueQuerySortThenBy + on QueryBuilder<StoreValue, StoreValue, QSortThenBy> { + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByIntValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'intValue', Sort.asc); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByIntValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'intValue', Sort.desc); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByStrValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'strValue', Sort.asc); + }); + } + + QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByStrValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'strValue', Sort.desc); + }); + } +} + +extension StoreValueQueryWhereDistinct + on QueryBuilder<StoreValue, StoreValue, QDistinct> { + QueryBuilder<StoreValue, StoreValue, QDistinct> distinctByIntValue() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'intValue'); + }); + } + + QueryBuilder<StoreValue, StoreValue, QDistinct> distinctByStrValue( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'strValue', caseSensitive: caseSensitive); + }); + } +} + +extension StoreValueQueryProperty + on QueryBuilder<StoreValue, StoreValue, QQueryProperty> { + QueryBuilder<StoreValue, int, QQueryOperations> idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder<StoreValue, int?, QQueryOperations> intValueProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'intValue'); + }); + } + + QueryBuilder<StoreValue, String?, QQueryOperations> strValueProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'strValue'); + }); + } +} diff --git a/mobile/lib/shared/providers/asset.provider.dart b/mobile/lib/shared/providers/asset.provider.dart index 09da33f9c4..1f90f07763 100644 --- a/mobile/lib/shared/providers/asset.provider.dart +++ b/mobile/lib/shared/providers/asset.provider.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.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/shared/models/store.dart'; import 'package:immich_mobile/shared/services/asset.service.dart'; import 'package:immich_mobile/shared/services/asset_cache.service.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; @@ -106,7 +107,6 @@ class AssetNotifier extends StateNotifier<AssetsState> { _getAllAssetInProgress = true; bool isCacheValid = await _assetCacheService.isValid(); stopwatch.start(); - final Box box = Hive.box(userInfoBox); if (isCacheValid && state.allAssets.isEmpty) { final List<Asset>? cachedData = await _assetCacheService.get(); if (cachedData == null) { @@ -122,7 +122,7 @@ class AssetNotifier extends StateNotifier<AssetsState> { } final localTask = _assetService.getLocalAssets(urgent: !isCacheValid); final remoteTask = _assetService.getRemoteAssets( - etag: isCacheValid ? box.get(assetEtagKey) : null, + etag: isCacheValid ? Store.get(StoreKey.assetETag) : null, ); int remoteBegin = state.allAssets.indexWhere((a) => a.isRemote); @@ -151,7 +151,7 @@ class AssetNotifier extends StateNotifier<AssetsState> { log.info("Combining assets: ${stopwatch.elapsedMilliseconds}ms"); - box.put(assetEtagKey, remoteResult.second); + Store.put(StoreKey.assetETag, remoteResult.second); } finally { _getAllAssetInProgress = false; } @@ -279,8 +279,7 @@ class AssetNotifier extends StateNotifier<AssetsState> { final index = state.allAssets.indexWhere((a) => asset.id == a.id); if (index > 0) { - state.allAssets.removeAt(index); - state.allAssets.insert(index, Asset.remote(newAsset)); + state.allAssets[index] = newAsset; _updateAssetsState(state.allAssets); } diff --git a/mobile/lib/shared/providers/db.provider.dart b/mobile/lib/shared/providers/db.provider.dart new file mode 100644 index 0000000000..e03e037f36 --- /dev/null +++ b/mobile/lib/shared/providers/db.provider.dart @@ -0,0 +1,5 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:isar/isar.dart'; + +// overwritten in main.dart due to async loading +final dbProvider = Provider<Isar>((_) => throw UnimplementedError()); diff --git a/mobile/lib/shared/services/asset.service.dart b/mobile/lib/shared/services/asset.service.dart index 0cc04936fb..91e3a015db 100644 --- a/mobile/lib/shared/services/asset.service.dart +++ b/mobile/lib/shared/services/asset.service.dart @@ -8,6 +8,7 @@ import 'package:immich_mobile/modules/backup/background_service/background.servi import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart'; import 'package:immich_mobile/modules/backup/services/backup.service.dart'; import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/utils/openapi_extensions.dart'; @@ -37,7 +38,7 @@ class AssetService { final Pair<List<AssetResponseDto>, String?>? remote = await _apiService.assetApi.getAllAssetsWithETag(eTag: etag); if (remote == null) { - return const Pair(null, null); + return Pair(null, etag); } return Pair( remote.first.map(Asset.remote).toList(growable: false), @@ -45,7 +46,7 @@ class AssetService { ); } catch (e, stack) { log.severe('Error while getting remote assets', e, stack); - return const Pair(null, null); + return Pair(null, etag); } } @@ -62,7 +63,7 @@ class AssetService { } final box = await Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox); final HiveBackupAlbums? backupAlbumInfo = box.get(backupInfoKey); - final String userId = Hive.box(userInfoBox).get(userIdKey); + final String userId = Store.get(StoreKey.userRemoteId); if (backupAlbumInfo != null) { return (await _backupService .buildUploadCandidates(backupAlbumInfo.deepCopy())) @@ -105,12 +106,16 @@ class AssetService { } } - Future<AssetResponseDto?> updateAsset(Asset asset, UpdateAssetDto updateAssetDto) async { - return await _apiService.assetApi.updateAsset(asset.id, updateAssetDto); + Future<Asset?> updateAsset( + Asset asset, + UpdateAssetDto updateAssetDto, + ) async { + final dto = + await _apiService.assetApi.updateAsset(asset.remoteId!, updateAssetDto); + return dto == null ? null : Asset.remote(dto); } - Future<AssetResponseDto?> changeFavoriteStatus(Asset asset, bool isFavorite) { + Future<Asset?> changeFavoriteStatus(Asset asset, bool isFavorite) { return updateAsset(asset, UpdateAssetDto(isFavorite: isFavorite)); } - } diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart new file mode 100644 index 0000000000..d74dff9027 --- /dev/null +++ b/mobile/lib/utils/migration.dart @@ -0,0 +1,24 @@ +import 'package:flutter/cupertino.dart'; +import 'package:hive/hive.dart'; +import 'package:immich_mobile/constants/hive_box.dart'; +import 'package:immich_mobile/shared/models/store.dart'; + +Future<void> migrateHiveToStoreIfNecessary() async { + try { + if (await Hive.boxExists(userInfoBox)) { + final Box box = await Hive.openBox(userInfoBox); + await _migrateSingleKey(box, userIdKey, StoreKey.userRemoteId); + await _migrateSingleKey(box, assetEtagKey, StoreKey.assetETag); + } + } catch (e) { + debugPrint("Error while migrating userInfoBox $e"); + } +} + +_migrateSingleKey(Box box, String hiveKey, StoreKey key) async { + final String? value = box.get(hiveKey); + if (value != null) { + await Store.put(key, value); + await box.delete(hiveKey); + } +} diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 54345703c2..24e49e8190 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -239,6 +239,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + dartx: + dependency: transitive + description: + name: dartx + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" easy_image_viewer: dependency: "direct main" description: @@ -547,6 +554,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" + isar: + dependency: "direct main" + description: + name: isar + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" + isar_flutter_libs: + dependency: "direct main" + description: + name: isar_flutter_libs + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" + isar_generator: + dependency: "direct dev" + description: + name: isar_generator + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" js: dependency: transitive description: @@ -1063,6 +1091,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.12" + time: + dependency: transitive + description: + name: time + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" timing: dependency: transitive description: @@ -1301,6 +1336,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.0" + xxh3: + dependency: transitive + description: + name: xxh3 + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" yaml: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 786d2342e1..1fd192fa2c 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -3,6 +3,7 @@ description: Immich - selfhosted backup media file on mobile phone publish_to: "none" version: 1.45.0+68 +isar_version: &isar_version 3.0.5 environment: sdk: ">=2.17.0 <3.0.0" @@ -41,6 +42,8 @@ dependencies: http_parser: ^4.0.1 flutter_web_auth: ^0.5.0 easy_image_viewer: ^1.2.0 + isar: *isar_version + isar_flutter_libs: *isar_version # contains Isar Core openapi: path: openapi @@ -58,6 +61,7 @@ dev_dependencies: auto_route_generator: ^5.0.2 flutter_launcher_icons: "^0.9.2" flutter_native_splash: ^2.2.16 + isar_generator: *isar_version integration_test: sdk: flutter From fd13265131f50cb81a8e46e350f88efb4628f4c6 Mon Sep 17 00:00:00 2001 From: Matthias Rupp <matthias.rupp@posteo.de> Date: Thu, 9 Feb 2023 18:35:44 +0100 Subject: [PATCH 22/25] feat(mobile): Home screen customization options (#1563) * Try staggered layout for home page * Introduce setting for dynamic layout * Fix some provider related bugs * Make asset grouping configurable * Add translation keys, refactor group title * Rename enum values * Fix enum names * Reformat long if statement * Fix timezone related bug * Minor clean up * Fix unit test * Add second assets check back to home screen --- mobile/assets/i18n/en-US.json | 6 +- .../asset_grid/asset_grid_data_structure.dart | 139 ++++++++++++------ ...tle_text.dart => group_divider_title.dart} | 4 +- .../home/ui/asset_grid/immich_asset_grid.dart | 10 +- mobile/lib/modules/home/views/home_page.dart | 4 +- .../search_result_page.provider.dart | 27 ++-- .../services/app_settings.service.dart | 2 + .../asset_list_layout_settings.dart | 99 +++++++++++++ .../asset_list_settings.dart | 2 + .../asset_list_storage_indicator.dart | 3 +- .../asset_list_tiles_per_row.dart | 3 +- .../lib/shared/providers/asset.provider.dart | 37 +++-- .../test/asset_grid_data_structure_test.dart | 87 ++++++----- 13 files changed, 298 insertions(+), 125 deletions(-) rename mobile/lib/modules/home/ui/asset_grid/{daily_title_text.dart => group_divider_title.dart} (94%) create mode 100644 mobile/lib/modules/settings/ui/asset_list_settings/asset_list_layout_settings.dart diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index bc62a48a61..25220eb269 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -12,6 +12,10 @@ "album_viewer_appbar_share_leave": "Leave album", "album_viewer_appbar_share_remove": "Remove from album", "album_viewer_page_share_add_users": "Add users", + "asset_list_layout_settings_dynamic_layout_title": "Dynamic layout", + "asset_list_layout_settings_group_by": "Group assets by", + "asset_list_layout_settings_group_by_month_day": "Month + day", + "asset_list_layout_settings_group_by_month": "Month", "asset_list_settings_subtitle": "Photo grid layout settings", "asset_list_settings_title": "Photo Grid", "backup_album_selection_page_albums_device": "Albums on device ({})", @@ -199,4 +203,4 @@ "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" -} \ No newline at end of file +} diff --git a/mobile/lib/modules/home/ui/asset_grid/asset_grid_data_structure.dart b/mobile/lib/modules/home/ui/asset_grid/asset_grid_data_structure.dart index 9c68cf7cbd..6c652409f1 100644 --- a/mobile/lib/modules/home/ui/asset_grid/asset_grid_data_structure.dart +++ b/mobile/lib/modules/home/ui/asset_grid/asset_grid_data_structure.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/shared/models/asset.dart'; @@ -9,14 +10,15 @@ final log = Logger('AssetGridDataStructure'); enum RenderAssetGridElementType { assetRow, - dayTitle, + groupDividerTitle, monthTitle; } class RenderAssetGridRow { final List<Asset> assets; + final List<double> widthDistribution; - RenderAssetGridRow(this.assets); + RenderAssetGridRow(this.assets, this.widthDistribution); } class RenderAssetGridElement { @@ -35,19 +37,36 @@ class RenderAssetGridElement { }); } +enum GroupAssetsBy { + day, + month; +} + +class AssetGridLayoutParameters { + final int perRow; + final bool dynamicLayout; + final GroupAssetsBy groupBy; + + AssetGridLayoutParameters( + this.perRow, + this.dynamicLayout, + this.groupBy, + ); +} + class _AssetGroupsToRenderListComputeParameters { final String monthFormat; final String dayFormat; final String dayFormatYear; - final Map<String, List<Asset>> groups; - final int perRow; + final List<Asset> assets; + final AssetGridLayoutParameters layout; _AssetGroupsToRenderListComputeParameters( this.monthFormat, this.dayFormat, this.dayFormatYear, - this.groups, - this.perRow, + this.assets, + this.layout, ); } @@ -56,62 +75,75 @@ class RenderList { RenderList(this.elements); + static Map<String, List<Asset>> _groupAssets( + List<Asset> assets, + GroupAssetsBy groupBy, + ) { + assets.sortByCompare<DateTime>( + (e) => e.createdAt, + (a, b) => b.compareTo(a), + ); + + if (groupBy == GroupAssetsBy.day) { + return assets.groupListsBy( + (element) => DateFormat('y-MM-dd').format(element.createdAt.toLocal()), + ); + } else if (groupBy == GroupAssetsBy.month) { + return assets.groupListsBy( + (element) => DateFormat('y-MM').format(element.createdAt.toLocal()), + ); + } + + return {}; + } + static Future<RenderList> _processAssetGroupData( _AssetGroupsToRenderListComputeParameters data, ) async { final monthFormat = DateFormat(data.monthFormat); final dayFormatSameYear = DateFormat(data.dayFormat); final dayFormatOtherYear = DateFormat(data.dayFormatYear); - final groups = data.groups; - final perRow = data.perRow; + final allAssets = data.assets; + final perRow = data.layout.perRow; + final dynamicLayout = data.layout.dynamicLayout; + final groupBy = data.layout.groupBy; List<RenderAssetGridElement> elements = []; DateTime? lastDate; + final groups = _groupAssets(allAssets, groupBy); + groups.forEach((groupName, assets) { try { - final date = DateTime.parse(groupName); - - if (lastDate == null || lastDate!.month != date.month) { - // Month title - - var monthTitleText = groupName; - - var groupDate = DateTime.tryParse(groupName); - if (groupDate != null) { - monthTitleText = monthFormat.format(groupDate); - } else { - log.severe("Failed to format date for day title: $groupName"); - } + final date = assets.first.createdAt.toLocal(); + // Month title + if (groupBy == GroupAssetsBy.day && + (lastDate == null || lastDate!.month != date.month)) { elements.add( RenderAssetGridElement( RenderAssetGridElementType.monthTitle, - title: monthTitleText, + title: monthFormat.format(date), date: date, ), ); } - // Add group title - var currentYear = DateTime.now().year; - var groupYear = DateTime.parse(groupName).year; - var formatDate = - currentYear == groupYear ? dayFormatSameYear : dayFormatOtherYear; + // Group divider title (day or month) + var formatDate = dayFormatOtherYear; - var dateText = groupName; + if (DateTime.now().year == date.year) { + formatDate = dayFormatSameYear; + } - var groupDate = DateTime.tryParse(groupName); - if (groupDate != null) { - dateText = formatDate.format(groupDate); - } else { - log.severe("Failed to format date for day title: $groupName"); + if (groupBy == GroupAssetsBy.month) { + formatDate = monthFormat; } elements.add( RenderAssetGridElement( - RenderAssetGridElementType.dayTitle, - title: dateText, + RenderAssetGridElementType.groupDividerTitle, + title: formatDate.format(date), date: date, relatedAssetList: assets, ), @@ -121,12 +153,37 @@ class RenderList { int cursor = 0; while (cursor < assets.length) { int rowElements = min(assets.length - cursor, perRow); + final rowAssets = assets.sublist(cursor, cursor + rowElements); + + // Default: All assets have the same width + var widthDistribution = List.filled(rowElements, 1.0); + + if (dynamicLayout) { + final aspectRatios = + rowAssets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList(); + final meanAspectRatio = aspectRatios.sum / rowElements; + + // 1: mean width + // 0.5: width < mean - threshold + // 1.5: width > mean + threshold + final arConfiguration = aspectRatios.map((e) { + if (e - meanAspectRatio > 0.3) return 1.5; + if (e - meanAspectRatio < -0.3) return 0.5; + return 1.0; + }); + + // Normalize: + final sum = arConfiguration.sum; + widthDistribution = + arConfiguration.map((e) => (e * rowElements) / sum).toList(); + } final rowElement = RenderAssetGridElement( RenderAssetGridElementType.assetRow, date: date, assetRow: RenderAssetGridRow( - assets.sublist(cursor, cursor + rowElements), + rowAssets, + widthDistribution, ), ); @@ -143,9 +200,9 @@ class RenderList { return RenderList(elements); } - static Future<RenderList> fromAssetGroups( - Map<String, List<Asset>> assetGroups, - int assetsPerRow, + static Future<RenderList> fromAssets( + List<Asset> assets, + AssetGridLayoutParameters layout, ) async { // Compute only allows for one parameter. Therefore we pass all parameters in a map return compute( @@ -154,8 +211,8 @@ class RenderList { "monthly_title_text_date_format".tr(), "daily_title_text_date".tr(), "daily_title_text_date_year".tr(), - assetGroups, - assetsPerRow, + assets, + layout, ), ); } diff --git a/mobile/lib/modules/home/ui/asset_grid/daily_title_text.dart b/mobile/lib/modules/home/ui/asset_grid/group_divider_title.dart similarity index 94% rename from mobile/lib/modules/home/ui/asset_grid/daily_title_text.dart rename to mobile/lib/modules/home/ui/asset_grid/group_divider_title.dart index 7cee410a19..6a92c9e21f 100644 --- a/mobile/lib/modules/home/ui/asset_grid/daily_title_text.dart +++ b/mobile/lib/modules/home/ui/asset_grid/group_divider_title.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -class DailyTitleText extends ConsumerWidget { - const DailyTitleText({ +class GroupDividerTitle extends ConsumerWidget { + const GroupDividerTitle({ Key? key, required this.text, required this.multiselectEnabled, diff --git a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart index 3db0da6753..c7fdf84836 100644 --- a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart @@ -7,7 +7,7 @@ import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'asset_grid_data_structure.dart'; -import 'daily_title_text.dart'; +import 'group_divider_title.dart'; import 'disable_multi_select_button.dart'; import 'draggable_scrollbar_custom.dart'; @@ -99,12 +99,12 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> { widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow; return Row( key: Key("asset-row-${row.assets.first.id}"), - children: row.assets.map((Asset asset) { + children: row.assets.mapIndexed((int index, Asset asset) { bool last = asset.id == row.assets.last.id; return Container( key: Key("asset-${asset.id}"), - width: size, + width: size * row.widthDistribution[index], height: size, margin: EdgeInsets.only( top: widget.margin, @@ -123,7 +123,7 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> { String title, List<Asset> assets, ) { - return DailyTitleText( + return GroupDividerTitle( text: title, multiselectEnabled: widget.selectionActive, onSelect: () => _selectAssets(assets), @@ -150,7 +150,7 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> { Widget _itemBuilder(BuildContext c, int position) { final item = widget.renderList.elements[position]; - if (item.type == RenderAssetGridElementType.dayTitle) { + if (item.type == RenderAssetGridElementType.groupDividerTitle) { return _buildTitle(c, item.title!, item.relatedAssetList!); } else if (item.type == RenderAssetGridElementType.monthTitle) { return _buildMonthTitle(c, item.title!); diff --git a/mobile/lib/modules/home/views/home_page.dart b/mobile/lib/modules/home/views/home_page.dart index a409ce73cf..298fb4e496 100644 --- a/mobile/lib/modules/home/views/home_page.dart +++ b/mobile/lib/modules/home/views/home_page.dart @@ -220,8 +220,8 @@ class HomePage extends HookConsumerWidget { top: true, child: Stack( children: [ - ref.watch(assetProvider).renderList == null || - ref.watch(assetProvider).allAssets.isEmpty + ref.watch(assetProvider).renderList == null + || ref.watch(assetProvider).allAssets.isEmpty ? buildLoadingIndicator() : ImmichAssetGrid( renderList: ref.watch(assetProvider).renderList!, diff --git a/mobile/lib/modules/search/providers/search_result_page.provider.dart b/mobile/lib/modules/search/providers/search_result_page.provider.dart index cbc7633999..102573bcad 100644 --- a/mobile/lib/modules/search/providers/search_result_page.provider.dart +++ b/mobile/lib/modules/search/providers/search_result_page.provider.dart @@ -1,4 +1,3 @@ -import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/modules/search/models/search_result_page_state.model.dart'; @@ -7,7 +6,6 @@ import 'package:immich_mobile/modules/search/services/search.service.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; import 'package:immich_mobile/shared/models/asset.dart'; -import 'package:intl/intl.dart'; class SearchResultPageNotifier extends StateNotifier<SearchResultPageState> { SearchResultPageNotifier(this._searchService) @@ -56,23 +54,16 @@ final searchResultPageProvider = return SearchResultPageNotifier(ref.watch(searchServiceProvider)); }); -final searchResultGroupByDateTimeProvider = StateProvider((ref) { - var assets = ref.watch(searchResultPageProvider).searchResult; - - assets.sortByCompare<DateTime>( - (e) => e.createdAt, - (a, b) => b.compareTo(a), - ); - return assets.groupListsBy( - (element) => DateFormat('y-MM-dd').format(element.createdAt.toLocal()), - ); -}); - final searchRenderListProvider = FutureProvider((ref) { - var assetGroups = ref.watch(searchResultGroupByDateTimeProvider); - var settings = ref.watch(appSettingsServiceProvider); - final assetsPerRow = settings.getSetting(AppSettingsEnum.tilesPerRow); - return RenderList.fromAssetGroups(assetGroups, assetsPerRow); + final assets = ref.watch(searchResultPageProvider).searchResult; + + final layout = AssetGridLayoutParameters( + settings.getSetting(AppSettingsEnum.tilesPerRow), + settings.getSetting(AppSettingsEnum.dynamicLayout), + GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)], + ); + + return RenderList.fromAssets(assets, layout); }); diff --git a/mobile/lib/modules/settings/services/app_settings.service.dart b/mobile/lib/modules/settings/services/app_settings.service.dart index 67bfedf774..d2f4b10648 100644 --- a/mobile/lib/modules/settings/services/app_settings.service.dart +++ b/mobile/lib/modules/settings/services/app_settings.service.dart @@ -6,6 +6,8 @@ enum AppSettingsEnum<T> { loadOriginal<bool>("loadOriginal", false), themeMode<String>("themeMode", "system"), // "light","dark","system" tilesPerRow<int>("tilesPerRow", 4), + dynamicLayout<bool>("dynamicLayout", false), + groupAssetsBy<int>("groupBy", 0), uploadErrorNotificationGracePeriod<int>( "uploadErrorNotificationGracePeriod", 2, diff --git a/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_layout_settings.dart new file mode 100644 index 0000000000..dec1f09f4c --- /dev/null +++ b/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_layout_settings.dart @@ -0,0 +1,99 @@ +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/home/ui/asset_grid/asset_grid_data_structure.dart'; +import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; +import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:immich_mobile/shared/providers/asset.provider.dart'; + +class LayoutSettings extends HookConsumerWidget { + const LayoutSettings({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final appSettingService = ref.watch(appSettingsServiceProvider); + + final useDynamicLayout = useState(true); + final groupBy = useState(GroupAssetsBy.day); + + void switchChanged(bool value) { + appSettingService.setSetting(AppSettingsEnum.dynamicLayout, value); + useDynamicLayout.value = value; + ref.watch(assetProvider.notifier).rebuildAssetGridDataStructure(); + } + + void changeGroupValue(GroupAssetsBy? value) { + if (value != null) { + appSettingService.setSetting(AppSettingsEnum.groupAssetsBy, value.index); + groupBy.value = value; + ref.watch(assetProvider.notifier).rebuildAssetGridDataStructure(); + } + } + + useEffect( + () { + useDynamicLayout.value = + appSettingService.getSetting<bool>(AppSettingsEnum.dynamicLayout); + groupBy.value = + GroupAssetsBy.values[appSettingService.getSetting<int>(AppSettingsEnum.groupAssetsBy)]; + + return null; + }, + [], + ); + + return Column( + children: [ + SwitchListTile.adaptive( + activeColor: Theme.of(context).primaryColor, + title: const Text( + "asset_list_layout_settings_dynamic_layout_title", + style: TextStyle( + fontSize: 12, + ), + ).tr(), + onChanged: switchChanged, + value: useDynamicLayout.value, + ), + ListTile( + title: const Text( + "asset_list_layout_settings_group_by", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ).tr(), + ), + RadioListTile( + activeColor: Theme.of(context).primaryColor, + title: const Text( + "asset_list_layout_settings_group_by_month_day", + style: TextStyle( + fontSize: 12, + ), + ).tr(), + value: GroupAssetsBy.day, + groupValue: groupBy.value, + onChanged: changeGroupValue, + controlAffinity: ListTileControlAffinity.trailing, + ), + RadioListTile( + activeColor: Theme.of(context).primaryColor, + title: const Text( + "asset_list_layout_settings_group_by_month", + style: TextStyle( + fontSize: 12, + ), + ).tr(), + value: GroupAssetsBy.month, + groupValue: groupBy.value, + onChanged: changeGroupValue, + controlAffinity: ListTileControlAffinity.trailing, + ), + ], + ); + } +} diff --git a/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_settings.dart b/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_settings.dart index 350022fcbf..ef7afb0432 100644 --- a/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_settings.dart +++ b/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_settings.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_layout_settings.dart'; import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_storage_indicator.dart'; import 'asset_list_tiles_per_row.dart'; @@ -27,6 +28,7 @@ class AssetListSettings extends StatelessWidget { children: const [ TilesPerRow(), StorageIndicator(), + LayoutSettings(), ], ); } diff --git a/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_storage_indicator.dart b/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_storage_indicator.dart index 44301447d4..9cd8ee5e9c 100644 --- a/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_storage_indicator.dart +++ b/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_storage_indicator.dart @@ -20,8 +20,7 @@ class StorageIndicator extends HookConsumerWidget { void switchChanged(bool value) { appSettingService.setSetting(AppSettingsEnum.storageIndicator, value); showStorageIndicator.value = value; - - ref.invalidate(assetProvider); + ref.watch(assetProvider.notifier).rebuildAssetGridDataStructure(); } useEffect( diff --git a/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_tiles_per_row.dart b/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_tiles_per_row.dart index 80b18ca96c..5e25dfce4f 100644 --- a/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_tiles_per_row.dart +++ b/mobile/lib/modules/settings/ui/asset_list_settings/asset_list_tiles_per_row.dart @@ -23,8 +23,7 @@ class TilesPerRow extends HookConsumerWidget { } void sliderChangedEnd(double _) { - ref.invalidate(assetProvider); - ref.watch(assetProvider.notifier).getAllAsset(); + ref.watch(assetProvider.notifier).rebuildAssetGridDataStructure(); } useEffect( diff --git a/mobile/lib/shared/providers/asset.provider.dart b/mobile/lib/shared/providers/asset.provider.dart index 1f90f07763..c124418943 100644 --- a/mobile/lib/shared/providers/asset.provider.dart +++ b/mobile/lib/shared/providers/asset.provider.dart @@ -25,11 +25,15 @@ class AssetsState { AssetsState(this.allAssets, {this.renderList}); - Future<AssetsState> withRenderDataStructure(int groupSize) async { + Future<AssetsState> withRenderDataStructure( + AssetGridLayoutParameters layout, + ) async { return AssetsState( allAssets, - renderList: - await RenderList.fromAssetGroups(await _groupByDate(), groupSize), + renderList: await RenderList.fromAssets( + allAssets, + layout, + ), ); } @@ -37,20 +41,6 @@ class AssetsState { return AssetsState([...allAssets, ...toAdd]); } - Future<Map<String, List<Asset>>> _groupByDate() async { - sortCompare(List<Asset> assets) { - assets.sortByCompare<DateTime>( - (e) => e.createdAt, - (a, b) => b.compareTo(a), - ); - return assets.groupListsBy( - (element) => DateFormat('y-MM-dd').format(element.createdAt.toLocal()), - ); - } - - return await compute(sortCompare, allAssets.toList()); - } - static AssetsState fromAssetList(List<Asset> assets) { return AssetsState(assets); } @@ -91,10 +81,19 @@ class AssetNotifier extends StateNotifier<AssetsState> { _assetCacheService.put(newAssetList); } - state = - await AssetsState.fromAssetList(newAssetList).withRenderDataStructure( + final layout = AssetGridLayoutParameters( _settingsService.getSetting(AppSettingsEnum.tilesPerRow), + _settingsService.getSetting(AppSettingsEnum.dynamicLayout), + GroupAssetsBy.values[_settingsService.getSetting(AppSettingsEnum.groupAssetsBy)], ); + + state = await AssetsState.fromAssetList(newAssetList) + .withRenderDataStructure(layout); + } + + // Just a little helper to trigger a rebuild of the state object + Future<void> rebuildAssetGridDataStructure() async { + await _updateAssetsState(state.allAssets, cache: false); } getAllAsset() async { diff --git a/mobile/test/asset_grid_data_structure_test.dart b/mobile/test/asset_grid_data_structure_test.dart index 20aa2b64d3..6b715d20b4 100644 --- a/mobile/test/asset_grid_data_structure_test.dart +++ b/mobile/test/asset_grid_data_structure_test.dart @@ -25,46 +25,61 @@ void main() { ); } - final Map<String, List<Asset>> groups = { - '2022-01-05': testAssets.sublist(0, 5).map((e) { + final List<Asset> assets = []; + + assets.addAll( + testAssets.sublist(0, 5).map((e) { e.createdAt = DateTime(2022, 1, 5); return e; }).toList(), - '2022-01-10': testAssets.sublist(5, 10).map((e) { + ); + assets.addAll( + testAssets.sublist(5, 10).map((e) { e.createdAt = DateTime(2022, 1, 10); return e; }).toList(), - '2022-02-17': testAssets.sublist(10, 15).map((e) { + ); + assets.addAll( + testAssets.sublist(10, 15).map((e) { e.createdAt = DateTime(2022, 2, 17); return e; }).toList(), - '2022-10-15': testAssets.sublist(15, 30).map((e) { + ); + assets.addAll( + testAssets.sublist(15, 30).map((e) { e.createdAt = DateTime(2022, 10, 15); return e; - }).toList() - }; + }).toList(), + ); group('Test grouped', () { test('test grouped check months', () async { - final renderList = await RenderList.fromAssetGroups(groups, 3); + final renderList = await RenderList.fromAssets( + assets, + AssetGridLayoutParameters( + 3, + false, + GroupAssetsBy.day, + ), + ); - // Jan - // Day 1 - // 5 Assets => 2 Rows - // Day 2 - // 5 Assets => 2 Rows - // Feb - // Day 1 - // 5 Assets => 2 Rows // Oct // Day 1 // 15 Assets => 5 Rows + // Feb + // Day 1 + // 5 Assets => 2 Rows + // Jan + // Day 2 + // 5 Assets => 2 Rows + // Day 1 + // 5 Assets => 2 Rows expect(renderList.elements.length, 18); expect( renderList.elements[0].type, RenderAssetGridElementType.monthTitle, ); - expect(renderList.elements[0].date.month, 1); + expect(renderList.elements[0].date.month, 10); expect( renderList.elements[7].type, RenderAssetGridElementType.monthTitle, @@ -74,38 +89,44 @@ void main() { renderList.elements[11].type, RenderAssetGridElementType.monthTitle, ); - expect(renderList.elements[11].date.month, 10); + expect(renderList.elements[11].date.month, 1); }); test('test grouped check types', () async { - final renderList = await RenderList.fromAssetGroups(groups, 5); + final renderList = await RenderList.fromAssets( + assets, + AssetGridLayoutParameters( + 5, + false, + GroupAssetsBy.day, + ), + ); - // Jan - // Day 1 - // 5 Assets - // Day 2 - // 5 Assets - // Feb - // Day 1 - // 5 Assets // Oct // Day 1 // 15 Assets => 3 Rows - + // Feb + // Day 1 + // 5 Assets => 1 Row + // Jan + // Day 2 + // 5 Assets => 1 Row + // Day 1 + // 5 Assets => 1 Row final types = [ RenderAssetGridElementType.monthTitle, - RenderAssetGridElementType.dayTitle, + RenderAssetGridElementType.groupDividerTitle, + RenderAssetGridElementType.assetRow, RenderAssetGridElementType.assetRow, - RenderAssetGridElementType.dayTitle, RenderAssetGridElementType.assetRow, RenderAssetGridElementType.monthTitle, - RenderAssetGridElementType.dayTitle, + RenderAssetGridElementType.groupDividerTitle, RenderAssetGridElementType.assetRow, RenderAssetGridElementType.monthTitle, - RenderAssetGridElementType.dayTitle, + RenderAssetGridElementType.groupDividerTitle, RenderAssetGridElementType.assetRow, + RenderAssetGridElementType.groupDividerTitle, RenderAssetGridElementType.assetRow, - RenderAssetGridElementType.assetRow ]; expect(renderList.elements.length, types.length); From 1d1d71c7793b006491abf4d232bdc10275cf53fb Mon Sep 17 00:00:00 2001 From: Alex <alex.tran1502@gmail.com> Date: Thu, 9 Feb 2023 11:36:46 -0600 Subject: [PATCH 23/25] [Localizely] Translations update (#1707) --- mobile/assets/i18n/da-DK.json | 19 +++- mobile/assets/i18n/de-DE.json | 7 ++ mobile/assets/i18n/en-US.json | 2 +- mobile/assets/i18n/es-ES.json | 7 ++ mobile/assets/i18n/fi-FI.json | 7 ++ mobile/assets/i18n/fr-FR.json | 41 ++++--- mobile/assets/i18n/it-IT.json | 7 ++ mobile/assets/i18n/ja-JP.json | 7 ++ mobile/assets/i18n/nl-NL.json | 202 +++++++++++++++++++++------------- mobile/assets/i18n/pl-PL.json | 7 ++ 10 files changed, 206 insertions(+), 100 deletions(-) diff --git a/mobile/assets/i18n/da-DK.json b/mobile/assets/i18n/da-DK.json index 170b0cab85..c53fe6c364 100644 --- a/mobile/assets/i18n/da-DK.json +++ b/mobile/assets/i18n/da-DK.json @@ -108,23 +108,30 @@ "experimental_settings_new_asset_list_title": "Aktiver eksperimentelt fotogitter", "experimental_settings_subtitle": "Brug på eget ansvar!", "experimental_settings_title": "Eksperimentelle", + "favorites_page_title": "Favorites", "home_page_add_to_album_conflicts": "Tilføjede {added} billeder og videoer til album {album}. {failed} billeder og videoer er allerede i albummet.", "home_page_add_to_album_success": "Tilføjede {added} billeder og videoer til album {album}.", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Albummer", + "library_page_favorites": "Favorites", "library_page_new_album": "Nyt album", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "Log ind", - "login_form_email_hint": "din-email@email.com", + "login_form_email_hint": "din-e-mail@e-mail.com", "login_form_endpoint_hint": "http://din-server-ip:port/api", "login_form_endpoint_url": "Server Endpoint URL", "login_form_err_http": "Angiv venligst http:// eller https://", - "login_form_err_invalid_email": "Ugyldig email", - "login_form_err_invalid_url": "Invalid URL", + "login_form_err_invalid_email": "Ugyldig e-mail", + "login_form_err_invalid_url": "Ugyldig webadresse", "login_form_err_leading_whitespace": "Mellemrum før", "login_form_err_trailing_whitespace": "Mellemrum efter", - "login_form_failed_get_oauth_server_config": "Fejl med at logge på med OAuth. Tjek serveres URL", + "login_form_failed_get_oauth_server_config": "Fejl med at logge på med OAuth. Tjek serveres webadresse", "login_form_failed_get_oauth_server_disable": "OAuth er ikke tilgængelig på denne server", - "login_form_failed_login": "Der opstod en vejl ved at logge ind. Tjek server URL, email og kodeordet", - "login_form_label_email": "Email", + "login_form_failed_login": "Der opstod en vejl ved at logge ind. Tjek server webadressen, e-mailen og kodeordet", + "login_form_label_email": "E-mail", "login_form_label_password": "Kodeord", "login_form_password_hint": "kodeord", "login_form_save_login": "Forbliv logget ind", diff --git a/mobile/assets/i18n/de-DE.json b/mobile/assets/i18n/de-DE.json index 3984b696f7..9d7969fa12 100644 --- a/mobile/assets/i18n/de-DE.json +++ b/mobile/assets/i18n/de-DE.json @@ -108,10 +108,17 @@ "experimental_settings_new_asset_list_title": "Enable experimental photo grid", "experimental_settings_subtitle": "Use at your own risk!", "experimental_settings_title": "Experimentell", + "favorites_page_title": "Favorites", "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", "home_page_add_to_album_success": "Added {added} assets to album {album}.", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Alben", + "library_page_favorites": "Favorites", "library_page_new_album": "Neues Album", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "Anmelden", "login_form_email_hint": "deine@email.de", "login_form_endpoint_hint": "http://deine-server-ip:port/api", diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 25220eb269..815e79603d 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -118,8 +118,8 @@ "home_page_building_timeline": "Building the timeline", "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Albums", - "library_page_new_album": "New album", "library_page_favorites": "Favorites", + "library_page_new_album": "New album", "library_page_sharing": "Sharing", "library_page_sort_created": "Most recently created", "library_page_sort_title": "Album title", diff --git a/mobile/assets/i18n/es-ES.json b/mobile/assets/i18n/es-ES.json index f325a96b7b..8f4fdebc9c 100644 --- a/mobile/assets/i18n/es-ES.json +++ b/mobile/assets/i18n/es-ES.json @@ -108,10 +108,17 @@ "experimental_settings_new_asset_list_title": "Enable experimental photo grid", "experimental_settings_subtitle": "Use at your own risk!", "experimental_settings_title": "Experimental", + "favorites_page_title": "Favorites", "home_page_add_to_album_conflicts": "Añadidos {added} elementos al álbum {album}. {failed} elementos ya están añadidos.", "home_page_add_to_album_success": "Añadidos {added} elementos al álbum {album}.", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Albums", + "library_page_favorites": "Favorites", "library_page_new_album": "New album", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "Iniciar Sesión", "login_form_email_hint": "tucorreo@correo.com", "login_form_endpoint_hint": "http://tu-ip-de-servidor:puerto/api", diff --git a/mobile/assets/i18n/fi-FI.json b/mobile/assets/i18n/fi-FI.json index 233adb6984..c557f03d7a 100644 --- a/mobile/assets/i18n/fi-FI.json +++ b/mobile/assets/i18n/fi-FI.json @@ -108,10 +108,17 @@ "experimental_settings_new_asset_list_title": "Ota käyttöön kokeellinen kuvaruudukko", "experimental_settings_subtitle": "Käyttö omalla vastuulla!", "experimental_settings_title": "Kokeellinen", + "favorites_page_title": "Favorites", "home_page_add_to_album_conflicts": "Lisätty {added} kohdetta albumiin {album}. {failed} kohdetta on jo albumissa.", "home_page_add_to_album_success": "Lisätty {added} kohdetta albumiin {album}.", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Albumit", + "library_page_favorites": "Favorites", "library_page_new_album": "Uusi albumi", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "Kirjaudu", "login_form_email_hint": "sahkopostisi@esimerkki.fi", "login_form_endpoint_hint": "http://palvelimesi-osoite:portti/api", diff --git a/mobile/assets/i18n/fr-FR.json b/mobile/assets/i18n/fr-FR.json index 27d9f58b58..ae793e32e2 100644 --- a/mobile/assets/i18n/fr-FR.json +++ b/mobile/assets/i18n/fr-FR.json @@ -35,7 +35,7 @@ "backup_controller_page_background_battery_info_title": "Optimisation de la batterie", "backup_controller_page_background_charging": "Seulement pendant la charge", "backup_controller_page_background_configure_error": "Échec de la configuration du service d'arrière-plan", - "backup_controller_page_background_delay": "Delay new assets backup: {}", + "backup_controller_page_background_delay": "Retarder la sauvegarde des nouveaux éléments d'actif : {}", "backup_controller_page_background_description": "Activez le service d'arrière-plan pour sauvegarder automatiquement tous les nouveaux éléments sans avoir à ouvrir l'application.", "backup_controller_page_background_is_off": "La sauvegarde automatique en arrière-plan est désactivée", "backup_controller_page_background_is_on": "La sauvegarde automatique en arrière-plan est activée", @@ -83,10 +83,10 @@ "cache_settings_subtitle": "Contrôler le comportement de mise en cache de l'application mobile Immich", "cache_settings_thumbnail_size": "Taille du cache des miniatures ({} éléments)", "cache_settings_title": "Paramètres de mise en cache", - "control_bottom_app_bar_add_to_album": "Add to album", - "control_bottom_app_bar_album_info": "{} items", - "control_bottom_app_bar_album_info_shared": "{} items · Shared", - "control_bottom_app_bar_create_new_album": "Create new album", + "control_bottom_app_bar_add_to_album": "Ajouter à l'album", + "control_bottom_app_bar_album_info": "{} éléments", + "control_bottom_app_bar_album_info_shared": "{} éléments - Partagés", + "control_bottom_app_bar_create_new_album": "Créer un nouvel album", "control_bottom_app_bar_delete": "Supprimer", "control_bottom_app_bar_share": "Partager", "create_album_page_untitled": "Sans titre", @@ -108,28 +108,35 @@ "experimental_settings_new_asset_list_title": "Activez la grille de photos expérimentale", "experimental_settings_subtitle": "Utilisez à vos dépends !", "experimental_settings_title": "Expérimental", - "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", - "home_page_add_to_album_success": "Added {added} assets to album {album}.", + "favorites_page_title": "Favorites", + "home_page_add_to_album_conflicts": "{added} éléments ajoutés à l'album {album}. Les éléments {failed} sont déjà dans l'album.", + "home_page_add_to_album_success": "{added} éléments ajoutés à l'album {album}.", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Albums", + "library_page_favorites": "Favorites", "library_page_new_album": "Nouvel album", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "Connexion", "login_form_email_hint": "votreemail@email.com", "login_form_endpoint_hint": "http://adresse-ip-serveur:port/api", "login_form_endpoint_url": "URL du point d'accès au serveur", "login_form_err_http": "Veuillez préciser http:// ou https://", "login_form_err_invalid_email": "Email invalide", - "login_form_err_invalid_url": "Invalid URL", + "login_form_err_invalid_url": "URL invalide", "login_form_err_leading_whitespace": "Espace en début de ligne", "login_form_err_trailing_whitespace": "Espace de fin de ligne", - "login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL", - "login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server", + "login_form_failed_get_oauth_server_config": "Erreur de connexion par OAuth, vérifiez l\"URL du serveur", + "login_form_failed_get_oauth_server_disable": "La fonctionnalité OAuth n'est pas disponible sur ce serveur", "login_form_failed_login": "Erreur de connexion, vérifiez l'url du serveur, l'email et le mot de passe", "login_form_label_email": "Email", "login_form_label_password": "Mot de passe", "login_form_password_hint": "mot de passe", "login_form_save_login": "Rester connecté", "monthly_title_text_date_format": "MMMM y", - "profile_drawer_app_logs": "Logs", + "profile_drawer_app_logs": "Journaux", "profile_drawer_client_server_up_to_date": "Le client et le serveur sont à jour", "profile_drawer_settings": "Paramètres", "profile_drawer_sign_out": "Se déconnecter", @@ -142,17 +149,17 @@ "select_additional_user_for_sharing_page_suggestions": "Suggestions", "select_user_for_sharing_page_err_album": "Échec de la création de l'album", "select_user_for_sharing_page_share_suggestions": "Suggestions", - "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", - "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", - "setting_image_viewer_original_title": "Load original image", - "setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.", - "setting_image_viewer_preview_title": "Load preview image", + "setting_image_viewer_help": "Le visualiseur de détails charge d'abord la petite miniature, puis l'aperçu de taille moyenne (s'il est activé), enfin l'original (s'il est activé).", + "setting_image_viewer_original_subtitle": "Activez cette option pour charger l'image originale en résolution originale (volumineux !). Désactiver pour réduire l'utilisation des données (réseau et cache de l'appareil).", + "setting_image_viewer_original_title": "Charger l'image originale", + "setting_image_viewer_preview_subtitle": "Activer pour charger une image de résolution moyenne. Désactiver pour charger directement l'original ou utiliser uniquement la miniature.", + "setting_image_viewer_preview_title": "Charger l'image d'aperçu", "setting_notifications_notify_failures_grace_period": "Notifier les échecs de la sauvegarde en arrière-plan : {}", "setting_notifications_notify_hours": "{} heures", "setting_notifications_notify_immediately": "immédiatement", "setting_notifications_notify_minutes": "{} minutes", "setting_notifications_notify_never": "jamais", - "setting_notifications_notify_seconds": "{} seconds", + "setting_notifications_notify_seconds": "{} secondes", "setting_notifications_single_progress_subtitle": "Informations détaillées sur la progression du transfert par élément", "setting_notifications_single_progress_title": "Afficher la progression du détail de la sauvegarde en arrière-plan", "setting_notifications_subtitle": "Ajustez vos préférences de notification", diff --git a/mobile/assets/i18n/it-IT.json b/mobile/assets/i18n/it-IT.json index 9a1cee38bf..6c75e43a3e 100644 --- a/mobile/assets/i18n/it-IT.json +++ b/mobile/assets/i18n/it-IT.json @@ -108,10 +108,17 @@ "experimental_settings_new_asset_list_title": "Attiva griglia di foto sperimentale", "experimental_settings_subtitle": "Usalo a tuo rischio!", "experimental_settings_title": "Sperimentale", + "favorites_page_title": "Favorites", "home_page_add_to_album_conflicts": "Aggiunti {added} elementi all'album {album}. {failed} elementi erano già presenti nell'album.", "home_page_add_to_album_success": "Aggiunti {added} elementi all'album {album}", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Album", + "library_page_favorites": "Favorites", "library_page_new_album": "Nuovo Album", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "Login", "login_form_email_hint": "tuaemail@email.com", "login_form_endpoint_hint": "http://ip-del-tuo-server:port/api", diff --git a/mobile/assets/i18n/ja-JP.json b/mobile/assets/i18n/ja-JP.json index cee1742d7e..7b6849b38b 100644 --- a/mobile/assets/i18n/ja-JP.json +++ b/mobile/assets/i18n/ja-JP.json @@ -108,10 +108,17 @@ "experimental_settings_new_asset_list_title": "試験的なグリッドを有効", "experimental_settings_subtitle": "試験的だから自己責任でね", "experimental_settings_title": "試験的", + "favorites_page_title": "Favorites", "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", "home_page_add_to_album_success": "Added {added} assets to album {album}.", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "アルバム", + "library_page_favorites": "Favorites", "library_page_new_album": "新しいアルバム", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "ログイン", "login_form_email_hint": "example@email.com", "login_form_endpoint_hint": "https://example.com:port/api", diff --git a/mobile/assets/i18n/nl-NL.json b/mobile/assets/i18n/nl-NL.json index d27232b533..7193c62af9 100644 --- a/mobile/assets/i18n/nl-NL.json +++ b/mobile/assets/i18n/nl-NL.json @@ -1,6 +1,9 @@ { "album_info_card_backup_album_excluded": "UITGESLOTEN", "album_info_card_backup_album_included": "INGESLOTEN", + "album_thumbnail_card_item": "1 item", + "album_thumbnail_card_items": "{} items", + "album_thumbnail_card_shared": " · Gedeeld", "album_viewer_appbar_share_delete": "Verwijder album", "album_viewer_appbar_share_err_delete": "Fout bij verwijderen album", "album_viewer_appbar_share_err_leave": "Fout bij verlaten album", @@ -9,6 +12,8 @@ "album_viewer_appbar_share_leave": "Verlaat album", "album_viewer_appbar_share_remove": "Verwijder uit album", "album_viewer_page_share_add_users": "Voeg gebruiker toe", + "asset_list_settings_subtitle": "Foto grid layout instellingen", + "asset_list_settings_title": "Foto Grid", "backup_album_selection_page_albums_device": "Albums op apparaat ({})", "backup_album_selection_page_albums_tap": "Tik om in te voegen, dubbel tik om uit te sluiten", "backup_album_selection_page_assets_scatter": "Items kunnen over verschillende albums verdeeld zijn, dus albums kunnen ingesloten of uitgesloten zijn van het backup proces.", @@ -16,137 +21,182 @@ "backup_album_selection_page_selection_info": "Selectie info", "backup_album_selection_page_total_assets": "Totaal unieke items", "backup_all": "Alle", - "backup_background_service_default_notification": "Controleren op nieuw items…", - "backup_background_service_upload_failure_notification": "Fout bij upload {}", - "backup_background_service_in_progress_notification": "Backuppen van items…", - "backup_background_service_current_upload_notification": "Uploaden {}", - "backup_background_service_error_title": "Backup fout", - "backup_background_service_connection_failed_message": "Fout bij verbinden server. Opnieuw proberen…", "backup_background_service_backup_failed_message": "Fout bij backuppen items. Opnieuw proberen…", - "backup_controller_page_albums": "Backup Albums", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Geselecteerd: ", - "backup_controller_page_backup_sub": "Foto's en video's gebackupped", - "backup_controller_page_background_description": "Gebruik achtergrondservice om automatisch nieuwe items te uploaden naar server zonder de app te openen", - "backup_controller_page_background_wifi": "Alleen op WiFi", + "backup_background_service_connection_failed_message": "Fout bij verbinden server. Opnieuw proberen…", + "backup_background_service_current_upload_notification": "Uploaden {}", + "backup_background_service_default_notification": "Controleren op nieuw items…", + "backup_background_service_error_title": "Backup fout", + "backup_background_service_in_progress_notification": "Back-up maken van items…", + "backup_background_service_upload_failure_notification": "Fout bij upload {}", + "backup_controller_page_albums": "Back-up albums", + "backup_controller_page_background_battery_info_link": "Toon me hoe", + "backup_controller_page_background_battery_info_message": "Schakel voor de beste back-up ervaring op de achtergrond alle batterij optimalisaties uit, die de achtergrondactiviteit van Immich beperkt.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.", + "backup_controller_page_background_battery_info_ok": "OK", + "backup_controller_page_background_battery_info_title": "Batterij optimalisaties", "backup_controller_page_background_charging": "Alleen tijdens opladen", - "backup_controller_page_background_is_on": "Automatische achtergrond backup staat aan", - "backup_controller_page_background_is_off": "Automatische achtergrond backup staat uit", - "backup_controller_page_background_turn_on": "Zet achtergrondservice aan", - "backup_controller_page_background_turn_off": "Zet achtergrondservice uit", "backup_controller_page_background_configure_error": "Achtergrondservice configuratie mislukt", + "backup_controller_page_background_delay": "Back-up vertraging nieuwe items: {}", + "backup_controller_page_background_description": "Schakel de achtergrondservice in om automatisch een back-up te maken van nieuwe items zonder de app te hoeven openen", + "backup_controller_page_background_is_off": "Automatische achtergrond back-up staat uit", + "backup_controller_page_background_is_on": "Automatische achtergrond back-up staat aan", + "backup_controller_page_background_turn_off": "Zet achtergrondservice uit", + "backup_controller_page_background_turn_on": "Zet achtergrondservice aan", + "backup_controller_page_background_wifi": "Alleen op WiFi", + "backup_controller_page_backup": "Back-up", + "backup_controller_page_backup_selected": "Geselecteerd: ", + "backup_controller_page_backup_sub": "Geback-upte foto's en video's", "backup_controller_page_cancel": "Annuleren", "backup_controller_page_created": "Gemaakt op: {}", - "backup_controller_page_desc_backup": "Configureer backup om automatisch nieuwe items te uploaden naar server.", + "backup_controller_page_desc_backup": "Schakel back-up op de voorgrond in om automatisch nieuwe items naar de server te uploaden bij het openen van de app.", "backup_controller_page_excluded": "Uitgezonderd: ", "backup_controller_page_failed": "Mislukt ({})", "backup_controller_page_filename": "Bestandsnaam: {} [{}]", "backup_controller_page_id": "ID: {}", - "backup_controller_page_info": "Backup informatie", + "backup_controller_page_info": "Back-up informatie", "backup_controller_page_none_selected": "Geen geselecteerd", - "backup_controller_page_remainder": "Rest", - "backup_controller_page_remainder_sub": "Overgebleven foto's en video's om te backuppen uit selectie", + "backup_controller_page_remainder": "Resterend", + "backup_controller_page_remainder_sub": "Resterende foto's en video's om een back-up van te maken uit selectie", "backup_controller_page_select": "Selecteer", - "backup_controller_page_server_storage": "Server Opslag", - "backup_controller_page_start_backup": "Start Backup", - "backup_controller_page_status_off": "Backup staat uit", - "backup_controller_page_status_on": "Backup staat aan", + "backup_controller_page_server_storage": "Server opslag", + "backup_controller_page_start_backup": "Back-up uitvoeren", + "backup_controller_page_status_off": "Automatische back-up op de voorgrond staat uit", + "backup_controller_page_status_on": "Automatische back-up op de voorgrond staat aan", "backup_controller_page_storage_format": "{} van {} gebruikt", - "backup_controller_page_to_backup": "Albums om te backuppen", + "backup_controller_page_to_backup": "Albums om een back-up van te maken", "backup_controller_page_total": "Totaal", "backup_controller_page_total_sub": "Alle unieke foto's en video's uit geselecteerde albums", - "backup_controller_page_turn_off": "Backup uitzetten", - "backup_controller_page_turn_on": "Backup aanzetten", + "backup_controller_page_turn_off": "Zet back-up op de voorgrond uit", + "backup_controller_page_turn_on": "Zet back-up op de voorgrond aan", "backup_controller_page_uploading_file_info": "Bestandsgegevens uploaden", - "backup_err_only_album": "Kan niet alleen het album verwijderen", + "backup_err_only_album": "Kan het enige album niet verwijderen", "backup_info_card_assets": "items", + "cache_settings_album_thumbnails": "Thumbnails bibliotheekpagina ({} items)", + "cache_settings_clear_cache_button": "Cache wissen", + "cache_settings_clear_cache_button_title": "Wist de cache van de app. Dit zal de presentaties van de app aanzienlijk beïnvloeden totdat de cache opnieuw is opgebouwd.", + "cache_settings_image_cache_size": "Grootte afbeeldingscache ({} items)", + "cache_settings_statistics_album": "Bibliotheek thumbnails", + "cache_settings_statistics_assets": "{} items ({})", + "cache_settings_statistics_full": "Volledige afbeeldingen", + "cache_settings_statistics_shared": "Gedeeld album thumbnails", + "cache_settings_statistics_thumbnail": "Thumbnails", + "cache_settings_statistics_title": "Cachegebruik", + "cache_settings_subtitle": "Beheer het cachegedrag van de Immich app", + "cache_settings_thumbnail_size": "Thumbnail cachegrootte ({} items)", + "cache_settings_title": "Cache instellingen", + "control_bottom_app_bar_add_to_album": "Toevoegen aan album", + "control_bottom_app_bar_album_info": "{} items", + "control_bottom_app_bar_album_info_shared": "{} items · Gedeeld", + "control_bottom_app_bar_create_new_album": "Maak nieuw album", "control_bottom_app_bar_delete": "Verwijderen", - "create_shared_album_page_share": "Delen", + "control_bottom_app_bar_share": "Delen", + "create_album_page_untitled": "Naamloos", "create_shared_album_page_create": "Aanmaken", - "create_shared_album_page_share_add_assets": "VOEG FOTO'S TOE", - "create_shared_album_page_share_select_photos": "Selecteer Foto's", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", - "date_format": "E, LLL d, y • h:mm a", + "create_shared_album_page_share": "Delen", + "create_shared_album_page_share_add_assets": "ITEMS TOEVOEGEN", + "create_shared_album_page_share_select_photos": "Selecteer foto's", + "daily_title_text_date": "E dd MMM", + "daily_title_text_date_year": "E dd MMM yyyy", + "date_format": "E d LLL y • H:mm", "delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat", "delete_dialog_cancel": "Annuleren", "delete_dialog_ok": "Verwijderen", - "delete_dialog_title": "Verwijder permanent", - "exif_bottom_sheet_description": "Voeg beschrijving toe...", + "delete_dialog_title": "Permanent verwijderen", + "exif_bottom_sheet_description": "Beschrijving toevoegen...", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATIE", - "login_form_button_text": "Login", + "experimental_settings_new_asset_list_subtitle": "Werk in uitvoering", + "experimental_settings_new_asset_list_title": "Experimenteel foto grid inschakelen", + "experimental_settings_subtitle": "Gebruik op eigen risico!", + "experimental_settings_title": "Experimenteel", + "favorites_page_title": "Favorites", + "home_page_add_to_album_conflicts": "{added} items toegevoegd aan album {album}. {failed} items staan al in het album.", + "home_page_add_to_album_success": "{added} items toegevoegd aan album {album}.", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", + "library_page_albums": "Albums", + "library_page_favorites": "Favorites", + "library_page_new_album": "Nieuw album", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", + "login_form_button_text": "Inloggen", "login_form_email_hint": "jouwemail@email.com", "login_form_endpoint_hint": "http://jouw-server-ip:port/api", "login_form_endpoint_url": "Server URL", "login_form_err_http": "Voer http:// of https:// in", - "login_form_err_invalid_email": "Ongeldige Email", + "login_form_err_invalid_email": "Ongeldig e-mailadres", + "login_form_err_invalid_url": "Ongeldige URL", "login_form_err_leading_whitespace": "Spatie aan het begin", "login_form_err_trailing_whitespace": "Spatie aan het eind", - "login_form_failed_login": "Fout bij inloggen, controleer server url, email en wachtwoord", - "login_form_label_email": "Email", + "login_form_failed_get_oauth_server_config": "Fout bij inloggen met OAuth, controleer server URL", + "login_form_failed_get_oauth_server_disable": "OAuth functie is niet beschikbaar op deze server", + "login_form_failed_login": "Fout bij inloggen, controleer server URL, e-mailadres en wachtwoord", + "login_form_label_email": "E-mailadres", "login_form_label_password": "Wachtwoord", "login_form_password_hint": "wachtwoord", "login_form_save_login": "Ingelogd blijven", "monthly_title_text_date_format": "MMMM y", - "profile_drawer_client_server_up_to_date": "Client en Server zijn up-to-date", - "profile_drawer_sign_out": "Uitloggen", + "profile_drawer_app_logs": "Logboek", + "profile_drawer_client_server_up_to_date": "Client en server zijn up-to-date", "profile_drawer_settings": "Instellingen", - "search_bar_hint": "Zoek je foto's", + "profile_drawer_sign_out": "Uitloggen", + "search_bar_hint": "Zoeken naar foto's", "search_page_no_objects": "Geen object gegevens beschikbaar", "search_page_no_places": "Geen locatie gegevens beschikbaar", "search_page_places": "Plaatsen", "search_page_things": "Dingen", - "search_result_page_new_search_hint": "Nieuw resultaat", + "search_result_page_new_search_hint": "Nieuwe zoekopdracht", "select_additional_user_for_sharing_page_suggestions": "Suggesties", "select_user_for_sharing_page_err_album": "Album aanmaken mislukt", "select_user_for_sharing_page_share_suggestions": "Suggesties", + "setting_image_viewer_help": "De gedetailleerde weergave laadt eerst de kleine thumbnail, vervolgens het middelgrote voorbeeld (indien ingeschakeld) en ten slotte het origineel (indien ingeschakeld).", + "setting_image_viewer_original_subtitle": "Inschakelen om de originele afbeelding met volledige resolutie (groot!) te laden. Uitschakelen om datagebruik te verminderen (zowel netwerk- als apparaatcache).", + "setting_image_viewer_original_title": "Originele afbeelding laden", + "setting_image_viewer_preview_subtitle": "Schakel in om een afbeelding met middelgrote resolutie te laden. Schakel uit om alleen het origineel direct te laden of alleen de thumbnail te gebruiken.", + "setting_image_viewer_preview_title": "Voorbeeldafbeelding laden", + "setting_notifications_notify_failures_grace_period": "Meld back-upfouten op de achtergrond: {}", + "setting_notifications_notify_hours": "{} uur", + "setting_notifications_notify_immediately": "meteen", + "setting_notifications_notify_minutes": "{} minuten", + "setting_notifications_notify_never": "nooit", + "setting_notifications_notify_seconds": "{} seconden", + "setting_notifications_single_progress_subtitle": "Gedetaileerde informatie over de uploadvoortgang per item", + "setting_notifications_single_progress_title": "Toon gedetailleerde informatie over back-ups op de achtergrond", + "setting_notifications_subtitle": "Werk je notificatievoorkeuren bij", + "setting_notifications_title": "Notificaties", + "setting_notifications_total_progress_subtitle": "Algehele uploadvoortgang (voltooid/totaal aantal items)", + "setting_notifications_total_progress_title": "Toon de totale voortgang van achtergrond back-up", + "setting_pages_app_bar_settings": "Instellingen", + "settings_require_restart": "Start Immich opnieuw op om deze instelling toe te passen", "share_add": "Toevoegen", "share_add_photos": "Foto's toevoegen", "share_add_title": "Titel toevoegen", "share_create_album": "Album aanmaken", + "share_dialog_preparing": "Voorbereiden...", "share_invite": "Uitnodigen voor album", "sharing_page_album": "Gedeelde albums", "sharing_page_description": "Maak gedeelde albums om foto's en video's te delen met mensen in je netwerk.", "sharing_page_empty_list": "LEGE LIJST", "sharing_silver_appbar_create_shared_album": "Maak gedeeld album", "sharing_silver_appbar_share_partner": "Delen met partner", + "tab_controller_nav_library": "Bibliotheek", "tab_controller_nav_photos": "Foto's", "tab_controller_nav_search": "Zoeken", "tab_controller_nav_sharing": "Delen", - "tab_controller_nav_library": "Bibliotheek", + "theme_setting_asset_list_storage_indicator_title": "Laat ruimte indicator zien bij item tegels", + "theme_setting_asset_list_tiles_per_row_title": "Aantal items per rij ({})", + "theme_setting_dark_mode_switch": "Donkere modus", + "theme_setting_image_viewer_quality_subtitle": "Pas de kwaliteit aan van de gedetailleerde foto weergave", + "theme_setting_image_viewer_quality_title": "Foto weergave kwaliteit", + "theme_setting_system_theme_switch": "Automatisch (volg systeeminstelling)", + "theme_setting_theme_subtitle": "Kies de thema instelling van de app", + "theme_setting_theme_title": "Thema", + "theme_setting_three_stage_loading_subtitle": "Laden in drie fasen kan de laadprestaties verbeteren, maar veroorzaakt een aanzienlijk hogere netwerkbelasting", + "theme_setting_three_stage_loading_title": "Schakel laden in drie fasen in", "version_announcement_overlay_ack": "Bevestig", "version_announcement_overlay_release_notes": "release opmerkingen", "version_announcement_overlay_text_1": "Er is een nieuwe versie beschikbaar van", "version_announcement_overlay_text_2": "neem je tijd en bezoek de ", - "version_announcement_overlay_text_3": " controleer of je docker-compose en .env up-to-date zijn om te voorkomen dat er misconfiguraties zijn, in het bijzonder als je gebruik maakt van WatchTower of een ander mechanisme dat je server automatisch configureert.", - "version_announcement_overlay_title": "Nieuwe server versie beschikbaar \uD83C\uDF89", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", - "album_thumbnail_card_shared": " · Gedeeld", - "library_page_albums": "Albums", - "library_page_new_album": "Nieuw album", - "create_album_page_untitled": "Naamloos", - "share_dialog_preparing": "Voorbereiden...", - "control_bottom_app_bar_share": "Delen", - "setting_pages_app_bar_settings": "Instellingen", - "theme_setting_theme_title": "Thema", - "theme_setting_theme_subtitle": "Kies de thema instelling van de app", - "theme_setting_system_theme_switch": "Automatisch (volg systeeminstelling)", - "theme_setting_dark_mode_switch": "Donkere modus", - "theme_setting_image_viewer_quality_title": "Foto weergave kwaliteit", - "theme_setting_image_viewer_quality_subtitle": "Pas de kwaliteit aan van de gedetailleerde foto weergave", - "theme_setting_three_stage_loading_title": "Drie-laags laden inschakelen", - "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", - "asset_list_settings_title": "Foto Grid", - "asset_list_settings_subtitle": "Foto grid layout instellingen", - "theme_setting_asset_list_storage_indicator_title": "Laat ruimte indicator zien bij item tegels", - "theme_setting_asset_list_tiles_per_row_title": "Aantal items per rij ({})", - "setting_notifications_title": "Notificaties", - "setting_notifications_subtitle": "Werk je notificatievoorkeuren bij", - "setting_notifications_notify_failures_grace_period": "Melding achtergrond backup fouten: {}", - "setting_notifications_notify_immediately": "meteen", - "setting_notifications_notify_minutes": "{} minuten", - "setting_notifications_notify_hours": "{} uur", - "setting_notifications_notify_never": "nooit" -} + "version_announcement_overlay_text_3": " controleer of je docker-compose en .env up-to-date zijn om te voorkomen dat er misconfiguraties zijn, in het bijzonder als je gebruik maakt van WatchTower of een ander mechanisme dat je serverapplicatie automatisch bijwerkt.", + "version_announcement_overlay_title": "Nieuwe serverversie beschikbaar \uD83C\uDF89" +} \ No newline at end of file diff --git a/mobile/assets/i18n/pl-PL.json b/mobile/assets/i18n/pl-PL.json index 81a4ee5c8b..3ffcf4a688 100644 --- a/mobile/assets/i18n/pl-PL.json +++ b/mobile/assets/i18n/pl-PL.json @@ -108,10 +108,17 @@ "experimental_settings_new_asset_list_title": "Enable experimental photo grid", "experimental_settings_subtitle": "Use at your own risk!", "experimental_settings_title": "Experimental", + "favorites_page_title": "Favorites", "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", "home_page_add_to_album_success": "Added {added} assets to album {album}.", + "home_page_building_timeline": "Building the timeline", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "library_page_albums": "Albumy", + "library_page_favorites": "Favorites", "library_page_new_album": "Nowy album", + "library_page_sharing": "Sharing", + "library_page_sort_created": "Most recently created", + "library_page_sort_title": "Album title", "login_form_button_text": "Login", "login_form_email_hint": "twojmail@email.com", "login_form_endpoint_hint": "http://ip-twojego-serwera:port/api", From 263598f2cf120a558d3da0b8967a15c08ec86a6f Mon Sep 17 00:00:00 2001 From: Alex <alex.tran1502@gmail.com> Date: Thu, 9 Feb 2023 11:55:24 -0600 Subject: [PATCH 24/25] fix(server): get shared link album info doesn't contain owner property (#1708) --- server/libs/infra/src/db/entities/album.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/libs/infra/src/db/entities/album.entity.ts b/server/libs/infra/src/db/entities/album.entity.ts index ef87fe4f55..c700a9d934 100644 --- a/server/libs/infra/src/db/entities/album.entity.ts +++ b/server/libs/infra/src/db/entities/album.entity.ts @@ -20,7 +20,7 @@ export class AlbumEntity { @Column() ownerId!: string; - @ManyToOne(() => UserEntity) + @ManyToOne(() => UserEntity, { eager: true }) owner!: UserEntity; @Column({ default: 'Untitled Album' }) From 2cd45ed1de3a55c6ce23b894f1a366c1d5503476 Mon Sep 17 00:00:00 2001 From: Immich Release Bot <bot@immich.app> Date: Thu, 9 Feb 2023 17:59:47 +0000 Subject: [PATCH 25/25] Version v1.46.0 --- mobile/android/fastlane/Fastfile | 4 ++-- mobile/ios/fastlane/Fastfile | 2 +- mobile/pubspec.yaml | 2 +- server/immich-openapi-specs.json | 2 +- server/package-lock.json | 2 +- server/package.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index ebd854b76f..bdf7456f5a 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,8 +35,8 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 68, - "android.injected.version.name" => "1.45.0", + "android.injected.version.code" => 69, + "android.injected.version.name" => "1.46.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 4b0f721b4c..9c6aadf15b 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -19,7 +19,7 @@ platform :ios do desc "iOS Beta" lane :beta do increment_version_number( - version_number: "1.45.0" + version_number: "1.46.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 1fd192fa2c..a5e3613502 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: "none" -version: 1.45.0+68 +version: 1.46.0+69 isar_version: &isar_version 3.0.5 environment: diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index bd7442c78b..d0f0ff777d 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -2689,7 +2689,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.45.0", + "version": "1.46.0", "contact": {} }, "tags": [], diff --git a/server/package-lock.json b/server/package-lock.json index 880c0d1397..98c38f1c42 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.45.0", + "version": "1.46.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/server/package.json b/server/package.json index b82c210ebc..3ded629917 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.45.0", + "version": "1.46.0", "description": "", "author": "", "private": true,

vqz+GM+E(7a0LfwIn*_0!cCp#11!? zltQYZYj&<&xd1{ArczL#MOuFd*Wq=-4cHOujL8aCN6FRtuKiBQ zPzOy6yz-Ddb5Sa7dWk^2e#h$NUjM@mvCD_w`f+-&lAM62)5WZmqMSjqx`br!0&y+P zAP1-n*V!OR4OF3iP%}p!wYK!#ZbWLDAdb6>4G-sr#bC2wkI{1$nO0Olvyir0K;pS$LI!FekE>Zvnugo_ZXQrzdD72sD^_E8}TVQO-r4}h44NvA=j}@ zCBP>|3U6ykw6E834CP`9@6HM=;79sObSFel=jypdjvf9Q559}rb+q=k%rNnePlV&O zqUw&T{T+8^+{vf&o60e|i|Q@hJRKcL3&l$XZ*u$oQP+!{6x z{<=GkWb>G3Ji+~S2SIYOMu7q<**;%kV^5Hz#r@|B&81xPgj&O+UIInfW@tV0uMR9B z11$1iH^{9|AhPp7<}Fg2n?#DIzO_3IdS_{dla}*azp6#K)}yqhnrb)LQ^j)D>9 z9T(awn5c_&gi{KuqYnO}IWnk3&&c;ROp|=9HB0Hy|0-VlwmR}N$LMD_rm@@D(p|XpLA;8Hh+t*+Y*#>aVX$YT7-oL2 zYeey|-CS!0a4lME6BgPq7Sph!JP3pBnTh_EJ81ITZ1%}hVfr-Y?qJDZMwdH20$ z)XoYkKT@VJi}mGzjB)_3;_r@;CrM2Iyc4UE-YAdD_uPmU6mTVymJX~vYCX5EH7s?d zro~7)%QazB>fWXK@2YX?)-MLXF1QsK_uyD6{`$Omj&6V8BvTS-fXjprmaO3 z9onz0@b+tZA8*wjH$ET*Y8)IOR%x}QSqr`zt{X7Jzvty#07r_cA1MMHDN51U0F+@> z%SWrAWWf)zgU+F*uSyAUffOhSu+2oN-kq(Q%+2ajgC?1tFopZs>O0EqQdTe1 zq3}5ZQa5Y-+wP`BAT=p>j#y|2|Z)45s{i zvRgaTC@Mb&N1(_xw5ER=RPgi%2P}z&Qo!}TJ3vYp1%%SK$Y8lgK1k*I-$5`+ziw}W z{XHMr&qne3JFP%U4OvA(n|fc8XG5RSXeZm| zz@zvFn$hQSMWzX(*G%HjE6ez!Z?TficGc?R^WyuqV0Ol%fI!zTM}+QVzY!ZBJ2zh| z6{f)mvA8K+vridSDmxAZ1B(z>pg6T7Xi0jC1co~{44tgpY25fRF5q7~iyL=J=SOh( z053-kdLH%j2=rbX@Bh}ST<+K36c`J0o=itqtt&zxxQ zt=cHd^brH+@-9VNiU!|sy+(|R6-Z;lZ-~!-O`8e`-4||VPa8bvPKfQdIPk9tTpdfn zzu5JBV>g6tyG;2sTHJylQMkxSY3CrLEC9k!zK+Y3-xz^0#<5u#6!1xf`;Yw`t&k+S zHc^Y(XoD0un63oqI-=ftxTBq3W5g(`{WpFJ4bRVzf)c{IkmhsqCfS*qMh`Rjy|sC9@=-IBQ44?YwX&~c-=mh;=66y|PX2+x zbpV6EyeGd*vhI|>VS4VJYV^r}$y^ppUDCkBQdZK{QqYwei0@b%tdy=2YcTsLH;Jvx zgO{AiNr=)Cx^lRvqlrNHzcl?Vr%cp8PW7|2gl=wt!p-j8kn7i{pgznB&6R2=$0vBf z)zI$wTo2X>`8p^yBC6#YhSVRJF77~T$=*9Pt3;MjO(C2sgY~jttV*LINgKi26G4@< zl3ND`B1N!ml>qL+?IDn4S(=|n_$fero_l3lmif3x0kab(PmkCxJXArdl6X8fq)SoV zAID;h-gNUyU6y6i;btS&=dsRTL6u1Ahrs^YH zk6MLel&C6b42YN*axKgq+4yoIIewTRvxoDWMIl{81t7-iz?#_K4mwiK^I&$j6empXl*=uNAhsscK5D0ox)3h3HI+CW-N<7xQL_HdCsXq>~Z4mL$7b&g=&;eP@NTjwS=*4CuB| z2T0KJRmp-H@%p|a1tqc&1b23x9i^$$YqTzos`_cxl1o?Dh{oG5nycoZsqA zWX(X`tIgs3HxqWCp`|l29}0qHlHS?Gh`q13V`oYh6KZ^MjZB$t<}2c!zS8neWBI^u z$JS)zCNc?of07q6>AUaQ+8*oEq~JY6FIxvc=EJvfmYN;Tf)|qiMId*bwfp}Eo~a8R-v4wi+SO^2?AD^6ezCe zv>N;8eP-hXKU#*7Y@4LznrpNW8$JKOXD6}2W3kefK!)?2hN(5?rmcaAN$Uv`b(#f5 zi1vQ~t>92qFqtQi^mFZU55&?;qZ-en!?UV=7OaUYAB1ur5PSLMgMRRSo`(jjI`5o! zfo*cM5JYCr5$%bn5yhYvG)jT0dB&#_zs%557k1&qF;861#))j1gp~Y}}06 z9^psIim}gfR;@$qL9@$r)(dH z*GMeREO#&44-qBf9EP<*m0ZQYVC1wKrv>d2Eok5NZRARkyW=!y_kZ-?-N9B(Y`u@o z*%?|Mvsh#$bek$z%+!h1NNl8aMG|y34m=8kiau2Pjhx=TyjT~{Xu8M>=R_To_AoUe z70<FU}$%1rsi6! za5c%O!;@Y7u~QTwHw^%@2?RSF-Z<-lsOdL{&eQ}RAay|MO4AmZvd=3gHhf$Q7Wr;Q z*I;DWwVX(~!1=iRI(?HwwmRb3g|v5qh%30!&f?IVz0QjU&r zCrAo{np9kDPW>C)>O~w01a@KG4<88+R5fC(4_?$!ER|rt2yu!8VyWsDRblHXRk+_w zcdRY#8lSn00m9YKPfZUdpK3}iH=mN z9qF@Q`}RmH+O`LZM?x2w%{dWT4m)4amOIGzU0ESJjxa$9&}#{Ut%?u{;3PgVDCH+u znoUky+KkD+X+S(`=mxy}J$>URRXLIfDpODfeJSv8395ONSNFJ3e1XU6VwFRw#m*~~ zU(L1%6N*98NZooM*=M$HR`s+Ae*&1i+CKCGG(h$MDmA5B1_&EK^K6!uLa&`$X z4k@|?s+CjUY<6~OdL?)2eY5>xoWgv1EKcg1&)4b~ctJ4EYrdEar)j#Q$C|oakGxaf)O3>@KKFK{eJpr& ze^`uqx8SWsOzOp3R+^;m{LQh%Wtr`u{=)pGY+4z}pp}8EsS1Z_qh35to#ZT}->ry>RCD;PZg*Bp zN$MW{NI~2J>fa-Vlhl!llHse;OEN?o(Pq9)<0lPI#=y?@AEcUo(ZEPz)wgEYytAc! zy6|I#Vuga##ZEm$Ks&4>tPPqUojh5M4?ow-zXpUvDSB_RuUayjl zedFZp`n2{`6g>BzqZx+QAN9Oy{IEUl8t!k@NbRw#a=Ibuf(c9*ZI$yC8^I=FG!B&2M3rMg8`zR;F>=!Dp!8-s@2qj$M^j3 zwLwSEKrNqdua}KYopz`KKs+0>(xQ59J9O7d03I!>Z27|JJD5^rCi+%K$$Kaj(VU8RQeeBK50x&i&! zzx*e&iHkfh$w!I4%djv&g{b*(9jW!l;rtC|jYdHuiX3jhAzu+RwwTY?kotb+NT8}M z?Lx_xDwKG*4?DUa71UCBf==7cpr#^gb6~@!^d@X|-*didMqEDGs-$%rUlDGdW+Ai(a&o5p3EdGY$no{r{&`)O%L^bOY7 z_zip#%uXV@*&LyXIO&HmBV5TO)aym$U*4I;Y z5Snl}vWE7CJGV#RtQaX8>#c_arx_HBOMmYX+S@4FXQeCw1qQYcZ5+ieG{1<#E*<>6 zGqmJu(kdNXxw!fI4@vmTmnC4_-ncq)tZ*<3)nq75vMHHgs7yH1_uMj)S21x?@eb(NE!_y1cG^y$KJMq+g z_$&OVkT8iNnBO!-HdywXLFkd04&fHRVeRo#bL(L1QZ)flcX=_cey20nwcmkQm!~3Z z-J+Crm?fY3+R9;rLx*Pqyqt^n^6+V2!;=jHYlEu?Sw2s>X5I&@d#&kHH#N(SQMBq= zM;ilfa8G-kEF3S%TOe6l6wppAoak!Jz#c^g6p#^KEIx0_tGC1imAnNY{H_vcL zSt+1O6js)$+2aZ*6VInf&|c{r%~jj7pv_*{!neC&{$Bq4wQ%F$u_ylaP*Z3Vm|4-( z%w0~JK_-?5PSrZ+zwKt{#q2d#&}R7G;YadFQVN*(Ao};QHOgF7h+~y`Eh<(lzpZY% zx~Ib|+%>NL62z8Mmza~Uk0lXx$m1J94Q}gG<;P02dJ&^z^n81q>(t{p`q~AW z;dKtpjf23~{%(P_rASx@-N*-{&4OGH``s-Q#>AEjcp7v!d;oe{5@x0EdOySJeizG$ zGr8(o+~Y|b85+mYcuoz>zhIQ4EYQrxaclw&+ukb+PTsg%7p%EBY=*@V&TDI>Y`g`I znlpr3Sw5y@=!`{l3+V@4-?Uo6>4sNL+4iUp5VsZ^7f_u-+D#6Ceehu$N|~;+uoYVA zS8&1~qau%OW;?R??7vMs-Mx01xY_K|G3^xClbE1&-;&{5YHIdeLsLr7xlIFv;aJHT zoK=fVj_(7R;3nznU zE6?6A%&S>RPvIMz>-n{B5S_#!OM z)s;DT6y#egwZEl@Qs&vS=jJw9EsbE5^^UTzyo{g*tdmlwef3Z3v4ah;E_XsoB{uur zx83i2fGXH%Y8OWvpB?CIf)1i^pk<>`*(D5uesygRZ`(ciNsBi4i5|wnxRjZag)PP z+#s%gL{Vhrl(hj3haATS=yss6ZG+=pCwpd>q$Mp|?i{xa-!U(50IaM6z#x69-eS1v z!K=)If>O!ppTaxYmcEGkE@8xU5NP9Z9syQ{@nXJ@!HUr%!>8ml=gi26RV?dR`o6Xlh(^^ry53dS?)mOp0&1Shf*Y1w3$$33O z*JR4G66>@Wxavzs+B~ASw5B&*A|DJBP41yFVF1h^!2KfgK4>YWO7{| z*Y<>uH+M#=SvA!MU=GpWp$W4+5b}Q_N68q+V;k^!XLJj>HNb%#v9;G?r#myg(rD=k zXbTJO1BsYWDqpZFZebq6m%RH5;!HPOVIt6j(JqkS9iweKC3#3jEblN!W)9h+_hoe7K(i z#}qHqQgD?9!v%S;8&%3i+{x>}!+eQ{vAC9SPWWW8{P{O1u*>-kYy*tZIxuh$EM#PP z9L)M8*G4YbCGH>emq`al9TWaR3HnsL&H5nLXyu5QTs8Tv$B2-*jXSqJ)e+>lGJrq@ z-fckw?bFfThstF$W(kVxR?k-uwIs8hDBG8#e@UpW+`70VqoZ`-2R=d@P89{XBe@t{sLpZd^+ekBCUZ9%C6^N5@4d6dV&$Jv)W7~cW zgTnzp7XKYk8q28@)Co3~5<8Z0-2Yb&rOthD4`cg?Q1=4eF@MEO%5X#-CB2=LV0DNF z-Tw@j?Wqq>C6pEallHWUze+5>J1dcK8i5UVYFyUZ46*|h`79L~ze8E>Y3u)FBc~uw zFcPcz;S9t^3urJrkxcg0TQ^DjNwO)2(KgLqN2}tZHnP9bu|BC6rCg^YxzB%A(xY)d ztsOG30qqb~M-(zS9M)t;pSxFU=jiBpbkrlgM^yzWrqbZO1H9S`N4Iz+hIVEAP#0K} zv6|m)Y~l`hf)2{stnKv_Mi&A|&qllbB#D$-d;6d@NNuo|#7R~GdezAWe4d^6+9JpyztYO1ExR1- z&wD5`dLYAYy*UePv(C2nZRZvGY@2%?3`Tgu#rji?)XY{PD?@oA?#&AQjiCDZ62=>) zM8u8~SC`y--9RVk>xAwS&jLF*Y89!4ZWwnS&RLJ;h&K!A;tScq(5t5_F$}u(d|LCz zL>fmvr$NA@lm-FI;P(MIe|5*wMu}v2gaSpd-hCc8Qtn|D_Gs1c;pZjwQO(tV6sZ;D zKLU$Jc)xKj7II_e^^z}kZZW!$!7W%^UWW$yqN}h zL$#R-+!JP+t=1$e*^uA%Xvx1>)d&8QA;=Fkfn!=B29IeL-Gm$}Sg9L$kAeqg!7b%F zC@pFmJ^A(gr32mzqTFsZPEVVtd2<)=@}7zN1qGVT({YsP$lHaX?IMV^DTFTB)<++unKW0NFC&m|O&?z&q4UncoqyK0#8hMxl@_CHt+5~UvfO;Q)+@Fx`pp>qIGYQ{01vhz`-`J$= zuTDQ4y|x)UDfEwpJkNl}d{w*ebuqDEa^+TByJ5q%ZfLo$A4)m}u*<=hnvSC1FhaMr z(zG#mFvtRSS5R%N&OF4N8m`IHJN`Y6tR_eGn=49h?dL8b8!r0ht4MJhGd#DHq?SOw zm(xo9Wf;rv<9qbIrdu(2BvfM-*z)JpwNS8&Iu`rr?^L5{nd?=qc zzq;D2W7M(LFc7PuqDVO0{8Pe7q%V&|;M|71gxAXH27JWW@it&MsT@(w*VEDP$T5#Y zKtX_hH8-sBj^w!`lB&h|5E^tsX_U*XSem>vrx62A8#SAAFB+UA^56z{#oRqhT0NS& zs2C8jba$G}2oHEL-FoM<7#oZ%OhU1JTXhEkMdpde%%L2P*yg}~(mr`gR3lhxTTO8z%STrp; zYH=xbtUm_P z=>baj%IXG&CufT5?eIri!g`Y2;onyXa`q$}~x87gAb^nz81%0)U zJeTKY%(wVX)R$Fm`DR@*J=+bjb-Hlc^i}}meR0^?uOarA!$laP$8Bd*wH4QOzP!91 zR!mM?_#7dZSXLDgXuB4WhP|6rDC>z57O0=~+?0Om+m>l-@e5f5Wx?-u{9As>`9s@S z=$3DTdm7f0;pxZtLS@9QOLu&VUUUZExmTcB@NL!S6a=E^a*_^w^F>kOyDVn8J~fY} z#M7R2uJw(5!Y4x`Hjkn7#1cJa`p*WoYpo>_lSpCrC;b?%yTgSbY#3f=Ox(QR)s&6} zh1iHg?jDH;Nqs0^Wi!4{?YB7!l0JXIYb{EB`&+}Kw3HufXMDSQ!5yw;sJkJc>!?2C z+KZ{05=EKb@eWJ7FkbwGp}1$1=vyxQW!xm*d*X?7*v`RKjF|I=N8iN|e~%l*hcouN$Q&v#DGR#V&3n-+$8+x${2;clvO^r?S&Z z?nj+fi21ZNCiJwd@BG)*oLA^Ev%2m)@e;kO{3jk8zthV89_`FOTk%=yOSoOnqrE5b zj;Z(o(UxPriF$og)<;|VH;I>9>=Je;Y)A4z^FIIXsUAV$!ot!K!Sh?hdq>k(zlg02 z=hVZL6G@%03FG1$RKz=Qx)7o$bTI_jtACQb>XWWkTRiAoEryc*B<$WE7F_u3bnhQqmgWl zbmj3twp;Q&543I#>*?Og=j~69uBu6UMZIpp3T8EPdNM@5CUi&H_uPB+WGv#1LZ!9j z&hKkJ-%cHBO^itqIQ4!AbYXTw&qo;D=5yawS|>dNz7|iFsZ^PFp%@#Vx|y5T#G`|R z#M)kd1Yy}5-2e**Ur7hR3NiHa5IgZ}v;H{mgylV4MD@`bme#5Buh68IB^iUx!O^p`QK(k?6Ox3O~3#y9S+uaIX>cWhcKAZCi!-Q(|^ii$)UVEm9#VZgUnITp#BABL@t+*T1;)Y$>!|elu@-| z8H&eSlZ7w>)jrH)%7N=5FxP+N4evav$QK;b))4=s+C^QP^p*uMPWO5H2P#s=6kGQi zB@DA@5O(`#V0P0z>m1w1qUS=T_J-~;tpD=4fhqN1_`;dbJIQjx=>&ANt*o25n=i@c z4tZ3tK*`(p*^?rC+)`ZdVsr2F)5$(Hf*fv$pGIVxxyv)qp}B`JJF)VrKK`?ce;>6l z(vSgh`IAQJoWj(bU(QuGdDZH#Pqt;Z*z7V9IB_jQW7_i|E=||35{XaKL0SvRWQquW zVS78zwzPQaP=t)mEoW7zfp!_5b3V)4Gv04Q&TsSG`77mAx&T~w3Piy-aPq;4``*2XX1btMUiWJ?J);e_YA_DB<>RT;0*qm8zVAn*CQ$0?fYxDMiezxn$CF~uUD`ibg(H5QLo1lGg5S5Zy zbg1-C-dXb2qhMZzirbCS8PBZ>;_~yu$jY3TMXN5b?}*BrQ>5RwnY*{~c|WPK`u(e> zwYa>0CH2QYp*eWmq&jC$)4YG?KkqB=jG%Y^(e^6<=`}WbnGf4-fB$W8>&%tA!gn-VOK6+_`}XM1vMjgDNog&$)_*h?}rwAY({)Bh-ywi|I?&W z&Q#$yX7qtc>Fv2!tRa)U+oYqit@|_6V;OKBx?4R;`j`{ZTl|0jNh#L<7W_(Ono*xl z^TfcZgysw8lTMFRIXiraKd>b-jz4zGpz5DF?4BEP4aW1t7?=h$oJXX+P8L{NAy?k< zzDy;BoJUwN)IRyzH6$uX*SX3S-lc2}o6*@Xy0N24naMcrp%{;kafjs+Cb>OVjGWfLVQ9bCq3Xh^>);}jPwW~<27C>7 zak1jHyI}*x@QPxuZsZ${^x1-(eR!1!O>_DkFh0rbQWvepYIFWTWS5EC#wS?GFfW|V z@90fyv1yWUxS0y4Xm*UR@yrt01Ly-wqcG%o$GE7`g4d46R2X>C16?5n@QXzt`hr#i z8zHYJuTAc`uaccX#!=0iJ)L->K8|}kwLXW7TqOp58Iyk}qG$;76N7!)kYe00s&K(? z8;|sw=C*@+sRZg}-PvJvnUv0jUO+p>J2ix2k_mXqV@q5s& zJx2^~j=6Om#ObzF^jXDF*!hM+*`g-JkVXpbvM%G7xWK&PxWxPvOB?OzJY-0PCEL1!d*)d4-8Qq)TDYN$jldiM3Y{l@bCogEuknw_F9ol-Ee zWQ-f4oQ4ZG+6M4Z9$8`tqCrmQOwOF471-AFKLRd9w0Z~xi7&Uir@C5D?EJyVZqsd?fZ`Z?aXOpFUQ>LdM;^I$uz4u@CH`D+=Fg{SNj{H zyy_Xtrr6{prOl_j&F_o-q(gp;QJ}J+36b1YP{OZ1;mUtp_hIH{+rF>DY)`{&e4d_i zTH`Qq_eZ1sd#al`YE7(4Z&9l>@>6s<@kaxQ$`@kj`ikryByYeJecV?ug9DR$VLiUm zCH%)H1{k{X+=6|qZbB*CcZZX=_byhyuI^eq_GG$K_}2jp1Y?5cAvzP=jwi1gVv64j zm*wJv9z-aq=9g~GFgvT1yYydP_6&@j6=8EeuVdMNg9#?MmMm;*)`^;s-?VK!P(R%+ z@`01%vdEeTxg5%3wT5_a#cxNa_p{29uJf$7Z~iF}?DTY_uV?4?_pbE7ZnVIE6Eqt5ht z2_cB~e*{bh?_@cb8@V`o^r20;@~N)lpx1RU{9kc8oZI(;lr#0eJ2=fusm_R$v6d~0 zR@c3{q7IDoH2Wa1LMg8Puu*>E5USCCB06S3Z|%fDd_5O1O*?soq62`I?vqbEn{LE4 z+0Hgt!=%ds1%hF9IHkPt4q{;-;oi$dJ&ds5 zRP&``8ox0%N=I$f*hZO&igTD^W`xqyFtWH^O{RT$CQrZ{LBPZZbpHk2G3e?~IM-yg z=PE|0^Bfr8%US1?E=;GzuN%Oe|CLPLc)q6;f2>MLdPpWVH!b{I(eVkg<2Bm9K)LCs z3VXx+jxlo{p0(b&Cs@6Z+H5{tXZe%gB#Q5|piM>JO+%>5m8TuAlcT&Yzq+5u+SzTZ zFYF=zENY7N7!ANRq)7iNCWJ@RkURV2Gq^53pzH4WFbeaOGFPtet?NBq~&{AYa4a0 zd@5QQY@@GkkG(9v8Kk@mb4fp``$NFw{O!zyMd2_1Ok|zEVs|%s?d#-sdy|}2iYq>E zSoT=cL6m{BedYg^OO8P-yRMW_{WosN{la34!<_R_hx<4)&bz}mZ}q;CE$|}ADV>T` zs_@|_SAVRjnAm)6#g%Z`miGf6wm));D&BZ3X>v16dA_zuDi@1|u#9?@@Su{f)CrFD zm(gR0&I$`QIgwW_W;u(82PP$@*qmh&e6vJ5N_d@2l9dm8mnfS5TJr1%ve4Ze)YKbi z%)v1SdGy-IekxsRyBP&*%MM!0F|KVEVL@s6Q4Cj07%oT-|Ht&F{$s|7QNU9(8)TiX zFAsBXv6`q={$MGfTx7Nf7|rn!;?Hgnv&K{^^EBlkU35oFx}Tm2dvo(;bXMoDjhl~E z*-B;WT9gT@Ps;9^oKSPU`l7r)y@!A>1L(NLkg^>f&oFKPQxj<|~A^lT=5W@+A2_;mn(7E`0iU)b&{atC}xk zD+23gZ+C>A$Qz~30!mQ)KRJ>4HSFNcPf^^*uXC}d>XMVg)m{s~@?19u%%y8N#V5p( zmvmEOQL6KYNsO)4&DxLY_oW)HIiKVklrZ5RHmhwh4?lbR<{69cE6!GKjA;_7Yb~1b z_Lc7Eg1f#H!91b>RI+2C&(7`Ye}5MLxobzpWQM4CWANUDYTD6~#mHfUdVEQrZF2t@ zG4QtFZ+4Q>k}#x8R)qAx3UWtn;M#}S9ZQ(Wzhf&)# zH7-80$hF##METSj-9DUIHnQ;Vi0eYDW#ifFg?w7ztj`zm72DjCwHBLQ$0lpG8d!f5 zOWv;Bp2Sz`!3DDW=Hv)~3{O1hMB}$!cPY~6{H>836W5@soaSggV=LHod2TJBA|#IXPi@@hco}esOoW2I^!G{8Vlx~@7TuDV2OMmwgYl4s2(8A|Hg|V(##p3gdyk4>(3h9#n zHF@k+M{d!v95sRy-mE|6MSZ{j8C3M&BFQB&-?KNWR`}>`i{1%IY<_T+Xz_VWrMus4 z&ds;Eo^lDTW5~cdmb1t(C8+r*VAtyNGOCKFrsQ1J*~pEUr}h?4`TZ$p9OFzc zw}AR!|1BOK2t?{Y&(nIY0ac@{31{|@smvR(>H$A)39Eg^Heuc#QF(rkw4hn>wm zzO4nhDWof;`_|p-W@fywRYUn>tgnB~hWd4ytkHgP9YVW~XiP9G>ZgXKHT~wr*okGq zJ)AAxdK&WXM8yh0*m!Z5%wBVY+aR`o3P0ouWxWvd_WTP}pN-g~&C`2o1`KRhvN*JTp9?_P8&^8N z`)BNl_H&N~wU6GkcdXF=K~w8*y_&%GruN#aF$4m+)ngCOqN?XD5Ri>ui0D(Xvb{A-+~>95eNIQ^fXpA+yypWuzt zWug1b<1*)?@H9?bJN1S3)7xFV52{lL`sYf^`k2K(ayvyHt`FY1Fg~i6m}$a|FNY#$ z2L>j6JRRrdYqSM;M}Fjde?Q@~Z7(++YnJGwm#`=Q+4Ro6PqW9~`wZv7`~JGW-@y*h zDe72vKKksXa&rNz*8Mbfs~Vj3=`!m>W461)_^nOJr4$w3((iq*!#U50I6*PkBsss{ zl|$b#uM(;tUXy6QUU-nQwh_s2Awg|%^nMxo=Qce3dCek<`G)GL3^o0moPQa{=wJ=o zo3y7g{hf85SnwXMnq6?-(xgT79o>JKamy~hOIsz=fBisrw04*H^1zT2W67Oj_ucQU z@St7)*)~6qxe1#QMf$~isT%={+>yGs+$t>YYv+D=ul3PQ>r<0=Cq-O;Ooln6Su6g^ z?#TV1E6*(*jVmjsy3MmJCXGnPd5a`Fkqe4d*N$leN8bM{y+lxYMaqxV|4BV!!MmOq zc%2`%5Lq;!edoeNuzCA-+|!Z`hw6uzbs(3WZ_r1kN&H&TO-u#7pOJ@)&C@a8qE&OM z&`eX#UvR$rYl49#22G3t{wxj%$;Z2ym`9jitD9rn&ZGV6r<0pi(?nS@$y1(-`X?{+ zNRRVeaC^SlBIGjYq886=48^u--m`@O_zq;_-y~5_TKZY z$?XXnjSY3HAR?kvMT#_05s(^{CLn|&ARtAmbm`IpiVBE;bm>Tu&^t&C2+|=)4ZRZx zgb1OBB=3rz_gvTc56*|*{^Exno@cF@duHyLSglo3^0j z`a#~I;*03nnF2RY8Ku@sAx~>V!5K{iKKn7P?I$+b$C2X^p2(T(dzWm=qoG$)$Js+> z;?_h!aH<4!0pC<-9XA_4iPGL@-P@5&M7Lf)+`lq?0U;luG0c!6{}C@?t~IXn)$Wyp zPl7m8aP~Vjsev4`FZ(8HIwgzDjzU>CXDE`5l(71`yE8HIb zl(^UQaGZlqvWFle?XHT{k_$k1)2=5nRfV0~hRHt&ISScxAn(T?r9IqjVb>Q?&Nk6F zyJpaVURO3Bpd2y;>i$_qnH}|TC9eD_@Jsn5kY;>#xzry3{N_ucwAEc2yTAB{h%~9DH<)rYbE5Ax+4y&YIR;O4hk7Of)@B~lg3?YDug&p^ z%L;B+cQ`~jXL^^--{B(P;LB=3oGu20v@zCG*o;V9qcqiV_tKpC*4^AA;b-q?d)V=< zMHULzQk1d&#jj05tP<7QR(L-vr;tJ(aXvHRX+x<;xvKVYhR{j|(@?6%lL5ut zbHSg>`j}jXQW5M_61xom%#1^c#ZdJ<)9(>WM*8E=R{bg2UB3e@6b>hQ&6z4DQ5@&G z>t3JHH|ww{%TW1GAx=kmBkkN43kY)lsp3I}t1pn+yUCy^o?j5b+1lar2D(?Sl>0@+ zzID$Bw`0MLV?PtpC0W@14D(;?b^68qLnR@x$7?U*S2snXeOzO&Lgl=86FWbhPkcW6 zW68zEBR5fC>_~Q0(`1vEgPsZHe9*_&l^=KNIz0`?(7&4XKY}O(%8eZE@`mj5Q_8h| zzu#-?d(d+o(c;S1yOpnY-8gu!e%+39L**VeiQ~>-d$ef6p7+>qgQ^1yZWqV7(iguF zzDK1cTrISb+=srQ4o^cCR4?JArW#oRB6Z3#9RF)OQ`igatHfC~vT^-M+|^%I8@=`x z{j7~k5m&!$+^!C!<~2)UXVf`YLiV}Mz6g3nBO_JLWb;Hwln210s$bQ?8{<6*g7Nrd z&c-TX)1JAmXvN^^t3SY$3COI>Ker~&DQ~L-VaD_?S4rnVH+kDf|GNgldM}mGG9;q{`c&vM3#a!s&Jv(19wtJbOqR{mV!4X)dZ&=W;-4y*SS2QY@laE9vYwu&ZXX{FUV*gh1qr}YrQ@eN1U|i99fla zGx=|w<5qfvwtUj;q@10zYMoG(wzz0 zo@HAjtp`NwNm|_y`Xe8Nk3W{)YsBA#d!-IEpnS($9(?c#Y0@oveS@w%{> z%rUF>gW|375nTGm5i$Dljf=olTS&xBLTe(TMqhPv`n?Iqamiv@6j;k$Bh42H^>Rtk z`}*fPwuDD-i2{-T-wJD(V);QJXkp0I`@DBFxqrv8#;f@5ZAl5J{_98C{YJ0znic_Q zd=6$^_0-n8ZiO@*K&t{fNuF=zohQ4+;!k@aM6s?$Z7~thD{(KR%D}O=!G7l0X8X@e zOPufCFK!?3c=3LHaN}oN#qJYd_o8onSJ*qlr`1{FKEt? zrz_(W5&T?Bt931*B0^~HMR%7o*R`F_ABFk?|AEZnY@wxcRIu#n{#jDv*Wc_R!ktbU z@;v%KwQHX0oIQ-E$9BfXWRp2Fb}sShU2c^WlO(wx5R>HMPMwE5y3*~`m!!~bKD z`HOFtD$r;2BlV+L6QLa4b(C*EFS*8t20t1UTw@I6y0>Bi0tDkjvKTjW0eTA;AyI+T zZIqIWI`>(_s=<*LEM-XSpL=P)v%!;XQT8LvQI96Uk9E9{TcF12t|b!n>Xn0Q{`KW+ zz@51CJjsd>9E;BCaF1mlJQcz(@m=U%juc9qp4V{~&6`^#s>+R}?a*1j_n6Ca%KvAb zYi>uqs%4ZgO<~ukN7aO-+bhM1Uzva(;vkS>%XX_Z=SBt1+S6Fa?d~35k;tGIDV(>X zibwXJc+@i)PjNByC@H5Q(gWFW7eI#)2KWl}dcJ|ijT$Yo{pSMYq+PqqpH`D7#?U|4 zWmhlzxpZjdq*W=+J1sKrhO-&R-{4lQ{1m3a_kL6UT8IYGsHpkOZyEmI+Ar5*K=1KP znf#xt-S_b2!deEtSFUAp+~skPi!K>sY@G4@Q+Iv1j_dTmL(VT;31oVX@}W7YYWd+7 zMlW{coySwbWWaUP|D~UK?-a|ulz}4o%XHkBm)C!ezCevmTf3n9$48?66Ju+w8!g4H zx)gBjkc9RczH9YuzNeHF&>8C3e!jG?9zk&&r+O16~hm_+^+Dm49A;r z1D|It8WIrf0_|-Q`KTs4iw%QKq@32sTF07 zc_J3l_$6DmxI}q=L8>M~rn!js^FMBUtzIgH#xr5?p>&IfEj$o12_a_HMr_Ylu-g&PkUQA`wOcSVLvg@7l&W*XyZ7iTJbxf zmh^hI8Z^xYtHg4Bs|K$h-bq+7?{bK>U+okwieQ{pv3Kj2k$LIhq@E*J$YiYu5^$XF>B{E zy7V4+h9!xp$sgnIt7~q4uFc4+xgEC$}S$<{i%aFSCr73x^r z%puNoX+<#>y~3jS%0V%n!MAvKkuv?LQirt03Wxq{^+QZgQ|oF0d0D^h$=XU43UT|- zCbY6tZbS-(avg>g%M^764R)&Vzfi01^f(%@DfeN>1>Hj7sMz4=r?nDry0Le5MIU2t z+RWlqVpnpzxI5wbtJT9~dAo>N9GfH!#^(}`T>|=wWZP(~Nynew>ndxzAW4Ft18ff? zsl}#k(5g?J2F)#7JtnOkh3q#Up6S#^;NVrK4-%oj=fJkv)pSL3Z6CW_XBz~`L7JT@ zRD8bI!9K}`@M%Rr9(!pzHOB7F z9hJcA1K$1xo#_+MT@=OzSKW`)dCT{BxPj|w276YFU&M;})i%O{)zcz$*~La-cMG8q z>^yNuf8Hih+Aa5m#n?cPAx0GeQ};Otd~TYduNtp9TExf3fLlk(Eyi z4bX){Cjza!Pa=P~gH8>psUfuG44;=CCM5TC8*`R!=hhwiK(5Ct5A(>u$QoZp-Yxg~ z{0Vxn!1RlMJ;T*|z#8+tTNWHVRqK_+926+~k)(;-lmuKG2*+K|cm^VEQeFaWHo&4p? zbxonl!sR?xBIu?Ht8eifWU*<`YS}Jm8z93$ezGwDXk?4Ue0djrgG8_H|>j!S-siN2tnG z@}$f4=_1{;hxGcWJoS9fk*L~ZbDpq_&Z6&c_?+G_NS$r8&PcOX@U(0TJ)I0lR4E&^9}FbL9dXdoy;uG3J$BEh^$rSR`hE> zQ@BPi^Z6pT&buOByM$DAV-Ca#WFTio$Bf1|i8v3lI?MT*d|IOie)h$jKT%FXP;un| z((8{yF4Jkhh<6?ROE0&lbm(5O>rIqD-RZ;m`%%=o0U0ifZ@$ABT%`fKokLG3WQARX zgRPx+_-7PXfKg0YC>RQ^2UjJxPWYiM&i29W>!K^~+ot>P$^X&s;!*XoaN+wIg6VYn zVDDn2-^HnuNt@HCYQR-%lS;bUSX8WG<7Ih!+1#*fuOp1xJ}CCJWASsxi&HVgFTKQ@ z^RPh0v`^gf6&s%Q4R?xhoCCrTQeA*7lg4H@gl?m1OJmB;4jEa1kj}+&cklIqLxWM! zC#e`52#G!l;e*18!SeTGxFf~$TM?3Qnk`S4*CSk7uhKu+XRHT5**&knhrN+e>(BD- z>8=wJ38iMgrYnCd`%NXlYT`aURV^ElQ>w5upI%qdwfM5$odw(44@Prp%HvBKdfUAd zCr8u)=C3l&HRo*Yp1VC|tgPtAAyv_RH(Ss>@?`Bjdiakt*-JOLR6X5v#<1m2l_MqN z#Ti5ov}iGTCrMJpfv@R#WNX2xK0%-A=KkI#jcqzp9S!&GaTj$Vi*l}M$!z} z&HpV2+rG2!OQ)<{P2xqDaV?#;P#~@wHQjLRbl>A^3ELcsfV47fV!X_5D;Lt;GXLy? z)~26ok$pnTOrH0!b^R_P82b%z9GBs&K2P#v37!}t|CJ9r)QjX?kGM|hnQ;n$gxV!TSxhJ$b-wB#OZdMPLwy$PlbErv+LZKEX|Xnwa~B_Ul!j55Q{@yiTBr*@d(%RQ zKB*VKh`mQ`LqHy~|F!6Vd*F0kLOpxtvxwINE6sd#kuM%em%`gdZg=L=@z422t+l4+ z{0fWi%)3|Rb9NyB`;zCHq0vP2yp?HISIV;neua|yk#A~IiSY(CYH=BCF_qn<(mzx6 z4V~A`9~oNEmffYk%>Sc7HX~ZK@XDQ@k-mB5m0=H_^K!>##Pk`m-EFRFP zGIB>Al5Yon@AT&NvHA2tX)Ap&)E#{)v^sRIcfaCd9i?&tBGnBS-=rmx7YN_Ij}Cd+B6t#dGR$wsB`BiwRdh$Ewv`5bg#T~;I0=rDyLly zQX3Ot7(0_*kgR7SII?*hEw(m+FXLWv;>^$Tee{zDtP1ki2|-T$WLS{#axEz1bdiNV zY3V}#wk{!|N66My(P&@V_9ql0KSN@Gz&alG{?pa#0A8e4rw#vvvxS2E3gR>4ODMU6fuQ&Qo=p8v?-MEKpHR|$ z^2pQpJh`=EtnB@(UD@*QJ*(iOqAhZv{lpt*$JuM1ny@l!@0#Nn+4xDqaib>uQsNN} zTp!+%l)^t7#KIC8>$LKko#@~%$htsc0d*V<&2K93O*{TAq~oUPcWz|3VCfFz4?jnJ|oxP@gZY8=bG9ec)lgpm+)Ekd&oXYV> zV!YCPwB7NA)%lVrH&U8UUr28z=8fd(sNxCA+kMgxFvwSGh$oI`CLtcv?hQst6qouf zKPz{Ie;`4cYQg8o0oOLO_k!<#mBUyuR+k~oGmGQbk(}r!KI>(xBNnu|)1uv*qv77T z?M`d|Xl~)Tfzt0opg~ z&YDQ^0(RY7#n(NiS6cU3%!t+6y?xv|U@+&}Tkt7@n$z+*C-#k~Aobpz>}q~52^@V}aLnr^|M;FB(8T?k*`FyUi*t6Y%O zJmut>ys)|vXE0t$Ik8EM3<@dyH3?OXq(J*TJK5+-q@8Ew<@wl;kaXMQ*CAq+iglh( zeCK3jJn;JN!h|2NZLbK&eE3G=X+?GRHCQ!I8f#seayi9^Jn)8`c@tOaL^roXm||NK zx@s8ya+VDUOrdpl=cLf)k)mWiJ5lk?KN@T!}0t@x`A28 zbY68)DW^g(+!GN+-LpXBI@!^_?^CVAaD(7=*h5lTA#VDml;MsHk)72h0+;Bhw){;J zmb#QIZGvJEmObbpPWzscYu%Z-SGO@m$U;9?a?Ww^_$bDDd$qBPPZ&%i> zEm9DtRVKqi_2*s&y{%fi>nPZE+{#W>JeK1D{NMu+bPO|Yy4ReriHOYe z(a(}tNqQ;1cX}J2k-Fy({EeFPOsfr-y$TA~a@>}B&7>NVqZjtD zMWr^qn{KqR>K2Dv;x-f4EG=_h?pqFD zC@AyI8HqZPzVDxrN+Zz4Cf$C+3GGkLwRbaJ41+S&=ibHBv5|~|^J8tXr-ESJ?;+*@ubq?7$ zTu(i49rspoQbndsU#k;OTKH0&)r@^}y36z2ly7cvb-46PH?t#EU@C9r_>ZMOr$ROi zuEN{CY^<|a`q=C1C<{;^uCDM9wI>61wx(9i6A?P)uU=k^c7GvF8GEp?qw3ob#iP#N zb#HYI9fhH+?iDA>VG$CkeV$Kc?!!^H$0*IeLpEE?2t(P`)f@HF@VIk1i76;u@QF~t zDXJVFXHF5z(b9ET*^i4>n!=&pJ!HD@iuS$~N43TWy(HI0&QyvP>z=Ucn?k%`@h|t) zfe&rdM)KDO2D>3CU#SNu`-iq-dfTvZ|p!}7>oq8 z6`132f_&BU{oP=DZ3PjSw_zE~&ITO~nm>1#a9TPQ**_zMMOjJk`p?Oj*NS?LM((2Z zm<}XOjFqF;dQf?eri`R}*+M2p>uL`->s(*iZ}k_CZCvT@o7AeaZOlE95+$~KvFij0 z&V2L~mQNjf;XoB;U=HYwVtGCa(wzcx=_4r%0G3mJ)a`iyrG7Y<@hg6c^__G%R#vz3LgsW$BSoKhF+>#~|(mk@uIo>L1yYOB~h zk#0Oxq|crfEcr6b=UUa#K}v1^qc9I)ivO3O^W@*GL9NAF-tyHt$nlgrLF%jx*=dIo zM+pM=`I}7^rINoVrnoZ6UWSPA0BHUCBwT+$o1mmO)y|A&hpt)EzF>xD$?V3hjx2Iy zHDCNOW)Fs&u;07VZY(kSi!RUMU?r72jcVOU2^!r>^l|mAL0a|e5G!HXWMNqjpK3#5 z+{fj@N{7`60^3C-hu0aCM%9%f0&A5FJZRc;Hjp%gI=0ZDH!}b6i@!Sc{lIWF%IZ}{ zcJds}E-c;<#D5Y#@C+CJqn9>W6PJ2zX4Z+>hc0ghhA8F6#Drf-5aZD&)B%zUZa%r=#1O?iSU4Dh?=d|jr7mm)Gk*sThF`n#hKXmIG=8wfZsHJ{xE8SQw?B-ohxS5|YN-q> z?_H}nTq-Z~$>I%jnlIKw>7tTg8vBtxm7}bZbc`6x7#rn zxwd0Xc)>d}4C-F_@X9)*6^~yZhW*}qWAUid?I`k4u9dZ>K;E$?Me01mXRa5#_@^2U z`5HEYwc%T65`N{XO$5olRx|Y<+reDN;7bGbc6wX=|tFJi$ou35CLm--V0d} zmi^Od)Z4ewqVCg_xx2l$9GhXxx+5U4E?V5@z7ELUNaJZUiaDSHhUT71L-^Z-T70;- z+l#eKea|OVUtkI{4~lkrNkv5L4@a_utEp>>rLkw)Q4KrG=z5>~ z!_28VHRktF@idGih20(Rm_I#1X$E0e7Lj(C^J`;HVBPg5F`w#D9s<|;iNPd*z%&z? zM?^d~zP5gP^Ax4>Z}cTLyV;e|*KxpHgIFaVK7fX^L@c1B{&Y(7x!D^_NZw7=d+II6 z{9dtpvlP+(R8)%MKFc8DN;`GKgV5UQbvx%5xi_kKXZ-6sY($u2^BkEs00M@^21h184fNQbHuz_?Ky9^=0~Cx9Mr z7@~p?qbvRpQ(H1&N+hFXO5_3UXAI#^1%o1Q8|6k-gnFQq$X08sA?<_hvim>sSB*6B zN$xd!+z5v?%z;TNz}eED_4f!c2{u}Sau53sF}RTH|A_#X?66s`bxg#jH7xbV>@E+l zrp7HJ<*X^A4{l-=oXHrb=eRX6MA2)gneYNHqN&t|ZxwxL3e=9lw)2Mg@lVIvBuQb{N6BPH`$*lr!a`z&@g5cq+?86qfM25v0CUnnPNs1 zFE704F;^<(!WkK4iwhR+tyT;C!b8W=^+=7KJr33BndmFTPsc*k@GT`rWFK90K60~H z?_w;D)H~Dt@@Z<;0~9Vg$iUB1lm}PnpgN-*$lY%UoMu=$yLQcLcJob-0}r^T{#ECLu30! z&mAQuro$e1!_xnY1>r0#kyx6ze*!QUE@TN zv@MKj80S(iogqpBrX=|~sU^-plA3VX6?_x}284MoY2z+A*$>ah(m+hne^pu|pcE@e zNuul~TwJkrJp{bFsQ!oF@+PT)M0pyh98Z_H!0Z(~Q1{(1TIttrh_kU%_`-9A&1=hZ zPF@|OCzu}GpTQCKPeAeO;S5SnV@Zd_!Bf12U~<+E$KeZ*rV^?!ReUA-2qOhFN+3OoPM`DlUWp-oZB5m78n`tG&>2nQFc zhXx3JfNPC&i2&Y0M}3K{u@~Rw!E)?D3WBb!^x%i|qis*!V#|3#t+T!HHTP{!TBtC# z87$85wCr8UyW}-evo$eZ4$PK)jyZaJ9+CtzURsgxA+B15dEoa!j2R=t03{^5Qi&(J zSMEJ~d6PT)W%uF-vc+*bwbIGrSD=F?>~QQFDudGqd%m)nP9x&dNt)L zr2)MtRpkg>6gw7h;URD&Hd|N~ud)dt9r3bbtSs)KigkMdC-7IU*+eX9tdtHGcDfFj zzFT1dd6BfZ25o3~#6lW7Cbhwq&53ifW-)fmnAtr^fi+d{mXC#RI7b4m6X1$<1s8mX zn{z({vFD;XU@g_vEx1ZuwEx1DwcDCZ_s1KgO?tde%SsHeO95;htO9JcO}f6dBWVul zJ0C&Yce5Vs{hX^NBIn(=UckJ5jQ3{1+1)kkm&HFGFmE<4)_kE2%gbrrBGk?M9gf{G zF^?PVfexO_41d1J$MLDl)5XufiWhJr0@krOq_*$1DPIr2mCS4=6Gq|~#M)`7u{ZmX}#!Uc7 zj?XwgXEO3a`PdJ~#=5mugmrfQOu|qEMpl%N$YI!N*Yp0)#Gq+Wa^Rj1cI(XsuGG&P z@J~1H;=A2;?2?4UJ+;6&_j8^3B_uUps~qfWW__e^TDk+AHigm zhatDskEdsMMO!1iVMq!_gm9eXM&VUbXYX#oPCo#Ns@!K|wjI55FNA~c??u#_BopS) zjc>AKJi-`r<5t5oBT|<(Srb{=-9759jCo{W>J2`8OpWb)4dc^fof}V!#y-+7Yb-fy ziiep;NWQ*=TMFJIZj^di4OLbAnC0zekbrW_=IHazumjAH(Di`&8=I}dpShoa+_48b zRoNa!)agvM((E)LS^)UvFjp_d1wCKVz`#rWyqM1;6JfOX!@C$U$!RnK`tI|R$QSCa z&S6J2eOJh=`-!mN!^%*YEP-AedVo*}x(^V3Q0mpkNh@zL+aHyV4tU@=+6cH-9cRhwKh5Nlh&OpVh z@Gp9rMLg%Fea?=ej`c+yPk$3;*uETDQ;lG`JvSuX{yQ{-d{;I?KZrL>{WHbN-dKE) zNr1qiBMv^=rp}L$$LLKxJhnA+hF2j>biARyc1V=R4J1%Y60cLCKxYi{)tTX{p~MMJ zltc`^%*!L07=li{;;#6jxH{OCNlM-}jj7eH-Y!zEd6{`vG+sf-ivvFsHgG+rGrwp` zLI`Kw;fE<%U#$7)Lj}%(V-_oU^3~Z2)LXi^#~EW=Le_Mqo*Z*z+F4bs ziw|;Kx;W2tn*GiIJJEw#@EE1c_bKyxw$QU$TX#Lvaz|SaI3jSTGx|Q;^)Y(*-p4w{ z_fymcOpVk16Y%N#2U}XZ{*CX~tfVkC(?Wn8W)u5f?4@%XOQC9FFbAFZgLsVOD^MKp z958)8bt3MK-|}u}t`(*%FA03yK&6n$`#*j4eUG6`_VGsU!XG;Pg%`gW(*^JC*ldzL ztfXFL$;1VIG6&}6A5SCTCUZ-O>=Y(_b|+G#QoP7n)1Kh6C2?62TI4s@W0%Z=YLN2a zD0@%#_oQ#1^gi2Qg^^FG#Y5-)ItjQVedYpYk75M$i7L{Nue&=UjeTctQ`R3|6iAzL z6{!o&Zg1b<(;PKvTf0*&LNztrK#==tElDY#eAwbXh+qR@H|QQLNgy2%^QHC(P*_;L zp#xJ3{Pr?5;pVF}U+ex9D}Fk;I(wdrUHQRA7l%~>&xSKearB#!r}b5WqcE~vbKQ}f z#)}eRTWqpkoESUdIc88qDIjSx_iPe|4`^9auFj0ZRvAluh>Z2@<=vjfe(BWMD+-EW zw}y#>rrcSvzI@gm{`8gkzc(6WE_}S@=TjAE#q$O}|MX%EudM71>=jVCS^M6c=SVDG zud+UiF)_cirhjB{cGNDbS;e*GwKKDz9VPA!2gb5hM0VUZ`*`xadBmxpMSjq)7px9w(dz*x@0z^e~PaIb0|%AEaa5ZJcdl*csZo_GOpDwtbbi{T0{6^l~GP%F~!)F~Z(6FYv#~z%e+* z(R2@R#7}lsU4dWRNu>gCn3{M!KRs+aB}0Of2mIa)H`pKdqD=vHOl{z{49$q2K%r%) zhBmcEU`4G4fY3{SADNh2sENFnmAZ8a3v2LNLy$QmP_E~uph6Cf83#xA@>q7hA?R9( zn}xrUL0Kggv)dW%hM4Ph-j9^%n;243H87z;`aMCF6p&S#Z-L1EyQEzXVmm zw>WmSqF^ zGOX=j#;9wJ>KWDT!`E`)WD={sE%Lkq?YXo&Kqk8GV4ztdkcmCb!R_>$neaNSqFAn! ze1j}DJIte)VZw7nYrLj^v3(3UZJ$!FnWsw+Z=o1t@c(r^>UXfhT4_q$>XIwkVwDpP zBg8dCX;hSNR$j&4rt5hyfwORXe~nhGsY`=>(VY= z*m&HLjBaH$D1CDjg7G`g9mung^f#Ly8ktO1IMQfTTeMT8r3Hi7>2NvQxc#Rdt>kN! zF@1(luf~J{WI9vsYUlI7tsCi+a%k7QMZUq}IX;XTo%T-daU)m6DTU;VS+tb+E6v1F8w?4b)JdqPv>d-Qz3ksTw#_>c{XrsmF`t z?d-dphM*mJ7G;{zQak$TICLNf$WX zWeRo013Y{-x`TS@<+2+8S^YB6my@mgU#M=A)Ds zRAY!c+C10veZXNDI{a!+J$#Dk+0?nvD2i@P%TpQeQhxOlWV`61JY z?P?tfepq<_#|g~)`#)~-*14sQ{*b=F6FCita%y~-ztNDA9Utij^oQYkoK4ANz0}u- zx*`)Syf0CTu`MmgX#hEWD&*p*kdxdjBq?nEB5#!Xpz?fP>bv}LSxBfQl40A#)lcZ7 zEBR7KkR))eTYwtQ zz|gjMtfAUJ2krN2xS=F(`JC6uhn7Ukp+Jz^JL!NGJ_EN)HhtgS#IQx_#dzDVPfKFe zb@p2F^j|UF>Dp-pAvszH+|j55LUPF}SX?(i=$myo57sjeEN+fAy_XMmc(hFQnmZsf zfUM?STuB0H2i!{>>&;l*^5<-TSX#Ml?rz23pMXhg5D`#4It6|*S4x~l2!WSr2w7iy zBUa=eA!!A=JE_zv(hl{9L_o)^0q?hN+fdEh`0y=J7*cE2TWcz`*qIuUXzKz0>l{)+ z44j34YzX)R{P$8~E6~)d<+^Sq$g}V?#VKo0qE{RSv+%vCEkgYvEpHWQ_DuLxW3si!whp`D2=hMIem>vUr2m*T$CO8fi@;e4CB?vFpxPkqmjUNoJANdWd zL7Egn^)f&Kc=Z{Z3LpPS8qecX8Cdx9GKmxE2GXM@-Rf84;!Wy2`d#zEjApU_z^etd zK%7*}oSH~{|B&T(^0EFRD@+%#asl(bS<&^NSg&7|wub6QJ*D{Qsbfu?)PZ>L9e^cczU=>8u!i|b&(8dx&-8I8Ul3b5AX_w1r9emYJ-iv za`Lqzs2xvP3U~skAd_0a(^I*Rjs;``1U20Q(sBYnS6qkR$I=3L99^j;@`})JAI6HfdA2i4M z+qOdyr`1HItf}@8Lj|#1Nx9C9Un?KfYcUm0VcASWh2`l?F;L9!&V@r>`5ib8X#xkS zpaZ7!W`i`M=}U4s>5H@`ZWrXiJLtw6*jZaCvo^Eky&8+*I@40Xc;--})McmuVi9m- ztqSXuboT8ulY8K&iI>jD`h|5H&Rw}#oVPa%>@DZ-W5KZ1CVrr2jM#WXUE9X#Oh+Sk zXwoE#$@wz`VQ{cfqc^H0>8w{?UWc`Uq{{x+B7u_Z~As$Pq37<22J6j?20L0D5S)L`rD6I%cr`c<&gKV`(Vjj z9zye9iwnJ(ETF;OR7>-qTACHrbVWxTh6!bh5dip@*4+!5LC+?&Ivbre3ae%>mKfyu zRi+(6m1RSRWBg}00Gsm70joC^0WFe;3Sc=tz(%dj_l6KH`5QSJm{g!J^5fGu&~cRl zEiU8rY3mE#(n2YrWAUop4kKbO*n`VA6)BD$;P-afQS&B+uu`a;K9xg z+gNLN3e1qqhjxLoxAKyXL8xMI1eOsBeCx~7IvK1}v_BXUAGn!H_ANaPJQEbuU{KKv z5rt;3q~`cc_G@@d4i!%OCghTn0q$9#J4v5}0iM!Vyvs0ZXl95rs36Dzm}$(MR{#+6EA?|9Y7lU=x#_i8GqHBAi0qzj)XU}r=(GuIos;2* zRF$Dn$=o05AO-?EmAuO zz@tfTJMyV^IPrM>7@eW6JQpa%bd&nGZ^7THnhk=IH>eSYX0Ttb%pNZqiQQ}SaqCkN zzBjy{ECWE`&0LQ>f8;r&3|c)?OxHF}L0H1U<)~tyOG7p(^?H&ytlNxNe#19!~&M$8b1z!I*sxNtyFd%5AKN z&fc>~={Z8?Jn8DV?|m=FB(QX)whfE%WKaF25=^D0@Ta|bB^_{+Xu)Ud>wkcDR#Zzw zCpcUYWRAH}N~y0nh`Z~rS*-(gMKh(?ddS`bHNbc1DcN0Xkb=#&LtpIOEj1Wi-MX^o+swTZwjo{dPL`%9Xp*Ln!>h-TWQqwbsU2-kD88 z7!{TuLCXO0fm#x2TV?~qGlXn_LL?!@Qs zNHQKO3>jt^W5YQhx8lL_|EmP9lr72AK_+`I6%J)fpy_U>=>KxKFzBUeKBen7%*&H% z>rn&%jxpuP8(%PdZke%!+K1rxfQ5NC88mG0@0$(fXQ)@^F+uFvSq|u+|ALjZ8DSOx zFd8j;8~a-mspFuhx{EbXfhE{ zD52S|gakygLsq+2-5Mn2Khb{XLsh^}qJ%SLFYQzrs957{Jw}ey`5>kw@F<8LP?yQ4 zUJ>9)lx;4)k`~0uYK|6`6#C{2l+)!6PrLW+%8g8wWGB?wZUD^7UYTI}j@32n#`jte zUhna7+TJ03I)p$*_<+z~i;8r2z?P|2^G4g4glz_CMkR4#ZbDNy#9MpYb(OhcxeSOb z8nnqtb8sxC^BZCEVNg+X_i{hxoMw(v1F!c!mbsn>7%vFa0TRIejqRsXL4{(eGlRhY z98@5chTK0Mxp-BU!4w+!u-k8N0v~-zI+gh~H~0JxU*^cn`uQCC5Qv0*L}o^jOly26 zfo|!gHOJ@iyTHR+29MG}{sSd6$dzosudZLZY_RO z<@hvSnhMg}qH_^4H)j%}F@lm*anjgaJU%6Y-Ov=*Z{zl79S)Nez_x(M?)hIWnxWY_F(d9L*u;1NZ~mePZ~eh@3POFr65f_AR>Z8 zez%gr55bwt*T6qRAOSytn8APlfty;uKmYrm{}aOhknlfq2mogAKcDbFA^a~o{4YfQ mk0<<(Ed7r${{NR5Q#f+0&o^zCMR8H%m9m1me9>dmcmD^&!b3v< literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night/background.png b/mobile/android/app/src/main/res/drawable-night/background.png new file mode 100644 index 0000000000000000000000000000000000000000..1b5df34e7b11f0f86b98ee1597aa936078ce13fb GIT binary patch literal 70 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k92}1TpU9x7?Tqd5`OSAFiJ481h8%G Q0!lD=y85}Sb4q9e0En~=M*si- literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night/launch_background.xml b/mobile/android/app/src/main/res/drawable-night/launch_background.xml new file mode 100644 index 0000000000..3cc4948a14 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable-night/launch_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/mobile/android/app/src/main/res/drawable-v21/background.png b/mobile/android/app/src/main/res/drawable-v21/background.png new file mode 100644 index 0000000000000000000000000000000000000000..649393cb5a6abaf3949702e48f9f99d4edba69f0 GIT binary patch literal 70 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k92}1TpU9x7?Xc~dwjp1f%6LU2cvm+ QTY(Y`p00i_>zopr0O8INo&W#< literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-v21/launch_background.xml b/mobile/android/app/src/main/res/drawable-v21/launch_background.xml index f74085f3f6..3cc4948a14 100644 --- a/mobile/android/app/src/main/res/drawable-v21/launch_background.xml +++ b/mobile/android/app/src/main/res/drawable-v21/launch_background.xml @@ -1,12 +1,9 @@ - - - - - + + + + + + diff --git a/mobile/android/app/src/main/res/drawable-xhdpi/android12splash.png b/mobile/android/app/src/main/res/drawable-xhdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..86e4a379f6bb3a9b288aee7dc803494bcb116d3a GIT binary patch literal 14657 zcmeIZ^+S{0|35wuWH(AoDM7QTy(lVrM$NTA_j02##tJqT~ zd|6N&3CYL6clS!3wdu+=xUgiO#uQdPsO(wqDde&KaP|=w+wYYfCwxopk*njFPmh6p z?M$B*OWmh(h~Axk<7cHIoVeP7ptw5Ins00Jgj?J}@Sqr6{kso6qzs(OQ4#zS`tK5? zc4QthjQQs7AJ8B}W2`7!Bs#u}B~glhui?0$2_2r|*NG3-{75!(uq?&VtrW=UJi)q7 zoPjd)D(&BE9<#jCV#~OCfSMY!*HGG%cn!sjrO5yGreF&E@1>D{@qM8$`k=+(jVx~^ ztk4K*Yp^;fah&45N}U!4MDf1k>Bn!=A&Zzvg{H9J%p)R6AJp5P=Zgdv2qYPxsj6%o zNV0?ZC!6(Ej^zH9rW74n1nQI+1q>DG?lMx~v zCD2d)li@&i-_8Q^U+%r+N3k*eX5IYc1;c5iqfUiwe59j@%E~*UzknxrArBITc%JB0 zZy|q;DHe@^Rm+7&leunRw$1UAbgP0$`_#;zdO$->Z=dGm&OS}}D&i6KMN)%SrhWMw zq@n~OH9R8>XRsRk$>02)3avrJZ=$%smsMK>ph-yheTOWXp#7%?2(0lhjD~d`ejNP7 zNfmsaULingtOAPAQ9FCiwB+PAi7y+`<|Y%0p*@ZQ{<@tDB5N6tz0lF3D%MM)^?fo) zPjXWm6mh#(?fTSY`b9J=2f9C)o6Oq6Pc6I?nC20*_If>I+uBe5osKJQj-@Q$_e>BO zA;@Wxo=F{Y8`~au`EI46hC-Byl=kHV;lom6>-U^#GGTP#_wSO@{(0pGJm(0ZO|oeV z?S5ZbLopHtJZFEzNn@iwfip34m_bR#g2?XChTmtzBXzIm=Bu|TL6MvwWrbWLKXTB! zx4>j)xC#MLMt2+k4qFi$^isdc^o

60n`d`O}EoKpv1i41oOiThTY1?XngaeaQ=q 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

gL5doA9l#3~SR?h@`>L}&%_H6M8or1TKH z1vL3(W)!AL(L-?tN!b`kSo|LaLKSr<`L01BN!6v`A}kQ^eLlkF(z#NT=M)fedXTj6 zd^%B|fc$NbU+P7w8t)h@GwUP>(!5g^EAW4gd+KDc9Zf5ig^t1td5$$ODi*$Cdx}jVQ!4#2t#f^bC@& zR0yRGq|11;=`tZFRd}E0oA0T|*m3z&*_)Di@30sWLjvw-fX_>)lafyW=f zAs}H#knleN;szm~jl+Ei}ZI&XIwR!X54w%J%`ht*xUSKpTZ*0oBawNttRx#C-)f<_U&H@5 z*5N~Ze!y%KBpe75ayjx??-aCP>q;|XKLYyBVl;qbK5MlAWTV{UCoXYr!8LA$8rYM* zlyj17Tl+>1hm!SaDXKXK@9@825&_xR7?k<`YckFJkEZ`T_soF%Ny6hmLe7@=i<9Bx z(ex<(xJny535bF!Td>an_OvG)+$Bi3XoM)3B?=0mzOaTGrl{t=y}!XThu~fI^^aP_ z9$pvLnoay5t(i1P*bOA)8uex|^Oz(&w@|Cp0-l^gM&jTKRG2j+0QorJ_H)Ml!w_lf z*fe?{knn1}ldm0b5TJ!zi!q_4eGL+B4iaX{mof4)9QnG%8SY9z6im<5a}*BmqN1+m z3CKsWkg8n3l?m>V(bI8HAkhilRj1c|3Yy~(kgeiB<#{WR@NJOL)>NXhYMk+F(qNAj zjDWzrbn_ApAfKv!^qz9wgmK>XAmu{mJqW)jVDehM?o+cI@Aq_os5B10NeuwmUIXcl z$LSmy)CK>KNBBr8RG`)2EIBo=24R8Oej7}6^4&-oco9g*y+gui zTBiu}e&HCmmk|(OtJ;;AQ-zU)$X7~DHbG5+cGXKxS9mG0iP_#^v&Hv(qG-NmrtnOUOo%W+>| zWAvV5RvnI?p*X0%Pi4+=kBx4xZu80(U)$bu_{jdD{*RWv2YzT^ML{^#^2dg7IN4Ja z?3al{LBg#;!j)1;oe2V>GO5GZ*Z!}Bfarctta^StfQqq})KDQ@Xjd1s19^Z4@z~ru zTbEuq8gcDCkZ?!&oQZgEw)#58Nk`fzcuy*DKIFJg`PnF3d!Lgnzr;Yc$3Z%3F6}6T zI19EL6dEo4E`F9{qv9CwR*?-HrJOHT&L2QUeC>?v1PDgQMb}q|vIF9nAfes$a?%w= z#pwbN_-1J(7}ikhHQc*^Cm`4{!m?5E#CC{d;@y3R3LxQ5N>`lY*brfVMk{y^TEQ$3 z5Z&-bIiq2XHTHE9a9fx7RR=1@vR*@>4^qQX0lw>;AQqJ;Dv?i(y9y+1kLRreiOv)S zv&=BC*UBk}3`IaZ(^}cUJCgjB$X5j7e&pr{#szfCVT+;I3)sZvOgW6cWJ*{=Qv^$- zR3soOAS-)}4N}hUR?bHRy#KWj&uRw}UI!9h0zyne8Bm8W%=-805OG!PsGNs!g}XIK z*cK$LsHTM$?_#ukML8(W`y=cRXa)a(RxkquwDcrP2`HMs7_9&itZqVj3n&7GenY?{ zcJhx>f{`HM%^)G|jJK0M3Dd+BJjW8I;~ig&a6g61Lj%OSP{t)lI@^Z~dxxMOb6O~h z(^LXptRW*#{82eqyCqM(y(6yr%xKD+bqhzg_ceg201npeZc1I=N*e1xK5N58kdICy z{X8JJ!P0cGY=%YADN-5G;@yOnujt8?dW2j#K`S^Ct)OlKVjS2yM?FoIfLYjI%$kLx za`=T52i$1=Myru}jagN17RFz@t=9*q0nLMyln2=W?QK`w0_A1gt3SjxT9 z;Czl>WDo4i35mULva)Ny2$x=hA`g3qQBLOC%X4~lNLuYIB-ReXpDCHzCj^ymf6b~3 zjU{Rv5ak3E{rxFC{<}zfFi3be^3e|@WM2GaqG86($0FT=S%7J#u`+Qz$aobwMw|UH*Fop(Ni^M z#D=AogaRzF;dOW-jpU;xBxrt_V{42Lv%hE_yjMa+%Q~cG=_cac_?x!Md>x)M8Ra%a z{?8SV+47w3)5gp540OZI1njRh7s9F}+iGo+5?q{=pk<2VfHDjWJZk}3!6T5TnY8ak z*!O+zsbsjdm2&LK*Gfo7zYr|cy*~);eKl!8?Dl(9&u6up7#T^g0`TXAGbB01sQ(^ zQqsBjedva@5>S|*ADER%h^y%$_l8sct+u+=q9d;QEVtl_b|fFWs+ASux;Y|W1@c+i z*3xfbdzUzAf^yx#@|?22OXWGWiUO^?Q~?c{ARsErnpmKsRDChO2|(&tpEoVz9^~(1 zw1V6O@G_66WnD1t;f9QSC;pBuyCZ$Fr|`h7v|e(8~QsB~7&duf0aYePV!^@JmcABAmOOhNP%Oho>rTOLRPM&73) zAuI?I3FNs8#EC^f+&>oST#18YI}&AZ@3Eg5O{U4Q9oL&Z_3d!)GThf$Zm``=y(Zm+ zjFyM^pm#SzB&X<@)Ju zH*Hm@)O_A+|1`Er13|iX)IgAq<4XEg?i2C7R4hjNPPf0r!#GCC^BykOwSWZd=o>3t zyvjeQ1XUL4Dt7~6)}Y18p~a#OT^yf<6r0=$vT>rrHQ!-Kd!l>>br3B*L|msz*i;-H zaz=rWARogXbV>6rT)Pn@?7*EX@jh(X_Lgx6m+dXrt1A^tsd^T61L;b*9M8GN?xO z!8mqwBcF}-i1RW?Nbme*s#YK!HIC?W$IYQq96Uzb=t>bV=~(5lBBIqQkcHpQH&(i^ zUEgk}gasj1lAAEwU`34QtXC8&=Gyw4ak&&m6Uzgnu@^{K2NGH{V~~z5CtK7smpgmL zxR=`1+EnpKK&@U76csF0z81tEbK0hqB;%;{d2^I#ZXts97L*s>CcD{0_8jp8_sT;n z7^O_u+h`?V7LgT8{;05xyQ=A5kvl|1Az)z73~4|+0hcw-Go-6sb#`0PE%4!ER~h;0 zIUjTL-|Pol1QW(ZTwLtvAH@|~T`-e?-1b!4Zab!F(jc5-5p}V@VwdpfmF>;AD)>I& z#ON28ggE!b;{@Utem7(M$DP$F45o{?dp_vBzajEXjW|!!72N0kQCy*Q2u1?Zzn)H6 zAjwAZsV$8T(GUg0Bwd+bY|I{a!7TDE5NiM?7=8q_6e_s;k0? z6qh79`=AtsF zZ#V?;%RCC8qS&Q!MZ|AqrNnLqgnQc=X3lSwGaA_D!AJ9X81kMc$#{nlxnV{_ z-ttQWGSZBa2g%r9=F9KhF2a3NUBaDueyZP}$3-@9rQLe4f^_Y)6UdXj@Q}fQRBb^* zt}IZ?S8WY%LvwY3o54Cr$6Y4bDgSK5_?7CqTSLmHeo z*s?u!;Cvf$hw0}i)+uLeBxRFiJk=k z55UnI=fyodLSO(=86e4smNz%V?xsV)%M+Gr>X62AWK&epfs8elBgy$X(bT!H7d zqKh<;>}7N}+=y9K(fShMZNsDovd{%=5;m_BIrEk0VH6+s2YHSJ0nu_97+c2jgfI}@ z^gv0*J&qo`JWC0F9pUP(>q#p`1jX;T5jRhq-4Qi-qj+qJ3tGQf!f=p{ z!r#d9G1X6UOnigN`jt%cEx*Vv_^Ua;4JIYu&_{C=--B-WC(`N*5(e@sjr`Lf-L)){ z4i#T@EI_P5{s-Crln@ZD&`W5I=s78ngU19xTZ^~w{9XK`dILnMWI7oNbw+MenI>nt zVZEP9d_$#*HYo5(sBmgsCoztjYtG-O&h{`G1;`7R2&-izUjmdxiTt!|Kn@bz05Vu= zNma{L!NX`Q&j%n13A2@N#9%o5`2n*`q)CBz9JjU`qq1*Dq5l%cPRVVbwdN@iYrLL& z9o;%7WalN5pMWshLBi`nLeAivYoSSkVvrF5b~_J>8xuHmKVKef5xIOwr|_NQXU!EK;QhCAl8^8dNq`vK z_R|0n?d7nzpk#hH(*2`vOa;rv%rnfqNrM`CS6`0faHR2$OVqMWP@(|jvsxCke*6HF zTB~J#DVkZ1%BF%sYR;f~j#LkdRwnadHxdi0p1S@E(aYe7Bw_PgR-~f2p@+u zon8upD6#s%l1a*S;EF2lVFBX3&sG4(X>!kw#BH2WeyYfa!-xA3_BI`FGC&R~_Qyx| zq*oe7gGmL|Ve(+USq>z1SZ}VE;9Y%(ae;)gvYWKbH@UfF(IDYGyx$s-@Cj)fLQ&Vz+{mU|@@qyi{gkBYpJ3ICZd!G|hkzj&zRE~mD$6b|L30uf)0R?$23N{V_ zJJWL%$QRXhZV+4x5>n(8*GIFP5r)0_ z4A*#lJ=24Pvrurn$4y*Qi+Hv`K7XL>IJQ%VIJPncS0Ey;=!kwG?^;R515v&~c6bkj z#0ZENr|c-S#)=0hCp*nLGel>6y7p(^8iX7SVg?yUB_Z(53Jx&m=Qff8WIsHkoIgf) zCh|Orb)Ora0uh}`4Uv%u0YN_c9oaL9w3IYoMB(k*rLiwn#i-3|tCUY!);MpfH05-~ ze$)Ep^1`gu9n?LLXBT^Pr5Gr)68Rp=&yr=*s7fTOc(6N_J`7>hEjeX&Ec=q!1f-x- zq+?aL4+YaG#E;P84F?G+nYYF`rde>E>vbc@cTx)Z(T$2LJt!;gtE^7+dMoE|rja32 zm`z=$^{WR7Bdb-%A|PIxT54&UbaFgtw`BZ+Z4XnMf{=IH&LCY-O@nal=rwj#&NNGQ zK6(Qmt|d%IOO>i-6G%w8V&o;u$OM!!y(IkERRZ_yn1sSS17uqT(s7(=n;#Cs|8$4z zW?-{4<$PrdAYaMs6y>!$f91p@^-U;g{)sW+ZvHXL5vK+GdyIR-U?@mPucj+NHZH*p z63uobkKzBV>BZi04-JTtPyo#mTxXsr8%{3N@vb`7^W9xOA?ykg?hO)#)-jAkK)m!V zQSOTYs>55&Jjm9`VLGr9|8I=&;tUZ5@r{R9_#~mq+Gtmuq@zhUlr4Xv9@y=wZtYIQ zhiVN|@cuN^s^&zDn+=8TilerG!~CNp3l#PzDBQx=ZHPJyKGcElYLKus?Mjp|s#U2X ziKI~!Ll_NWQOeG~Kg2UWPy4swybz(YJ~@~8A$=0_=NJf8>I!T z+CsGp`k?R|QP_>tF$CeLKd2JnD};5yHA@IB*M_NB>$lI`D#W!44)Oh3&KZbX=AZ~9 zS%FrN^0rE9R*MnO5|tn>)u8V6$(Ph5ZU)|;6D(D8`DK*H!90T*s}7>!C;jzZ@Qc%=&omm=(RT(dmZgDkgno8Ir%Cew7Yqu^MQI&ro`A*@^g2@t2~ime&t3pdi(+*gi@ccoaxsYt+$wTmrrg661k z!9{?;25q%UKw<_eW)oDx6ps6rjhUA-^Crh}wz|>&a<<%oGDMB}j0Li5 z^F%H?>T~c?j!>qjp$Le=2<4%m3gGTZT~Ha9cOGr64RiBF&cYO5ErJ>~GeJV_n{tN% z-iu>DHwA+2xo;g$(CW1X`J#G8O$(to6}AP;(wAoAGE`P?REi~%0MYgV;Z79Nvy{Y% zGVCZ^*StHLKkz=>*UhO^`yt-3I2#7}_`ZQ{Z?cL|1VjORow9G{sOXM-t*JANjk%Ex zNH%k6wbYHA6sPQ{x)BEPxVAVuX!;rv$`m3B0Vxlw`Z7;Q?1c(u z3)K&mXqj1pTD0r^z6igZUpWBP?SMoMQ*PVC?e!1KI(wLBuPv(>sv z6qYf)T11Yq_eGwI@;_-vL}d$YyBUQ6*Wts&k3vAI$~%{#vWnTLc(x`xp;E22RFH}W zPXj=#i|~7c%RT$!cvSJ5O;QB$VDBY|>n%TqO5n=P{>WpAsVQ2$Kbh`h!mPs6i>((z z6A*>8GNTg_FIan_AkP$6?1KDTs#_o;t@dsN(YOT1uC1rX{chwdQ;kBp&xR})j#1CC zw~18&;!&0Cj4+911fey8qK*OS*}m390M>>$API%ly*N=}AT95N^aBGjvYX@*9G&}k zsWNeveW1rRcJ237$iwS6M&j8II4{U`Tn=xJ4DvD=#M|h8L;L56L_ieaQ>aJ|kacf% z9R(DRJSkhNdodUryl`}TOTGzW(bnW#d61B7{iuh>2>{1<^AN|=YZS_5hBOS~raEkK z=>=jI!aZ#=(ArTxIUVGy=?Njo7s-QK(p(*QFNySO1aAHb zzb&}6lAGT!NNnnluK3>|Ar&WEnp-q4eCi*iMdD?UZ+KFPB`SNOM|P0#H=;1(Yw7ql zjsZUh`S$jY795ESKS9ECK|;#Gg63Vx}R z#0ccs%`CO5&`v-CB&1!eQA5jjUWQ53kITAZF6~@41Nm0#&IpkS0@{$vcl43+obJ3- z0`mE#$0as_b3LwQ0pmct!0tn;flLvQK;_b>Gh0Kt&CQ|RS!2xF_b z`?{rp%n{HAi1-tbkY zq=bND=lX?k9p)kYm?S{_-N=Ogw8+%Fjp*HAz4lAO;85 ztw2P!HuTU?(SLIU43K1x1SxCdrZ-&o4!w&|1<*@=52XKYf@x|&O%u=tNWzM?_D%yC z>92(z^5e{$->Wl>o#^m9iTrUJFp!Y>dN4*Z%*$}3bz2g7$_iQp0abvE6a;U=bC8k# zU07io2fUr#`4KMK&anaFFkfs9d*hcf&L9e(T(!6Y_gF)|O1eQ=EL zbzYzE_#?jWA0#^6p4aob9*@U;JRa9~Wko5XJ2ZD75D1Zs^xF>*2(AJ4>oy()0+Ed* z%!EL2{LLjLm1QI)AAh#DH8Hm`hCp1Cy&^=U+ms*pD~(#bdr7Qu&#p^3g*EU8zXkaZ zWrndkg@W-7M^a`Z;h8o3cZzP|dsE~Svk%k>*4+8JND`PI6!j}8+7>Cx#!D z+U9}Ak!bAWX5q#&tFz9IBH&EYx!+cN@7|l+DI0MK#(lCM?lc`cDK)CO*rl!_(5A<^ zZb3LW5w72;vlSW?g)7Y;CZoS+6g*I?r?V%(O;0j2Pa<22Ybjw+#K3p=BICa<4S2Z_Gle5 zWkW&{@cJ6b~SbV(qGWs@orrRIrSGR7tI-Tqf7GJGvW2P&-&Cx-<))s`LHdPh8 zT2LKpIfl`tK8xNprm+Y0fpOdg5S~{mkJuLlEgo!{OgjzcXy(jV+xsYjq(22d9R z=ts#1yO>L_rgbV15)dEqb=Nvv?B9me?zLdQw~*fW?_abeApiYd?zRE;*9HVQcd_4D z8GLZSHy@^(udqLs_(8Ct1&RINfBxTw{Vz!Vml*#m2LG#`|3i%bq2&L6nZaJ}{q#Q_ z{ErGhRfkkeu8(|M6B-(vETQxGkMMU31Y%QpJblkLT1na(v zGq$ewD{JeXb|X3-#)0tr13G5*$|K_Z)4v!0=X>X}2!e=)9?k;Xu=^1oL21tt~mU+hf zAiBGU8XCD?p8Dyt8})>J2vhS-7HQI8B%4kIt=)Gwx9b-9)fBt)TOlCqLP$Ue#W;<4 z@ui@vd>$9V{T{q~i+SWP#!F)|W*n=xTKN?29Ftic+UY}s9c|d$RupIa~&y#-_6yPCn_WHrZY_7(VCUe*E7I*V$wRYxD zn6G}NolgT*8gLl;X#xDq=1OCyeOb$-PaIpz6+t%j@-GuDj1+<-8 z#lxFwwvYc2VQ_i)O|%P7!yEGq6G!5(SZCF#-@$CDyZ0b1qSy~|(n8OZGBN)jyh*|R(;wH*B~IEQzadZPs8FdiSm zx`dut&zI{;#OGje|4*(f1p5_hcW7JTrjBzK7zgDDRvcY_I1xK`G=)H%!6E(p{yg8A zU(dPIwT^>7OU^zlx0BhX|6G8O5BJR|c2}OgX^mqqNq$^ULUqT6``LLp6+6RrDRsRA zo%F!Kp`$m@w83Df72X8IUa1%sLgyIk*@WjQlhbP}FicOIS!QEWcke=e8r}!bB9Pv_ zPtK9VB0Nu`#>UQlnhL_LAH@2qM;!;m=Not{2tC+Uw^XYurNrhO%-8Zd_m6Kk9upYY zy4ErNKcAqar14C%Vn1;dQ!Z@L7q_;FU;|O^NrnAG>x1Jbo$0xqP593ac$tf|{CbHx zb-nQXeaM@($38edwwUh~&Z)aMEec{PFd+^-&mk@NU}Y^*At1+c==G7C@D}Tps9%iq z@zJ&GI!Z1#AjtVIg@5vJz9>vE7@zMHR#%ZpD>O-F?1i6C!qvAc)rd0`pnUdQ-(79Tte94dBU zQr7IOf+d@K5rvIkXLMIKn_AdB6v|Dg!7l&bpO7re&Z&&+Hy&xtN-<3~?j#Ix7~3k( zO|Zj9+FyFLcgkfpvemncR~DCSt;(dq;T1)cb_G`K*BxowPwFYqfv}?0aG#x~l4ZDu;+4BX1 zr>=U_3?-LrocHxGa;IAs$&7kD=^5mpO5f-Bt27Rab@}uOrBzCrj>`ohqctX#8gkja|dyR&04mfxJmpY=eUVsHQFt`O@Vben#)SSD}$Y!47qdG{qO+b;_?kj7HJ2^mBaT>g1RDfc%|R z!G511Yv-Af>}xW-${IF?VJmrUhXOiw63BB9D?aY_h6X}pWD7iw@QX3z>fU|x6q>!A z-AFX`lUZgP-Mz&hYyv`rzXdtB8x@$YgKM`-LAvUPxBbN1CfhPVxpc?WsDD?C>?cG3}H(nqR--slLnOHc= zzn$#-#5bVkg;MYuhBd6leL9S6KrII+({klx4s`$C`uA_Uft<9Z;<1zjE}F2?;j@F` z@aV|zhHH!AjQ-0f5{e7VOO|DJiab_kG!rPr1QU3CifyX2G`DR5LVTDQlFI^4cFeb6 z(_E#@O%Cd}T5U7VkbB7yfKL=iO((LL`1>U@`=FRwc&z3t5m3+GuQW8@4e9lhN$_yi zIR5ubN+HNkoEa83uR?*zJR0mFc_Q(Ma3qaO94@I)wu;7wq+tuW;Lx3b!%q3}8@G$4 z2~P|(E6(So@~TCgM*odXZ(<0W1&o^FFTOTQ-k3@$!{D127NGCN4F8DrCeev0t@urr znqVgQ?CwjpUmt}$Ywv`gBpYR!Powk?*N=r{d#7{OHx9}X>TrK5C34YYV%V0M*4Lbl0;N4A z{2s5SM10XZ=e+r%^k1Oh-{NvvH&Huo@GHju#MS*V?)T`+Cx3+#ujT~kF+?Jz6Qe0O z5L-~Je4rcD3-6>F^f?D4s6zTHNr~4HHH&}pbnKn!@=pIXEw9j{E_by)0A2uXZ72=- zYToalcew36awp6Dx|zkYom8udMQFXoP7yJBuz6*d{-?0#q;gX_L@`0AxZqZd$i$Q= zQYeXpvQcZAY-vZ~uzfUmp_s4JKF#7uUp(1&BfP}nJWKiG-p3G#A{LtX0OoUg_l;qK z%V!LYGR0SjH&-;HR80qC?m5pLclZ0hV`etqm5~kFIWQ8q$TO|el0~VG9-O5~&$3&u zgGyT#Reo3zE}eyA1$a_q4|{a1ch?~QBQ|*^99G`vk6l0fy;Y~7VQM}fdtn^ksuM@B^ne%=r`hr2et)I%gK2y)26eehKTw0-KZVq2RHFb7%kxZeC zho)-&oP5r1>7OGK=iSvem*TO-zj>hetjOyp-fN;E=#+$SZofAPPl@oe%bucbnalc3*qJz_S zVY_b;#i2ucaxO7LXPy?8qV8@xP5t!lCUvj@mdb;B zMa|*;{7UFH1wr*~8iLTU9S-5os2|oy#d>;d5c-4t>b%PmQlN zMIJPKEl*|rl5gDlcvQ-n2XZA14HhsO%i}F4qkUhbGfd#dDp!S)^^j_cy*?#!dQhAC zI9;0W)1ORo5r*N0G>4W^Jsh8o5D@D;uhJ1+*IWZzZVR#~ zy(CWCKE@<01{IwC17T783>`S4oGD(A&G;%mlP!9(XhG9c*aM&ub(?w+9gp58vba}W zx<<*PgiaRe^$f)f8)g&g#ydsP=4&j|@Fv&F>BqCH4sr2pNSAok7^bIMbYLgMcwZT) z9IVFan?DLo>ut-(x;3$PS*2OIc;J;G-&G#fmtThQ74&*Cco1hGqCR8#!m>|a$psHW z!v%svjnUFQF?4FqoW@pWFY(7CfMz0MMs-SxDGoE22G9k)uVW6Rtwp@!#nvYY6&|tl z&vga%N9Rq21x$XLvwtxu*}j_K50V+{j20x4T+P~a8Y}dCnRO+UZM^RERDtlj8MV7h zzUmgwQB(PIf*)cLJKWP2<`+4t8cFR0<;mjm64`L2Geea1gn2OP**hswa!Q)yrx@Jd z9=-q~)=hT@L>NzdTvj=jkmwG}A2!M3VIbi7PKkKDj~t~SKRFqFa9Xtc9v(`^TTxJ> zNMNfH0q;oSg{)?0JT$CyTxJ}F2&iV!aF^}c5)HPiPXiYX(77i@w)^{&fbL#;jZ73s z7Ab{NZ%p=3Nx2Z{3ywII+l8);{XWB1TaL@b%c z@hcV}RF3^A#I~5doXy@zvgH>WuT@4Gs<{T%X2PmRybABQ8BSNVYoGtqoVeCp!B0R4 z;U!G2kcDWlUA#Sa&h`8;52W?%MIsNqYr1#23gFChHp!zeSrqfe2CH2Umwq*?oxFcB zn%M249{D-8$(8KVO>A3>x@OMS%rJ}T1P21he(s0CJ@zZkB|Q}9!0>(8>#eFLMzKi9 zNdeMV&|g`i9WnB;{)lHFFK{(f*24xUS4yVL)oAkk=T3;{qYbuvT>OUNXIXZ5_b!Wm zKm0Oc;Z{d6C1_6n>}euLKN@E{+^<0P_og~5n9(p+l7)ee-7+mkjBa7!R0c9STzWFX z-e6l%D;>&Xu-O%}5a3+(G9XUcEwQPmT;mAE0NOsUK36Jgb$8 z{-#8G-=Q(gGRV_bHFlj|P=CKQh;Cdox-Wcw(LyX4UGq1-RjfNLNW}rc%}R_1iNO** za`wAch;m6~Olj(_6s{Q=*)R{MrTa{ChDnFl^0}X1env;}^xKu^!xCh1aUvI~et`m{ zPZU{JcC|Jq)vsKdUKAQ=WmxzN!tBy{o72~j?x&AW#%RVW6%nPF{)< z$~pvLb&o6Y6!mZm$|4krq6L3DH{Rqk4}I&_@8|cnS^b6OqtLRv@A$16#to$|(!t+~ z-8uwXvsIs&ZdJ7Zpo~TnMObd!m|lQ7u2_BRMmt8`EaAG-1c3l0r<{kZL-PRyxs2J? z|F_x1eNRslSvWSEN+T%XG>`)r9)}*~$wGFdcZco?5-?aM`^68O{A?pnI&B-hr;4k>w!n3<0l#4@vBRJ z#G{2shgg9hF;qgmkD_B)(>Aj(Qb{%k%DNXnM=+Dzp5N5>%j7b%*izSA8AXSMQydnP zl_qRm;H(6{<)S>^w)gIafdsM|2Jwng_uP;FlAYqmp{F!J*{1p8jF`E-n3GhQNcBF> z&l}i!BGDSB$XwrD0~`92CL!Fc3{HNIlLZq6Tu7lH@m1cs84<%b$^V(j8s zJIm&4cGKf8+|<4J1XDiSN&M1gpOq)j$2btH@fMbttcAF}ZJQW0YONzPLnnTHXS2UB!+MKAswyl<)J(=zmUNFz5knhM_4e z5ZyTQhvkHsYwz)el{{Sw+@gi*X_+D8xRL>`Z;iO{I;{viIc-e4Cae4QY2ksK9Bo&>l`Up{IrNG_UGs5$i zj?0we#xfxk03Drufz{P7#86Zv-SJHb46E@eDeLbsFBPP;v~bce2m z5YoxdgZ&@t*Cl=YBdgK*e3Q9isKZTNR}O{Tq7&<~z2L&{`-LA#i?Nu+hOF(0#DyB$ zWpUQF0cL$F?WTR1TbiWQHdSJ)|5%I(tVd|(kD zupnYL9qYstH-rur|BVS;=dI0^&y!JZT7?>-!+1sLq}fv})~m2G8xF)RN$)OW2X(H+ z$^^mX`z;ZpYDJk8)r>m~&=%%6q2&titaT=2akcE77pPH_)jH;@?aaaD zH{q%WENA6=VSd^sjaVxusVW?&uJc$#_}-bWa7b<&Bgnf z5%L5}#H)c#Cur9%SJ={*jPZtDRr5ML4JSfXWMITX^{NIWDoq@pKmlEQm}nuNB(@e& z&73nJz%#Zy%Sg*ONW**PtD>8+c>=jHMR zc!U^+Yc$F9<-s{ws5Uc(uOHn-o#&@6>AkM{x6N9#8MMaE89<5`!~Q^|q|RZqI>~s1 zIC&@8Pdm%?Lw;LdvGWdFxHGcB-(_uv( z{8J~Tu%LkPfCK@|rumguJ;KE3A~N`e+W~8id6z4zamWjM^f~Za0utPdM5h^Yy^2AS zF;>T?@DTeMzhkVp*~qRof5Mt`#mtOAwI|~eSMPE~A?eW%Ef(wxj_Sv$IuXIPlwQLU z9M#>XmnjHksV_M~xRBKM5Zs}EdQ?*bN^tSXcX5Nqjxd^d#i*o{(^VeG|%4+>tSj-eHr5+cJPi|nKorUqvu+yp_s1e^Ms8n(Q(z69V&c~_;u#N zhMB64Efz`&1lz^<^m8506K0$jXx~~Bt7e<57%B|ax|i{4*=lgsi+oxV=05vm;aR5v z)VxX#m-BNEul-VAspfNMv^Y^-Hli`KGAQS4{{H=CR~2FoXQZlI+yp6i8gh0#IOoX| zpKKt(es)K}2824^IyU|py)-ir-1)rqFIvF+1qn=MIHI4)M*&Gbww7Z*wGe%)1OSc` zRt$$HM`>HrXCb|5}AcWQCjeQ512_h!>XHpqc{HX>ZB?A?v2(3y)!5kPVU&#AO$ zNn9zuke!EErJWAksD?ckKM*@ObC!|bdV%vR!0y|TuA4CME|>v=BGiv(CQ@dqh#QMk zB>?L$?cnk8I^6LN+hvcB;<0VhKTCZ?@I91#bC9RLJ3dk~*kRZd7W4Vq>@dJj`+Oef z%_wk7hyFEU^Hku+v7;Ex6j_w2$#s_JOu8S(b3_ zh;CZIR4Ft?Ss$dRX{T_k``C4vUX0l0uE+`Kx}TSuUN`8O+`VeF+T|qAuBe9+pY~;%CHs z^iV*ScqPl}^?-qzBJS{?x~4(xzF1!1oA(-p0@dUMUF!+nrY+{sYCiI<1~u%%B;TEl zu?byXqmtMZF-Ff?D(8`l7P(@V_3>}taF=z_W|Nx9#$GWg;% zfni&lhB0d)DB^X6C?Ym&&4xnZU+pw5@>!!dnx)d6+|n%6s(zNk->1;42%!V zv%O$(*zpDCXpNrmU-d4h6i1p>qwMpq?2{?8c{{oUR_I5w@t7C~!Cx{m-bLKO?A!yx z;xLD4fZsmRJfM0gP~zNu6U8g+-f~xUP#sMZQT^0YWD{zk-#V!~eE}na6=%GQ>Zevk zS?mIz^peSwsb|->vhf=e!^m#97>O5C3{i5jU=BN@p=9Q*@x8f-xVib9T!XntJNL&7 z+nEUEs4Xq?)>UPx7ng%q_Qj)Rfc0-ha<~EFu=oP|{D}t6unq1SPAp+P`v*J&_y0O5 z1>?kn3O_=}UO9y&O>XMPDPqwk$7Ml3&Qi;HwPXY5@SC-cN3o|%fTdf=XTHH}x53Hx zl-tSsSGG;_UL{8&n0d>qI@iQa>iZwz+P$LOCb&!n`Y*%}8L>?YVPNw8l-UA?*<|v_ zmSYod>(8>xODkYjc=XVR?>R{VnSYl7kHIGS#4N}P7 z|7J-vwCQTrB6Sj_o1+sOhnma_cX_Y4>E$}u5b~Y0HM9s}NStkp#^}8blq8JN)$fIK zYn)%#CZI!3nhkd&1rhqUA^HK>b=18o@sbW`IS~UIp2p|u;M#1guf}?nWSf>WgYk6? zxpP;Hb92R6_7!AEGA#AX@X6z#P##6abdT@3P?k<$d9e{TZxME z!t-J0Rj9o$lh1MnM9C9DH<1T&*_(Vt*boc3e#%yCkXg|i>G8I*z%}J{?ZHKPHlT+XySFx@hchGbOW4jlD>~e6*$^T3BM)FH1Cjb8nwChAc&F7WXdVv zd<$av4#*|dETjrg{s}88orMwc3MdxMRg%nh=iwWS87!&sv6_1oe#I`1-oQ;tvA@88 z8r;5yiJ)qnm?z^xUo;Y5?yv2s7fmo`Qm@2r2Yc;ws4#WxmpwXhaobWqi=^BbNDry4 z?GHw@*@r3$G;|$%wRP(4*W9ciupby6U)liLbq1(Lu9X@bA2|F2oWxUcj5U z1;FvRcV`FWU{u1mUx}zLHE~>eHgn}%w-&8#L;+(+*4}F$Y?NS61~~&h)%uJfs@uqk zHYV4Gh}Bj($EV3{?qR*eH|KMeH|te`txkCrgU#{P`-q}-tF%%Jqkqv0N)@28yE&W> zT4?>7_v)~ruKq_Z0`!z81J1U_3M*olCFmQh1SI7OUo<#SbI=7e`?1T@#B+Y!{q-n| zu$Sc7w}7q2cg2aLQt3xc#TCHLrPEIJ-gw(iD7`qcX`_8SmDB3p5S!zvH%YkR`1c~_ zz2sywi^tXMJigpX-|VSu9JMbGL~QXOx`^>)gGtrBnJwKqO*A~*Oh2y0zHs}h)l&?* z*?)BH%a|#$0{EPd0=5YCfFfkZc_*zd&g9+N%s;@_wk>w`u zstJQ)HdXa3Jn_b@GE z94pI(i7bxw%;uW(SL>3K=+jC&jnl&_i>USy{lw(gmslFg#9-u=Hmiq;6R3QiYnd(rjW`LN+;iO#>g zlgbOvkbY})$ZMrOuhEj1Tt3gR6~DU%RN4Sy@f{Wx!rems&_wF$@(Tl0I2G;XZ&&Yd zJl`~#$GPKUZGX53zxA#akIZNsIOcM*@gX=WI=hmxn3}{yTs6+3Mydxvi+1x|J@He+ zW@^170W3(DJ{;woYX>KZ@t5)1CN=j0F0pMSFN>9W#|l{XzOPgmt&ppuvk&J=|7M0k z;}g^_mwRswlhW3oDebjaCz6I9%<*{A&O4q;FCSB<)zUUW@xE1VrtTfgm*pb^$DayU#-7yN ztYRVl#O~#aT;9)G-2;8y%P`92oU6Ca-pe-h15Y@BLBNF}B>6-XMEBEcY*8C_ol0KR zrHjFk&rZZFM2)3qdFN+tbjg(E6MmTd=P`80KTO5|Nm}i;&PeJx50%;pa(n2+eb})m zx>P6s=4^TToGWDxS;<*!LlXyuhBF#Fl}|3?$Cx!2Ij^6GsJjN98M=BLG6cQGHM{fQ z%VFi{MqIT*Kz{Via2FuUuGG-SO_@`NP{hl6y_d-+hCqx)&gjBgC#QY}E|5^8-xqN| z54I+QtJ@izs-wx*JY7G_4TL`0M52tI)NQH#)u`N*V`|c>fp0z`kbx<(2!DPHFLFny zdC7N(9mE3;1}WY z`CFXOOMm=GG$mZs9i<7Za>{^83zgEnZ=fZyzIqm5Ki_z=m~cz*M}{iFV6g%MddsQd z#hNCnz{}fxFD)y~q^~t#;*)|>y;Q2~?~`_(Lp^OSY5}28`B%rzs3EamCLNb~*vO5C zOkar4_*d4(U&z}if>v4@7~rtsFl$^tBPs>}#ypTr_uJNHzIG?bB%u#W~a9+XEi0C2KB2LFb1i_>es+f`BQe-~NJu)$(+`-{Y+&=7i%7V)~e+SVi z&zk4$^Mv@{U|C5yA%|B*GZqZi**9xRnEYZi^mR+E^$tpQFY&3@Ng+-|f0%orUNeai zjVpNvzEdp_T9wfksZKA@3m;}086^f_y3;e3rViVS!+k~wPmD>Bb74-MDBT?MMX2OM zdojTZPV{WDXH-;*?Z^`e&nG_bfRD#6s|^~9ix_O&_<<;P=E|Yu-P3@&ecoz4mXjj^ zbj7J|c4zHq)60EulD1pGt$kNtxB-!JzpOTk?mTat^!nN4!`P^+u^2@&-$^twH4Ct02wP5Zh z&da?*FvgJpH^GIzn^j| zO`mG&Di1VxuAoYj#T_->F4~-fM97{z6E06M*7;kcH5jtJHA; z92WDng;#xi8=$;A<1-9*Q*ov*3L9__4^>8=I5I<8@PL_=Hp$g+#G4oasJF=Tp00%m zDEuBkjdn^Cb^i4v?2uW;GfKjcra%0T27Z5MCwu|Z;1Ic7I36Y_E^r5N%Hm$aWZH>f z@-bf(CE`yd{(GJtYAdr=gYDvQ2d02#o)i$@jqLOG{@C9LBpFIO_qfS zWeMHo?~;hvzF*(-2c02zcv>j10aiYFywf!+QS29%lCY1sS@LLlFuckc6c68+#ywCA zgP43a4{p>IX}K9RK0VkKU8xS%IcT7J+V|QmRNSQ2t_WwnpIs(ljdBUFfE}!%Bnif# z%Fu52pSXT1_LnguqrKH^bl#3&R^>YCvNIExhOX`5K}dcnwm6RDeGz=kv?PbZ zSOhA&mR=O-J9+)o95S!ABJVm>>Kfb?Aoyr{H)zKV-d0aqiBF7iE=Peu{#F$7K7(9n6iXL)(Xc z-&k2kzi*$EIo*1`gq$q%$j@uW)XzDY_qLRDW?fd*d`4CCH61N}J2T220FE`hD(~JC z%a&q_V!JHF(kxcmm#-pBt(1MPmc1=$i?k^Po%UpaNJdl8WBU8%Yc2KCgq`;`LP7DU z&jW%-g`EW1wR}7xo0HkWEcjYAJ4WN)(MiNY`Zo|douIazRsatvvE9#QXK52Y=6gvh zpbBnG~yhsFa5m~6Xz zD=~;G88%U!4Gl0YfVT*_*U=v@#&o}M_|ZcGW;tsI_$L!y=BQ4lPI;bo<>5=zA?~p* zAqA#s4GQB-DW5QGCjc&^(I6&rDnwxD-=WQ*+&kh@ITp}10%uvWET(&^2haO%NuN22 z`>+iP75qJ^-MXk#w!_B2+O{g7=dzev;m}JT{^rD&!g{drpN^4pc9Gp9oralxo7Tt0 z%_OXuFg`MNoNYp^lB5Ch$ETQ($4N)6hI0txsn0G(b>F^e;muJ7D0F01FIOrgh=+|} z!=Jfo{&=jXf@TWdHyBURtMxWX6|NP<%!QLNNnlUHB0D*9+mGjHh-O_*TUxvG%C9pp zpf!eZ*|RM|Yw%RvN$6+t>zD0iIb8*QmD_m>8k;GGEZXNH?Qq{ zk;<)yDa_ip5&U@RpCg0=Ap#_}WwMgRa)H&szGqu+6(3By9xbGuun(q1SG6AB0-SXs zBGMzcH&BN0b%94~FWO2+_zG}>PVRgC^jycr1cs`Xc5E&;raIVag;M7t%T=W!9k&dv zfMbB3To$pe!H!_^khXl#r5lCtwtK>qpFKXR8}PNiKG{rif8`>xnP_kwGoJ?Cwe%Ww zVvO~uBZCYn14#3n0TX$%4+AFgORno4Ae71aH{U%!QJPCUjzAJhcmy}-@upZi7ExRP zu5i&D4{JO`pmXq>o16ENGB9^Y`ocSH!zcRtEqVBO>Z=u4B#D!fU$3kAOB2Aty4ODN zAqHa)Gra*W=0U1}W+_N54w;aRUmayS9&8EgO@lROI37~2tm3f3hXnLiCQlQhdB%&Q z@E{9;V8^Njg4qj@{aLkP#-C7ATJZ1(w0TJB&As@fQDU_)y^uSEfjJdic*YR zep5M{jrHPUa#AeK{Eqzgj^wmjcp)zpfh_S1APLhxRW?5bv(9D_6tl+J+gF*Xh@7TN zR}_g6Zrj4F?#4kp#rlS26UpNI(`dbyO9e;kX=O7PEb#E*hd8rT#ZD5}!N?~o2=_W> z$k09P#-)UQ9?SYvo3jI}ft$znmWINUPh8NTdsSt_&f3EvMvQXFp{Ot`U=%7kmkRST zzS@l$wdt4fE_JovO6Dm4g>Uxi+_$2Nr{TRq_T#1(2t#u>I0202qiy7Thv4zh_*uu& zP}z1+FMXGXkt*I%qJS$VtG9&TJdpKun?Z>&sPWN<9^A0s()?e%xWG)qXM}|=k~NSSyWvWca=46 z<+!6BS4&yDcOOgT7(herv@S?w{!87I%juherdHgnn7xW~LtV4aad&de_GXlqIE(An z`wjb2*8R~lk8!sDvpj)c&!KSLUs;Wg$oXZtcTgVUvlF++V*z#v*Znaf%^Qq6OMR;L z5OJFS*8B32CDkBr4t~jQocyNOACQ8vML2Oy^PVHcI-7#fg>zd^HwZ2y?O&m{0PY(F zH+Q>v!Rg3yBTQ^^N@ou-M>K`TQZxr>u$`#6_8B(Fsr?;yxhyg^=&Z4Ru@c6i+srC; zBR+O$vL|%Ba7knQc6?C+uT<;gIhMI^RCnK~{DA4nvRp49_-ww7XM$UIOa zt8mQMG%mjQLw3TAX`<4}es^-M2}lbw76b~waN@a(uJuH$h5T#16Ao6(A`K8EFZ1@M z_&Vy%x@vfV(ldSP6GK7rJ&_vy{o*3~q3P!Fj6CH98gzJoLxClk^A*#_BRcg>jw1Z< zz1)?lKN(551Hvd-Rf?9k`r3yR5DH*&zZ850Z5M?fGG{~c_56}SN%{iADl%fBC1?3el52)GV!sy?)3v~B;LF?c4fj>eAvEX+kCVE zq`hoDz9MRj^EH^CEXCU>Cc4i1S6WC;Z#H}~$9`o!X#EUFq3!fb*;?V#aG{r10bt&l z8vA(Gkx|-QM!nmcYPbG^F{!uR*;&1Z?rDQFKR9}1yzNZQ!XoNEJG5g0wsgK#yfa2| zP4PFQ64`&~Qa58EFjUc1fNLeoQVbm7p~3rs(1bQ$yt}V*5WVYy+y{EvEFASkjAAPk zMX;*Ei<@T)j^bPTP8*s6jVBi7^i=DCQHrKkZWEegAsRv#rc7bTAFy-Nl`(JK+Z%K2 ztUpy}4~eb&#y>I&UcPFMoOFv)R&s&-A3WhW+`=@!od6BackG?ya=j=p#%b5VG2<^l zzs0exB$qFhz5*M)4BzETCKEY{St!G>m}e;Lpq6Tdl_lb>ytDCvNxn@3 zyi>VSq%?xm+&elO-9R3f(ku%N=GJqCq8@rY0keN~2SGc)b}df%XhDCMXKwSk+6Soq zpILBEB6=Z=4_uEJ0Dw{SYT)sN@0|2L&9|GA~v5s=m%>W%2%@_-rHkTysh zRI~NQIWJ#s{tK@AC$~?kLh+ z=eY^=w=REV9Br}dvd-+p=H*8y`R8-s&6==F!$8|+B0Z~F=11sA>jQ7TUH@Xq5ofvC zmtW+3Cq2Ne8xKB8c$zPhXJshSoee|Bl%j8k{h)eMq^_PhI=DkvU_!{CvXx?*Tv!#h zvakY10-N{);whu=PA9q4Q4_cN6bRh?WlM*&z?yS*T271b$21RyF#|tK8!@Zadgm~K z3sc4Opfp(n!H8Pd21Q>T_+$5J`vAKQ50@W`uJ3%~HFoPe)UJCg@v;#CFt2szvqxF} zOg(LrF#XE_8^@Q~S60V}SB;aKhdS{6a@4b&J^}UMr@XHoS7<7?9MLqyjSQWnS5$*r zH6z32^Bsd6rf&Lw%)fBF5d4rM-qG%lhg=^TBvbLVoSb+<0M8#hxacGg9pplA3>1_h zo8>gd9T{vOdxF?Z>6?{mDSP=ylT{pM3{mx;rf26GsH}j)(o*cx8Q(|(Bp&mR@@Cc{ zy2Y_(%sN0;A9hA0G z0Ld{8P#Db62<&_Zs`8pS0J?BVs#^Vo+B9Qw{DrqSaR{9Nv*Vg{N)uXR?UzpFM}*C0 z5XHAwvmZLHm1cUsENNacU2F+ex^^n9Ui>K=zm?6SW!~*ZD~_r4t4TShA%M|7_3V$H zw;LHK{MQI@=>~xz-`d)m@cG7AfN1PqnWjWv8<-=mi;c%?kLsYV5uJ5B?OFpj7!V7d zHD-GDiVM|6l#wl2?|9f#sP4}+YAQb@(iaRVD{6mdA={iWnT0?r^~^a`?PYN%*K5>S zk-NF1{RBOYXff2zJN}n>6(o=*VC!M?%KP1*-SlMH962goP;KYd2u#?m;4_MTfDmDVYOHVd4Ksdma+C_jolV$J{*)M!NZsew69Gws>1T&0%u zC&yM6J!6cKIm|}KGarxH0hnKc!kp`y&D}+@(PsMn7~&M+)rIg-+BT{AgP^q4XQt~} za*!mGWL0`ZLJg0cRERV7rU$^*ipo)JBjqmA7x2R3UZB7Gh@vGF89uF`AoY_|E# zV6X3Mz$ya~p9r!vO?_A;L$1RAgzAiJ`bwiJt@mUQ`t#;Ft+s*r4V(L3Ej_Se>gJM#IL6vg=U7k%5bHPSs%=&kp zl_OKnfTKeN7ySxA9Wz8=4-3(`G5D`8%a@>TAKGg9if(BZG4x_^eWd=2Mjb{fB606p zXIg?ht>{H9ZHKsTBSBzFMsVcS&(XWPq+G$QqtTTf#h#G!LWSGQG17q#F;$>DVv z!U%3O_CIwb3Lzw#*o=clXO;`F6}U!+9?hkIiP&{;kKt;4&5{LgPj&QUjomK#>D`xl zEd4sTRLyCSJ?)EI_#jjdy`I)x#UQ<8$#H!%3dXend4>vJ&eS;rm;Z(G>qJx@Mi%mUO07vc$wPY4Zjvj|r#=G_35$`zqo`{t+q;D@6X62EL;rHj1R zXb3AP9WKKN>4u^wm!SAPi56>R2(4Qkcz7FuDMF)oEO-S!&^O%=MZzJA zqSd2oA=WtX43Tr9AGjKNbhXtck}e`l*K*`g5P70FfX@MUTjaWtMJ`N#KMjs{^ z+&riR&0Fu#e|$V#3LScUlvlCNMTtm^1ny$VZ!#lTeQE#tU~$~d7>`ezNw(V_4l-LS z8SlfWw>vyGZ_o5sUo4x}%OPB|2$Zy~Wl2pE8r>U;PiwSOVaj!T4pSaUj!WwgygWe% z#VgDsbKXGM0X0?znm;_EwdPunzP z9Kh5og5BL9qYRITUav0wOt9m3R51oqh@Cdz_Ui^qy)`c_`waIBE*8nJ@9o-yfYo2a z3HW)KF&dv}&ejW6oaq${xL>7}CJ^ax%l4=nf|bXIwjMSZp8;#30R2gU_jKnn-|g=H zXs93bMhiDDnS#c=Z1{PY@D&$<;rEVOgZ=5&;j55`6N6X#B=|)`zVE_PNB_i=v4Bxz z>CVT8UrrdkJbL>F*|T=^bABU~i$KjSu1A?qr}*n1R*{+_z~mi8^xdRGL15TpUH-f797H=D}fit7;E#rBl0SuuK9XXrb3PG#vl zw$FnHDbyOY0JG{8B2s8jbw+R=H@_*ygEZzK=i8xI|w^#bZ7*X6M; z>?ACW>-7_yZF&&butyIc483bDWWOz<3q{K=)E7Da*pzTX6uMD{JmZrn{pAh(&(ODt~4w#(Y-s|5F7zHpv;BZ23Z^5p}tqrfydZDbSpZ z8xib-uqR7My2(SwW=-X!^%LUn+7!Td3hwbnDUn=ZA=C7eW}A$c-}jL{?DYXeOpCfP zc{A1vX^-H!cb!(TSgxy@Gciv&RJ*fv5o3}kx(CS2gar$$AUy9;Xp}P;1LxJNa8ohx z@y1w^tC!}T0zyMDGF}UuzfH_P} zaEU}u3JeM`k)I0P@kc#i43PVCMQl=VgYwqnq7@-Q$a20|AV&iq%(|K=u%uG{_+sp= zvb3g1{8E_l%=C_O-+ICf!Pm!uyI*foiQhxB5#Q%3j{An{{yLtcup`Ipmf6ocVDy2~ zfKa>BpQ6OV;Q7?=P&~G&6W!#X=flq7S+p8dWA))ugjyk`(Rm|;&Hk(6dHlJ0M<#(8 zo&LwvB$jbnj54MH0|img_B;=;*`q zl}p4L!G23F{LE>xf5!L`&V=NvkN<9)wBN;C4aQpc zq`2@D|4KKOf52b9e?|H65N_pha6rkQ{N0E=?CaElze0EBs+Fzc{rqmx-icPxWL$pT zFprOK3H^WNOlrOJvZrc$LPu(x_{0b?`2D=`XYWTD>TLDwFA$8$-c zoH=Lhz1LoQ?eEMqQNc^&bbAkvZ|vZnz-ROE$|nA1m+9>b7uSLEH99v<`dQUTr-%hj znTDK+c;7zUR+|N@1{vvs=i6RrfScNDWxLNt{8q+csr@Yd*geMV`yL(?BYhr(-7``P za3`kyXB#H3V`9M^Py^jeCQtGpBFGlU6W#1JW+eU#dHhcjJ}P7@QLAoDBw}2$QfhHI z2X3HD1=1vn-Ds4V(Lhrg=C%;i68awxY&S`jm}n%1{dUNH+~t7qYU6sr`ZPle4!3UB^K*DV^^1b+zLkMFyffT=h*YMNtE&8qxm+ zd&XWpmNZRioD>;`I$hGDn&7nk&d4Ej z(EjHd=USG_W_u`~?gb^7``QSdj;UlM6qykkLqHJzr-`Hbt?iV-Qw}rFF;yA%1ytgs z#=o#zi%C1)9z$*TSc3gk-qOOsYa9yCw*pnO{8A+;>%)%kzxxMhfCbFvTXk*&RD{!9 zFLU^!@Hv2B3s%0?XfQ|^r~dstRum7M@?~=RBw8h6OEBi0#v8OYC2JbxT5Z+o+H^mj z^Js*QtINUCM$*!k>^R>eLNV%N`2ELvo>KlR>Y#r`&5c~?bQ*A)^Cr99y|&PM_l=28 zB;35J!Nr-eKMSODml5h0OYqHfSWui1tFyWu!Dc%O>!e*>oDyB8*aER66ddj%kJ)|m zp_g%twZTA@(zzv1Q78V-be~GMdSPhD=^NDi_?!>51d=cXoR68Me@(P|ACg&bLwz|p zSnShgv}@~W6;$SaK&}tdel>{$x*c;Tf){y`1KK_)%Wpjq4Brmk(r;x&-5}jCfAcNb%=dK#@x6q!^6D{(YH{b5F zvp&H`j;tkYgbMdU=cv(gj#A`8dxV<@jh#$cuDadzI7%M$=~{4LP^no;C!ZBJCakiB zqF*c6CdE=}+9I48+LEi`X9q#-tkQk#3LN+enSOtnn$g$JbWA^ke#idkxfIu%>nr#hR-I+EwYDle`JPex})+y^Q96VlX zI$+^L?w!qP)~vqu)EuY9@a5ncUcSyqQVr`-Yow+wAyV^Nd3wVn&)-|LC0X&4>N^za zv2W;F_>cBrlJ+IV1YSkGtjFc7=mEaeql>O)*Bn06SYl`Ui%oqY>GKempl8N-4KRjeCKVqoq<+%*G!)tx zy<4Vg&V!0PBK~@L)}@V0PA3jrTvBxII8oZ5*T+8;q`+Je%@C1P=}KVzWGp+|@$s>SnqzWp?*-KLM?*)V`ATDS zPxtkigAn>3w0?Y@l_x|Rdu5}+rVn&k?%y74SQqRT^ER0anGh3twrnb~Pktl5>VD*Q^CG54STN@vz$5^ zSc`wjH8O-~=&+HLcSMG)BThBlBHy0P+U1+~fQL9AX(VJL*V^>7VtM<|OQ1K6eutw$ z%^Cgvde~Ci)&8!CdxFqdO43A9+XI1l_i=RmuW;MQQpH}X_-w=eqB9MUJd3*2hQi1Z zWr=LvTMVT9|Jcay<$x&370+0s=2Y4T|6#i)e?*BW&rGb2wyHH; z(&Re&ALKo6#}?sziT2sBO!%~p(ap|}%k^UYuWwW7KkEE>P6s71gea;o5h-!S+lG(s z!)f(?f6}4+W0Q|N;oPuVzNbHZfoeH3fvB8$aScnbTRs-Qp4>$a7tjL2-G;bazdNa2 zU-o@_-{Y`4Lws+qPcS7qDE2gwU&YndA7?K0fkPrYTuQ_v=lvD9yuz9`2}u%MdNhFv z>YkBfei^?yM&0odIG@l88wz>d>FqFK6L3YjpQ2o+u`Mylr< zjAFzikQ{RyJ~_(C6pIwv!jF4sm?X}A!(`36X{w`WONy3%Y`G6rtNqf9+TvF>c_}Yx zu3(!K+#Z8L2IDq|=Go8)#G$~#t+5LvJ~s62Oz&m#K)Vm7vi(JClrU7%VvXoYhj8LQ zzVY;x4RjNq04X(u@Z4$6W#5Sfl?v8B^Z+sQK}uDs8sXjLe#@I&3}Ms04_Mzznd2f&G|8Y?=iJh<|?!Vt3Ci47Giw+QpUzHJdvLY zEW$rP4nFfY%O5hNe3|x&+>oI>-t_{}*EU(LHLN`>BeFMh?*Cxx{byHUZY*^difUKFF(3MW!0ZZd7d zce?&&CM$*R^w!Kf^IDS1ofi*xo@jD)Qo}3UN2}r~Ci5jIyo9mK_mK^&pY=t(d~Jmw zsAVvWwUK6;8J?q^{Id5AP^1_CAo%i{k9LbEESW{T^^Dg6K0Ar^k`PK<;uOEh*ug%0 zgo?@{UndUlVga<4<+%2yzby&(@Ii^&VuP_&%*Yf;BDE}>X|7s+4JYZ8@MB?4vv0o; z8B>vABgekIQgY)o=;9{|#wFTIea%HM)9e3%FLeL%HHDHcpAN`%`=Qz|9(x%#)ZwF4 ztUPmss{Yfj5Gg>l%me=WqMPA2e4I}RuP9MYYF=ojbEO50nicP-Nd`-joyRu|88%Ez z_nq)xQy4aX@?3}2{Fs{r)PX4R8crDXeR>3)K-Fg)a2MG7+);FEhdV(b;DRo>h%rCg z7z26f{dW&f$=C>M955}_*JC0+l=pxNM}KZYsND8T_+fd2!0^S^wbiQqH1(QOpU$L4 z$PmIf?cXUs(Uwlw-U>foW9TSEi1aDZK;Vf?<;gFgLJNGsa_Q;rk|uGHcI*Bu=6$Vx zM6cFUb#{L2y7EHFETw5{HC-hA1{|++rFmJRlf*?Tl88Vfd~e&F$%=w*jb|nH^&V+0 zi~g>pvj0RD@@jE0xYu2Ys#V0aQ4j9kfojHfSM2CF4b%s@RnDdOs8AIWo9ZRQrYfC9-PVzH<^?%u7 zH%IYnG~OGF?KoRLz7|G)zHi5Utt26QK=R&8s^jw7wlU^)6mDn*H+(GfkX1<~T;vG9 z?x!pQ$)(t6ETGuej{EgS9eS%_@d-9jCC1rU_bh96a8Ks%iyq#Ms%k@c31)l@<5dDW z%gN*(NyP^^kReRQKM%g)rqA}``waakqz=LkSoH-Zp@rMLVw6&mXQ`Q5X?zVlt~c6q zf51WUvxtz7FEi&_K_)Xa^A>Xx7X3Yrg{}%FNrBv$UyvVo!oJT}U?IVRVa;ebUYJ@nq|^)i{%@H>ye59Gm)@^MXFxMvc$<l(-z+IZG%6xBq%y=PIB2lH0O<+KdqMQ8B)7j-r9N?T%QPFaB!I8o5-M6El2yP)Kgo{JNFD?Ofjz!l@$Qe>YR>Pi zybTlbNH(O5_0qTQJwjSzBUAF+GT1aJ%YjB*ry0MDB-{-m00QDq9_$<&=F?J7;mRFGf?Ry-gY|uY+8^aUE*I;<=B1EHM67x>T zM{E_=A@OJ0{w4J0@Fp@w&bFy%Za~9VI!-hLPF{j>jOx%4)m)W6q@>Re;Ss&?91HWg z!dDjaPWOsyc^zSyuW<%}l$0hjLw8J#w zjT`VCSC{>gWb~(DFSeI8;;(xaWdA~sCtD!os1MIm1(MMl6h|4ydM{W1{j_v7BThJs z#NIDk3s`zu%Y?GCuqdymD@D^BNh^iMplTuta|#g>icxJGIFjK_%s9$CS@xNE#<0L+s-;Ul~zz2M9cA9EA~ z(Fn4WEk9^GQdqRh)KM8VQkLr+C09b|0PLSxTp%*>&<@v&H;50qE!}qGCDlJlA9R&5 zy0A=L)c8xa{@$9Y2V00xp0s;=cGf)zr2*-ZdGzP;i`qtZ_KOLMdb;P9h54@!->yuHuI!LZy#-XrO9!Rboqpr_R70bwXE>f zZA-N}tZVdh^xA%OA^9JFe3M}&@QsoLYryh2{k%hK@%&NSta2%o?it;~P-mt=l^Zf0Mdg))Fuz?_5zqGETdy3ec z^%N%PvXOU)izfa6c21ac#}1(P)cWme&}^7;5h;SG$ES-~VPBasi77%FxTMVL{dh`v z`{1-bb8CL^;w?+h??lhk#dxZ+nLvgqEFnU3H&!;%F(rw1*AQQT|Hu}#_3wIb!f3V? zHb6idEC1(LNYVio81foZXyM0!)K?j$#m9P6Wfo_}w7Uw#<0W%I9#an4r$^BqPm!LF zgrTEi+J_|(7i`xP84(+Kx!z$!8|^1G z`a_A#)s-;&K$28iNnCe@0qu)h6yE@4#V+_?y8fn_n85^{)FdYxc-6f7Ht2c<51$Z1 z`kH}*?!BK8zOcN%RXSbJ(^8ujNoxU|B)^v1T9)Ri&wH8hxvE5h9BleboUxd zOc$ew;6+=%HiC~H&|&TOvFhH4Mnym53X&ZnDbXhxALGaryU)n&oktYC0t$xlzZ(bx zI&)g!x{0OeBoT^8DaG#^K`I3-Rh}_S#pvirbkuaX;-s=^k{4C*aw(z?33k8;q$JRL z&^P4tNI0%{tLE@Ey&b>)j0KrO1!_6J^(SVNZcWr;5qjd{g-RgU!TfE{Xy$J%_6aIe z~20L=sD9&gBSWw=3*?)74 z#=pE@1Bnoz6|?rbR#;&`Dc;!)%@u|ML^&aUhz`Wx&i~o33d#V~Lv%oo)=+bIq5%3L z0fi7VJ}5kOBQf>;=k$hgWnAt~N0=rbkM?16!5V^imsnOy{OjSyd?RM sQgWcv74OPV!RE<2_QI^h-aOg^ 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