Skip to content

Commit

Permalink
Add shared user Icons to mobile UI
Browse files Browse the repository at this point in the history
Add settings option to show user icons on timeline

Hide icons from partner image grid

Replace with translatable text

Remove partner sharing references in shared albums

Minor improvements to phrasing in detail view
  • Loading branch information
x24git committed Oct 16, 2024
1 parent 240de12 commit 96d3706
Show file tree
Hide file tree
Showing 18 changed files with 336 additions and 27 deletions.
10 changes: 9 additions & 1 deletion mobile/assets/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@
"partner_page_title": "Partner",
"partners": "Partners",
"people": "People",
"partner_sharing_dialog_title": "Partner Sharing",
"permission_onboarding_back": "Back",
"permission_onboarding_continue_anyway": "Continue anyway",
"permission_onboarding_get_started": "Get started",
Expand Down Expand Up @@ -574,11 +575,16 @@
"sync_albums": "Sync albums",
"sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums",
"sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich",
"sharing_silver_appbar_shared_links": "Shared links",
"storage_asset_local": "On Device (Not backed up)",
"storage_asset_remote": "Immich Server only",
"storage_asset_merged": "Backed up to Immich Server",
"storage_asset_partner": "From Partner Sharing",
"tab_controller_nav_library": "Library",
"tab_controller_nav_photos": "Photos",
"tab_controller_nav_search": "Search",
"tab_controller_nav_sharing": "Sharing",
"theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles",
"theme_setting_asset_list_storage_indicator_title": "Show storage indicator",
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
"theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.",
"theme_setting_colorful_interface_title": "Colorful interface",
Expand All @@ -593,6 +599,8 @@
"theme_setting_theme_title": "Theme",
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
"timeline_settings_title": "Tile Options",
"timeline_show_partner_switch": "Show user thumbnails",
"translated_text_options": "Options",
"trash": "Trash",
"trash_emptied": "Emptied trash",
Expand Down
1 change: 1 addition & 0 deletions mobile/lib/entities/store.entity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ enum StoreKey<T> {
logLevel<int>(115, type: int),
preferRemoteImage<bool>(116, type: bool),
loopVideo<bool>(117, type: bool),
showPartnerIconInTimeline<bool>(118, type: bool),
// map related settings
mapShowFavoriteOnly<bool>(118, type: bool),
mapRelativeDate<int>(119, type: int),
Expand Down
1 change: 1 addition & 0 deletions mobile/lib/pages/library/partner/partner_detail.page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class PartnerDetailPage extends HookConsumerWidget {
onRefresh: () => ref.read(assetProvider.notifier).getAllAsset(),
deleteEnabled: false,
favoriteEnabled: false,
showUserThumbnail: false,
),
);
}
Expand Down
5 changes: 5 additions & 0 deletions mobile/lib/services/app_settings.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ enum AppSettingsEnum<T> {
mapRelativeDate<int>(StoreKey.mapRelativeDate, null, 0),
allowSelfSignedSSLCert<bool>(StoreKey.selfSignedCert, null, false),
ignoreIcloudAssets<bool>(StoreKey.ignoreIcloudAssets, null, false),
showPartnerIconInTimeline<bool>(
StoreKey.showPartnerIconInTimeline,
null,
true,
),
selectedAlbumSortReverse<bool>(
StoreKey.selectedAlbumSortReverse,
null,
Expand Down
19 changes: 18 additions & 1 deletion mobile/lib/services/user.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class UserService {
final IUserRepository _userRepository;
final SyncService _syncService;
final Logger _log = Logger("UserService");
final Map<Id, User> inMemoryUserMap = {};

UserService(
this._partnerApiRepository,
Expand Down Expand Up @@ -100,6 +101,22 @@ class UserService {
Future<bool> refreshUsers() async {
final users = await getUsersFromServer();
if (users == null) return false;
return _syncService.syncUsersFromServer(users);
final isSyncSuccess = await _syncService.syncUsersFromServer(users);
if (isSyncSuccess) {
inMemoryUserMap.clear();
final users = await getUsersInDb(self: true);
for (var user in users) {
inMemoryUserMap[user.isarId] = user;
}
}
return isSyncSuccess;
}

User? lookupUserById(Id userId) {
if (inMemoryUserMap.containsKey(userId)) {
return inMemoryUserMap[userId];
} else {
return null;
}
}
}
12 changes: 12 additions & 0 deletions mobile/lib/utils/storage_indicator.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/entities/asset.entity.dart';

Expand All @@ -12,3 +13,14 @@ IconData storageIcon(Asset asset) {
return Icons.cloud_done_outlined;
}
}

String storageText(Asset asset) {
switch (asset.storage) {
case AssetState.local:
return "storage_asset_local".tr();
case AssetState.remote:
return "storage_asset_remote".tr();
case AssetState.merged:
return "storage_asset_merged".tr();
}
}
4 changes: 4 additions & 0 deletions mobile/lib/widgets/asset_grid/immich_asset_grid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
final int? assetsPerRow;
final double margin;
final bool? showStorageIndicator;
final bool? showUserThumbnail;
final ImmichAssetGridSelectionListener? listener;
final bool selectionActive;
final List<Asset>? assets;
Expand All @@ -41,6 +42,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
this.renderList,
this.assetsPerRow,
this.showStorageIndicator,
this.showUserThumbnail,
this.listener,
this.margin = 2.0,
this.selectionActive = false,
Expand Down Expand Up @@ -105,6 +107,8 @@ class ImmichAssetGrid extends HookConsumerWidget {
listener: listener,
showStorageIndicator: showStorageIndicator ??
settings.getSetting(AppSettingsEnum.storageIndicator),
showUserThumbnail: showUserThumbnail ??
settings.getSetting(AppSettingsEnum.showPartnerIconInTimeline),
renderList: renderList,
margin: margin,
selectionActive: selectionActive,
Expand Down
9 changes: 9 additions & 0 deletions mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class ImmichAssetGridView extends ConsumerStatefulWidget {
final int assetsPerRow;
final double margin;
final bool showStorageIndicator;
final bool showUserThumbnail;
final ImmichAssetGridSelectionListener? listener;
final bool selectionActive;
final Future<void> Function()? onRefresh;
Expand All @@ -61,6 +62,7 @@ class ImmichAssetGridView extends ConsumerStatefulWidget {
required this.renderList,
required this.assetsPerRow,
required this.showStorageIndicator,
required this.showUserThumbnail,
this.listener,
this.margin = 5.0,
this.selectionActive = false,
Expand Down Expand Up @@ -187,6 +189,7 @@ class ImmichAssetGridViewState extends ConsumerState<ImmichAssetGridView> {
final section = widget.renderList.elements[index];
return _Section(
showStorageIndicator: widget.showStorageIndicator,
showUserThumbnail: widget.showUserThumbnail,
selectedAssets: _selectedAssets,
selectionActive: widget.selectionActive,
sectionIndex: index,
Expand Down Expand Up @@ -601,6 +604,7 @@ class _Section extends StatelessWidget {
final bool showStack;
final int heroOffset;
final bool showStorageIndicator;
final bool showUserThumbnail;

const _Section({
required this.section,
Expand All @@ -618,6 +622,7 @@ class _Section extends StatelessWidget {
required this.showStack,
required this.heroOffset,
required this.showStorageIndicator,
required this.showUserThumbnail,
});

@override
Expand Down Expand Up @@ -680,6 +685,7 @@ class _Section extends StatelessWidget {
showStack: showStack,
heroOffset: heroOffset,
showStorageIndicator: showStorageIndicator,
showUserThumbnail: showUserThumbnail,
selectionActive: selectionActive,
onSelect: (asset) => selectAssets([asset]),
onDeselect: (asset) => deselectAssets([asset]),
Expand Down Expand Up @@ -763,6 +769,7 @@ class _AssetRow extends StatelessWidget {
final RenderList renderList;
final bool selectionActive;
final bool showStorageIndicator;
final bool showUserThumbnail;
final int heroOffset;
final bool showStack;
final Function(Asset)? onSelect;
Expand All @@ -782,6 +789,7 @@ class _AssetRow extends StatelessWidget {
required this.renderList,
required this.selectionActive,
required this.showStorageIndicator,
required this.showUserThumbnail,
required this.heroOffset,
required this.showStack,
required this.isSelectionActive,
Expand Down Expand Up @@ -859,6 +867,7 @@ class _AssetRow extends StatelessWidget {
asset: asset,
multiselectEnabled: selectionActive,
isSelected: isSelectionActive && selectedAssets.contains(asset),
showUserThumbnail: showUserThumbnail,
showStorageIndicator: showStorageIndicator,
heroOffset: heroOffset,
showStack: showStack,
Expand Down
3 changes: 3 additions & 0 deletions mobile/lib/widgets/asset_grid/multiselect_grid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class MultiselectGrid extends HookConsumerWidget {
this.buildLoadingIndicator,
this.onRemoveFromAlbum,
this.topWidget,
this.showUserThumbnail = true,
this.stackEnabled = false,
this.archiveEnabled = false,
this.deleteEnabled = true,
Expand All @@ -51,6 +52,7 @@ class MultiselectGrid extends HookConsumerWidget {
final Future<bool> Function(Iterable<Asset>)? onRemoveFromAlbum;
final Widget? topWidget;
final bool stackEnabled;
final bool showUserThumbnail;
final bool archiveEnabled;
final bool unarchive;
final bool deleteEnabled;
Expand Down Expand Up @@ -406,6 +408,7 @@ class MultiselectGrid extends HookConsumerWidget {
: ImmichAssetGrid(
renderList: data,
listener: selectionListener,
showUserThumbnail: showUserThumbnail,
selectionActive: selectionEnabledHook.value,
onRefresh: onRefresh == null
? null
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/widgets/asset_grid/thumbnail_image.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/services/user.service.dart';
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
import 'package:immich_mobile/utils/storage_indicator.dart';
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';

class ThumbnailImage extends ConsumerWidget {
/// The asset to show the thumbnail image for
Expand All @@ -14,6 +17,9 @@ class ThumbnailImage extends ConsumerWidget {
/// Whether to show the storage indicator icont over the image or not
final bool showStorageIndicator;

/// Whether to show the user thumbnail for partner assets over the image or not
final bool showUserThumbnail;

/// Whether to show the show stack icon over the image or not
final bool showStack;

Expand All @@ -33,6 +39,7 @@ class ThumbnailImage extends ConsumerWidget {
super.key,
required this.asset,
this.showStorageIndicator = true,
this.showUserThumbnail = true,
this.showStack = false,
this.isSelected = false,
this.multiselectEnabled = false,
Expand All @@ -47,6 +54,7 @@ class ThumbnailImage extends ConsumerWidget {
: context.primaryColor.lighten(amount: 0.8);
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
final isFromDto = asset.id == noDbId;
final userService = ref.watch(userServiceProvider);

Widget buildSelectionIcon(Asset asset) {
if (isSelected) {
Expand Down Expand Up @@ -207,6 +215,19 @@ class ThumbnailImage extends ConsumerWidget {
size: 18,
),
),
// Not possible to favorite photos belonging to other users, so reuse lower left corner for partner images
if (asset.ownerId != Store.get(StoreKey.currentUser).isarId &&
showUserThumbnail)
Positioned(
left: 8,
bottom: 5,
child: UserCircleAvatar(
user: userService.lookupUserById(asset.ownerId),
radius: 8,
size: 18,
).build(context),
// userAvatar(context, Store.get(StoreKey.currentUser), radius: 8),
),
if (!asset.isImage) buildVideoIcon(),
if (asset.stackCount > 0) buildStackIcon(),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/asset.provider.dart';
import 'package:immich_mobile/widgets/asset_viewer/detail_panel/asset_state_details.dart';
import 'package:immich_mobile/widgets/asset_viewer/detail_panel/file_info.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/exif_info.entity.dart';
Expand Down Expand Up @@ -37,6 +38,7 @@ class AssetDetails extends ConsumerWidget {
).tr(),
FileInfo(asset: asset),
if (exifInfo?.make != null) CameraInfo(exifInfo: exifInfo!),
AssetStateInfo(asset: asset),
],
),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/entities/user.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/album/current_album.provider.dart';
import 'package:immich_mobile/services/user.service.dart';
import 'package:immich_mobile/utils/storage_indicator.dart';
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';

class AssetStateInfo extends ConsumerWidget {
final Asset asset;

const AssetStateInfo({
super.key,
required this.asset,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
final textColor = context.isDarkTheme ? Colors.white : Colors.black;
final userService = ref.watch(userServiceProvider);
final isInAlbum = ref.watch(currentAlbumProvider)?.isRemote ?? false;
final User? user = (asset.ownerId == Store.get(StoreKey.currentUser).isarId)
? null
: userService.lookupUserById(asset.ownerId);

return ListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
leading: (user == null)
? Icon(
storageIcon(asset),
color: textColor.withAlpha(200),
)
: UserCircleAvatar(
user: user,
radius: 12,
size: 30,
).build(context),
title: Text(
(user == null)
? storageText(asset)
: isInAlbum
? "album_thumbnail_shared_by".tr(args: [user.name])
: user.name,
style: context.textTheme.labelLarge,
),
subtitle: (user == null || isInAlbum)
? null
: Text(
"storage_asset_partner".tr(),
style: context.textTheme.bodySmall,
),
);
}
}
Loading

0 comments on commit 96d3706

Please sign in to comment.