fix(mobile): allow deep linking to full memory lane

This commit is contained in:
Ritinpaul 2026-08-12 13:30:13 +05:30
parent 199723261c
commit 7ca6d3de20
2 changed files with 87 additions and 3 deletions

View file

@ -51,7 +51,7 @@ class DeepLinkService {
final queryParams = link.uri.queryParameters;
return switch (intent) {
"memory" => await _buildMemoryDeepLink(queryParams['id'] ?? ''),
"memory" => await _buildMemoryDeepLink(queryParams['id']),
"asset" => await _buildAssetDeepLink(queryParams['id'] ?? '', ref),
"album" => await _buildAlbumDeepLink(queryParams['id'] ?? ''),
"people" => await _buildPeopleDeepLink(queryParams['id'] ?? ''),
@ -83,6 +83,9 @@ class DeepLinkService {
final peopleId = peopleRegex.firstMatch(path)?.group(1) ?? '';
return _buildPeopleDeepLink(peopleId);
}
if (path == '/memory') {
return _buildMemoryDeepLink(null);
}
return null;
}

View file

@ -3,7 +3,9 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/services/asset.service.dart';
import 'package:immich_mobile/domain/services/memory.service.dart';
import 'package:immich_mobile/domain/services/people.service.dart';
@ -32,11 +34,23 @@ class MockAssetViewerStateNotifier extends Mock implements AssetViewerStateNotif
const _assetId = 'aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb';
const _albumId = 'cccccccc-4444-5555-6666-dddddddddddd';
const _memoryId = 'eeeeeeee-7777-8888-9999-ffffffffffff';
const _userId = 'user-1';
final _user = UserDto(
id: _userId,
name: 'Test User',
email: 'test@example.com',
isAdmin: false,
profileChangedAt: DateTime(2026, 6, 12),
avatarColor: AvatarColor.primary,
memoryEnabled: true,
);
final _asset = RemoteAsset(
id: _assetId,
name: 'photo.jpg',
ownerId: 'user-1',
ownerId: _userId,
checksum: 'checksum-1',
type: AssetType.image,
createdAt: DateTime(2026, 6, 12),
@ -44,6 +58,18 @@ final _asset = RemoteAsset(
isEdited: false,
);
final _memory = DriftMemory(
id: _memoryId,
createdAt: DateTime(2026, 6, 12),
updatedAt: DateTime(2026, 6, 12),
ownerId: _userId,
type: MemoryTypeEnum.onThisDay,
data: const MemoryData(year: 2020),
isSaved: false,
memoryAt: DateTime(2020, 6, 12),
assets: [],
);
final _album = RemoteAlbum(
id: _albumId,
name: 'Shared Album',
@ -62,14 +88,17 @@ void main() {
late MockTimelineFactory timelineFactory;
late MockAssetService assetService;
late MockRemoteAlbumService remoteAlbumService;
late MockDriftMemoryService memoryService;
late MockWidgetRef ref;
late List<TimelineService> createdTimelineServices;
late DeepLinkService sut;
late DeepLinkService sutWithUser;
setUp(() {
timelineFactory = MockTimelineFactory();
assetService = MockAssetService();
remoteAlbumService = MockRemoteAlbumService();
memoryService = MockDriftMemoryService();
ref = MockWidgetRef();
createdTimelineServices = [];
@ -90,11 +119,20 @@ void main() {
timelineFactory,
assetService,
remoteAlbumService,
MockDriftMemoryService(),
memoryService,
MockDriftPeopleService(),
null,
);
sutWithUser = DeepLinkService(
timelineFactory,
assetService,
remoteAlbumService,
memoryService,
MockDriftPeopleService(),
_user,
);
addTearDown(() async {
for (final timelineService in createdTimelineServices) {
await timelineService.dispose();
@ -102,12 +140,20 @@ void main() {
});
});
/// Creates a mock [PlatformDeepLink] for an https://my.immich.app path.
PlatformDeepLink link(String path) {
final deepLink = MockPlatformDeepLink();
when(() => deepLink.uri).thenReturn(Uri.parse('https://my.immich.app$path'));
return deepLink;
}
/// Creates a mock [PlatformDeepLink] for an immich:// scheme URI.
PlatformDeepLink schemeLink(String path) {
final deepLink = MockPlatformDeepLink();
when(() => deepLink.uri).thenReturn(Uri.parse('immich:/$path'));
return deepLink;
}
test('album photo link carries the album into the viewer route', () async {
when(() => assetService.getRemoteAsset(_assetId)).thenAnswer((_) async => _asset);
when(() => remoteAlbumService.get(_albumId)).thenAnswer((_) async => _album);
@ -137,4 +183,39 @@ void main() {
expect((route!.args! as AssetViewerRouteArgs).currentAlbum, isNull);
verifyNever(() => remoteAlbumService.get(any()));
});
group('memory deep links', () {
test('immich://memory opens the full Memory Lane', () async {
when(() => memoryService.getMemoryLane(_userId)).thenAnswer((_) async => [_memory]);
final route = await sutWithUser.handleScheme(schemeLink('/memory'), ref);
expect(route, isA<DriftMemoryRoute>());
final args = route!.args! as DriftMemoryRouteArgs;
expect(args.memories, [_memory]);
expect(args.memoryIndex, 0);
});
test('immich://memory?id=<uuid> opens that specific memory', () async {
when(() => memoryService.get(_memoryId)).thenAnswer((_) async => _memory);
final route = await sut.handleScheme(schemeLink('/memory?id=$_memoryId'), ref);
expect(route, isA<DriftMemoryRoute>());
final args = route!.args! as DriftMemoryRouteArgs;
expect(args.memories, [_memory]);
expect(args.memoryIndex, 0);
});
test('https://my.immich.app/memory opens the full Memory Lane', () async {
when(() => memoryService.getMemoryLane(_userId)).thenAnswer((_) async => [_memory]);
final route = await sutWithUser.handleMyImmichApp(link('/memory'), ref);
expect(route, isA<DriftMemoryRoute>());
final args = route!.args! as DriftMemoryRouteArgs;
expect(args.memories, [_memory]);
expect(args.memoryIndex, 0);
});
});
}