pull/28282/merge
Alex 2026-06-03 16:35:27 +02:00 committed by GitHub
commit 1a5b9dae9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 94 additions and 2 deletions

View File

@ -54,6 +54,8 @@ class TimelineFactory {
TimelineService localAlbum({required String albumId}) =>
TimelineService(_timelineRepository.localAlbum(albumId, groupBy));
TimelineService allLocal() => TimelineService(_timelineRepository.allLocal(groupBy));
TimelineService remoteAlbum({required String albumId}) =>
TimelineService(_timelineRepository.remoteAlbum(albumId, groupBy));

View File

@ -119,6 +119,40 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
origin: TimelineOrigin.localAlbum,
);
TimelineQuery allLocal(GroupAssetsBy groupBy) => (
bucketSource: () => _watchAllLocalBucket(groupBy: groupBy),
assetSource: (offset, count) => _getAllLocalAssets(offset: offset, count: count),
origin: TimelineOrigin.localAlbum,
);
Stream<List<Bucket>> _watchAllLocalBucket({GroupAssetsBy groupBy = GroupAssetsBy.day}) {
if (groupBy == GroupAssetsBy.none) {
return _db.localAssetEntity.count().map(_generateBuckets).watchSingle();
}
final assetCountExp = _db.localAssetEntity.id.count();
final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy, toLocal: true);
final query = _db.localAssetEntity.selectOnly()
..addColumns([assetCountExp, dateExp])
..groupBy([dateExp])
..orderBy([OrderingTerm.desc(dateExp)]);
return query.map((row) {
final timeline = row.read(dateExp)!.truncateDate(groupBy);
final assetCount = row.read(assetCountExp)!;
return TimeBucket(date: timeline, assetCount: assetCount);
}).watch();
}
Future<List<BaseAsset>> _getAllLocalAssets({required int offset, required int count}) {
final query = _db.localAssetEntity.select()
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
..limit(count, offset: offset);
return query.map((row) => row.toDto()).get();
}
Stream<List<Bucket>> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
if (groupBy == GroupAssetsBy.none) {
return _db.localAlbumAssetEntity

View File

@ -356,9 +356,9 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
),
);
} else {
log.severe('Missing crucial offline login info - Logging out completely');
log.info('No stored credentials - showing local gallery in guest mode');
unawaited(ref.read(authProvider.notifier).logout());
unawaited(context.replaceRoute(const LoginRoute()));
unawaited(context.replaceRoute(const GuestGalleryRoute()));
return;
}

View File

@ -0,0 +1,32 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/routing/router.dart';
@RoutePage()
class GuestGalleryPage extends StatelessWidget {
const GuestGalleryPage({super.key});
@override
Widget build(BuildContext context) {
return ProviderScope(
overrides: [timelineServiceProvider.overrideWith((ref) => ref.watch(localOnlyTimelineServiceProvider))],
child: Timeline(
appBar: SliverAppBar(
title: const Text('On this device'),
floating: true,
actions: [
TextButton.icon(
onPressed: () => context.replaceRoute(const LoginRoute()),
icon: const Icon(Icons.login),
label: const Text('Sign in'),
),
],
),
showStorageIndicator: false,
),
);
}
}

View File

@ -26,6 +26,12 @@ final timelineServiceProvider = Provider<TimelineService>(
dependencies: [],
);
final localOnlyTimelineServiceProvider = Provider<TimelineService>((ref) {
final timelineService = ref.watch(timelineFactoryProvider).allLocal();
ref.onDispose(timelineService.dispose);
return timelineService;
});
final timelineFactoryProvider = Provider<TimelineFactory>(
(ref) => TimelineFactory(
timelineRepository: ref.watch(timelineRepositoryProvider),

View File

@ -65,6 +65,7 @@ import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart';
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
import 'package:immich_mobile/presentation/pages/edit/drift_edit.page.dart';
import 'package:immich_mobile/presentation/pages/guest_gallery.page.dart';
import 'package:immich_mobile/presentation/pages/local_timeline.page.dart';
import 'package:immich_mobile/presentation/pages/profile/profile_picture_crop.page.dart';
import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart';
@ -116,6 +117,7 @@ class AppRouter extends RootStackRouter {
@override
late final List<AutoRoute> routes = [
AutoRoute(page: SplashScreenRoute.page, initial: true),
AutoRoute(page: GuestGalleryRoute.page),
AutoRoute(page: LoginRoute.page),
AutoRoute(page: ChangePasswordRoute.page),
AutoRoute(

View File

@ -1287,6 +1287,22 @@ class FolderRouteArgs {
int get hashCode => key.hashCode ^ folder.hashCode;
}
/// generated route for
/// [GuestGalleryPage]
class GuestGalleryRoute extends PageRouteInfo<void> {
const GuestGalleryRoute({List<PageRouteInfo>? children})
: super(GuestGalleryRoute.name, initialChildren: children);
static const String name = 'GuestGalleryRoute';
static PageInfo page = PageInfo(
name,
builder: (data) {
return const GuestGalleryPage();
},
);
}
/// generated route for
/// [HeaderSettingsPage]
class HeaderSettingsRoute extends PageRouteInfo<void> {