mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat(mobile): drift map page
This commit is contained in:
parent
da5deffd03
commit
d308d023c2
15 changed files with 783 additions and 0 deletions
101
mobile/lib/infrastructure/repositories/map.repository.dart
Normal file
101
mobile/lib/infrastructure/repositories/map.repository.dart
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/map.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
|
||||
class DriftMapRepository extends DriftDatabaseRepository {
|
||||
final Drift _db;
|
||||
|
||||
const DriftMapRepository(super._db) : _db = _db;
|
||||
|
||||
Stream<List<Marker>> watchMainMarker(
|
||||
List<String> userIds, {
|
||||
required LatLngBounds bounds,
|
||||
}) {
|
||||
final query = _db.remoteExifEntity.select().join([
|
||||
innerJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteExifEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.remoteExifEntity.latitude.isNotNull() &
|
||||
_db.remoteExifEntity.longitude.isNotNull() &
|
||||
_db.remoteExifEntity.inBounds(bounds) &
|
||||
_db.remoteAssetEntity.visibility
|
||||
.equalsValue(AssetVisibility.timeline) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.ownerId.isIn(userIds),
|
||||
);
|
||||
|
||||
return query
|
||||
.map((row) => row.readTable(_db.remoteExifEntity).toMarker())
|
||||
.watch()
|
||||
.throttle(const Duration(seconds: 3));
|
||||
}
|
||||
|
||||
Stream<List<Marker>> watchRemoteAlbumMarker(
|
||||
String albumId, {
|
||||
required LatLngBounds bounds,
|
||||
}) {
|
||||
final query = _db.remoteExifEntity.select().join([
|
||||
innerJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteExifEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.remoteExifEntity.latitude.isNotNull() &
|
||||
_db.remoteExifEntity.longitude.isNotNull() &
|
||||
_db.remoteExifEntity.inBounds(bounds) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAlbumAssetEntity.albumId.equals(albumId),
|
||||
);
|
||||
|
||||
return query
|
||||
.map((row) => row.readTable(_db.remoteExifEntity).toMarker())
|
||||
.watch()
|
||||
.throttle(const Duration(seconds: 3));
|
||||
}
|
||||
}
|
||||
|
||||
extension MapBounds on $RemoteExifEntityTable {
|
||||
Expression<bool> inBounds(LatLngBounds bounds) {
|
||||
final isLatitudeInBounds =
|
||||
latitude.isBiggerOrEqualValue(bounds.southwest.latitude) &
|
||||
latitude.isSmallerOrEqualValue(bounds.northeast.latitude);
|
||||
|
||||
final Expression<bool> isLongitudeInBounds;
|
||||
|
||||
if (bounds.southwest.longitude <= bounds.northeast.longitude) {
|
||||
isLongitudeInBounds =
|
||||
longitude.isBiggerOrEqualValue(bounds.southwest.longitude) &
|
||||
longitude.isSmallerOrEqualValue(bounds.northeast.longitude);
|
||||
} else {
|
||||
isLongitudeInBounds =
|
||||
longitude.isBiggerOrEqualValue(bounds.southwest.longitude) |
|
||||
longitude.isSmallerOrEqualValue(bounds.northeast.longitude);
|
||||
}
|
||||
|
||||
return isLatitudeInBounds & isLongitudeInBounds;
|
||||
}
|
||||
}
|
||||
|
||||
extension on RemoteExifEntityData {
|
||||
Marker toMarker() {
|
||||
return Marker(
|
||||
assetId: assetId,
|
||||
location: LatLng(latitude!, longitude!),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
|||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/map.repository.dart';
|
||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
|
||||
class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
|
|
@ -427,6 +429,88 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
|
||||
}
|
||||
|
||||
TimelineQuery map(LatLngBounds bounds, GroupAssetsBy groupBy) => (
|
||||
bucketSource: () => _watchMapBucket(
|
||||
bounds,
|
||||
groupBy: groupBy,
|
||||
),
|
||||
assetSource: (offset, count) => _getMapBucketAssets(
|
||||
bounds,
|
||||
offset: offset,
|
||||
count: count,
|
||||
),
|
||||
);
|
||||
|
||||
Stream<List<Bucket>> _watchMapBucket(
|
||||
LatLngBounds bounds, {
|
||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||
}) {
|
||||
if (groupBy == GroupAssetsBy.none) {
|
||||
// TODO: Support GroupAssetsBy.none
|
||||
throw UnsupportedError(
|
||||
"GroupAssetsBy.none is not supported for _watchMapBucket",
|
||||
);
|
||||
}
|
||||
|
||||
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||
|
||||
final query = _db.remoteAssetEntity.selectOnly()
|
||||
..addColumns([assetCountExp, dateExp])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.remoteExifEntity,
|
||||
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.remoteExifEntity.latitude.isNotNull() &
|
||||
_db.remoteExifEntity.longitude.isNotNull() &
|
||||
_db.remoteExifEntity.inBounds(bounds) &
|
||||
_db.remoteAssetEntity.visibility
|
||||
.equalsValue(AssetVisibility.timeline) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull(),
|
||||
)
|
||||
..groupBy([dateExp])
|
||||
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||
|
||||
return query.map((row) {
|
||||
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||
final assetCount = row.read(assetCountExp)!;
|
||||
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||
}).watch();
|
||||
}
|
||||
|
||||
Future<List<BaseAsset>> _getMapBucketAssets(
|
||||
LatLngBounds bounds, {
|
||||
required int offset,
|
||||
required int count,
|
||||
}) {
|
||||
final query = _db.remoteAssetEntity.select().join(
|
||||
[
|
||||
innerJoin(
|
||||
_db.remoteExifEntity,
|
||||
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
],
|
||||
)
|
||||
..where(
|
||||
_db.remoteExifEntity.latitude.isNotNull() &
|
||||
_db.remoteExifEntity.longitude.isNotNull() &
|
||||
_db.remoteExifEntity.inBounds(bounds) &
|
||||
_db.remoteAssetEntity.visibility
|
||||
.equalsValue(AssetVisibility.timeline) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull(),
|
||||
)
|
||||
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
return query
|
||||
.map((row) => row.readTable(_db.remoteAssetEntity).toDto())
|
||||
.get();
|
||||
}
|
||||
|
||||
TimelineQuery _remoteQueryBuilder({
|
||||
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
|
||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue