chore: bump line length to 120 (#20191)
This commit is contained in:
@@ -21,8 +21,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final AsyncValue<List<UserDto>> suggestedShareUsers =
|
||||
ref.watch(otherUsersProvider);
|
||||
final AsyncValue<List<UserDto>> suggestedShareUsers = ref.watch(otherUsersProvider);
|
||||
final sharedUsersList = useState<Set<UserDto>>({});
|
||||
|
||||
addNewUsersHandler() {
|
||||
@@ -138,8 +137,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed:
|
||||
sharedUsersList.value.isEmpty ? null : addNewUsersHandler,
|
||||
onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler,
|
||||
child: const Text(
|
||||
"add",
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
|
||||
@@ -65,10 +65,8 @@ class AlbumAssetSelectionPage extends HookConsumerWidget {
|
||||
if (selected.value.isNotEmpty || canDeselect)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
var payload =
|
||||
AssetSelectionPageResult(selectedAssets: selected.value);
|
||||
AutoRouter.of(context)
|
||||
.popForced<AssetSelectionPageResult>(payload);
|
||||
var payload = AssetSelectionPageResult(selectedAssets: selected.value);
|
||||
AutoRouter.of(context).popForced<AssetSelectionPageResult>(payload);
|
||||
},
|
||||
child: Text(
|
||||
canDeselect ? "done" : "add",
|
||||
|
||||
@@ -42,16 +42,12 @@ class AlbumDateRange extends ConsumerWidget {
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
String _getDateRangeText(DateTime startDate, DateTime endDate) {
|
||||
if (startDate.day == endDate.day &&
|
||||
startDate.month == endDate.month &&
|
||||
startDate.year == endDate.year) {
|
||||
if (startDate.day == endDate.day && startDate.month == endDate.month && startDate.year == endDate.year) {
|
||||
return DateFormat.yMMMd().format(startDate);
|
||||
}
|
||||
|
||||
final String startDateText = (startDate.year == endDate.year
|
||||
? DateFormat.MMMd()
|
||||
: DateFormat.yMMMd())
|
||||
.format(startDate);
|
||||
final String startDateText =
|
||||
(startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format(startDate);
|
||||
final String endDateText = DateFormat.yMMMd().format(endDate);
|
||||
return "$startDateText - $endDateText";
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||
as entity;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity;
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||
@@ -28,8 +27,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
final sharedUsers =
|
||||
useState(album.sharedUsers.map((u) => u.toDto()).toList());
|
||||
final sharedUsers = useState(album.sharedUsers.map((u) => u.toDto()).toList());
|
||||
final owner = album.owner.value;
|
||||
final userId = ref.watch(authProvider).userId;
|
||||
final activityEnabled = useState(album.activityEnabled);
|
||||
@@ -50,8 +48,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
isProcessing.value = true;
|
||||
|
||||
try {
|
||||
final isSuccess =
|
||||
await ref.read(albumProvider.notifier).leaveAlbum(album);
|
||||
final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album);
|
||||
|
||||
if (isSuccess) {
|
||||
context.navigateTo(
|
||||
@@ -99,8 +96,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
actions = [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.person_remove_rounded),
|
||||
title: const Text("shared_album_section_people_action_remove_user")
|
||||
.tr(),
|
||||
title: const Text("shared_album_section_people_action_remove_user").tr(),
|
||||
onTap: () => removeUserFromAlbum(user),
|
||||
),
|
||||
];
|
||||
@@ -126,9 +122,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
|
||||
buildOwnerInfo() {
|
||||
return ListTile(
|
||||
leading: owner != null
|
||||
? UserCircleAvatar(user: owner.toDto())
|
||||
: const SizedBox(),
|
||||
leading: owner != null ? UserCircleAvatar(user: owner.toDto()) : const SizedBox(),
|
||||
title: Text(
|
||||
album.owner.value?.name ?? "",
|
||||
style: const TextStyle(
|
||||
@@ -170,12 +164,8 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
),
|
||||
trailing: userId == user.id || isOwner
|
||||
? const Icon(Icons.more_horiz_rounded)
|
||||
: const SizedBox(),
|
||||
onTap: userId == user.id || isOwner
|
||||
? () => handleUserClick(user)
|
||||
: null,
|
||||
trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(),
|
||||
onTap: userId == user.id || isOwner ? () => handleUserClick(user) : null,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -204,20 +194,15 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
value: activityEnabled.value,
|
||||
onChanged: (bool value) async {
|
||||
activityEnabled.value = value;
|
||||
if (await ref
|
||||
.read(albumProvider.notifier)
|
||||
.setActivitystatus(album, value)) {
|
||||
if (await ref.read(albumProvider.notifier).setActivitystatus(album, value)) {
|
||||
album.activityEnabled = value;
|
||||
}
|
||||
},
|
||||
activeColor: activityEnabled.value
|
||||
? context.primaryColor
|
||||
: context.themeData.disabledColor,
|
||||
activeColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor,
|
||||
dense: true,
|
||||
title: Text(
|
||||
"comments_and_likes",
|
||||
style: context.textTheme.titleMedium
|
||||
?.copyWith(fontWeight: FontWeight.w500),
|
||||
style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
||||
).tr(),
|
||||
subtitle: Text(
|
||||
"let_others_respond",
|
||||
|
||||
@@ -48,8 +48,7 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
);
|
||||
|
||||
Future<bool> onRemoveFromAlbumPressed(Iterable<Asset> assets) async {
|
||||
final bool isSuccess =
|
||||
await ref.read(albumProvider.notifier).removeAsset(album, assets);
|
||||
final bool isSuccess = await ref.read(albumProvider.notifier).removeAsset(album, assets);
|
||||
|
||||
if (!isSuccess) {
|
||||
ImmichToast.show(
|
||||
@@ -65,8 +64,7 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
/// Find out if the assets in album exist on the device
|
||||
/// If they exist, add to selected asset state to show they are already selected.
|
||||
void onAddPhotosPressed() async {
|
||||
AssetSelectionPageResult? returnPayload =
|
||||
await context.pushRoute<AssetSelectionPageResult?>(
|
||||
AssetSelectionPageResult? returnPayload = await context.pushRoute<AssetSelectionPageResult?>(
|
||||
AlbumAssetSelectionRoute(
|
||||
existingAssets: album.assets,
|
||||
canDeselect: false,
|
||||
@@ -77,9 +75,7 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
// Check if there is new assets add
|
||||
isProcessing.value = true;
|
||||
|
||||
await ref
|
||||
.watch(albumProvider.notifier)
|
||||
.addAssets(album, returnPayload.selectedAssets);
|
||||
await ref.watch(albumProvider.notifier).addAssets(album, returnPayload.selectedAssets);
|
||||
|
||||
isProcessing.value = false;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@ class AlbumsPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final albums =
|
||||
ref.watch(albumProvider).where((album) => album.isRemote).toList();
|
||||
final albums = ref.watch(albumProvider).where((album) => album.isRemote).toList();
|
||||
final albumSortOption = ref.watch(albumSortByOptionsProvider);
|
||||
final albumSortIsReverse = ref.watch(albumSortOrderProvider);
|
||||
final sorted = albumSortOption.sortFn(albums, albumSortIsReverse);
|
||||
@@ -131,8 +130,7 @@ class AlbumsPage extends HookConsumerWidget {
|
||||
)
|
||||
: null,
|
||||
controller: searchController,
|
||||
onChanged: (_) =>
|
||||
onSearch(searchController.text, filterMode.value),
|
||||
onChanged: (_) => onSearch(searchController.text, filterMode.value),
|
||||
focusNode: searchFocusNode,
|
||||
onTapOutside: (_) => searchFocusNode.unfocus(),
|
||||
),
|
||||
@@ -180,9 +178,7 @@ class AlbumsPage extends HookConsumerWidget {
|
||||
const SortButton(),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
isGrid.value
|
||||
? Icons.view_list_outlined
|
||||
: Icons.grid_view_outlined,
|
||||
isGrid.value ? Icons.view_list_outlined : Icons.grid_view_outlined,
|
||||
size: 24,
|
||||
),
|
||||
onPressed: toggleViewMode,
|
||||
@@ -196,8 +192,7 @@ class AlbumsPage extends HookConsumerWidget {
|
||||
? GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 250,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
@@ -244,10 +239,8 @@ class AlbumsPage extends HookConsumerWidget {
|
||||
},
|
||||
) : 'owned'.t(context: context)}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style:
|
||||
context.textTheme.bodyMedium?.copyWith(
|
||||
color: context
|
||||
.colorScheme.onSurfaceSecondary,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
@@ -316,9 +309,7 @@ class QuickFilterButton extends StatelessWidget {
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: isSelected
|
||||
? context.colorScheme.onPrimary
|
||||
: context.colorScheme.onSurface,
|
||||
color: isSelected ? context.colorScheme.onPrimary : context.colorScheme.onSurface,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
@@ -356,28 +347,22 @@ class SortButton extends ConsumerWidget {
|
||||
? albumSortIsReverse
|
||||
? Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
color: albumSortOption == mode
|
||||
? context.colorScheme.onPrimary
|
||||
: context.colorScheme.onSurface,
|
||||
color:
|
||||
albumSortOption == mode ? context.colorScheme.onPrimary : context.colorScheme.onSurface,
|
||||
)
|
||||
: Icon(
|
||||
Icons.keyboard_arrow_up_rounded,
|
||||
color: albumSortOption == mode
|
||||
? context.colorScheme.onPrimary
|
||||
: context.colorScheme.onSurface,
|
||||
color:
|
||||
albumSortOption == mode ? context.colorScheme.onPrimary : context.colorScheme.onSurface,
|
||||
)
|
||||
: const Icon(Icons.abc, color: Colors.transparent),
|
||||
onPressed: () {
|
||||
final selected = albumSortOption == mode;
|
||||
// Switch direction
|
||||
if (selected) {
|
||||
ref
|
||||
.read(albumSortOrderProvider.notifier)
|
||||
.changeSortDirection(!albumSortIsReverse);
|
||||
ref.read(albumSortOrderProvider.notifier).changeSortDirection(!albumSortIsReverse);
|
||||
} else {
|
||||
ref
|
||||
.read(albumSortByOptionsProvider.notifier)
|
||||
.changeSortMode(mode);
|
||||
ref.read(albumSortByOptionsProvider.notifier).changeSortMode(mode);
|
||||
}
|
||||
},
|
||||
style: ButtonStyle(
|
||||
@@ -385,9 +370,7 @@ class SortButton extends ConsumerWidget {
|
||||
const EdgeInsets.fromLTRB(16, 16, 32, 16),
|
||||
),
|
||||
backgroundColor: WidgetStateProperty.all(
|
||||
albumSortOption == mode
|
||||
? context.colorScheme.primary
|
||||
: Colors.transparent,
|
||||
albumSortOption == mode ? context.colorScheme.primary : Colors.transparent,
|
||||
),
|
||||
shape: WidgetStateProperty.all(
|
||||
const RoundedRectangleBorder(
|
||||
|
||||
@@ -19,9 +19,7 @@ class AlbumPreviewPage extends HookConsumerWidget {
|
||||
final assets = useState<List<Asset>>([]);
|
||||
|
||||
getAssetsInAlbum() async {
|
||||
assets.value = await ref
|
||||
.read(albumMediaRepositoryProvider)
|
||||
.getAssets(album.localId!);
|
||||
assets.value = await ref.read(albumMediaRepositoryProvider).getAssets(album.localId!);
|
||||
}
|
||||
|
||||
useEffect(
|
||||
|
||||
@@ -19,8 +19,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums;
|
||||
final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums;
|
||||
final enableSyncUploadAlbum =
|
||||
useAppSettingsState(AppSettingsEnum.syncAlbums);
|
||||
final enableSyncUploadAlbum = useAppSettingsState(AppSettingsEnum.syncAlbums);
|
||||
final isDarkTheme = context.isDarkTheme;
|
||||
final albums = ref.watch(backupProvider).availableAlbums;
|
||||
|
||||
@@ -85,8 +84,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||
|
||||
buildSelectedAlbumNameChip() {
|
||||
return selectedBackupAlbums.map((album) {
|
||||
void removeSelection() =>
|
||||
ref.read(backupProvider.notifier).removeAlbumForBackup(album);
|
||||
void removeSelection() => ref.read(backupProvider.notifier).removeAlbumForBackup(album);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
@@ -117,9 +115,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||
buildExcludedAlbumNameChip() {
|
||||
return excludedBackupAlbums.map((album) {
|
||||
void removeSelection() {
|
||||
ref
|
||||
.watch(backupProvider.notifier)
|
||||
.removeExcludedAlbumForBackup(album);
|
||||
ref.watch(backupProvider.notifier).removeExcludedAlbumForBackup(album);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
@@ -215,11 +211,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||
title: Text(
|
||||
"backup_album_selection_page_albums_device".tr(
|
||||
namedArgs: {
|
||||
'count': ref
|
||||
.watch(backupProvider)
|
||||
.availableAlbums
|
||||
.length
|
||||
.toString(),
|
||||
'count': ref.watch(backupProvider).availableAlbums.length.toString(),
|
||||
},
|
||||
),
|
||||
style: context.textTheme.titleSmall,
|
||||
|
||||
@@ -30,11 +30,8 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
final hasAnyAlbum = backupState.selectedBackupAlbums.isNotEmpty;
|
||||
final didGetBackupInfo = useState(false);
|
||||
|
||||
bool hasExclusiveAccess =
|
||||
backupState.backupProgress != BackUpProgressEnum.inBackground;
|
||||
bool shouldBackup = backupState.allUniqueAssets.length -
|
||||
backupState.selectedAlbumsBackupAssetsIds.length ==
|
||||
0 ||
|
||||
bool hasExclusiveAccess = backupState.backupProgress != BackUpProgressEnum.inBackground;
|
||||
bool shouldBackup = backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length == 0 ||
|
||||
!hasExclusiveAccess
|
||||
? false
|
||||
: true;
|
||||
@@ -48,9 +45,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
ref.watch(iOSBackgroundSettingsProvider.notifier).refresh();
|
||||
}
|
||||
|
||||
ref
|
||||
.watch(websocketProvider.notifier)
|
||||
.stopListenToEvent('on_upload_success');
|
||||
ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success');
|
||||
|
||||
return () {
|
||||
WakelockPlus.disable();
|
||||
@@ -61,8 +56,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
if (backupState.backupProgress == BackUpProgressEnum.idle &&
|
||||
!didGetBackupInfo.value) {
|
||||
if (backupState.backupProgress == BackUpProgressEnum.idle && !didGetBackupInfo.value) {
|
||||
ref.watch(backupProvider.notifier).getBackupInfo();
|
||||
didGetBackupInfo.value = true;
|
||||
}
|
||||
@@ -183,9 +177,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
onPressed: () async {
|
||||
await context.pushRoute(const BackupAlbumSelectionRoute());
|
||||
// waited until returning from selection
|
||||
await ref
|
||||
.read(backupProvider.notifier)
|
||||
.backupAlbumSelectionDone();
|
||||
await ref.read(backupProvider.notifier).backupAlbumSelectionDone();
|
||||
// waited until backup albums are stored in DB
|
||||
ref.read(albumProvider.notifier).refreshDeviceAlbums();
|
||||
},
|
||||
@@ -203,8 +195,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
|
||||
void startBackup() {
|
||||
ref.watch(errorBackupListProvider.notifier).empty();
|
||||
if (ref.watch(backupProvider).backupProgress !=
|
||||
BackUpProgressEnum.inBackground) {
|
||||
if (ref.watch(backupProvider).backupProgress != BackUpProgressEnum.inBackground) {
|
||||
ref.watch(backupProvider.notifier).startBackupProcess();
|
||||
}
|
||||
}
|
||||
@@ -216,8 +207,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
),
|
||||
child: Container(
|
||||
child: backupState.backupProgress == BackUpProgressEnum.inProgress ||
|
||||
backupState.backupProgress ==
|
||||
BackUpProgressEnum.manualInProgress
|
||||
backupState.backupProgress == BackUpProgressEnum.manualInProgress
|
||||
? ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.grey[50],
|
||||
@@ -225,8 +215,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
// padding: const EdgeInsets.all(14),
|
||||
),
|
||||
onPressed: () {
|
||||
if (backupState.backupProgress ==
|
||||
BackUpProgressEnum.manualInProgress) {
|
||||
if (backupState.backupProgress == BackUpProgressEnum.manualInProgress) {
|
||||
ref.read(manualUploadProvider.notifier).cancelBackup();
|
||||
} else {
|
||||
ref.read(backupProvider.notifier).cancelBackup();
|
||||
|
||||
@@ -39,9 +39,7 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.getBackupStatus(currentUser.id);
|
||||
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
||||
await ref.read(driftBackupProvider.notifier).backup(currentUser.id);
|
||||
}
|
||||
|
||||
@@ -224,9 +222,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget {
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.getBackupStatus(currentUser.id);
|
||||
ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
||||
},
|
||||
child: const Text(
|
||||
"select",
|
||||
@@ -245,8 +241,7 @@ class _TotalCard extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final totalCount =
|
||||
ref.watch(driftBackupProvider.select((p) => p.totalCount));
|
||||
final totalCount = ref.watch(driftBackupProvider.select((p) => p.totalCount));
|
||||
|
||||
return BackupInfoCard(
|
||||
title: "total".tr(),
|
||||
@@ -261,8 +256,7 @@ class _BackupCard extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final backupCount =
|
||||
ref.watch(driftBackupProvider.select((p) => p.backupCount));
|
||||
final backupCount = ref.watch(driftBackupProvider.select((p) => p.backupCount));
|
||||
|
||||
return BackupInfoCard(
|
||||
title: "backup_controller_page_backup".tr(),
|
||||
@@ -277,8 +271,7 @@ class _RemainderCard extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final remainderCount =
|
||||
ref.watch(driftBackupProvider.select((p) => p.remainderCount));
|
||||
final remainderCount = ref.watch(driftBackupProvider.select((p) => p.remainderCount));
|
||||
return BackupInfoCard(
|
||||
title: "backup_controller_page_remainder".tr(),
|
||||
subtitle: "backup_controller_page_remainder_sub".tr(),
|
||||
|
||||
@@ -21,12 +21,10 @@ class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget {
|
||||
const DriftBackupAlbumSelectionPage({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<DriftBackupAlbumSelectionPage> createState() =>
|
||||
_DriftBackupAlbumSelectionPageState();
|
||||
ConsumerState<DriftBackupAlbumSelectionPage> createState() => _DriftBackupAlbumSelectionPageState();
|
||||
}
|
||||
|
||||
class _DriftBackupAlbumSelectionPageState
|
||||
extends ConsumerState<DriftBackupAlbumSelectionPage> {
|
||||
class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbumSelectionPage> {
|
||||
String _searchQuery = '';
|
||||
bool _isSearchMode = false;
|
||||
int _initialTotalAssetCount = 0;
|
||||
@@ -42,13 +40,10 @@ class _DriftBackupAlbumSelectionPageState
|
||||
_searchController = TextEditingController();
|
||||
_searchFocusNode = FocusNode();
|
||||
|
||||
_enableSyncUploadAlbum.value = ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting(AppSettingsEnum.syncAlbums);
|
||||
_enableSyncUploadAlbum.value = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
||||
ref.read(backupAlbumProvider.notifier).getAll();
|
||||
|
||||
_initialTotalAssetCount =
|
||||
ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -69,12 +64,8 @@ class _DriftBackupAlbumSelectionPageState
|
||||
return album.name.toLowerCase().contains(_searchQuery.toLowerCase());
|
||||
}).toList();
|
||||
|
||||
final selectedBackupAlbums = albums
|
||||
.where((album) => album.backupSelection == BackupSelection.selected)
|
||||
.toList();
|
||||
final excludedBackupAlbums = albums
|
||||
.where((album) => album.backupSelection == BackupSelection.excluded)
|
||||
.toList();
|
||||
final selectedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.selected).toList();
|
||||
final excludedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.excluded).toList();
|
||||
|
||||
handleSyncAlbumToggle(bool isEnable) async {
|
||||
if (isEnable) {
|
||||
@@ -98,16 +89,11 @@ class _DriftBackupAlbumSelectionPageState
|
||||
return;
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.getBackupStatus(currentUser.id);
|
||||
final currentTotalAssetCount =
|
||||
ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
||||
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||
|
||||
if (currentTotalAssetCount != _initialTotalAssetCount) {
|
||||
final isBackupEnabled = ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting(AppSettingsEnum.enableBackup);
|
||||
final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
||||
|
||||
if (!isBackupEnabled) {
|
||||
return;
|
||||
@@ -132,8 +118,7 @@ class _DriftBackupAlbumSelectionPageState
|
||||
autofocus: true,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: (value) =>
|
||||
setState(() => _searchQuery = value.trim()),
|
||||
onChanged: (value) => setState(() => _searchQuery = value.trim()),
|
||||
)
|
||||
: const Text(
|
||||
"backup_album_selection_page_select_albums",
|
||||
@@ -195,8 +180,7 @@ class _DriftBackupAlbumSelectionPageState
|
||||
SettingsSwitchListTile(
|
||||
valueNotifier: _enableSyncUploadAlbum,
|
||||
title: "sync_albums".t(context: context),
|
||||
subtitle: "sync_upload_album_setting_subtitle"
|
||||
.t(context: context),
|
||||
subtitle: "sync_upload_album_setting_subtitle".t(context: context),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
titleStyle: context.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -238,8 +222,7 @@ class _DriftBackupAlbumSelectionPageState
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(10)),
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
elevation: 5,
|
||||
title: Text(
|
||||
@@ -428,8 +411,7 @@ class _SelectedAlbumNameChips extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
backgroundColor: context.primaryColor,
|
||||
deleteIconColor:
|
||||
context.isDarkTheme ? Colors.black : Colors.white,
|
||||
deleteIconColor: context.isDarkTheme ? Colors.black : Colors.white,
|
||||
deleteIcon: const Icon(
|
||||
Icons.cancel_rounded,
|
||||
size: 15,
|
||||
@@ -504,9 +486,7 @@ class _SelectAllButton extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final canSelectAll = filteredAlbums
|
||||
.where((album) => album.backupSelection != BackupSelection.selected)
|
||||
.isNotEmpty;
|
||||
final canSelectAll = filteredAlbums.where((album) => album.backupSelection != BackupSelection.selected).isNotEmpty;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
@@ -518,9 +498,7 @@ class _SelectAllButton extends ConsumerWidget {
|
||||
? () {
|
||||
for (final album in filteredAlbums) {
|
||||
if (album.backupSelection != BackupSelection.selected) {
|
||||
ref
|
||||
.read(backupAlbumProvider.notifier)
|
||||
.selectAlbum(album);
|
||||
ref.read(backupAlbumProvider.notifier).selectAlbum(album);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -544,9 +522,7 @@ class _SelectAllButton extends ConsumerWidget {
|
||||
? () {
|
||||
for (final album in filteredAlbums) {
|
||||
if (album.backupSelection == BackupSelection.selected) {
|
||||
ref
|
||||
.read(backupAlbumProvider.notifier)
|
||||
.deselectAlbum(album);
|
||||
ref.read(backupAlbumProvider.notifier).deselectAlbum(album);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,7 @@ class DriftUploadDetailPage extends ConsumerWidget {
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 1,
|
||||
),
|
||||
body: uploadItems.isEmpty
|
||||
? _buildEmptyState(context)
|
||||
: _buildUploadList(uploadItems),
|
||||
body: uploadItems.isEmpty ? _buildEmptyState(context) : _buildUploadList(uploadItems),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,9 +77,7 @@ class DriftUploadDetailPage extends ConsumerWidget {
|
||||
|
||||
return Card(
|
||||
elevation: 0,
|
||||
color: item.isFailed != null
|
||||
? context.colorScheme.errorContainer
|
||||
: context.colorScheme.surfaceContainer,
|
||||
color: item.isFailed != null ? context.colorScheme.errorContainer : context.colorScheme.surfaceContainer,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(16),
|
||||
@@ -119,8 +115,7 @@ class DriftUploadDetailPage extends ConsumerWidget {
|
||||
Text(
|
||||
'Tap for more details',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.colorScheme.onSurface
|
||||
.withValues(alpha: 0.6),
|
||||
color: context.colorScheme.onSurface.withValues(alpha: 0.6),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@@ -163,13 +158,10 @@ class DriftUploadDetailPage extends ConsumerWidget {
|
||||
tween: Tween<double>(begin: 0.0, end: progress),
|
||||
duration: const Duration(milliseconds: 300),
|
||||
builder: (context, value, _) => CircularProgressIndicator(
|
||||
backgroundColor:
|
||||
context.colorScheme.outline.withValues(alpha: 0.2),
|
||||
backgroundColor: context.colorScheme.outline.withValues(alpha: 0.2),
|
||||
strokeWidth: 3,
|
||||
value: value,
|
||||
color: isCompleted
|
||||
? context.colorScheme.primary
|
||||
: context.colorScheme.secondary,
|
||||
color: isCompleted ? context.colorScheme.primary : context.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -279,12 +271,10 @@ class FileDetailDialog extends ConsumerWidget {
|
||||
height: 128,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: context.colorScheme.outline
|
||||
.withValues(alpha: 0.2),
|
||||
color: context.colorScheme.outline.withValues(alpha: 0.2),
|
||||
width: 1,
|
||||
),
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(12)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: asset != null
|
||||
? Thumbnail(
|
||||
@@ -314,8 +304,7 @@ class FileDetailDialog extends ConsumerWidget {
|
||||
"File Size",
|
||||
formatHumanReadableBytes(uploadStatus.fileSize, 2),
|
||||
),
|
||||
if (asset.width != null)
|
||||
_buildInfoRow(context, "Width", "${asset.width}px"),
|
||||
if (asset.width != null) _buildInfoRow(context, "Width", "${asset.width}px"),
|
||||
if (asset.height != null)
|
||||
_buildInfoRow(
|
||||
context,
|
||||
|
||||
@@ -97,9 +97,7 @@ class FailedBackupStatusPage extends HookConsumerWidget {
|
||||
),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.isDarkTheme
|
||||
? Colors.white70
|
||||
: Colors.grey[800],
|
||||
color: context.isDarkTheme ? Colors.white70 : Colors.grey[800],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
@@ -125,9 +123,7 @@ class FailedBackupStatusPage extends HookConsumerWidget {
|
||||
errorAsset.errorMessage,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: context.isDarkTheme
|
||||
? Colors.white70
|
||||
: Colors.grey[800],
|
||||
color: context.isDarkTheme ? Colors.white70 : Colors.grey[800],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -27,10 +27,8 @@ class ActivitiesPage extends HookConsumerWidget {
|
||||
final asset = ref.watch(currentAssetProvider);
|
||||
final user = ref.watch(currentUserProvider);
|
||||
|
||||
final activityNotifier = ref
|
||||
.read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier);
|
||||
final activities =
|
||||
ref.watch(albumActivityProvider(album.remoteId!, asset?.remoteId));
|
||||
final activityNotifier = ref.read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier);
|
||||
final activities = ref.watch(albumActivityProvider(album.remoteId!, asset?.remoteId));
|
||||
|
||||
final listViewScrollController = useScrollController();
|
||||
|
||||
@@ -49,10 +47,7 @@ class ActivitiesPage extends HookConsumerWidget {
|
||||
body: activities.widgetWhen(
|
||||
onData: (data) {
|
||||
final liked = data.firstWhereOrNull(
|
||||
(a) =>
|
||||
a.type == ActivityType.like &&
|
||||
a.user.id == user?.id &&
|
||||
a.assetId == asset?.remoteId,
|
||||
(a) => a.type == ActivityType.like && a.user.id == user?.id && a.assetId == asset?.remoteId,
|
||||
);
|
||||
|
||||
return SafeArea(
|
||||
@@ -71,18 +66,15 @@ class ActivitiesPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
final activity = data[index];
|
||||
final canDelete = activity.user.id == user?.id ||
|
||||
album.ownerId == user?.id;
|
||||
final canDelete = activity.user.id == user?.id || album.ownerId == user?.id;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
child: DismissibleActivity(
|
||||
activity.id,
|
||||
ActivityTile(activity),
|
||||
onDismiss: canDelete
|
||||
? (activityId) async => await activityNotifier
|
||||
.removeActivity(activity.id)
|
||||
: null,
|
||||
onDismiss:
|
||||
canDelete ? (activityId) async => await activityNotifier.removeActivity(activity.id) : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -130,10 +130,8 @@ class AppLogDetailPage extends HookConsumerWidget {
|
||||
child: ListView(
|
||||
children: [
|
||||
buildTextWithCopyButton("MESSAGE", logMessage.message),
|
||||
if (logMessage.error != null)
|
||||
buildTextWithCopyButton("DETAILS", logMessage.error.toString()),
|
||||
if (logMessage.logger != null)
|
||||
buildLogContext1(logMessage.logger.toString()),
|
||||
if (logMessage.error != null) buildTextWithCopyButton("DETAILS", logMessage.error.toString()),
|
||||
if (logMessage.logger != null) buildLogContext1(logMessage.logger.toString()),
|
||||
if (logMessage.stack != null)
|
||||
buildTextWithCopyButton(
|
||||
"STACK TRACE",
|
||||
|
||||
@@ -61,9 +61,7 @@ class _ChangeExperiencePageState extends ConsumerState<ChangeExperiencePage> {
|
||||
ref.read(websocketProvider.notifier).stopListenToOldEvents();
|
||||
ref.read(websocketProvider.notifier).startListeningToBetaEvents();
|
||||
|
||||
final permission = await ref
|
||||
.read(galleryPermissionNotifier.notifier)
|
||||
.requestGalleryPermission();
|
||||
final permission = await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission();
|
||||
|
||||
if (permission.isGranted) {
|
||||
await ref.read(backgroundSyncProvider).syncLocal(full: true);
|
||||
@@ -138,9 +136,7 @@ class _ChangeExperiencePageState extends ConsumerState<ChangeExperiencePage> {
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
context.replaceRoute(
|
||||
widget.switchingToBeta
|
||||
? const TabShellRoute()
|
||||
: const TabControllerRoute(),
|
||||
widget.switchingToBeta ? const TabShellRoute() : const TabControllerRoute(),
|
||||
);
|
||||
},
|
||||
child: const Text("Continue"),
|
||||
|
||||
@@ -27,8 +27,7 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final albumTitleController =
|
||||
useTextEditingController.fromValue(TextEditingValue.empty);
|
||||
final albumTitleController = useTextEditingController.fromValue(TextEditingValue.empty);
|
||||
final albumTitleTextFieldFocusNode = useFocusNode();
|
||||
final albumDescriptionTextFieldFocusNode = useFocusNode();
|
||||
final isAlbumTitleTextFieldFocus = useState(false);
|
||||
@@ -45,15 +44,12 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
if (albumTitleController.text.isEmpty) {
|
||||
albumTitleController.text = 'create_album_page_untitled'.tr();
|
||||
isAlbumTitleEmpty.value = false;
|
||||
ref
|
||||
.watch(albumTitleProvider.notifier)
|
||||
.setAlbumTitle('create_album_page_untitled'.tr());
|
||||
ref.watch(albumTitleProvider.notifier).setAlbumTitle('create_album_page_untitled'.tr());
|
||||
}
|
||||
}
|
||||
|
||||
onSelectPhotosButtonPressed() async {
|
||||
AssetSelectionPageResult? selectedAsset =
|
||||
await context.pushRoute<AssetSelectionPageResult?>(
|
||||
AssetSelectionPageResult? selectedAsset = await context.pushRoute<AssetSelectionPageResult?>(
|
||||
AlbumAssetSelectionRoute(
|
||||
existingAssets: selectedAssets.value,
|
||||
canDeselect: true,
|
||||
@@ -118,8 +114,7 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
child: FilledButton.icon(
|
||||
style: FilledButton.styleFrom(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
|
||||
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10),
|
||||
@@ -230,15 +225,12 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
).tr(),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed:
|
||||
albumTitleController.text.isNotEmpty ? createAlbum : null,
|
||||
onPressed: albumTitleController.text.isNotEmpty ? createAlbum : null,
|
||||
child: Text(
|
||||
'create'.tr(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: albumTitleController.text.isNotEmpty
|
||||
? context.primaryColor
|
||||
: context.themeData.disabledColor,
|
||||
color: albumTitleController.text.isNotEmpty ? context.primaryColor : context.themeData.disabledColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -34,8 +34,7 @@ class DownloadPanel extends ConsumerWidget {
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: showProgress
|
||||
? ConstrainedBox(
|
||||
constraints:
|
||||
BoxConstraints.loose(Size(context.width - 32, 300)),
|
||||
constraints: BoxConstraints.loose(Size(context.width - 32, 300)),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: tasks.length,
|
||||
@@ -122,8 +121,7 @@ class DownloadTaskTile extends StatelessWidget {
|
||||
child: LinearProgressIndicator(
|
||||
minHeight: 8.0,
|
||||
value: progress,
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(10.0)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
|
||||
@@ -267,8 +267,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
|
||||
PhotoViewGalleryPageOptions buildVideo(BuildContext context, Asset asset) {
|
||||
return PhotoViewGalleryPageOptions.customChild(
|
||||
onDragStart: (_, details, __, ___) =>
|
||||
localPosition.value = details.localPosition,
|
||||
onDragStart: (_, details, __, ___) => localPosition.value = details.localPosition,
|
||||
onDragUpdate: (_, details, __) => handleSwipeUpDown(details),
|
||||
heroAttributes: _getHeroAttributes(asset),
|
||||
filterQuality: FilterQuality.high,
|
||||
@@ -304,8 +303,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
|
||||
final stackId = newAsset.stackId;
|
||||
if (stackId != null && currentIndex.value == index) {
|
||||
final stackElements =
|
||||
ref.read(assetStackStateProvider(newAsset.stackId!));
|
||||
final stackElements = ref.read(assetStackStateProvider(newAsset.stackId!));
|
||||
if (stackIndex.value < stackElements.length) {
|
||||
newAsset = stackElements.elementAt(stackIndex.value);
|
||||
}
|
||||
@@ -319,8 +317,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
|
||||
return PopScope(
|
||||
// Change immersive mode back to normal "edgeToEdge" mode
|
||||
onPopInvokedWithResult: (didPop, _) =>
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge),
|
||||
onPopInvokedWithResult: (didPop, _) => SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge),
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: Stack(
|
||||
@@ -335,8 +332,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
|
||||
if (asset.isImage && !ref.read(isPlayingMotionVideoProvider)) {
|
||||
isZoomed.value = state != PhotoViewScaleState.initial;
|
||||
ref.read(showControlsProvider.notifier).show =
|
||||
!isZoomed.value;
|
||||
ref.read(showControlsProvider.notifier).show = !isZoomed.value;
|
||||
}
|
||||
},
|
||||
gaplessPlayback: true,
|
||||
@@ -454,9 +450,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
@pragma('vm:prefer-inline')
|
||||
PhotoViewHeroAttributes _getHeroAttributes(Asset asset) {
|
||||
return PhotoViewHeroAttributes(
|
||||
tag: asset.isInDb
|
||||
? asset.id + heroOffset
|
||||
: '${asset.remoteId}-$heroOffset',
|
||||
tag: asset.isInDb ? asset.id + heroOffset : '${asset.remoteId}-$heroOffset',
|
||||
transitionOnUserGestures: true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ class LargeLeadingTile extends StatelessWidget {
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: selected
|
||||
? selectedTileColor ??
|
||||
Theme.of(context).primaryColor.withAlpha(30)
|
||||
? selectedTileColor ?? Theme.of(context).primaryColor.withAlpha(30)
|
||||
: tileColor ?? Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
),
|
||||
|
||||
@@ -87,11 +87,9 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
|
||||
// Use a network URL for the video player controller
|
||||
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||
final isOriginalVideo = ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting<bool>(AppSettingsEnum.loadOriginalVideo);
|
||||
final String postfixUrl =
|
||||
isOriginalVideo ? 'original' : 'video/playback';
|
||||
final isOriginalVideo =
|
||||
ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loadOriginalVideo);
|
||||
final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback';
|
||||
final String videoUrl = asset.livePhotoVideoId != null
|
||||
? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl'
|
||||
: '$serverEndpoint/assets/${asset.remoteId}/$postfixUrl';
|
||||
@@ -119,8 +117,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
try {
|
||||
aspectRatio.value =
|
||||
await ref.read(assetServiceProvider).getAspectRatio(asset);
|
||||
aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset);
|
||||
} catch (error) {
|
||||
log.severe(
|
||||
'Error getting aspect ratio for asset ${asset.fileName}: $error',
|
||||
@@ -135,8 +132,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
final videoPlayback = ref.read(videoPlaybackValueProvider);
|
||||
if ((isBuffering.value ||
|
||||
videoPlayback.state == VideoPlaybackState.initializing) &&
|
||||
if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) &&
|
||||
videoPlayback.state != VideoPlaybackState.buffering) {
|
||||
ref.read(videoPlaybackValueProvider.notifier).value =
|
||||
videoPlayback.copyWith(state: VideoPlaybackState.buffering);
|
||||
@@ -195,8 +191,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
final videoPlayback =
|
||||
VideoPlaybackValue.fromNativeController(videoController);
|
||||
final videoPlayback = VideoPlaybackValue.fromNativeController(videoController);
|
||||
ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback;
|
||||
|
||||
isVideoReady.value = true;
|
||||
@@ -215,8 +210,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
final videoPlayback =
|
||||
VideoPlaybackValue.fromNativeController(videoController);
|
||||
final videoPlayback = VideoPlaybackValue.fromNativeController(videoController);
|
||||
if (videoPlayback.state == VideoPlaybackState.playing) {
|
||||
// Sync with the controls playing
|
||||
WakelockPlus.enable();
|
||||
@@ -225,8 +219,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
WakelockPlus.disable();
|
||||
}
|
||||
|
||||
ref.read(videoPlaybackValueProvider.notifier).status =
|
||||
videoPlayback.state;
|
||||
ref.read(videoPlaybackValueProvider.notifier).status = videoPlayback.state;
|
||||
}
|
||||
|
||||
void onPlaybackPositionChanged() {
|
||||
@@ -245,8 +238,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(videoPlaybackValueProvider.notifier).position =
|
||||
Duration(seconds: playbackInfo.position);
|
||||
ref.read(videoPlaybackValueProvider.notifier).position = Duration(seconds: playbackInfo.position);
|
||||
|
||||
// Check if the video is buffering
|
||||
if (playbackInfo.status == PlaybackStatus.playing) {
|
||||
@@ -265,18 +257,14 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
if (videoController.playbackInfo?.status == PlaybackStatus.stopped &&
|
||||
!ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting<bool>(AppSettingsEnum.loopVideo)) {
|
||||
!ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo)) {
|
||||
ref.read(isPlayingMotionVideoProvider.notifier).playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
void removeListeners(NativeVideoPlayerController controller) {
|
||||
controller.onPlaybackPositionChanged
|
||||
.removeListener(onPlaybackPositionChanged);
|
||||
controller.onPlaybackStatusChanged
|
||||
.removeListener(onPlaybackStatusChanged);
|
||||
controller.onPlaybackPositionChanged.removeListener(onPlaybackPositionChanged);
|
||||
controller.onPlaybackStatusChanged.removeListener(onPlaybackStatusChanged);
|
||||
controller.onPlaybackReady.removeListener(onPlaybackReady);
|
||||
controller.onPlaybackEnded.removeListener(onPlaybackEnded);
|
||||
}
|
||||
@@ -301,9 +289,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
nc.loadVideoSource(source).catchError((error) {
|
||||
log.severe('Error loading video source: $error');
|
||||
});
|
||||
final loopVideo = ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||
nc.setLoop(loopVideo);
|
||||
|
||||
controller.value = nc;
|
||||
@@ -397,8 +383,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||
children: [
|
||||
// This remains under the video to avoid flickering
|
||||
// For motion videos, this is the image portion of the asset
|
||||
if (!isVideoReady.value || asset.isMotionPhoto)
|
||||
Center(key: ValueKey(asset.id), child: image),
|
||||
if (!isVideoReady.value || asset.isMotionPhoto) Center(key: ValueKey(asset.id), child: image),
|
||||
if (aspectRatio.value != null && !isCasting)
|
||||
Visibility.maintain(
|
||||
key: ValueKey(asset),
|
||||
|
||||
@@ -129,8 +129,7 @@ class _TabletLayout extends HookWidget {
|
||||
const _TabletLayout();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedSection =
|
||||
useState<SettingSection>(SettingSection.values.first);
|
||||
final selectedSection = useState<SettingSection>(SettingSection.values.first);
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
||||
@@ -74,9 +74,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
||||
|
||||
if (context.router.current.name == SplashScreenRoute.name) {
|
||||
context.replaceRoute(
|
||||
Store.isBetaTimelineEnabled
|
||||
? const TabShellRoute()
|
||||
: const TabControllerRoute(),
|
||||
Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,8 +82,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
final hasPermission =
|
||||
await ref.read(galleryPermissionNotifier.notifier).hasPermission;
|
||||
final hasPermission = await ref.read(galleryPermissionNotifier.notifier).hasPermission;
|
||||
if (hasPermission) {
|
||||
// Resume backup (if enable) then navigate
|
||||
ref.watch(backupProvider.notifier).resumeBackup();
|
||||
|
||||
@@ -20,8 +20,7 @@ class TabControllerPage extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isRefreshingAssets = ref.watch(assetProvider);
|
||||
final isRefreshingRemoteAlbums = ref.watch(isRefreshingRemoteAlbumProvider);
|
||||
final isScreenLandscape =
|
||||
MediaQuery.orientationOf(context) == Orientation.landscape;
|
||||
final isScreenLandscape = MediaQuery.orientationOf(context) == Orientation.landscape;
|
||||
|
||||
Widget buildIcon({required Widget icon, required bool isProcessing}) {
|
||||
if (!isProcessing) return icon;
|
||||
@@ -118,8 +117,7 @@ class TabControllerPage extends HookConsumerWidget {
|
||||
Widget bottomNavigationBar(TabsRouter tabsRouter) {
|
||||
return NavigationBar(
|
||||
selectedIndex: tabsRouter.activeIndex,
|
||||
onDestinationSelected: (index) =>
|
||||
onNavigationSelected(tabsRouter, index),
|
||||
onDestinationSelected: (index) => onNavigationSelected(tabsRouter, index),
|
||||
destinations: navigationDestinations,
|
||||
);
|
||||
}
|
||||
@@ -135,8 +133,7 @@ class TabControllerPage extends HookConsumerWidget {
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (index) =>
|
||||
onNavigationSelected(tabsRouter, index),
|
||||
onDestinationSelected: (index) => onNavigationSelected(tabsRouter, index),
|
||||
selectedIndex: tabsRouter.activeIndex,
|
||||
labelType: NavigationRailLabelType.all,
|
||||
groupAlignment: 0.0,
|
||||
@@ -160,8 +157,7 @@ class TabControllerPage extends HookConsumerWidget {
|
||||
final tabsRouter = AutoTabsRouter.of(context);
|
||||
return PopScope(
|
||||
canPop: tabsRouter.activeIndex == 0,
|
||||
onPopInvokedWithResult: (didPop, _) =>
|
||||
!didPop ? tabsRouter.setActiveIndex(0) : null,
|
||||
onPopInvokedWithResult: (didPop, _) => !didPop ? tabsRouter.setActiveIndex(0) : null,
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: isScreenLandscape
|
||||
@@ -173,9 +169,7 @@ class TabControllerPage extends HookConsumerWidget {
|
||||
],
|
||||
)
|
||||
: child,
|
||||
bottomNavigationBar: multiselectEnabled || isScreenLandscape
|
||||
? null
|
||||
: bottomNavigationBar(tabsRouter),
|
||||
bottomNavigationBar: multiselectEnabled || isScreenLandscape ? null : bottomNavigationBar(tabsRouter),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -33,9 +33,7 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
ref.read(websocketProvider.notifier).connect();
|
||||
|
||||
final isEnableBackup = ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting(AppSettingsEnum.enableBackup);
|
||||
final isEnableBackup = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
||||
|
||||
await runNewSync(ref, full: true).then((_) async {
|
||||
if (isEnableBackup) {
|
||||
@@ -44,9 +42,7 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(driftBackupProvider.notifier)
|
||||
.handleBackupResume(currentUser.id);
|
||||
await ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -110,8 +106,7 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (index) =>
|
||||
_onNavigationSelected(tabsRouter, index, ref),
|
||||
onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref),
|
||||
selectedIndex: tabsRouter.activeIndex,
|
||||
labelType: NavigationRailLabelType.all,
|
||||
groupAlignment: 0.0,
|
||||
@@ -134,8 +129,7 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
|
||||
final tabsRouter = AutoTabsRouter.of(context);
|
||||
return PopScope(
|
||||
canPop: tabsRouter.activeIndex == 0,
|
||||
onPopInvokedWithResult: (didPop, _) =>
|
||||
!didPop ? tabsRouter.setActiveIndex(0) : null,
|
||||
onPopInvokedWithResult: (didPop, _) => !didPop ? tabsRouter.setActiveIndex(0) : null,
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: isScreenLandscape
|
||||
@@ -191,8 +185,7 @@ class _BottomNavigationBar extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isScreenLandscape = context.orientation == Orientation.landscape;
|
||||
final isMultiselectEnabled =
|
||||
ref.watch(multiSelectProvider.select((s) => s.isEnabled));
|
||||
final isMultiselectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled));
|
||||
|
||||
if (isScreenLandscape || isMultiselectEnabled) {
|
||||
return const SizedBox.shrink();
|
||||
@@ -200,8 +193,7 @@ class _BottomNavigationBar extends ConsumerWidget {
|
||||
|
||||
return NavigationBar(
|
||||
selectedIndex: tabsRouter.activeIndex,
|
||||
onDestinationSelected: (index) =>
|
||||
_onNavigationSelected(tabsRouter, index, ref),
|
||||
onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref),
|
||||
destinations: destinations,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -187,9 +187,7 @@ class _AspectRatioButton extends StatelessWidget {
|
||||
'7:5' => Icons.crop_7_5_rounded,
|
||||
_ => Icons.crop_free_rounded,
|
||||
},
|
||||
color: aspectRatio.value == ratio
|
||||
? context.primaryColor
|
||||
: context.themeData.iconTheme.color,
|
||||
color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color,
|
||||
),
|
||||
onPressed: () {
|
||||
cropController.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9);
|
||||
|
||||
@@ -40,9 +40,7 @@ class EditImagePage extends ConsumerWidget {
|
||||
image.image.resolve(const ImageConfiguration()).addListener(
|
||||
ImageStreamListener(
|
||||
(ImageInfo info, bool _) {
|
||||
info.image
|
||||
.toByteData(format: ImageByteFormat.png)
|
||||
.then((byteData) {
|
||||
info.image.toByteData(format: ImageByteFormat.png).then((byteData) {
|
||||
if (byteData != null) {
|
||||
completer.complete(byteData.buffer.asUint8List());
|
||||
} else {
|
||||
@@ -50,8 +48,7 @@ class EditImagePage extends ConsumerWidget {
|
||||
}
|
||||
});
|
||||
},
|
||||
onError: (exception, stackTrace) =>
|
||||
completer.completeError(exception),
|
||||
onError: (exception, stackTrace) => completer.completeError(exception),
|
||||
),
|
||||
);
|
||||
return completer.future;
|
||||
@@ -103,9 +100,7 @@ class EditImagePage extends ConsumerWidget {
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: isEdited
|
||||
? () => _saveEditedImage(context, asset, image, ref)
|
||||
: null,
|
||||
onPressed: isEdited ? () => _saveEditedImage(context, asset, image, ref) : null,
|
||||
child: Text(
|
||||
"save_to_gallery".tr(),
|
||||
style: TextStyle(
|
||||
|
||||
@@ -34,18 +34,14 @@ class FilterImagePage extends HookWidget {
|
||||
ColorFilter filter,
|
||||
) {
|
||||
final completer = Completer<ui.Image>();
|
||||
final size =
|
||||
Size(inputImage.width.toDouble(), inputImage.height.toDouble());
|
||||
final size = Size(inputImage.width.toDouble(), inputImage.height.toDouble());
|
||||
final recorder = ui.PictureRecorder();
|
||||
final canvas = Canvas(recorder);
|
||||
|
||||
final paint = Paint()..colorFilter = filter;
|
||||
canvas.drawImage(inputImage, Offset.zero, paint);
|
||||
|
||||
recorder
|
||||
.endRecording()
|
||||
.toImage(size.width.round(), size.height.round())
|
||||
.then((image) {
|
||||
recorder.endRecording().toImage(size.width.round(), size.height.round()).then((image) {
|
||||
completer.complete(image);
|
||||
});
|
||||
|
||||
@@ -67,8 +63,7 @@ class FilterImagePage extends HookWidget {
|
||||
final uiImage = await completer.future;
|
||||
|
||||
final filteredUiImage = await createFilteredImage(uiImage, filter);
|
||||
final byteData =
|
||||
await filteredUiImage.toByteData(format: ui.ImageByteFormat.png);
|
||||
final byteData = await filteredUiImage.toByteData(format: ui.ImageByteFormat.png);
|
||||
final pngBytes = byteData!.buffer.asUint8List();
|
||||
|
||||
return Image.memory(pngBytes, fit: BoxFit.contain);
|
||||
@@ -87,8 +82,7 @@ class FilterImagePage extends HookWidget {
|
||||
size: 24,
|
||||
),
|
||||
onPressed: () async {
|
||||
final filteredImage =
|
||||
await applyFilterAndConvert(colorFilter.value);
|
||||
final filteredImage = await applyFilterAndConvert(colorFilter.value);
|
||||
context.pushRoute(
|
||||
EditImageRoute(
|
||||
asset: asset,
|
||||
@@ -165,9 +159,7 @@ class _FilterButton extends StatelessWidget {
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(10),
|
||||
),
|
||||
border: isSelected
|
||||
? Border.all(color: context.primaryColor, width: 3)
|
||||
: null,
|
||||
border: isSelected ? Border.all(color: context.primaryColor, width: 3) : null,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(
|
||||
|
||||
@@ -21,9 +21,7 @@ RecursiveFolder? _findFolderInStructure(
|
||||
RecursiveFolder targetFolder,
|
||||
) {
|
||||
for (final folder in rootFolder.subfolders) {
|
||||
if (targetFolder.path == '/' &&
|
||||
folder.path.isEmpty &&
|
||||
folder.name == targetFolder.name) {
|
||||
if (targetFolder.path == '/' && folder.path.isEmpty && folder.name == targetFolder.name) {
|
||||
return folder;
|
||||
}
|
||||
|
||||
@@ -54,9 +52,7 @@ class FolderPage extends HookConsumerWidget {
|
||||
useEffect(
|
||||
() {
|
||||
if (folder == null) {
|
||||
ref
|
||||
.read(folderStructureProvider.notifier)
|
||||
.fetchFolders(sortOrder.value);
|
||||
ref.read(folderStructureProvider.notifier).fetchFolders(sortOrder.value);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -67,8 +63,7 @@ class FolderPage extends HookConsumerWidget {
|
||||
useEffect(
|
||||
() {
|
||||
if (folder != null && folderState.hasValue) {
|
||||
final updatedFolder =
|
||||
_findFolderInStructure(folderState.value!, folder!);
|
||||
final updatedFolder = _findFolderInStructure(folderState.value!, folder!);
|
||||
if (updatedFolder != null) {
|
||||
currentFolder.value = updatedFolder;
|
||||
}
|
||||
@@ -79,8 +74,7 @@ class FolderPage extends HookConsumerWidget {
|
||||
);
|
||||
|
||||
void onToggleSortOrder() {
|
||||
final newOrder =
|
||||
sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
|
||||
final newOrder = sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
|
||||
|
||||
ref.read(folderStructureProvider.notifier).fetchFolders(newOrder);
|
||||
|
||||
@@ -151,9 +145,7 @@ class FolderContent extends HookConsumerWidget {
|
||||
useEffect(
|
||||
() {
|
||||
if (folder == null) return;
|
||||
ref
|
||||
.read(folderRenderListProvider(folder!).notifier)
|
||||
.fetchAssets(sortOrder);
|
||||
ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder);
|
||||
return null;
|
||||
},
|
||||
[folder],
|
||||
@@ -211,13 +203,10 @@ class FolderContent extends HookConsumerWidget {
|
||||
),
|
||||
)
|
||||
: null,
|
||||
onTap: () =>
|
||||
context.pushRoute(FolderRoute(folder: subfolder)),
|
||||
onTap: () => context.pushRoute(FolderRoute(folder: subfolder)),
|
||||
),
|
||||
),
|
||||
if (!list.isEmpty &&
|
||||
list.allAssets != null &&
|
||||
list.allAssets!.isNotEmpty)
|
||||
if (!list.isEmpty && list.allAssets != null && list.allAssets!.isNotEmpty)
|
||||
...list.allAssets!.map(
|
||||
(asset) => LargeLeadingTile(
|
||||
onTap: () {
|
||||
|
||||
@@ -25,8 +25,7 @@ class LibraryPage extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
context.locale;
|
||||
final trashEnabled =
|
||||
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
|
||||
final trashEnabled = ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
|
||||
|
||||
return Scaffold(
|
||||
appBar: const ImmichAppBar(),
|
||||
@@ -388,8 +387,7 @@ class PlacesCollectionCard extends StatelessWidget {
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
color:
|
||||
context.colorScheme.secondaryContainer.withAlpha(100),
|
||||
color: context.colorScheme.secondaryContainer.withAlpha(100),
|
||||
),
|
||||
child: IgnorePointer(
|
||||
child: MapThumbnail(
|
||||
@@ -399,9 +397,7 @@ class PlacesCollectionCard extends StatelessWidget {
|
||||
-157.91959,
|
||||
),
|
||||
showAttribution: false,
|
||||
themeMode: context.isDarkTheme
|
||||
? ThemeMode.dark
|
||||
: ThemeMode.light,
|
||||
themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -54,8 +54,7 @@ class LocalAlbumsPage extends HookConsumerWidget {
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
),
|
||||
onTap: () => context
|
||||
.pushRoute(AlbumViewerRoute(albumId: albums[index].id)),
|
||||
onTap: () => context.pushRoute(AlbumViewerRoute(albumId: albums[index].id)),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -23,11 +23,10 @@ class PinAuthPage extends HookConsumerWidget {
|
||||
final isBetaTimeline = Store.isBetaTimelineEnabled;
|
||||
|
||||
Future<void> registerBiometric(String pinCode) async {
|
||||
final isRegistered =
|
||||
await ref.read(localAuthProvider.notifier).registerBiometric(
|
||||
context,
|
||||
pinCode,
|
||||
);
|
||||
final isRegistered = await ref.read(localAuthProvider.notifier).registerBiometric(
|
||||
context,
|
||||
pinCode,
|
||||
);
|
||||
|
||||
if (isRegistered) {
|
||||
context.showSnackBar(
|
||||
@@ -101,8 +100,7 @@ class PinAuthPage extends HookConsumerWidget {
|
||||
autoFocus: true,
|
||||
onSuccess: (_) {
|
||||
if (isBetaTimeline) {
|
||||
context
|
||||
.replaceRoute(const DriftLockedFolderRoute());
|
||||
context.replaceRoute(const DriftLockedFolderRoute());
|
||||
} else {
|
||||
context.replaceRoute(const LockedRoute());
|
||||
}
|
||||
|
||||
@@ -63,10 +63,8 @@ class DriftPartnerPage extends HookConsumerWidget {
|
||||
builder: (BuildContext context) {
|
||||
return ConfirmDialog(
|
||||
title: "stop_photo_sharing",
|
||||
content: "partner_page_stop_sharing_content"
|
||||
.tr(namedArgs: {'partner': partner.name}),
|
||||
onOk: () =>
|
||||
ref.read(partnerUsersProvider.notifier).removePartner(partner),
|
||||
content: "partner_page_stop_sharing_content".tr(namedArgs: {'partner': partner.name}),
|
||||
onOk: () => ref.read(partnerUsersProvider.notifier).removePartner(partner),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -53,8 +53,7 @@ class PartnerPage extends HookConsumerWidget {
|
||||
},
|
||||
);
|
||||
if (selectedUser != null) {
|
||||
final ok =
|
||||
await ref.read(partnerServiceProvider).addPartner(selectedUser);
|
||||
final ok = await ref.read(partnerServiceProvider).addPartner(selectedUser);
|
||||
if (ok) {
|
||||
ref.invalidate(partnerSharedByProvider);
|
||||
} else {
|
||||
@@ -73,8 +72,7 @@ class PartnerPage extends HookConsumerWidget {
|
||||
builder: (BuildContext context) {
|
||||
return ConfirmDialog(
|
||||
title: "stop_photo_sharing",
|
||||
content: "partner_page_stop_sharing_content"
|
||||
.tr(namedArgs: {'partner': u.name}),
|
||||
content: "partner_page_stop_sharing_content".tr(namedArgs: {'partner': u.name}),
|
||||
onOk: () => ref.read(partnerServiceProvider).removePartner(u),
|
||||
);
|
||||
},
|
||||
@@ -149,8 +147,7 @@ class PartnerPage extends HookConsumerWidget {
|
||||
centerTitle: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed:
|
||||
availableUsers.whenOrNull(data: (data) => addNewUsersHandler),
|
||||
onPressed: availableUsers.whenOrNull(data: (data) => addNewUsersHandler),
|
||||
icon: const Icon(Icons.person_add),
|
||||
tooltip: "add_partner".tr(),
|
||||
),
|
||||
|
||||
@@ -38,9 +38,8 @@ class PartnerDetailPage extends HookConsumerWidget {
|
||||
if (toggleInProcess) return;
|
||||
toggleInProcess = true;
|
||||
try {
|
||||
final ok = await ref
|
||||
.read(partnerSharedWithProvider.notifier)
|
||||
.updatePartner(partner, inTimeline: !inTimeline.value);
|
||||
final ok =
|
||||
await ref.read(partnerSharedWithProvider.notifier).updatePartner(partner, inTimeline: !inTimeline.value);
|
||||
if (ok) {
|
||||
inTimeline.value = !inTimeline.value;
|
||||
final action = inTimeline.value ? "shown on" : "hidden from";
|
||||
|
||||
@@ -66,9 +66,7 @@ class PeopleCollectionPage extends HookConsumerWidget {
|
||||
data: (people) {
|
||||
if (search.value != null) {
|
||||
people = people.where((person) {
|
||||
return person.name
|
||||
.toLowerCase()
|
||||
.contains(search.value!.toLowerCase());
|
||||
return person.name.toLowerCase().contains(search.value!.toLowerCase());
|
||||
}).toList();
|
||||
}
|
||||
return GridView.builder(
|
||||
@@ -107,8 +105,7 @@ class PeopleCollectionPage extends HookConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () =>
|
||||
showNameEditModel(person.id, person.name),
|
||||
onTap: () => showNameEditModel(person.id, person.name),
|
||||
child: person.name.isEmpty
|
||||
? Text(
|
||||
'add_a_name'.tr(),
|
||||
@@ -124,8 +121,7 @@ class PeopleCollectionPage extends HookConsumerWidget {
|
||||
child: Text(
|
||||
person.name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style:
|
||||
context.textTheme.titleSmall?.copyWith(
|
||||
style: context.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -59,8 +59,7 @@ class PlacesCollectionPage extends HookConsumerWidget {
|
||||
height: 200,
|
||||
width: context.width,
|
||||
child: MapThumbnail(
|
||||
onTap: (_, __) => context
|
||||
.pushRoute(MapRoute(initialLocation: currentLocation)),
|
||||
onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)),
|
||||
zoom: 8,
|
||||
centre: currentLocation ??
|
||||
const LatLng(
|
||||
@@ -68,8 +67,7 @@ class PlacesCollectionPage extends HookConsumerWidget {
|
||||
-157.91959,
|
||||
),
|
||||
showAttribution: false,
|
||||
themeMode:
|
||||
context.isDarkTheme ? ThemeMode.dark : ThemeMode.light,
|
||||
themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -77,9 +75,7 @@ class PlacesCollectionPage extends HookConsumerWidget {
|
||||
data: (places) {
|
||||
if (search.value != null) {
|
||||
places = places.where((place) {
|
||||
return place.label
|
||||
.toLowerCase()
|
||||
.contains(search.value!.toLowerCase());
|
||||
return place.label.toLowerCase().contains(search.value!.toLowerCase());
|
||||
}).toList();
|
||||
}
|
||||
return ListView.builder(
|
||||
@@ -110,8 +106,7 @@ class PlaceTile extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final thumbnailUrl =
|
||||
'${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail';
|
||||
final thumbnailUrl = '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail';
|
||||
|
||||
void navigateToPlace() {
|
||||
context.pushRoute(
|
||||
@@ -152,8 +147,7 @@ class PlaceTile extends StatelessWidget {
|
||||
fit: BoxFit.cover,
|
||||
imageUrl: thumbnailUrl,
|
||||
httpHeaders: ApiService.getRequestHeaders(),
|
||||
errorWidget: (context, url, error) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -58,8 +58,7 @@ class SharedLinkPage extends HookConsumerWidget {
|
||||
child: Icon(
|
||||
Icons.link_off,
|
||||
size: 100,
|
||||
color:
|
||||
context.themeData.iconTheme.color?.withValues(alpha: 0.5),
|
||||
color: context.themeData.iconTheme.color?.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -86,8 +85,7 @@ class SharedLinkPage extends HookConsumerWidget {
|
||||
if (constraints.maxWidth > 600) {
|
||||
// Two column
|
||||
return GridView.builder(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisExtent: 180,
|
||||
),
|
||||
@@ -121,8 +119,7 @@ class SharedLinkPage extends HookConsumerWidget {
|
||||
body: SafeArea(
|
||||
child: sharedLinks.widgetWhen(
|
||||
onError: (error, stackTrace) => buildNoShares(),
|
||||
onData: (links) =>
|
||||
links.isNotEmpty ? buildSharesList(links) : buildNoShares(),
|
||||
onData: (links) => links.isNotEmpty ? buildSharesList(links) : buildNoShares(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -31,11 +31,9 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
const padding = 20.0;
|
||||
final themeData = context.themeData;
|
||||
final colorScheme = context.colorScheme;
|
||||
final descriptionController =
|
||||
useTextEditingController(text: existingLink?.description ?? "");
|
||||
final descriptionController = useTextEditingController(text: existingLink?.description ?? "");
|
||||
final descriptionFocusNode = useFocusNode();
|
||||
final passwordController =
|
||||
useTextEditingController(text: existingLink?.password ?? "");
|
||||
final passwordController = useTextEditingController(text: existingLink?.password ?? "");
|
||||
final showMetadata = useState(existingLink?.showMetadata ?? true);
|
||||
final allowDownload = useState(existingLink?.allowDownload ?? true);
|
||||
final allowUpload = useState(existingLink?.allowUpload ?? false);
|
||||
@@ -155,15 +153,12 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
Widget buildShowMetaButton() {
|
||||
return SwitchListTile.adaptive(
|
||||
value: showMetadata.value,
|
||||
onChanged: newShareLink.value.isEmpty
|
||||
? (value) => showMetadata.value = value
|
||||
: null,
|
||||
onChanged: newShareLink.value.isEmpty ? (value) => showMetadata.value = value : null,
|
||||
activeColor: colorScheme.primary,
|
||||
dense: true,
|
||||
title: Text(
|
||||
"show_metadata",
|
||||
style: themeData.textTheme.labelLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
);
|
||||
}
|
||||
@@ -171,15 +166,12 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
Widget buildAllowDownloadButton() {
|
||||
return SwitchListTile.adaptive(
|
||||
value: allowDownload.value,
|
||||
onChanged: newShareLink.value.isEmpty
|
||||
? (value) => allowDownload.value = value
|
||||
: null,
|
||||
onChanged: newShareLink.value.isEmpty ? (value) => allowDownload.value = value : null,
|
||||
activeColor: colorScheme.primary,
|
||||
dense: true,
|
||||
title: Text(
|
||||
"allow_public_user_to_download",
|
||||
style: themeData.textTheme.labelLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
);
|
||||
}
|
||||
@@ -187,15 +179,12 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
Widget buildAllowUploadButton() {
|
||||
return SwitchListTile.adaptive(
|
||||
value: allowUpload.value,
|
||||
onChanged: newShareLink.value.isEmpty
|
||||
? (value) => allowUpload.value = value
|
||||
: null,
|
||||
onChanged: newShareLink.value.isEmpty ? (value) => allowUpload.value = value : null,
|
||||
activeColor: colorScheme.primary,
|
||||
dense: true,
|
||||
title: Text(
|
||||
"allow_public_user_to_upload",
|
||||
style: themeData.textTheme.labelLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
);
|
||||
}
|
||||
@@ -203,15 +192,12 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
Widget buildEditExpiryButton() {
|
||||
return SwitchListTile.adaptive(
|
||||
value: editExpiry.value,
|
||||
onChanged: newShareLink.value.isEmpty
|
||||
? (value) => editExpiry.value = value
|
||||
: null,
|
||||
onChanged: newShareLink.value.isEmpty ? (value) => editExpiry.value = value : null,
|
||||
activeColor: colorScheme.primary,
|
||||
dense: true,
|
||||
title: Text(
|
||||
"change_expiration_time",
|
||||
style: themeData.textTheme.labelLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
);
|
||||
}
|
||||
@@ -229,8 +215,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
enableFilter: false,
|
||||
width: context.width - 40,
|
||||
initialSelection: expiryAfter.value,
|
||||
enabled: newShareLink.value.isEmpty &&
|
||||
(existingLink == null || editExpiry.value),
|
||||
enabled: newShareLink.value.isEmpty && (existingLink == null || editExpiry.value),
|
||||
onSelected: (value) {
|
||||
expiryAfter.value = value!;
|
||||
},
|
||||
@@ -241,8 +226,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: 30,
|
||||
label: "shared_link_edit_expire_after_option_minutes"
|
||||
.tr(namedArgs: {'count': "30"}),
|
||||
label: "shared_link_edit_expire_after_option_minutes".tr(namedArgs: {'count': "30"}),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: 60,
|
||||
@@ -250,8 +234,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: 60 * 6,
|
||||
label: "shared_link_edit_expire_after_option_hours"
|
||||
.tr(namedArgs: {'count': "6"}),
|
||||
label: "shared_link_edit_expire_after_option_hours".tr(namedArgs: {'count': "6"}),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: 60 * 24,
|
||||
@@ -259,23 +242,19 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: 60 * 24 * 7,
|
||||
label: "shared_link_edit_expire_after_option_days"
|
||||
.tr(namedArgs: {'count': "7"}),
|
||||
label: "shared_link_edit_expire_after_option_days".tr(namedArgs: {'count': "7"}),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: 60 * 24 * 30,
|
||||
label: "shared_link_edit_expire_after_option_days"
|
||||
.tr(namedArgs: {'count': "30"}),
|
||||
label: "shared_link_edit_expire_after_option_days".tr(namedArgs: {'count': "30"}),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: 60 * 24 * 30 * 3,
|
||||
label: "shared_link_edit_expire_after_option_months"
|
||||
.tr(namedArgs: {'count': "3"}),
|
||||
label: "shared_link_edit_expire_after_option_months".tr(namedArgs: {'count': "3"}),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: 60 * 24 * 30 * 12,
|
||||
label: "shared_link_edit_expire_after_option_year"
|
||||
.tr(namedArgs: {'count': "1"}),
|
||||
label: "shared_link_edit_expire_after_option_year".tr(namedArgs: {'count': "1"}),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -346,27 +325,21 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
Future<void> handleNewLink() async {
|
||||
final newLink =
|
||||
await ref.read(sharedLinkServiceProvider).createSharedLink(
|
||||
albumId: albumId,
|
||||
assetIds: assetsList,
|
||||
showMeta: showMetadata.value,
|
||||
allowDownload: allowDownload.value,
|
||||
allowUpload: allowUpload.value,
|
||||
description: descriptionController.text.isEmpty
|
||||
? null
|
||||
: descriptionController.text,
|
||||
password: passwordController.text.isEmpty
|
||||
? null
|
||||
: passwordController.text,
|
||||
expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(),
|
||||
);
|
||||
final newLink = await ref.read(sharedLinkServiceProvider).createSharedLink(
|
||||
albumId: albumId,
|
||||
assetIds: assetsList,
|
||||
showMeta: showMetadata.value,
|
||||
allowDownload: allowDownload.value,
|
||||
allowUpload: allowUpload.value,
|
||||
description: descriptionController.text.isEmpty ? null : descriptionController.text,
|
||||
password: passwordController.text.isEmpty ? null : passwordController.text,
|
||||
expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(),
|
||||
);
|
||||
ref.invalidate(sharedLinksStateProvider);
|
||||
final externalDomain = ref.read(
|
||||
serverInfoProvider.select((s) => s.serverConfig.externalDomain),
|
||||
);
|
||||
var serverUrl =
|
||||
externalDomain.isNotEmpty ? externalDomain : getServerUrl();
|
||||
var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl();
|
||||
if (serverUrl != null && !serverUrl.endsWith('/')) {
|
||||
serverUrl += '/';
|
||||
}
|
||||
@@ -472,8 +445,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
child: buildAllowDownloadButton(),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: padding, right: 20, bottom: 20),
|
||||
padding: const EdgeInsets.only(left: padding, right: 20, bottom: 20),
|
||||
child: buildAllowUploadButton(),
|
||||
),
|
||||
if (existingLink != null)
|
||||
@@ -502,12 +474,9 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||
bottom: padding,
|
||||
),
|
||||
child: ElevatedButton(
|
||||
onPressed:
|
||||
existingLink != null ? handleEditLink : handleNewLink,
|
||||
onPressed: existingLink != null ? handleEditLink : handleNewLink,
|
||||
child: Text(
|
||||
existingLink != null
|
||||
? "shared_link_edit_submit_button"
|
||||
: "create_link",
|
||||
existingLink != null ? "shared_link_edit_submit_button" : "create_link",
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
@@ -24,8 +24,7 @@ class TrashPage extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final trashRenderList = ref.watch(trashTimelineProvider);
|
||||
final trashDays =
|
||||
ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays));
|
||||
final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays));
|
||||
final selectionEnabledHook = useState(false);
|
||||
final selection = useState(<Asset>{});
|
||||
final processing = useProcessingOverlay();
|
||||
@@ -68,16 +67,13 @@ class TrashPage extends HookConsumerWidget {
|
||||
processing.value = true;
|
||||
try {
|
||||
if (selection.value.isNotEmpty) {
|
||||
final isRemoved = await ref
|
||||
.read(assetProvider.notifier)
|
||||
.deleteAssets(selection.value, force: true);
|
||||
final isRemoved = await ref.read(assetProvider.notifier).deleteAssets(selection.value, force: true);
|
||||
|
||||
if (isRemoved) {
|
||||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: 'assets_deleted_permanently'
|
||||
.tr(namedArgs: {'count': "${selection.value.length}"}),
|
||||
msg: 'assets_deleted_permanently'.tr(namedArgs: {'count': "${selection.value.length}"}),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
@@ -110,15 +106,12 @@ class TrashPage extends HookConsumerWidget {
|
||||
processing.value = true;
|
||||
try {
|
||||
if (selection.value.isNotEmpty) {
|
||||
final result = await ref
|
||||
.read(trashProvider.notifier)
|
||||
.restoreAssets(selection.value);
|
||||
final result = await ref.read(trashProvider.notifier).restoreAssets(selection.value);
|
||||
|
||||
if (result && context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: 'assets_restored_successfully'
|
||||
.tr(namedArgs: {'count': "${selection.value.length}"}),
|
||||
msg: 'assets_restored_successfully'.tr(namedArgs: {'count': "${selection.value.length}"}),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
@@ -131,9 +124,7 @@ class TrashPage extends HookConsumerWidget {
|
||||
|
||||
String getAppBarTitle(String count) {
|
||||
if (selectionEnabledHook.value) {
|
||||
return selection.value.isNotEmpty
|
||||
? "${selection.value.length}"
|
||||
: "trash_page_select_assets_btn".tr();
|
||||
return selection.value.isNotEmpty ? "${selection.value.length}" : "trash_page_select_assets_btn".tr();
|
||||
}
|
||||
return 'trash_page_title'.tr(namedArgs: {'count': count});
|
||||
}
|
||||
@@ -147,9 +138,8 @@ class TrashPage extends HookConsumerWidget {
|
||||
selectionEnabledHook.value = false;
|
||||
selection.value = {};
|
||||
},
|
||||
icon: !selectionEnabledHook.value
|
||||
? const Icon(Icons.arrow_back_ios_rounded)
|
||||
: const Icon(Icons.close_rounded),
|
||||
icon:
|
||||
!selectionEnabledHook.value ? const Icon(Icons.arrow_back_ios_rounded) : const Icon(Icons.close_rounded),
|
||||
),
|
||||
centerTitle: !selectionEnabledHook.value,
|
||||
automaticallyImplyLeading: false,
|
||||
@@ -192,9 +182,7 @@ class TrashPage extends HookConsumerWidget {
|
||||
color: Colors.red[400],
|
||||
),
|
||||
label: Text(
|
||||
selection.value.isEmpty
|
||||
? 'trash_page_delete_all'.tr()
|
||||
: 'delete'.tr(),
|
||||
selection.value.isEmpty ? 'trash_page_delete_all'.tr() : 'delete'.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.red[400],
|
||||
@@ -212,9 +200,7 @@ class TrashPage extends HookConsumerWidget {
|
||||
Icons.history_rounded,
|
||||
),
|
||||
label: Text(
|
||||
selection.value.isEmpty
|
||||
? 'trash_page_restore_all'.tr()
|
||||
: 'restore'.tr(),
|
||||
selection.value.isEmpty ? 'trash_page_restore_all'.tr() : 'restore'.tr(),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
@@ -33,10 +33,8 @@ class PermissionOnboardingPage extends HookConsumerWidget {
|
||||
).tr(),
|
||||
const SizedBox(height: 18),
|
||||
ElevatedButton(
|
||||
onPressed: () => ref
|
||||
.read(galleryPermissionNotifier.notifier)
|
||||
.requestGalleryPermission()
|
||||
.then((permission) async {
|
||||
onPressed: () =>
|
||||
ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission().then((permission) async {
|
||||
if (permission.isGranted) {
|
||||
// If permission is limited, we will show the limited
|
||||
// permission page
|
||||
@@ -139,12 +137,8 @@ class PermissionOnboardingPage extends HookConsumerWidget {
|
||||
final Widget child = switch (permission) {
|
||||
PermissionStatus.limited => buildPermissionLimited(),
|
||||
PermissionStatus.denied => buildRequestPermission(),
|
||||
PermissionStatus.granted ||
|
||||
PermissionStatus.provisional =>
|
||||
buildPermissionGranted(),
|
||||
PermissionStatus.restricted ||
|
||||
PermissionStatus.permanentlyDenied =>
|
||||
buildPermissionDenied()
|
||||
PermissionStatus.granted || PermissionStatus.provisional => buildPermissionGranted(),
|
||||
PermissionStatus.restricted || PermissionStatus.permanentlyDenied => buildPermissionDenied()
|
||||
};
|
||||
|
||||
return Scaffold(
|
||||
|
||||
@@ -40,8 +40,7 @@ class MemoryPage extends HookConsumerWidget {
|
||||
final currentAsset = useState<Asset?>(null);
|
||||
|
||||
/// The list of all of the asset page controllers
|
||||
final memoryAssetPageControllers =
|
||||
List.generate(memories.length, (i) => usePageController());
|
||||
final memoryAssetPageControllers = List.generate(memories.length, (i) => usePageController());
|
||||
|
||||
/// The main vertically scrolling page controller with each list of memories
|
||||
final memoryPageController = usePageController(initialPage: memoryIndex);
|
||||
@@ -73,19 +72,16 @@ class MemoryPage extends HookConsumerWidget {
|
||||
// Wait for the next frame to ensure the page is built
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
final previousIndex = currentMemoryIndex.value - 1;
|
||||
final previousMemoryController =
|
||||
memoryAssetPageControllers[previousIndex];
|
||||
final previousMemoryController = memoryAssetPageControllers[previousIndex];
|
||||
|
||||
// Ensure the controller is attached
|
||||
if (previousMemoryController.hasClients) {
|
||||
previousMemoryController
|
||||
.jumpToPage(memories[previousIndex].assets.length - 1);
|
||||
previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1);
|
||||
} else {
|
||||
// Wait for the next frame until it is attached
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
if (previousMemoryController.hasClients) {
|
||||
previousMemoryController
|
||||
.jumpToPage(memories[previousIndex].assets.length - 1);
|
||||
previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -96,8 +92,7 @@ class MemoryPage extends HookConsumerWidget {
|
||||
toNextAsset(int currentAssetIndex) {
|
||||
if (currentAssetIndex + 1 < currentMemory.value.assets.length) {
|
||||
// Go to the next asset
|
||||
PageController controller =
|
||||
memoryAssetPageControllers[currentMemoryIndex.value];
|
||||
PageController controller = memoryAssetPageControllers[currentMemoryIndex.value];
|
||||
|
||||
controller.nextPage(
|
||||
curve: Curves.easeInOut,
|
||||
@@ -112,8 +107,7 @@ class MemoryPage extends HookConsumerWidget {
|
||||
toPreviousAsset(int currentAssetIndex) {
|
||||
if (currentAssetIndex > 0) {
|
||||
// Go to the previous asset
|
||||
PageController controller =
|
||||
memoryAssetPageControllers[currentMemoryIndex.value];
|
||||
PageController controller = memoryAssetPageControllers[currentMemoryIndex.value];
|
||||
|
||||
controller.previousPage(
|
||||
curve: Curves.easeInOut,
|
||||
@@ -126,8 +120,7 @@ class MemoryPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
updateProgressText() {
|
||||
assetProgress.value =
|
||||
"${currentAssetPage.value + 1}|${currentMemory.value.assets.length}";
|
||||
assetProgress.value = "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}";
|
||||
}
|
||||
|
||||
/// Downloads and caches the image for the asset at this [currentMemory]'s index
|
||||
@@ -180,8 +173,7 @@ class MemoryPage extends HookConsumerWidget {
|
||||
|
||||
// Precache the next page right away if we are on the first page
|
||||
if (currentAssetPage.value == 0) {
|
||||
Future.delayed(const Duration(milliseconds: 200))
|
||||
.then((_) => precacheAsset(1));
|
||||
Future.delayed(const Duration(milliseconds: 200)).then((_) => precacheAsset(1));
|
||||
}
|
||||
|
||||
Future<void> onAssetChanged(int otherIndex) async {
|
||||
@@ -212,12 +204,10 @@ class MemoryPage extends HookConsumerWidget {
|
||||
// maxScrollExtend contains the sum of horizontal pixels of all assets for depth = 1
|
||||
// or sum of vertical pixels of all memories for depth = 0
|
||||
if (notification is ScrollUpdateNotification) {
|
||||
final isEpiloguePage =
|
||||
(memoryPageController.page?.floor() ?? 0) >= memories.length;
|
||||
final isEpiloguePage = (memoryPageController.page?.floor() ?? 0) >= memories.length;
|
||||
|
||||
final offset = notification.metrics.pixels;
|
||||
if (isEpiloguePage &&
|
||||
(offset > notification.metrics.maxScrollExtent + 150)) {
|
||||
if (isEpiloguePage && (offset > notification.metrics.maxScrollExtent + 150)) {
|
||||
context.maybePop();
|
||||
return true;
|
||||
}
|
||||
@@ -358,8 +348,7 @@ class MemoryPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (currentAsset.value != null &&
|
||||
currentAsset.value!.isVideo)
|
||||
if (currentAsset.value != null && currentAsset.value!.isVideo)
|
||||
Positioned(
|
||||
bottom: 24,
|
||||
right: 32,
|
||||
|
||||
@@ -106,9 +106,7 @@ class PhotosPage extends HookConsumerWidget {
|
||||
return Stack(
|
||||
children: [
|
||||
MultiselectGrid(
|
||||
topWidget: (currentUser != null && currentUser.memoryEnabled)
|
||||
? const MemoryLane()
|
||||
: const SizedBox(),
|
||||
topWidget: (currentUser != null && currentUser.memoryEnabled) ? const MemoryLane() : const SizedBox(),
|
||||
renderListProvider: timelineUsers.length > 1
|
||||
? multiUsersTimelineProvider(timelineUsers)
|
||||
: singleUserTimelineProvider(currentUser?.id),
|
||||
@@ -120,9 +118,7 @@ class PhotosPage extends HookConsumerWidget {
|
||||
),
|
||||
AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
top: ref.watch(multiselectProvider)
|
||||
? -(kToolbarHeight + context.padding.top)
|
||||
: 0,
|
||||
top: ref.watch(multiselectProvider) ? -(kToolbarHeight + context.padding.top) : 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
|
||||
@@ -28,9 +28,7 @@ class AllPeoplePage extends HookConsumerWidget {
|
||||
body: curatedPeople.widgetWhen(
|
||||
onData: (people) => ExploreGrid(
|
||||
isPeople: true,
|
||||
curatedContent: people
|
||||
.map((e) => SearchCuratedContent(label: e.name, id: e.id))
|
||||
.toList(),
|
||||
curatedContent: people.map((e) => SearchCuratedContent(label: e.name, id: e.id)).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -13,8 +13,7 @@ class AllPlacesPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
AsyncValue<List<SearchCuratedContent>> places =
|
||||
ref.watch(getAllPlacesProvider);
|
||||
AsyncValue<List<SearchCuratedContent>> places = ref.watch(getAllPlacesProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
||||
@@ -48,8 +48,7 @@ class MapPage extends HookConsumerWidget {
|
||||
final layerDebouncer = useDebouncer(interval: const Duration(seconds: 1));
|
||||
final isLoading = useProcessingOverlay();
|
||||
final scrollController = useScrollController();
|
||||
final markerDebouncer =
|
||||
useDebouncer(interval: const Duration(milliseconds: 800));
|
||||
final markerDebouncer = useDebouncer(interval: const Duration(milliseconds: 800));
|
||||
final selectedAssets = useValueNotifier<Set<Asset>>({});
|
||||
const mapZoomToAssetLevel = 12.0;
|
||||
|
||||
@@ -64,8 +63,7 @@ class MapPage extends HookConsumerWidget {
|
||||
final bounds = await mapController.value!.getVisibleRegion();
|
||||
final inBounds = markers.value
|
||||
.where(
|
||||
(m) =>
|
||||
bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude)),
|
||||
(m) => bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude)),
|
||||
)
|
||||
.toList();
|
||||
// Notify bottom sheet to update asset grid only when there are new assets
|
||||
@@ -101,8 +99,7 @@ class MapPage extends HookConsumerWidget {
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
final currentAssetLink =
|
||||
ref.read(currentAssetProvider.notifier).ref.keepAlive();
|
||||
final currentAssetLink = ref.read(currentAssetProvider.notifier).ref.keepAlive();
|
||||
|
||||
loadMarkers();
|
||||
return currentAssetLink.close;
|
||||
@@ -128,8 +125,7 @@ class MapPage extends HookConsumerWidget {
|
||||
MapMarker marker, {
|
||||
bool shouldAnimate = true,
|
||||
}) async {
|
||||
final assetPoint =
|
||||
await mapController.value!.toScreenLocation(marker.latLng);
|
||||
final assetPoint = await mapController.value!.toScreenLocation(marker.latLng);
|
||||
selectedMarker.value = _AssetMarkerMeta(
|
||||
point: assetPoint,
|
||||
marker: marker,
|
||||
@@ -144,11 +140,9 @@ class MapPage extends HookConsumerWidget {
|
||||
if (mapController.value == null) {
|
||||
return;
|
||||
}
|
||||
final latlngBound =
|
||||
await mapController.value!.getBoundsFromPoint(point, 50);
|
||||
final latlngBound = await mapController.value!.getBoundsFromPoint(point, 50);
|
||||
final marker = markersInBounds.value.firstWhereOrNull(
|
||||
(m) =>
|
||||
latlngBound.contains(LatLng(m.latLng.latitude, m.latLng.longitude)),
|
||||
(m) => latlngBound.contains(LatLng(m.latLng.latitude, m.latLng.longitude)),
|
||||
);
|
||||
|
||||
if (marker != null) {
|
||||
@@ -211,16 +205,14 @@ class MapPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
void onBottomSheetScrolled(String assetRemoteId) {
|
||||
final assetMarker = markersInBounds.value
|
||||
.firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId);
|
||||
final assetMarker = markersInBounds.value.firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId);
|
||||
if (assetMarker != null) {
|
||||
updateAssetMarkerPosition(assetMarker);
|
||||
}
|
||||
}
|
||||
|
||||
void onZoomToAsset(String assetRemoteId) {
|
||||
final assetMarker = markersInBounds.value
|
||||
.firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId);
|
||||
final assetMarker = markersInBounds.value.firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId);
|
||||
if (mapController.value != null && assetMarker != null) {
|
||||
// Offset the latitude a little to show the marker just above the viewports center
|
||||
final offset = context.isMobile ? 0.02 : 0;
|
||||
@@ -236,8 +228,7 @@ class MapPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
void onZoomToLocation() async {
|
||||
final (location, error) =
|
||||
await MapUtils.checkPermAndGetLocation(context: context);
|
||||
final (location, error) = await MapUtils.checkPermAndGetLocation(context: context);
|
||||
if (error != null) {
|
||||
if (error == LocationPermission.unableToDetermine && context.mounted) {
|
||||
ImmichToast.show(
|
||||
@@ -360,8 +351,7 @@ class _AssetMarkerMeta {
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'_AssetMarkerMeta(point: $point, marker: $marker, shouldAnimate: $shouldAnimate)';
|
||||
String toString() => '_AssetMarkerMeta(point: $point, marker: $marker, shouldAnimate: $shouldAnimate)';
|
||||
}
|
||||
|
||||
class _MapWithMarker extends StatelessWidget {
|
||||
|
||||
@@ -35,8 +35,7 @@ class MapLocationPickerPage extends HookConsumerWidget {
|
||||
selectedLatLng.value = centre;
|
||||
controller.value?.animateCamera(CameraUpdate.newLatLng(centre));
|
||||
if (marker.value != null) {
|
||||
await controller.value
|
||||
?.updateSymbol(marker.value!, SymbolOptions(geometry: centre));
|
||||
await controller.value?.updateSymbol(marker.value!, SymbolOptions(geometry: centre));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,15 +44,13 @@ class MapLocationPickerPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
Future<void> getCurrentLocation() async {
|
||||
var (currentLocation, _) =
|
||||
await MapUtils.checkPermAndGetLocation(context: context);
|
||||
var (currentLocation, _) = await MapUtils.checkPermAndGetLocation(context: context);
|
||||
|
||||
if (currentLocation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentLatLng =
|
||||
LatLng(currentLocation.latitude, currentLocation.longitude);
|
||||
var currentLatLng = LatLng(currentLocation.latitude, currentLocation.longitude);
|
||||
selectedLatLng.value = currentLatLng;
|
||||
controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng));
|
||||
}
|
||||
@@ -75,11 +72,9 @@ class MapLocationPickerPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
child: MapLibreMap(
|
||||
initialCameraPosition:
|
||||
CameraPosition(target: initialLatLng, zoom: 12),
|
||||
initialCameraPosition: CameraPosition(target: initialLatLng, zoom: 12),
|
||||
styleString: style,
|
||||
onMapCreated: (mapController) =>
|
||||
controller.value = mapController,
|
||||
onMapCreated: (mapController) => controller.value = mapController,
|
||||
onStyleLoadedCallback: onStyleLoaded,
|
||||
onMapClick: onMapClick,
|
||||
dragEnabled: false,
|
||||
@@ -165,8 +160,7 @@ class _BottomBar extends StatelessWidget {
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: onUseLocation,
|
||||
child:
|
||||
const Text("map_location_picker_page_use_location").tr(),
|
||||
child: const Text("map_location_picker_page_use_location").tr(),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: onGetCurrentLocation,
|
||||
|
||||
@@ -48,8 +48,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
isFavorite: false,
|
||||
),
|
||||
mediaType: prefilter?.mediaType ?? AssetType.other,
|
||||
language:
|
||||
"${context.locale.languageCode}-${context.locale.countryCode}",
|
||||
language: "${context.locale.languageCode}-${context.locale.countryCode}",
|
||||
),
|
||||
);
|
||||
|
||||
@@ -87,9 +86,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
|
||||
isSearching.value = true;
|
||||
ref.watch(paginatedSearchProvider.notifier).clear();
|
||||
final hasResult = await ref
|
||||
.watch(paginatedSearchProvider.notifier)
|
||||
.search(filter.value);
|
||||
final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
|
||||
|
||||
if (!hasResult) {
|
||||
context.showSnackBar(
|
||||
@@ -103,9 +100,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
|
||||
loadMoreSearchResult() async {
|
||||
isSearching.value = true;
|
||||
final hasResult = await ref
|
||||
.watch(paginatedSearchProvider.notifier)
|
||||
.search(filter.value);
|
||||
final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
|
||||
|
||||
if (!hasResult) {
|
||||
context.showSnackBar(
|
||||
@@ -416,8 +411,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
if (value) {
|
||||
filterText
|
||||
.add('search_filter_display_option_not_in_album'.tr());
|
||||
filterText.add('search_filter_display_option_not_in_album'.tr());
|
||||
}
|
||||
break;
|
||||
case DisplayOption.archive:
|
||||
@@ -563,9 +557,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
'search_by_context'.tr(),
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: textSearchType.value == TextSearchType.context
|
||||
? context.colorScheme.primary
|
||||
: null,
|
||||
color: textSearchType.value == TextSearchType.context ? context.colorScheme.primary : null,
|
||||
),
|
||||
),
|
||||
selectedColor: context.colorScheme.primary,
|
||||
@@ -583,9 +575,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
'search_filter_filename'.tr(),
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: textSearchType.value == TextSearchType.filename
|
||||
? context.colorScheme.primary
|
||||
: null,
|
||||
color: textSearchType.value == TextSearchType.filename ? context.colorScheme.primary : null,
|
||||
),
|
||||
),
|
||||
selectedColor: context.colorScheme.primary,
|
||||
@@ -603,15 +593,11 @@ class SearchPage extends HookConsumerWidget {
|
||||
'search_by_description'.tr(),
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color:
|
||||
textSearchType.value == TextSearchType.description
|
||||
? context.colorScheme.primary
|
||||
: null,
|
||||
color: textSearchType.value == TextSearchType.description ? context.colorScheme.primary : null,
|
||||
),
|
||||
),
|
||||
selectedColor: context.colorScheme.primary,
|
||||
selected:
|
||||
textSearchType.value == TextSearchType.description,
|
||||
selected: textSearchType.value == TextSearchType.description,
|
||||
),
|
||||
onPressed: () {
|
||||
textSearchType.value = TextSearchType.description;
|
||||
@@ -645,9 +631,7 @@ class SearchPage extends HookConsumerWidget {
|
||||
hintText: searchHintText.value,
|
||||
key: const Key('search_text_field'),
|
||||
controller: textSearchController,
|
||||
contentPadding: prefilter != null
|
||||
? const EdgeInsets.only(left: 24)
|
||||
: const EdgeInsets.all(8),
|
||||
contentPadding: prefilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8),
|
||||
prefixIcon: prefilter != null
|
||||
? null
|
||||
: Icon(
|
||||
@@ -744,17 +728,13 @@ class SearchResultGrid extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: NotificationListener<ScrollEndNotification>(
|
||||
onNotification: (notification) {
|
||||
final isBottomSheetNotification = notification.context
|
||||
?.findAncestorWidgetOfExactType<
|
||||
DraggableScrollableSheet>() !=
|
||||
null;
|
||||
final isBottomSheetNotification =
|
||||
notification.context?.findAncestorWidgetOfExactType<DraggableScrollableSheet>() != null;
|
||||
|
||||
final metrics = notification.metrics;
|
||||
final isVerticalScroll = metrics.axis == Axis.vertical;
|
||||
|
||||
if (metrics.pixels >= metrics.maxScrollExtent &&
|
||||
isVerticalScroll &&
|
||||
!isBottomSheetNotification) {
|
||||
if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) {
|
||||
onScrollEnd();
|
||||
}
|
||||
|
||||
@@ -770,9 +750,7 @@ class SearchResultGrid extends StatelessWidget {
|
||||
dragScrollLabelEnabled: false,
|
||||
emptyIndicator: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: !isSearching
|
||||
? const SearchEmptyContent()
|
||||
: const SizedBox.shrink(),
|
||||
child: !isSearching ? const SearchEmptyContent() : const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -794,9 +772,7 @@ class SearchEmptyContent extends StatelessWidget {
|
||||
const SizedBox(height: 40),
|
||||
Center(
|
||||
child: Image.asset(
|
||||
context.isDarkTheme
|
||||
? 'assets/polaroid-dark.png'
|
||||
: 'assets/polaroid-light.png',
|
||||
context.isDarkTheme ? 'assets/polaroid-dark.png' : 'assets/polaroid-light.png',
|
||||
height: 125,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -38,9 +38,7 @@ class ShareIntentPage extends HookConsumerWidget {
|
||||
|
||||
void upload() async {
|
||||
for (final attachment in candidates) {
|
||||
await ref
|
||||
.read(shareIntentUploadProvider.notifier)
|
||||
.upload(attachment.file);
|
||||
await ref.read(shareIntentUploadProvider.notifier).upload(attachment.file);
|
||||
}
|
||||
|
||||
isUploaded.value = true;
|
||||
@@ -76,9 +74,7 @@ class ShareIntentPage extends HookConsumerWidget {
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
context.navigateTo(
|
||||
Store.isBetaTimelineEnabled
|
||||
? const TabShellRoute()
|
||||
: const TabControllerRoute(),
|
||||
Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute(),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
@@ -170,9 +166,7 @@ class ShareIntentPage extends HookConsumerWidget {
|
||||
height: 48,
|
||||
child: ElevatedButton(
|
||||
onPressed: isUploaded.value ? null : upload,
|
||||
child: isUploaded.value
|
||||
? UploadingText(candidates: candidates)
|
||||
: const Text('upload').tr(),
|
||||
child: isUploaded.value ? UploadingText(candidates: candidates) : const Text('upload').tr(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user