mirror of
https://github.com/immich-app/immich.git
synced 2025-12-17 01:11:13 +03:00
chore: bump dart sdk to 3.8 (#20355)
* chore: bump dart sdk to 3.8 * chore: make build * make pigeon * chore: format files --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
@@ -34,23 +34,15 @@ final _features = [
|
||||
final assets = await ref.read(remoteAssetRepositoryProvider).getSome(user.id);
|
||||
|
||||
final selectedAssets = await ctx.pushRoute<Set<BaseAsset>>(
|
||||
DriftAssetSelectionTimelineRoute(
|
||||
lockedSelectionAssets: assets.toSet(),
|
||||
),
|
||||
DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()),
|
||||
);
|
||||
|
||||
DLog.log(
|
||||
"Selected ${selectedAssets?.length ?? 0} assets",
|
||||
);
|
||||
DLog.log("Selected ${selectedAssets?.length ?? 0} assets");
|
||||
|
||||
return Future.value();
|
||||
},
|
||||
),
|
||||
_Feature(
|
||||
name: '',
|
||||
icon: Icons.vertical_align_center_sharp,
|
||||
onTap: (_, __) => Future.value(),
|
||||
),
|
||||
_Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()),
|
||||
_Feature(
|
||||
name: 'Sync Local',
|
||||
icon: Icons.photo_album_rounded,
|
||||
@@ -76,11 +68,7 @@ final _features = [
|
||||
icon: Icons.save_rounded,
|
||||
onTap: (_, ref) => ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"),
|
||||
),
|
||||
_Feature(
|
||||
name: '',
|
||||
icon: Icons.vertical_align_center_sharp,
|
||||
onTap: (_, __) => Future.value(),
|
||||
),
|
||||
_Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()),
|
||||
_Feature(
|
||||
name: 'Clear Delta Checkpoint',
|
||||
icon: Icons.delete_rounded,
|
||||
@@ -88,10 +76,7 @@ final _features = [
|
||||
),
|
||||
_Feature(
|
||||
name: 'Clear Local Data',
|
||||
style: const TextStyle(
|
||||
color: Colors.orange,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
|
||||
icon: Icons.delete_forever_rounded,
|
||||
onTap: (_, ref) async {
|
||||
final db = ref.read(driftProvider);
|
||||
@@ -102,10 +87,7 @@ final _features = [
|
||||
),
|
||||
_Feature(
|
||||
name: 'Clear Remote Data',
|
||||
style: const TextStyle(
|
||||
color: Colors.orange,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
|
||||
icon: Icons.delete_sweep_rounded,
|
||||
onTap: (_, ref) async {
|
||||
final db = ref.read(driftProvider);
|
||||
@@ -123,29 +105,20 @@ final _features = [
|
||||
),
|
||||
_Feature(
|
||||
name: 'Local Media Summary',
|
||||
style: const TextStyle(
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold),
|
||||
icon: Icons.table_chart_rounded,
|
||||
onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()),
|
||||
),
|
||||
_Feature(
|
||||
name: 'Remote Media Summary',
|
||||
style: const TextStyle(
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold),
|
||||
icon: Icons.summarize_rounded,
|
||||
onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()),
|
||||
),
|
||||
_Feature(
|
||||
name: 'Reset Sqlite',
|
||||
icon: Icons.table_view_rounded,
|
||||
style: const TextStyle(
|
||||
color: Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
|
||||
onTap: (_, ref) async {
|
||||
final drift = ref.read(driftProvider);
|
||||
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
|
||||
@@ -165,10 +138,7 @@ class FeatInDevPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Features in Development'),
|
||||
centerTitle: true,
|
||||
),
|
||||
appBar: AppBar(title: const Text('Features in Development'), centerTitle: true),
|
||||
body: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
@@ -178,10 +148,7 @@ class FeatInDevPage extends StatelessWidget {
|
||||
final feat = _features[index];
|
||||
return Consumer(
|
||||
builder: (ctx, ref, _) => ListTile(
|
||||
title: Text(
|
||||
feat.name,
|
||||
style: feat.style,
|
||||
),
|
||||
title: Text(feat.name, style: feat.style),
|
||||
trailing: Icon(feat.icon),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: () => unawaited(feat.onTap(ctx, ref)),
|
||||
@@ -200,12 +167,7 @@ class FeatInDevPage extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _Feature {
|
||||
const _Feature({
|
||||
required this.name,
|
||||
required this.icon,
|
||||
required this.onTap,
|
||||
this.style,
|
||||
});
|
||||
const _Feature({required this.name, required this.icon, required this.onTap, this.style});
|
||||
|
||||
final String name;
|
||||
final IconData icon;
|
||||
@@ -244,18 +206,11 @@ class _DevLogs extends StatelessWidget {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
logMessage.message,
|
||||
style: TextStyle(
|
||||
color: ctx.colorScheme.onSurface,
|
||||
fontSize: 14.0,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
style: TextStyle(color: ctx.colorScheme.onSurface, fontSize: 14.0, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
subtitle: Text(
|
||||
"at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}",
|
||||
style: TextStyle(
|
||||
color: ctx.colorScheme.onSurfaceSecondary,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
style: TextStyle(color: ctx.colorScheme.onSurfaceSecondary, fontSize: 12.0),
|
||||
),
|
||||
dense: true,
|
||||
visualDensity: VisualDensity.compact,
|
||||
|
||||
@@ -21,12 +21,7 @@ class _Summary extends StatelessWidget {
|
||||
final Future<int> countFuture;
|
||||
final void Function()? onTap;
|
||||
|
||||
const _Summary({
|
||||
required this.name,
|
||||
required this.countFuture,
|
||||
this.leading,
|
||||
this.onTap,
|
||||
});
|
||||
const _Summary({required this.name, required this.countFuture, this.leading, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -40,31 +35,17 @@ class _Summary extends StatelessWidget {
|
||||
} else if (snapshot.hasError) {
|
||||
subtitle = const Icon(Icons.error_rounded);
|
||||
} else {
|
||||
subtitle = Text(
|
||||
'${snapshot.data ?? 0}',
|
||||
style: ctx.textTheme.bodyLarge,
|
||||
);
|
||||
subtitle = Text('${snapshot.data ?? 0}', style: ctx.textTheme.bodyLarge);
|
||||
}
|
||||
return ListTile(
|
||||
leading: leading,
|
||||
title: Text(name),
|
||||
trailing: subtitle,
|
||||
onTap: onTap,
|
||||
);
|
||||
return ListTile(leading: leading, title: Text(name), trailing: subtitle, onTap: onTap);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final _localStats = [
|
||||
_Stat(
|
||||
name: 'Local Assets',
|
||||
load: (db) => db.managers.localAssetEntity.count(),
|
||||
),
|
||||
_Stat(
|
||||
name: 'Local Albums',
|
||||
load: (db) => db.managers.localAlbumEntity.count(),
|
||||
),
|
||||
_Stat(name: 'Local Assets', load: (db) => db.managers.localAssetEntity.count()),
|
||||
_Stat(name: 'Local Albums', load: (db) => db.managers.localAlbumEntity.count()),
|
||||
];
|
||||
|
||||
@RoutePage()
|
||||
@@ -97,10 +78,7 @@ class LocalMediaSummaryPage extends StatelessWidget {
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 15),
|
||||
child: Text(
|
||||
"Album summary",
|
||||
style: ctx.textTheme.titleMedium,
|
||||
),
|
||||
child: Text("Album summary", style: ctx.textTheme.titleMedium),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -117,15 +95,14 @@ class LocalMediaSummaryPage extends StatelessWidget {
|
||||
return SliverList.builder(
|
||||
itemBuilder: (_, index) {
|
||||
final album = albums[index];
|
||||
final countFuture =
|
||||
db.managers.localAlbumAssetEntity.filter((f) => f.albumId.id.equals(album.id)).count();
|
||||
final countFuture = db.managers.localAlbumAssetEntity
|
||||
.filter((f) => f.albumId.id.equals(album.id))
|
||||
.count();
|
||||
return _Summary(
|
||||
leading: const Icon(Icons.photo_album_rounded),
|
||||
name: album.name,
|
||||
countFuture: countFuture,
|
||||
onTap: () => context.router.push(
|
||||
LocalTimelineRoute(album: album),
|
||||
),
|
||||
onTap: () => context.router.push(LocalTimelineRoute(album: album)),
|
||||
);
|
||||
},
|
||||
itemCount: albums.length,
|
||||
@@ -141,38 +118,14 @@ class LocalMediaSummaryPage extends StatelessWidget {
|
||||
}
|
||||
|
||||
final _remoteStats = [
|
||||
_Stat(
|
||||
name: 'Remote Assets',
|
||||
load: (db) => db.managers.remoteAssetEntity.count(),
|
||||
),
|
||||
_Stat(
|
||||
name: 'Exif Entities',
|
||||
load: (db) => db.managers.remoteExifEntity.count(),
|
||||
),
|
||||
_Stat(
|
||||
name: 'Remote Albums',
|
||||
load: (db) => db.managers.remoteAlbumEntity.count(),
|
||||
),
|
||||
_Stat(
|
||||
name: 'Memories',
|
||||
load: (db) => db.managers.memoryEntity.count(),
|
||||
),
|
||||
_Stat(
|
||||
name: 'Memories Assets',
|
||||
load: (db) => db.managers.memoryAssetEntity.count(),
|
||||
),
|
||||
_Stat(
|
||||
name: 'Stacks',
|
||||
load: (db) => db.managers.stackEntity.count(),
|
||||
),
|
||||
_Stat(
|
||||
name: 'People',
|
||||
load: (db) => db.managers.personEntity.count(),
|
||||
),
|
||||
_Stat(
|
||||
name: 'AssetFaces',
|
||||
load: (db) => db.managers.assetFaceEntity.count(),
|
||||
),
|
||||
_Stat(name: 'Remote Assets', load: (db) => db.managers.remoteAssetEntity.count()),
|
||||
_Stat(name: 'Exif Entities', load: (db) => db.managers.remoteExifEntity.count()),
|
||||
_Stat(name: 'Remote Albums', load: (db) => db.managers.remoteAlbumEntity.count()),
|
||||
_Stat(name: 'Memories', load: (db) => db.managers.memoryEntity.count()),
|
||||
_Stat(name: 'Memories Assets', load: (db) => db.managers.memoryAssetEntity.count()),
|
||||
_Stat(name: 'Stacks', load: (db) => db.managers.stackEntity.count()),
|
||||
_Stat(name: 'People', load: (db) => db.managers.personEntity.count()),
|
||||
_Stat(name: 'AssetFaces', load: (db) => db.managers.assetFaceEntity.count()),
|
||||
];
|
||||
|
||||
@RoutePage()
|
||||
@@ -205,10 +158,7 @@ class RemoteMediaSummaryPage extends StatelessWidget {
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 15),
|
||||
child: Text(
|
||||
"Album summary",
|
||||
style: ctx.textTheme.titleMedium,
|
||||
),
|
||||
child: Text("Album summary", style: ctx.textTheme.titleMedium),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -225,15 +175,14 @@ class RemoteMediaSummaryPage extends StatelessWidget {
|
||||
return SliverList.builder(
|
||||
itemBuilder: (_, index) {
|
||||
final album = albums[index];
|
||||
final countFuture =
|
||||
db.managers.remoteAlbumAssetEntity.filter((f) => f.albumId.id.equals(album.id)).count();
|
||||
final countFuture = db.managers.remoteAlbumAssetEntity
|
||||
.filter((f) => f.albumId.id.equals(album.id))
|
||||
.count();
|
||||
return _Summary(
|
||||
leading: const Icon(Icons.photo_album_rounded),
|
||||
name: album.name,
|
||||
countFuture: countFuture,
|
||||
onTap: () => context.router.push(
|
||||
RemoteAlbumRoute(album: album),
|
||||
),
|
||||
onTap: () => context.router.push(RemoteAlbumRoute(album: album)),
|
||||
);
|
||||
},
|
||||
itemCount: albums.length,
|
||||
|
||||
@@ -35,13 +35,8 @@ class _DriftAlbumsPageState extends ConsumerState<DriftAlbumsPage> {
|
||||
pinned: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.add_rounded,
|
||||
size: 28,
|
||||
),
|
||||
onPressed: () => context.pushRoute(
|
||||
const DriftCreateAlbumRoute(),
|
||||
),
|
||||
icon: const Icon(Icons.add_rounded, size: 28),
|
||||
onPressed: () => context.pushRoute(const DriftCreateAlbumRoute()),
|
||||
),
|
||||
],
|
||||
showUploadButton: false,
|
||||
@@ -49,9 +44,7 @@ class _DriftAlbumsPageState extends ConsumerState<DriftAlbumsPage> {
|
||||
AlbumSelector(
|
||||
onAlbumSelected: (album) {
|
||||
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album);
|
||||
context.router.push(
|
||||
RemoteAlbumRoute(album: album),
|
||||
);
|
||||
context.router.push(RemoteAlbumRoute(album: album));
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -16,18 +16,16 @@ class DriftArchivePage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access archive');
|
||||
}
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access archive');
|
||||
}
|
||||
|
||||
final timelineService = ref.watch(timelineFactoryProvider).archive(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
final timelineService = ref.watch(timelineFactoryProvider).archive(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
appBar: MesmerizingSliverAppBar(
|
||||
|
||||
@@ -10,10 +10,7 @@ import 'package:immich_mobile/providers/user.provider.dart';
|
||||
@RoutePage()
|
||||
class DriftAssetSelectionTimelinePage extends ConsumerWidget {
|
||||
final Set<BaseAsset> lockedSelectionAssets;
|
||||
const DriftAssetSelectionTimelinePage({
|
||||
super.key,
|
||||
this.lockedSelectionAssets = const {},
|
||||
});
|
||||
const DriftAssetSelectionTimelinePage({super.key, this.lockedSelectionAssets = const {}});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -21,27 +18,19 @@ class DriftAssetSelectionTimelinePage extends ConsumerWidget {
|
||||
overrides: [
|
||||
multiSelectProvider.overrideWith(
|
||||
() => MultiSelectNotifier(
|
||||
MultiSelectState(
|
||||
selectedAssets: {},
|
||||
lockedSelectionAssets: lockedSelectionAssets,
|
||||
forceEnable: true,
|
||||
),
|
||||
MultiSelectState(selectedAssets: {}, lockedSelectionAssets: lockedSelectionAssets, forceEnable: true),
|
||||
),
|
||||
),
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception(
|
||||
'User must be logged in to access asset selection timeline',
|
||||
);
|
||||
}
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access asset selection timeline');
|
||||
}
|
||||
|
||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: const Timeline(),
|
||||
);
|
||||
|
||||
@@ -70,12 +70,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
|
||||
Widget _buildContent() {
|
||||
if (selectedAssets.isEmpty) {
|
||||
return SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
_buildEmptyState(),
|
||||
_buildSelectPhotosButton(),
|
||||
]),
|
||||
);
|
||||
return SliverList(delegate: SliverChildListDelegate([_buildEmptyState(), _buildSelectPhotosButton()]));
|
||||
} else {
|
||||
return _buildSelectedImageGrid();
|
||||
}
|
||||
@@ -84,10 +79,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
Widget _buildEmptyState() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 0, left: 18),
|
||||
child: Text(
|
||||
'create_shared_album_page_share_add_assets',
|
||||
style: context.textTheme.labelLarge,
|
||||
).t(),
|
||||
child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).t(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -97,27 +89,17 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
child: FilledButton.icon(
|
||||
style: FilledButton.styleFrom(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 24.0,
|
||||
horizontal: 16.0,
|
||||
),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 16.0),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10.0))),
|
||||
backgroundColor: context.colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
onPressed: onSelectPhotos,
|
||||
icon: Icon(Icons.add_rounded, color: context.primaryColor),
|
||||
label: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8.0,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
'create_shared_album_page_share_select_photos',
|
||||
style: context.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor),
|
||||
).t(),
|
||||
),
|
||||
),
|
||||
@@ -133,16 +115,13 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
crossAxisSpacing: 1.0,
|
||||
mainAxisSpacing: 1.0,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
final asset = selectedAssets.elementAt(index);
|
||||
return GestureDetector(
|
||||
onTap: onBackgroundTapped,
|
||||
child: Thumbnail(asset: asset),
|
||||
);
|
||||
},
|
||||
childCount: selectedAssets.length,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
final asset = selectedAssets.elementAt(index);
|
||||
return GestureDetector(
|
||||
onTap: onBackgroundTapped,
|
||||
child: Thumbnail(asset: asset),
|
||||
);
|
||||
}, childCount: selectedAssets.length),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -162,9 +141,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
|
||||
Future<void> onSelectPhotos() async {
|
||||
final assets = await context.pushRoute<Set<BaseAsset>>(
|
||||
DriftAssetSelectionTimelineRoute(
|
||||
lockedSelectionAssets: selectedAssets,
|
||||
),
|
||||
DriftAssetSelectionTimelineRoute(lockedSelectionAssets: selectedAssets),
|
||||
);
|
||||
|
||||
if (assets == null || assets.isEmpty) {
|
||||
@@ -183,16 +160,15 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
if (title.isEmpty) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('create_album_title_required'.t()),
|
||||
backgroundColor: context.colorScheme.error,
|
||||
),
|
||||
SnackBar(content: Text('create_album_title_required'.t()), backgroundColor: context.colorScheme.error),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final album = await ref.watch(remoteAlbumProvider.notifier).createAlbum(
|
||||
final album = await ref
|
||||
.watch(remoteAlbumProvider.notifier)
|
||||
.createAlbum(
|
||||
title: title,
|
||||
description: albumDescriptionController.text.trim(),
|
||||
assetIds: selectedAssets.map((asset) {
|
||||
@@ -203,18 +179,13 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
|
||||
if (album != null) {
|
||||
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album);
|
||||
context.replaceRoute(
|
||||
RemoteAlbumRoute(album: album),
|
||||
);
|
||||
context.replaceRoute(RemoteAlbumRoute(album: album));
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildTitleInputField() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 10.0,
|
||||
left: 10.0,
|
||||
),
|
||||
padding: const EdgeInsets.only(right: 10.0, left: 10.0),
|
||||
child: _AlbumTitleTextField(
|
||||
focusNode: albumTitleTextFieldFocusNode,
|
||||
textController: albumTitleController,
|
||||
@@ -230,11 +201,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
|
||||
Widget buildDescriptionInputField() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 10.0,
|
||||
left: 10.0,
|
||||
top: 8,
|
||||
),
|
||||
padding: const EdgeInsets.only(right: 10.0, left: 10.0, top: 8),
|
||||
child: _AlbumViewerEditableDescription(
|
||||
textController: albumDescriptionController,
|
||||
focusNode: albumDescriptionTextFieldFocusNode,
|
||||
@@ -244,11 +211,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
|
||||
Widget buildControlButton() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12.0,
|
||||
top: 8.0,
|
||||
bottom: 8.0,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 12.0, top: 8.0, bottom: 8.0),
|
||||
child: SizedBox(
|
||||
height: 42.0,
|
||||
child: ListView(
|
||||
@@ -272,10 +235,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
elevation: 0,
|
||||
centerTitle: false,
|
||||
backgroundColor: context.scaffoldBackgroundColor,
|
||||
leading: IconButton(
|
||||
onPressed: () => context.maybePop(),
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
),
|
||||
leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.close_rounded)),
|
||||
title: const Text('create_album').t(),
|
||||
actions: [
|
||||
TextButton(
|
||||
@@ -292,12 +252,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
||||
),
|
||||
body: GestureDetector(
|
||||
onTap: onBackgroundTapped,
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
_buildSliverAppBar(),
|
||||
_buildContent(),
|
||||
],
|
||||
),
|
||||
child: CustomScrollView(slivers: [_buildSliverAppBar(), _buildContent()]),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -341,11 +296,7 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> {
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
focusNode: widget.focusNode,
|
||||
style: TextStyle(
|
||||
fontSize: 28.0,
|
||||
color: context.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: TextStyle(fontSize: 28.0, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold),
|
||||
controller: widget.textController,
|
||||
onTap: () {
|
||||
if (widget.textController.text == 'create_album_page_untitled'.t(context: context)) {
|
||||
@@ -353,35 +304,23 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> {
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 16.0,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0),
|
||||
suffixIcon: widget.textController.text.isNotEmpty && widget.isFocus
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
widget.textController.clear();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.cancel_rounded,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
icon: Icon(Icons.cancel_rounded, color: context.primaryColor),
|
||||
splashRadius: 10.0,
|
||||
)
|
||||
: null,
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.transparent),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16.0),
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(16.0)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: context.primaryColor.withValues(alpha: 0.3),
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(16.0),
|
||||
),
|
||||
borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
|
||||
),
|
||||
hintText: 'add_a_title'.t(),
|
||||
hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith(
|
||||
@@ -398,10 +337,7 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> {
|
||||
}
|
||||
|
||||
class _AlbumViewerEditableDescription extends StatefulWidget {
|
||||
const _AlbumViewerEditableDescription({
|
||||
required this.textController,
|
||||
required this.focusNode,
|
||||
});
|
||||
const _AlbumViewerEditableDescription({required this.textController, required this.focusNode});
|
||||
|
||||
final TextEditingController textController;
|
||||
final FocusNode focusNode;
|
||||
@@ -448,37 +384,23 @@ class _AlbumViewerEditableDescriptionState extends State<_AlbumViewerEditableDes
|
||||
minLines: 1,
|
||||
controller: widget.textController,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 16.0,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 16.0),
|
||||
suffixIcon: widget.focusNode.hasFocus && widget.textController.text.isNotEmpty
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
widget.textController.clear();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.cancel_rounded,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
icon: Icon(Icons.cancel_rounded, color: context.primaryColor),
|
||||
splashRadius: 10.0,
|
||||
)
|
||||
: null,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: context.colorScheme.outline.withValues(alpha: 0.3),
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(16.0),
|
||||
),
|
||||
borderSide: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.3)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: context.primaryColor.withValues(alpha: 0.3),
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(16.0),
|
||||
),
|
||||
borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
|
||||
),
|
||||
hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith(
|
||||
fontSize: 16.0,
|
||||
|
||||
@@ -16,18 +16,16 @@ class DriftFavoritePage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access favorite');
|
||||
}
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access favorite');
|
||||
}
|
||||
|
||||
final timelineService = ref.watch(timelineFactoryProvider).favorite(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
final timelineService = ref.watch(timelineFactoryProvider).favorite(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
appBar: MesmerizingSliverAppBar(
|
||||
|
||||
@@ -27,12 +27,7 @@ class DriftLibraryPage extends ConsumerWidget {
|
||||
return const Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
ImmichSliverAppBar(
|
||||
snap: false,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
showUploadButton: false,
|
||||
),
|
||||
ImmichSliverAppBar(snap: false, floating: false, pinned: true, showUploadButton: false),
|
||||
_ActionButtonGrid(),
|
||||
_CollectionCards(),
|
||||
_QuickAccessButtonList(),
|
||||
@@ -47,9 +42,7 @@ class _ActionButtonGrid extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isTrashEnable = ref.watch(
|
||||
serverInfoProvider.select((state) => state.serverFeatures.trash),
|
||||
);
|
||||
final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash));
|
||||
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 12),
|
||||
@@ -97,11 +90,7 @@ class _ActionButtonGrid extends ConsumerWidget {
|
||||
}
|
||||
|
||||
class _ActionButton extends StatelessWidget {
|
||||
const _ActionButton({
|
||||
required this.icon,
|
||||
required this.onTap,
|
||||
required this.label,
|
||||
});
|
||||
const _ActionButton({required this.icon, required this.onTap, required this.label});
|
||||
|
||||
final IconData icon;
|
||||
final VoidCallback onTap;
|
||||
@@ -114,13 +103,7 @@ class _ActionButton extends StatelessWidget {
|
||||
onPressed: onTap,
|
||||
label: Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: context.colorScheme.onSurface,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)),
|
||||
),
|
||||
style: FilledButton.styleFrom(
|
||||
elevation: 0,
|
||||
@@ -129,16 +112,10 @@ class _ActionButton extends StatelessWidget {
|
||||
alignment: Alignment.centerLeft,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
||||
side: BorderSide(
|
||||
color: context.colorScheme.onSurface.withAlpha(10),
|
||||
width: 1,
|
||||
),
|
||||
side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1),
|
||||
),
|
||||
),
|
||||
icon: Icon(
|
||||
icon,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
icon: Icon(icon, color: context.primaryColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -155,11 +132,7 @@ class _CollectionCards extends StatelessWidget {
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
_PeopleCollectionCard(),
|
||||
_PlacesCollectionCard(),
|
||||
_LocalAlbumsCollectionCard(),
|
||||
],
|
||||
children: [_PeopleCollectionCard(), _PlacesCollectionCard(), _LocalAlbumsCollectionCard()],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -188,22 +161,15 @@ class _PeopleCollectionCard extends ConsumerWidget {
|
||||
height: size,
|
||||
width: size,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(20),
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
context.colorScheme.primary.withAlpha(30),
|
||||
context.colorScheme.primary.withAlpha(25),
|
||||
],
|
||||
colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
child: people.widgetWhen(
|
||||
onLoading: () => const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
onLoading: () => const Center(child: CircularProgressIndicator()),
|
||||
onData: (people) {
|
||||
return GridView.count(
|
||||
crossAxisCount: 2,
|
||||
@@ -253,9 +219,7 @@ class _PlacesCollectionCard extends StatelessWidget {
|
||||
final size = context.width * widthFactor - 20.0;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => context.pushRoute(
|
||||
DriftPlaceRoute(currentLocation: null),
|
||||
),
|
||||
onTap: () => context.pushRoute(DriftPlaceRoute(currentLocation: null)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -270,10 +234,7 @@ class _PlacesCollectionCard extends StatelessWidget {
|
||||
child: IgnorePointer(
|
||||
child: MapThumbnail(
|
||||
zoom: 8,
|
||||
centre: const LatLng(
|
||||
21.44950,
|
||||
-157.91959,
|
||||
),
|
||||
centre: const LatLng(21.44950, -157.91959),
|
||||
showAttribution: false,
|
||||
themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light,
|
||||
),
|
||||
@@ -323,10 +284,7 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
context.colorScheme.primary.withAlpha(30),
|
||||
context.colorScheme.primary.withAlpha(25),
|
||||
],
|
||||
colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
@@ -340,24 +298,14 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget {
|
||||
children: albums.when(
|
||||
data: (data) {
|
||||
return data.take(4).map((album) {
|
||||
return LocalAlbumThumbnail(
|
||||
albumId: album.id,
|
||||
);
|
||||
return LocalAlbumThumbnail(albumId: album.id);
|
||||
}).toList();
|
||||
},
|
||||
error: (error, _) {
|
||||
return [
|
||||
Center(
|
||||
child: Text('Error: $error'),
|
||||
),
|
||||
];
|
||||
return [Center(child: Text('Error: $error'))];
|
||||
},
|
||||
loading: () {
|
||||
return [
|
||||
const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
];
|
||||
return [const Center(child: CircularProgressIndicator())];
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -394,13 +342,8 @@ class _QuickAccessButtonList extends ConsumerWidget {
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: context.colorScheme.onSurface.withAlpha(10),
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(20),
|
||||
),
|
||||
border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
context.colorScheme.primary.withAlpha(10),
|
||||
@@ -425,41 +368,26 @@ class _QuickAccessButtonList extends ConsumerWidget {
|
||||
bottomRight: Radius.circular(partners.isEmpty ? 20 : 0),
|
||||
),
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.folder_outlined,
|
||||
size: 26,
|
||||
),
|
||||
leading: const Icon(Icons.folder_outlined, size: 26),
|
||||
title: Text(
|
||||
'folders'.t(context: context),
|
||||
style: context.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onTap: () => context.pushRoute(FolderRoute()),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(
|
||||
Icons.lock_outline_rounded,
|
||||
size: 26,
|
||||
),
|
||||
leading: const Icon(Icons.lock_outline_rounded, size: 26),
|
||||
title: Text(
|
||||
'locked_folder'.t(context: context),
|
||||
style: context.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onTap: () => context.pushRoute(const DriftLockedFolderRoute()),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(
|
||||
Icons.group_outlined,
|
||||
size: 26,
|
||||
),
|
||||
leading: const Icon(Icons.group_outlined, size: 26),
|
||||
title: Text(
|
||||
'partners'.t(context: context),
|
||||
style: context.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onTap: () => context.pushRoute(const DriftPartnerRoute()),
|
||||
),
|
||||
@@ -494,22 +422,13 @@ class _PartnerList extends StatelessWidget {
|
||||
bottomRight: Radius.circular(isLastItem ? 20 : 0),
|
||||
),
|
||||
),
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 12.0,
|
||||
right: 18.0,
|
||||
),
|
||||
leading: PartnerUserAvatar(
|
||||
partner: partner,
|
||||
),
|
||||
contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0),
|
||||
leading: PartnerUserAvatar(partner: partner),
|
||||
title: const Text(
|
||||
"partner_list_user_photos",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: TextStyle(fontWeight: FontWeight.w500),
|
||||
).t(context: context, args: {'user': partner.name}),
|
||||
onTap: () => context.pushRoute(
|
||||
DriftPartnerDetailRoute(partner: partner),
|
||||
),
|
||||
onTap: () => context.pushRoute(DriftPartnerDetailRoute(partner: partner)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -16,14 +16,7 @@ class DriftLocalAlbumsPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
LocalAlbumsSliverAppBar(),
|
||||
_AlbumList(),
|
||||
],
|
||||
),
|
||||
);
|
||||
return const Scaffold(body: CustomScrollView(slivers: [LocalAlbumsSliverAppBar(), _AlbumList()]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +30,7 @@ class _AlbumList extends ConsumerWidget {
|
||||
return albums.when(
|
||||
loading: () => const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
error: (error, stack) => SliverToBoxAdapter(
|
||||
@@ -49,9 +39,7 @@ class _AlbumList extends ConsumerWidget {
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Text(
|
||||
'Error loading albums: $error, stack: $stack',
|
||||
style: TextStyle(
|
||||
color: context.colorScheme.error,
|
||||
),
|
||||
style: TextStyle(color: context.colorScheme.error),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -60,10 +48,7 @@ class _AlbumList extends ConsumerWidget {
|
||||
if (albums.isEmpty) {
|
||||
return const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Text('No albums found'),
|
||||
),
|
||||
child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -77,30 +62,12 @@ class _AlbumList extends ConsumerWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: LargeLeadingTile(
|
||||
leadingPadding: const EdgeInsets.only(
|
||||
right: 16,
|
||||
),
|
||||
leading: SizedBox(
|
||||
width: 80,
|
||||
height: 80,
|
||||
child: LocalAlbumThumbnail(
|
||||
albumId: album.id,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
album.name,
|
||||
style: context.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
leadingPadding: const EdgeInsets.only(right: 16),
|
||||
leading: SizedBox(width: 80, height: 80, child: LocalAlbumThumbnail(albumId: album.id)),
|
||||
title: Text(album.name, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)),
|
||||
subtitle: Text(
|
||||
'items_count'.t(
|
||||
context: context,
|
||||
args: {'count': album.assetCount},
|
||||
),
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
'items_count'.t(context: context, args: {'count': album.assetCount}),
|
||||
style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
||||
),
|
||||
onTap: () => context.pushRoute(LocalTimelineRoute(album: album)),
|
||||
),
|
||||
|
||||
@@ -45,27 +45,23 @@ class _DriftLockedFolderPageState extends ConsumerState<DriftLockedFolderPage> w
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access locked folder');
|
||||
}
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access locked folder');
|
||||
}
|
||||
|
||||
final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: _showOverlay
|
||||
? const SizedBox()
|
||||
: PopScope(
|
||||
onPopInvokedWithResult: (didPop, _) => didPop ? ref.read(authProvider.notifier).lockPinCode() : null,
|
||||
child: Timeline(
|
||||
appBar: MesmerizingSliverAppBar(
|
||||
title: 'locked_folder'.t(context: context),
|
||||
),
|
||||
appBar: MesmerizingSliverAppBar(title: 'locked_folder'.t(context: context)),
|
||||
bottomSheet: const LockedFolderBottomSheet(),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -22,20 +22,14 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
final List<DriftMemory> memories;
|
||||
final int memoryIndex;
|
||||
|
||||
const DriftMemoryPage({
|
||||
required this.memories,
|
||||
required this.memoryIndex,
|
||||
super.key,
|
||||
});
|
||||
const DriftMemoryPage({required this.memories, required this.memoryIndex, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final currentMemory = useState(memories[memoryIndex]);
|
||||
final currentAssetPage = useState(0);
|
||||
final currentMemoryIndex = useState(memoryIndex);
|
||||
final assetProgress = useState(
|
||||
"${currentAssetPage.value + 1}|${currentMemory.value.assets.length}",
|
||||
);
|
||||
final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}");
|
||||
const bgColor = Colors.black;
|
||||
final currentAsset = useState<RemoteAsset?>(null);
|
||||
|
||||
@@ -55,19 +49,13 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
});
|
||||
|
||||
toNextMemory() {
|
||||
memoryPageController.nextPage(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
|
||||
}
|
||||
|
||||
void toPreviousMemory() {
|
||||
if (currentMemoryIndex.value > 0) {
|
||||
// Move to the previous memory page
|
||||
memoryPageController.previousPage(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
|
||||
|
||||
// Wait for the next frame to ensure the page is built
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
@@ -94,10 +82,7 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
// Go to the next asset
|
||||
PageController controller = memoryAssetPageControllers[currentMemoryIndex.value];
|
||||
|
||||
controller.nextPage(
|
||||
curve: Curves.easeInOut,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
);
|
||||
controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500));
|
||||
} else {
|
||||
// Go to the next memory since we are at the end of our assets
|
||||
toNextMemory();
|
||||
@@ -109,10 +94,7 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
// Go to the previous asset
|
||||
PageController controller = memoryAssetPageControllers[currentMemoryIndex.value];
|
||||
|
||||
controller.previousPage(
|
||||
curve: Curves.easeInOut,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
);
|
||||
controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500));
|
||||
} else {
|
||||
// Go to the previous memory since we are at the end of our assets
|
||||
toPreviousMemory();
|
||||
@@ -160,14 +142,7 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
|
||||
// Precache the asset
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
await precacheImage(
|
||||
getFullImageProvider(
|
||||
asset,
|
||||
size: Size(size.width, size.height),
|
||||
),
|
||||
context,
|
||||
size: size,
|
||||
);
|
||||
await precacheImage(getFullImageProvider(asset, size: Size(size.width, size.height)), context, size: size);
|
||||
}
|
||||
|
||||
// Precache the next page right away if we are on the first page
|
||||
@@ -219,9 +194,7 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
backgroundColor: bgColor,
|
||||
body: SafeArea(
|
||||
child: PageView.builder(
|
||||
physics: const BouncingScrollPhysics(
|
||||
parent: AlwaysScrollableScrollPhysics(),
|
||||
),
|
||||
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
||||
scrollDirection: Axis.vertical,
|
||||
controller: memoryPageController,
|
||||
onPageChanged: (pageNumber) {
|
||||
@@ -249,23 +222,13 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
final yearsAgo = DateTime.now().year - memories[mIndex].data.year;
|
||||
final title = 'years_ago'.t(
|
||||
context: context,
|
||||
args: {
|
||||
'years': yearsAgo.toString(),
|
||||
},
|
||||
);
|
||||
final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()});
|
||||
// Build horizontal page
|
||||
final assetController = memoryAssetPageControllers[mIndex];
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 24.0,
|
||||
right: 24.0,
|
||||
top: 8.0,
|
||||
bottom: 2.0,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0),
|
||||
child: AnimatedBuilder(
|
||||
animation: assetController,
|
||||
builder: (context, child) {
|
||||
@@ -285,9 +248,7 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
child: Stack(
|
||||
children: [
|
||||
PageView.builder(
|
||||
physics: const BouncingScrollPhysics(
|
||||
parent: AlwaysScrollableScrollPhysics(),
|
||||
),
|
||||
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
||||
controller: assetController,
|
||||
onPageChanged: onAssetChanged,
|
||||
scrollDirection: Axis.horizontal,
|
||||
@@ -298,11 +259,7 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.black,
|
||||
child: DriftMemoryCard(
|
||||
asset: asset,
|
||||
title: title,
|
||||
showTitle: index == 0,
|
||||
),
|
||||
child: DriftMemoryCard(asset: asset, title: title, showTitle: index == 0),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Row(
|
||||
@@ -343,35 +300,24 @@ class DriftMemoryPage extends HookConsumerWidget {
|
||||
// turn off full screen mode here
|
||||
// https://github.com/Milad-Akarie/auto_route_library/issues/1799
|
||||
context.maybePop();
|
||||
SystemChrome.setEnabledSystemUIMode(
|
||||
SystemUiMode.edgeToEdge,
|
||||
);
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
},
|
||||
shape: const CircleBorder(),
|
||||
color: Colors.white.withValues(alpha: 0.2),
|
||||
elevation: 0,
|
||||
child: const Icon(
|
||||
Icons.close_rounded,
|
||||
color: Colors.white,
|
||||
),
|
||||
child: const Icon(Icons.close_rounded, color: Colors.white),
|
||||
),
|
||||
),
|
||||
if (currentAsset.value != null && currentAsset.value!.isVideo)
|
||||
Positioned(
|
||||
bottom: 24,
|
||||
right: 32,
|
||||
child: Icon(
|
||||
Icons.videocam_outlined,
|
||||
color: Colors.grey[200],
|
||||
),
|
||||
child: Icon(Icons.videocam_outlined, color: Colors.grey[200]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
DriftMemoryBottomInfo(
|
||||
memory: memories[mIndex],
|
||||
title: title,
|
||||
),
|
||||
DriftMemoryBottomInfo(memory: memories[mIndex], title: title),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -15,28 +15,20 @@ import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
|
||||
class DriftPartnerDetailPage extends StatelessWidget {
|
||||
final PartnerUserDto partner;
|
||||
|
||||
const DriftPartnerDetailPage({
|
||||
super.key,
|
||||
required this.partner,
|
||||
});
|
||||
const DriftPartnerDetailPage({super.key, required this.partner});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(partner.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(partner.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
appBar: MesmerizingSliverAppBar(
|
||||
title: partner.name,
|
||||
icon: Icons.person_outline,
|
||||
),
|
||||
appBar: MesmerizingSliverAppBar(title: partner.name, icon: Icons.person_outline),
|
||||
topSliverWidget: _InfoBox(partner: partner),
|
||||
topSliverWidgetHeight: 110,
|
||||
bottomSheet: const PartnerDetailBottomSheet(),
|
||||
@@ -48,9 +40,7 @@ class DriftPartnerDetailPage extends StatelessWidget {
|
||||
class _InfoBox extends ConsumerStatefulWidget {
|
||||
final PartnerUserDto partner;
|
||||
|
||||
const _InfoBox({
|
||||
required this.partner,
|
||||
});
|
||||
const _InfoBox({required this.partner});
|
||||
|
||||
@override
|
||||
ConsumerState<_InfoBox> createState() => _InfoBoxState();
|
||||
@@ -72,10 +62,7 @@ class _InfoBoxState extends ConsumerState<_InfoBox> {
|
||||
}
|
||||
|
||||
try {
|
||||
await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline(
|
||||
widget.partner.id,
|
||||
user.id,
|
||||
);
|
||||
await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline(widget.partner.id, user.id);
|
||||
|
||||
setState(() {
|
||||
_inTimeline = !_inTimeline;
|
||||
@@ -101,18 +88,10 @@ class _InfoBoxState extends ConsumerState<_InfoBox> {
|
||||
padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: context.colorScheme.onSurface.withAlpha(10),
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(20),
|
||||
),
|
||||
border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
context.colorScheme.primary.withAlpha(10),
|
||||
context.colorScheme.primary.withAlpha(15),
|
||||
],
|
||||
colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
@@ -122,18 +101,13 @@ class _InfoBoxState extends ConsumerState<_InfoBox> {
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
"Show in timeline",
|
||||
style: context.textTheme.titleSmall?.copyWith(
|
||||
color: context.colorScheme.primary,
|
||||
),
|
||||
style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary),
|
||||
),
|
||||
subtitle: Text(
|
||||
"Show photos and videos from this user in your timeline",
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Switch(
|
||||
value: _inTimeline,
|
||||
onChanged: (_) => _toggleInTimeline(),
|
||||
),
|
||||
trailing: Switch(value: _inTimeline, onChanged: (_) => _toggleInTimeline()),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -52,9 +52,7 @@ class _PlaceSliverAppBar extends StatelessWidget {
|
||||
pinned: true,
|
||||
snap: false,
|
||||
backgroundColor: context.colorScheme.surfaceContainer,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
|
||||
automaticallyImplyLeading: search.value == null,
|
||||
centerTitle: true,
|
||||
title: search.value != null
|
||||
@@ -98,20 +96,14 @@ class _Map extends StatelessWidget {
|
||||
child: MapThumbnail(
|
||||
onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)),
|
||||
zoom: 8,
|
||||
centre: currentLocation ??
|
||||
const LatLng(
|
||||
21.44950,
|
||||
-157.91959,
|
||||
),
|
||||
centre: currentLocation ?? const LatLng(21.44950, -157.91959),
|
||||
showAttribution: false,
|
||||
themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SliverToBoxAdapter(
|
||||
child: SizedBox.shrink(),
|
||||
);
|
||||
: const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,10 +119,7 @@ class _PlaceList extends ConsumerWidget {
|
||||
return places.when(
|
||||
loading: () => const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
error: (error, stack) => SliverToBoxAdapter(
|
||||
@@ -139,9 +128,7 @@ class _PlaceList extends ConsumerWidget {
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Text(
|
||||
'Error loading places: $error, stack: $stack',
|
||||
style: TextStyle(
|
||||
color: context.colorScheme.error,
|
||||
),
|
||||
style: TextStyle(color: context.colorScheme.error),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -174,21 +161,10 @@ class _PlaceTile extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return LargeLeadingTile(
|
||||
onTap: () => context.pushRoute(DriftPlaceDetailRoute(place: place.$1)),
|
||||
title: Text(
|
||||
place.$1,
|
||||
style: context.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
title: Text(place.$1, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)),
|
||||
leading: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(20),
|
||||
),
|
||||
child: Thumbnail(
|
||||
size: const Size(80, 80),
|
||||
fit: BoxFit.cover,
|
||||
remoteId: place.$2,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
child: Thumbnail(size: const Size(80, 80), fit: BoxFit.cover, remoteId: place.$2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,28 +9,20 @@ import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
|
||||
class DriftPlaceDetailPage extends StatelessWidget {
|
||||
final String place;
|
||||
|
||||
const DriftPlaceDetailPage({
|
||||
super.key,
|
||||
required this.place,
|
||||
});
|
||||
const DriftPlaceDetailPage({super.key, required this.place});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).place(place);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).place(place);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
appBar: MesmerizingSliverAppBar(
|
||||
title: place,
|
||||
icon: Icons.location_on,
|
||||
),
|
||||
appBar: MesmerizingSliverAppBar(title: place, icon: Icons.location_on),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,24 +15,18 @@ class DriftRecentlyTakenPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception(
|
||||
'User must be logged in to access recently taken',
|
||||
);
|
||||
}
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access recently taken');
|
||||
}
|
||||
|
||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t()),
|
||||
),
|
||||
child: Timeline(appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,7 @@ import 'package:immich_mobile/widgets/common/remote_album_sliver_app_bar.dart';
|
||||
class RemoteAlbumPage extends ConsumerStatefulWidget {
|
||||
final RemoteAlbum album;
|
||||
|
||||
const RemoteAlbumPage({
|
||||
super.key,
|
||||
required this.album,
|
||||
});
|
||||
const RemoteAlbumPage({super.key, required this.album});
|
||||
|
||||
@override
|
||||
ConsumerState<RemoteAlbumPage> createState() => _RemoteAlbumPageState();
|
||||
@@ -40,16 +37,16 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
||||
final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(widget.album.id);
|
||||
|
||||
final newAssets = await context.pushRoute<Set<BaseAsset>>(
|
||||
DriftAssetSelectionTimelineRoute(
|
||||
lockedSelectionAssets: albumAssets.toSet(),
|
||||
),
|
||||
DriftAssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()),
|
||||
);
|
||||
|
||||
if (newAssets == null || newAssets.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final added = await ref.read(remoteAlbumProvider.notifier).addAssets(
|
||||
final added = await ref
|
||||
.read(remoteAlbumProvider.notifier)
|
||||
.addAssets(
|
||||
widget.album.id,
|
||||
newAssets.map((asset) {
|
||||
final remoteAsset = asset as RemoteAsset;
|
||||
@@ -60,21 +57,14 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
||||
if (added > 0) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: "assets_added_to_album_count".t(
|
||||
context: context,
|
||||
args: {
|
||||
'count': added.toString(),
|
||||
},
|
||||
),
|
||||
msg: "assets_added_to_album_count".t(context: context, args: {'count': added.toString()}),
|
||||
toastType: ToastType.success,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addUsers(BuildContext context) async {
|
||||
final newUsers = await context.pushRoute<List<String>>(
|
||||
DriftUserSelectionRoute(album: widget.album),
|
||||
);
|
||||
final newUsers = await context.pushRoute<List<String>>(DriftUserSelectionRoute(album: widget.album));
|
||||
|
||||
if (newUsers == null || newUsers.isEmpty) {
|
||||
return;
|
||||
@@ -86,12 +76,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
||||
if (newUsers.isNotEmpty) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: "users_added_to_album_count".t(
|
||||
context: context,
|
||||
args: {
|
||||
'count': newUsers.length,
|
||||
},
|
||||
),
|
||||
msg: "users_added_to_album_count".t(context: context, args: {'count': newUsers.length}),
|
||||
toastType: ToastType.success,
|
||||
);
|
||||
}
|
||||
@@ -107,9 +92,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
||||
}
|
||||
|
||||
Future<void> toggleAlbumOrder() async {
|
||||
await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(
|
||||
widget.album.id,
|
||||
);
|
||||
await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(widget.album.id);
|
||||
|
||||
ref.invalidate(timelineServiceProvider);
|
||||
}
|
||||
@@ -123,16 +106,9 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'album_delete_confirmation'.t(
|
||||
context: context,
|
||||
args: {'album': widget.album.name},
|
||||
),
|
||||
),
|
||||
Text('album_delete_confirmation'.t(context: context, args: {'album': widget.album.name})),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'album_delete_confirmation_description'.t(context: context),
|
||||
),
|
||||
Text('album_delete_confirmation_description'.t(context: context)),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
@@ -142,9 +118,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
style: TextButton.styleFrom(foregroundColor: Theme.of(context).colorScheme.error),
|
||||
child: Text('delete_album'.t(context: context)),
|
||||
),
|
||||
],
|
||||
@@ -230,13 +204,11 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: widget.album.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: widget.album.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
appBar: RemoteAlbumSliverAppBar(
|
||||
@@ -245,9 +217,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
||||
onToggleAlbumOrder: () => toggleAlbumOrder(),
|
||||
onEditTitle: () => showEditTitleAndDescription(context),
|
||||
),
|
||||
bottomSheet: RemoteAlbumBottomSheet(
|
||||
album: widget.album,
|
||||
),
|
||||
bottomSheet: RemoteAlbumBottomSheet(album: widget.album),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -257,18 +227,13 @@ class _EditAlbumData {
|
||||
final String name;
|
||||
final String? description;
|
||||
|
||||
const _EditAlbumData({
|
||||
required this.name,
|
||||
this.description,
|
||||
});
|
||||
const _EditAlbumData({required this.name, this.description});
|
||||
}
|
||||
|
||||
class _EditAlbumDialog extends ConsumerStatefulWidget {
|
||||
final RemoteAlbum album;
|
||||
|
||||
const _EditAlbumDialog({
|
||||
required this.album,
|
||||
});
|
||||
const _EditAlbumDialog({required this.album});
|
||||
|
||||
@override
|
||||
ConsumerState<_EditAlbumDialog> createState() => _EditAlbumDialogState();
|
||||
@@ -302,19 +267,14 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||
final newTitle = titleController.text.trim();
|
||||
final newDescription = descriptionController.text.trim();
|
||||
|
||||
await ref.read(remoteAlbumProvider.notifier).updateAlbum(
|
||||
widget.album.id,
|
||||
name: newTitle,
|
||||
description: newDescription.isEmpty ? null : newDescription,
|
||||
);
|
||||
await ref
|
||||
.read(remoteAlbumProvider.notifier)
|
||||
.updateAlbum(widget.album.id, name: newTitle, description: newDescription.isEmpty ? null : newDescription);
|
||||
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop(
|
||||
_EditAlbumData(
|
||||
name: newTitle,
|
||||
description: newDescription.isEmpty ? null : newDescription,
|
||||
),
|
||||
);
|
||||
Navigator.of(
|
||||
context,
|
||||
).pop(_EditAlbumData(name: newTitle, description: newDescription.isEmpty ? null : newDescription));
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
@@ -331,11 +291,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
insetPadding: const EdgeInsets.all(24),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16),
|
||||
),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||
child: SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
@@ -348,16 +304,9 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: context.colorScheme.primary,
|
||||
size: 24,
|
||||
),
|
||||
Icon(Icons.edit_outlined, color: context.colorScheme.primary, size: 24),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'edit_album'.t(context: context),
|
||||
style: context.textTheme.titleMedium,
|
||||
),
|
||||
Text('edit_album'.t(context: context), style: context.textTheme.titleMedium),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
@@ -365,9 +314,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||
// Album Name
|
||||
Text(
|
||||
'album_name'.t(context: context).toUpperCase(),
|
||||
style: context.textTheme.labelSmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
TextFormField(
|
||||
@@ -375,9 +322,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||
maxLines: 1,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
|
||||
filled: true,
|
||||
fillColor: context.colorScheme.surface,
|
||||
),
|
||||
@@ -394,9 +339,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||
// Description
|
||||
Text(
|
||||
'description'.t(context: context).toUpperCase(),
|
||||
style: context.textTheme.labelSmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
TextFormField(
|
||||
@@ -404,11 +347,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||
maxLines: 4,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(12),
|
||||
),
|
||||
),
|
||||
border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
|
||||
filled: true,
|
||||
fillColor: context.colorScheme.surface,
|
||||
),
|
||||
|
||||
@@ -16,18 +16,16 @@ class DriftTrashPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access trash');
|
||||
}
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to access trash');
|
||||
}
|
||||
|
||||
final timelineService = ref.watch(timelineFactoryProvider).trash(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
final timelineService = ref.watch(timelineFactoryProvider).trash(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
showStorageIndicator: true,
|
||||
@@ -42,18 +40,14 @@ class DriftTrashPage extends StatelessWidget {
|
||||
topSliverWidgetHeight: 24,
|
||||
topSliverWidget: Consumer(
|
||||
builder: (context, ref, child) {
|
||||
final trashDays = ref.watch(
|
||||
serverInfoProvider.select((v) => v.serverConfig.trashDays),
|
||||
);
|
||||
final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays));
|
||||
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 24.0,
|
||||
child: const Text(
|
||||
"trash_page_info",
|
||||
).t(context: context, args: {"days": "$trashDays"}),
|
||||
child: const Text("trash_page_info").t(context: context, args: {"days": "$trashDays"}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -49,10 +49,7 @@ final driftUsersProvider = FutureProvider.autoDispose<List<UserDto>>((ref) async
|
||||
class DriftUserSelectionPage extends HookConsumerWidget {
|
||||
final RemoteAlbum album;
|
||||
|
||||
const DriftUserSelectionPage({
|
||||
super.key,
|
||||
required this.album,
|
||||
});
|
||||
const DriftUserSelectionPage({super.key, required this.album});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -65,17 +62,9 @@ class DriftUserSelectionPage extends HookConsumerWidget {
|
||||
|
||||
buildTileIcon(UserDto user) {
|
||||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: context.primaryColor,
|
||||
child: const Icon(
|
||||
Icons.check_rounded,
|
||||
size: 25,
|
||||
),
|
||||
);
|
||||
return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25));
|
||||
} else {
|
||||
return UserCircleAvatar(
|
||||
user: user,
|
||||
);
|
||||
return UserCircleAvatar(user: user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,31 +77,19 @@ class DriftUserSelectionPage extends HookConsumerWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Chip(
|
||||
backgroundColor: context.primaryColor.withValues(alpha: 0.15),
|
||||
label: Text(
|
||||
user.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return ListView(
|
||||
children: [
|
||||
Wrap(
|
||||
children: [...usersChip],
|
||||
),
|
||||
Wrap(children: [...usersChip]),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'suggestions'.tr(),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
ListView.builder(
|
||||
@@ -122,31 +99,15 @@ class DriftUserSelectionPage extends HookConsumerWidget {
|
||||
return ListTile(
|
||||
leading: buildTileIcon(users[index]),
|
||||
dense: true,
|
||||
title: Text(
|
||||
users[index].name,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
users[index].email,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||
subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)),
|
||||
onTap: () {
|
||||
if (sharedUsersList.value.contains(users[index])) {
|
||||
sharedUsersList.value = sharedUsersList.value
|
||||
.where(
|
||||
(selectedUser) => selectedUser.id != users[index].id,
|
||||
)
|
||||
.where((selectedUser) => selectedUser.id != users[index].id)
|
||||
.toSet();
|
||||
} else {
|
||||
sharedUsersList.value = {
|
||||
...sharedUsersList.value,
|
||||
users[index],
|
||||
};
|
||||
sharedUsersList.value = {...sharedUsersList.value, users[index]};
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -159,9 +120,7 @@ class DriftUserSelectionPage extends HookConsumerWidget {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'invite_to_album',
|
||||
).tr(),
|
||||
title: const Text('invite_to_album').tr(),
|
||||
elevation: 0,
|
||||
centerTitle: false,
|
||||
leading: IconButton(
|
||||
@@ -173,10 +132,7 @@ class DriftUserSelectionPage extends HookConsumerWidget {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler,
|
||||
child: const Text(
|
||||
"add",
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -15,22 +15,18 @@ class DriftVideoPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to video');
|
||||
}
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final user = ref.watch(currentUserProvider);
|
||||
if (user == null) {
|
||||
throw Exception('User must be logged in to video');
|
||||
}
|
||||
|
||||
final timelineService = ref.watch(timelineFactoryProvider).video(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
final timelineService = ref.watch(timelineFactoryProvider).video(user.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
appBar: MesmerizingSliverAppBar(title: 'videos'.t()),
|
||||
),
|
||||
child: Timeline(appBar: MesmerizingSliverAppBar(title: 'videos'.t())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,11 @@ class LocalTimelinePage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).localAlbum(albumId: album.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).localAlbum(albumId: album.id);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
appBar: MesmerizingSliverAppBar(title: album.name),
|
||||
|
||||
@@ -44,12 +44,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
location: preFilter?.location ?? SearchLocationFilter(),
|
||||
camera: preFilter?.camera ?? SearchCameraFilter(),
|
||||
date: preFilter?.date ?? SearchDateFilter(),
|
||||
display: preFilter?.display ??
|
||||
SearchDisplayFilters(
|
||||
isNotInAlbum: false,
|
||||
isArchive: false,
|
||||
isFavorite: false,
|
||||
),
|
||||
display: preFilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false),
|
||||
mediaType: preFilter?.mediaType ?? AssetType.other,
|
||||
language: "${context.locale.languageCode}-${context.locale.countryCode}",
|
||||
),
|
||||
@@ -68,10 +63,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
|
||||
SnackBar searchInfoSnackBar(String message) {
|
||||
return SnackBar(
|
||||
content: Text(
|
||||
message,
|
||||
style: context.textTheme.labelLarge,
|
||||
),
|
||||
content: Text(message, style: context.textTheme.labelLarge),
|
||||
showCloseIcon: true,
|
||||
behavior: SnackBarBehavior.fixed,
|
||||
closeIconColor: context.colorScheme.onSurface,
|
||||
@@ -92,9 +84,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
|
||||
|
||||
if (!hasResult) {
|
||||
context.showSnackBar(
|
||||
searchInfoSnackBar('search_no_result'.t(context: context)),
|
||||
);
|
||||
context.showSnackBar(searchInfoSnackBar('search_no_result'.t(context: context)));
|
||||
}
|
||||
|
||||
previousFilter.value = filter.value;
|
||||
@@ -106,9 +96,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
|
||||
|
||||
if (!hasResult) {
|
||||
context.showSnackBar(
|
||||
searchInfoSnackBar('search_no_more_result'.t(context: context)),
|
||||
);
|
||||
context.showSnackBar(searchInfoSnackBar('search_no_more_result'.t(context: context)));
|
||||
}
|
||||
|
||||
isSearching.value = false;
|
||||
@@ -116,39 +104,26 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
|
||||
searchPreFilter() {
|
||||
if (preFilter != null) {
|
||||
Future.delayed(
|
||||
Duration.zero,
|
||||
() {
|
||||
search();
|
||||
Future.delayed(Duration.zero, () {
|
||||
search();
|
||||
|
||||
if (preFilter!.location.city != null) {
|
||||
locationCurrentFilterWidget.value = Text(
|
||||
preFilter!.location.city!,
|
||||
style: context.textTheme.labelLarge,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
if (preFilter!.location.city != null) {
|
||||
locationCurrentFilterWidget.value = Text(preFilter!.location.city!, style: context.textTheme.labelLarge);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
Future.microtask(
|
||||
() => ref.invalidate(paginatedSearchProvider),
|
||||
);
|
||||
searchPreFilter();
|
||||
useEffect(() {
|
||||
Future.microtask(() => ref.invalidate(paginatedSearchProvider));
|
||||
searchPreFilter();
|
||||
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
showPeoplePicker() {
|
||||
handleOnSelect(Set<PersonDto> value) {
|
||||
filter.value = filter.value.copyWith(
|
||||
people: value,
|
||||
);
|
||||
filter.value = filter.value.copyWith(people: value);
|
||||
|
||||
peopleCurrentFilterWidget.value = Text(
|
||||
value.map((e) => e.name != '' ? e.name : 'no_name'.t(context: context)).join(', '),
|
||||
@@ -157,9 +132,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
handleClear() {
|
||||
filter.value = filter.value.copyWith(
|
||||
people: {},
|
||||
);
|
||||
filter.value = filter.value.copyWith(people: {});
|
||||
|
||||
peopleCurrentFilterWidget.value = null;
|
||||
search();
|
||||
@@ -175,10 +148,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
expanded: true,
|
||||
onSearch: search,
|
||||
onClear: handleClear,
|
||||
child: PeoplePicker(
|
||||
onSelect: handleOnSelect,
|
||||
filter: filter.value.people,
|
||||
),
|
||||
child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -187,11 +157,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
showLocationPicker() {
|
||||
handleOnSelect(Map<String, String?> value) {
|
||||
filter.value = filter.value.copyWith(
|
||||
location: SearchLocationFilter(
|
||||
country: value['country'],
|
||||
city: value['city'],
|
||||
state: value['state'],
|
||||
),
|
||||
location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']),
|
||||
);
|
||||
|
||||
final locationText = <String>[];
|
||||
@@ -207,16 +173,11 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
locationText.add(value['city']!);
|
||||
}
|
||||
|
||||
locationCurrentFilterWidget.value = Text(
|
||||
locationText.join(', '),
|
||||
style: context.textTheme.labelLarge,
|
||||
);
|
||||
locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge);
|
||||
}
|
||||
|
||||
handleClear() {
|
||||
filter.value = filter.value.copyWith(
|
||||
location: SearchLocationFilter(),
|
||||
);
|
||||
filter.value = filter.value.copyWith(location: SearchLocationFilter());
|
||||
|
||||
locationCurrentFilterWidget.value = null;
|
||||
search();
|
||||
@@ -233,15 +194,10 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: context.viewInsets.bottom,
|
||||
),
|
||||
padding: EdgeInsets.only(bottom: context.viewInsets.bottom),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: LocationPicker(
|
||||
onSelected: handleOnSelect,
|
||||
filter: filter.value.location,
|
||||
),
|
||||
child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -252,10 +208,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
showCameraPicker() {
|
||||
handleOnSelect(Map<String, String?> value) {
|
||||
filter.value = filter.value.copyWith(
|
||||
camera: SearchCameraFilter(
|
||||
make: value['make'],
|
||||
model: value['model'],
|
||||
),
|
||||
camera: SearchCameraFilter(make: value['make'], model: value['model']),
|
||||
);
|
||||
|
||||
cameraCurrentFilterWidget.value = Text(
|
||||
@@ -265,9 +218,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
handleClear() {
|
||||
filter.value = filter.value.copyWith(
|
||||
camera: SearchCameraFilter(),
|
||||
);
|
||||
filter.value = filter.value.copyWith(camera: SearchCameraFilter());
|
||||
|
||||
cameraCurrentFilterWidget.value = null;
|
||||
search();
|
||||
@@ -283,10 +234,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
onClear: handleClear,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: CameraPicker(
|
||||
onSelect: handleOnSelect,
|
||||
filter: filter.value.camera,
|
||||
),
|
||||
child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -318,9 +266,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
);
|
||||
|
||||
if (date == null) {
|
||||
filter.value = filter.value.copyWith(
|
||||
date: SearchDateFilter(),
|
||||
);
|
||||
filter.value = filter.value.copyWith(date: SearchDateFilter());
|
||||
|
||||
dateRangeCurrentFilterWidget.value = null;
|
||||
search();
|
||||
@@ -330,13 +276,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
filter.value = filter.value.copyWith(
|
||||
date: SearchDateFilter(
|
||||
takenAfter: date.start,
|
||||
takenBefore: date.end.add(
|
||||
const Duration(
|
||||
hours: 23,
|
||||
minutes: 59,
|
||||
seconds: 59,
|
||||
),
|
||||
),
|
||||
takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -365,24 +305,20 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
// MEDIA PICKER
|
||||
showMediaTypePicker() {
|
||||
handleOnSelected(AssetType assetType) {
|
||||
filter.value = filter.value.copyWith(
|
||||
mediaType: assetType,
|
||||
);
|
||||
filter.value = filter.value.copyWith(mediaType: assetType);
|
||||
|
||||
mediaTypeCurrentFilterWidget.value = Text(
|
||||
assetType == AssetType.image
|
||||
? 'image'.t(context: context)
|
||||
: assetType == AssetType.video
|
||||
? 'video'.t(context: context)
|
||||
: 'all'.t(context: context),
|
||||
? 'video'.t(context: context)
|
||||
: 'all'.t(context: context),
|
||||
style: context.textTheme.labelLarge,
|
||||
);
|
||||
}
|
||||
|
||||
handleClear() {
|
||||
filter.value = filter.value.copyWith(
|
||||
mediaType: AssetType.other,
|
||||
);
|
||||
filter.value = filter.value.copyWith(mediaType: AssetType.other);
|
||||
|
||||
mediaTypeCurrentFilterWidget.value = null;
|
||||
search();
|
||||
@@ -394,10 +330,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
title: 'search_filter_media_type_title'.t(context: context),
|
||||
onSearch: search,
|
||||
onClear: handleClear,
|
||||
child: MediaTypePicker(
|
||||
onSelect: handleOnSelected,
|
||||
filter: filter.value.mediaType,
|
||||
),
|
||||
child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -409,33 +342,19 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
value.forEach((key, value) {
|
||||
switch (key) {
|
||||
case DisplayOption.notInAlbum:
|
||||
filter.value = filter.value.copyWith(
|
||||
display: filter.value.display.copyWith(
|
||||
isNotInAlbum: value,
|
||||
),
|
||||
);
|
||||
filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value));
|
||||
if (value) {
|
||||
filterText.add(
|
||||
'search_filter_display_option_not_in_album'.t(context: context),
|
||||
);
|
||||
filterText.add('search_filter_display_option_not_in_album'.t(context: context));
|
||||
}
|
||||
break;
|
||||
case DisplayOption.archive:
|
||||
filter.value = filter.value.copyWith(
|
||||
display: filter.value.display.copyWith(
|
||||
isArchive: value,
|
||||
),
|
||||
);
|
||||
filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value));
|
||||
if (value) {
|
||||
filterText.add('archive'.t(context: context));
|
||||
}
|
||||
break;
|
||||
case DisplayOption.favorite:
|
||||
filter.value = filter.value.copyWith(
|
||||
display: filter.value.display.copyWith(
|
||||
isFavorite: value,
|
||||
),
|
||||
);
|
||||
filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value));
|
||||
if (value) {
|
||||
filterText.add('favorite'.t(context: context));
|
||||
}
|
||||
@@ -448,19 +367,12 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
displayOptionCurrentFilterWidget.value = Text(
|
||||
filterText.join(', '),
|
||||
style: context.textTheme.labelLarge,
|
||||
);
|
||||
displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge);
|
||||
}
|
||||
|
||||
handleClear() {
|
||||
filter.value = filter.value.copyWith(
|
||||
display: SearchDisplayFilters(
|
||||
isNotInAlbum: false,
|
||||
isArchive: false,
|
||||
isFavorite: false,
|
||||
),
|
||||
display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false),
|
||||
);
|
||||
|
||||
displayOptionCurrentFilterWidget.value = null;
|
||||
@@ -473,10 +385,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
title: 'display_options'.t(context: context),
|
||||
onSearch: search,
|
||||
onClear: handleClear,
|
||||
child: DisplayOptionPicker(
|
||||
onSelect: handleOnSelect,
|
||||
filter: filter.value.display,
|
||||
),
|
||||
child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -484,27 +393,15 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
handleTextSubmitted(String value) {
|
||||
switch (textSearchType.value) {
|
||||
case TextSearchType.context:
|
||||
filter.value = filter.value.copyWith(
|
||||
filename: '',
|
||||
context: value,
|
||||
description: '',
|
||||
);
|
||||
filter.value = filter.value.copyWith(filename: '', context: value, description: '');
|
||||
|
||||
break;
|
||||
case TextSearchType.filename:
|
||||
filter.value = filter.value.copyWith(
|
||||
filename: value,
|
||||
context: '',
|
||||
description: '',
|
||||
);
|
||||
filter.value = filter.value.copyWith(filename: value, context: '', description: '');
|
||||
|
||||
break;
|
||||
case TextSearchType.description:
|
||||
filter.value = filter.value.copyWith(
|
||||
filename: '',
|
||||
context: '',
|
||||
description: value,
|
||||
);
|
||||
filter.value = filter.value.copyWith(filename: '', context: '', description: value);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -512,10 +409,10 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
IconData getSearchPrefixIcon() => switch (textSearchType.value) {
|
||||
TextSearchType.context => Icons.image_search_rounded,
|
||||
TextSearchType.filename => Icons.abc_rounded,
|
||||
TextSearchType.description => Icons.text_snippet_outlined,
|
||||
};
|
||||
TextSearchType.context => Icons.image_search_rounded,
|
||||
TextSearchType.filename => Icons.abc_rounded,
|
||||
TextSearchType.description => Icons.text_snippet_outlined,
|
||||
};
|
||||
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
@@ -528,21 +425,11 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
style: MenuStyle(
|
||||
elevation: const WidgetStatePropertyAll(1),
|
||||
shape: WidgetStateProperty.all(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: const WidgetStatePropertyAll(
|
||||
EdgeInsets.all(4),
|
||||
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))),
|
||||
),
|
||||
padding: const WidgetStatePropertyAll(EdgeInsets.all(4)),
|
||||
),
|
||||
builder: (
|
||||
BuildContext context,
|
||||
MenuController controller,
|
||||
Widget? child,
|
||||
) {
|
||||
builder: (BuildContext context, MenuController controller, Widget? child) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
if (controller.isOpen) {
|
||||
@@ -616,13 +503,8 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
],
|
||||
title: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: context.colorScheme.onSurface.withAlpha(0),
|
||||
width: 0,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
context.colorScheme.primary.withValues(alpha: 0.075),
|
||||
@@ -638,12 +520,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
key: const Key('search_text_field'),
|
||||
controller: textSearchController,
|
||||
contentPadding: preFilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8),
|
||||
prefixIcon: preFilter != null
|
||||
? null
|
||||
: Icon(
|
||||
getSearchPrefixIcon(),
|
||||
color: context.colorScheme.primary,
|
||||
),
|
||||
prefixIcon: preFilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary),
|
||||
onSubmitted: handleTextSubmitted,
|
||||
focusNode: ref.watch(searchInputFocusProvider),
|
||||
),
|
||||
@@ -705,10 +582,7 @@ class DriftSearchPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
if (isSearching.value)
|
||||
const SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
)
|
||||
const SliverFillRemaining(hasScrollBody: false, child: Center(child: CircularProgressIndicator()))
|
||||
else
|
||||
_SearchResultGrid(onScrollEnd: loadMoreSearchResult),
|
||||
],
|
||||
@@ -747,19 +621,13 @@ class _SearchResultGrid extends ConsumerWidget {
|
||||
child: SliverFillRemaining(
|
||||
child: ProviderScope(
|
||||
overrides: [
|
||||
timelineServiceProvider.overrideWith(
|
||||
(ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
},
|
||||
),
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: Timeline(
|
||||
key: ValueKey(searchResult.totalAssets),
|
||||
appBar: null,
|
||||
groupBy: GroupAssetsBy.none,
|
||||
),
|
||||
child: Timeline(key: ValueKey(searchResult.totalAssets), appBar: null, groupBy: GroupAssetsBy.none),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -784,16 +652,10 @@ class _SearchEmptyContent extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: Text(
|
||||
'search_page_search_photos_videos'.t(context: context),
|
||||
style: context.textTheme.labelLarge,
|
||||
),
|
||||
child: Text('search_page_search_photos_videos'.t(context: context), style: context.textTheme.labelLarge),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: _QuickLinkList(),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: _QuickLinkList()),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -807,13 +669,8 @@ class _QuickLinkList extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(20),
|
||||
),
|
||||
border: Border.all(
|
||||
color: context.colorScheme.outline.withAlpha(10),
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
context.colorScheme.primary.withAlpha(10),
|
||||
@@ -876,19 +733,9 @@ class _QuickLink extends StatelessWidget {
|
||||
);
|
||||
|
||||
return ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
leading: Icon(
|
||||
icon,
|
||||
size: 26,
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: context.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: borderRadius),
|
||||
leading: Icon(icon, size: 26),
|
||||
title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,10 +24,7 @@ class PaginatedSearchNotifier extends StateNotifier<SearchResult> {
|
||||
return false;
|
||||
}
|
||||
|
||||
state = SearchResult(
|
||||
assets: [...state.assets, ...result.assets],
|
||||
nextPage: result.nextPage,
|
||||
);
|
||||
state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user