import 'package:flutter/cupertino.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/models/user.dart'; import 'package:isar/isar.dart'; import 'package:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; part 'album.g.dart'; @Collection(inheritance: false) class Album { @protected Album({ this.remoteId, this.localId, required this.name, required this.createdAt, required this.modifiedAt, required this.shared, }); Id id = Isar.autoIncrement; @Index(unique: false, replace: false, type: IndexType.hash) String? remoteId; @Index(unique: false, replace: false, type: IndexType.hash) String? localId; String name; DateTime createdAt; DateTime modifiedAt; bool shared; final IsarLink owner = IsarLink(); final IsarLink thumbnail = IsarLink(); final IsarLinks sharedUsers = IsarLinks(); final IsarLinks assets = IsarLinks(); RenderList _renderList = RenderList.empty(); @ignore RenderList get renderList => _renderList; @ignore bool get isRemote => remoteId != null; @ignore bool get isLocal => localId != null; @ignore int get assetCount => assets.length; @ignore String? get ownerId => owner.value?.id; @ignore String? get ownerName { // Guard null owner if (owner.value == null) { return null; } final name = []; if (owner.value?.firstName != null) { name.add(owner.value!.firstName); } if (owner.value?.lastName != null) { name.add(owner.value!.lastName); } return name.join(' '); } Stream watchRenderList(GroupAssetsBy groupAssetsBy) async* { final query = assets.filter().sortByFileCreatedAt(); _renderList = await RenderList.fromQuery(query, groupAssetsBy); yield _renderList; await for (final _ in query.watchLazy()) { _renderList = await RenderList.fromQuery(query, groupAssetsBy); yield _renderList; } } @override bool operator ==(other) { if (other is! Album) return false; return id == other.id && remoteId == other.remoteId && localId == other.localId && name == other.name && createdAt.isAtSameMomentAs(other.createdAt) && modifiedAt.isAtSameMomentAs(other.modifiedAt) && shared == other.shared && owner.value == other.owner.value && thumbnail.value == other.thumbnail.value && sharedUsers.length == other.sharedUsers.length && assets.length == other.assets.length; } @override @ignore int get hashCode => id.hashCode ^ remoteId.hashCode ^ localId.hashCode ^ name.hashCode ^ createdAt.hashCode ^ modifiedAt.hashCode ^ shared.hashCode ^ owner.value.hashCode ^ thumbnail.value.hashCode ^ sharedUsers.length.hashCode ^ assets.length.hashCode; static Album local(AssetPathEntity ape) { final Album a = Album( name: ape.name, createdAt: ape.lastModified?.toUtc() ?? DateTime.now().toUtc(), modifiedAt: ape.lastModified?.toUtc() ?? DateTime.now().toUtc(), shared: false, ); a.owner.value = Store.get(StoreKey.currentUser); a.localId = ape.id; return a; } static Future remote(AlbumResponseDto dto) async { final Isar db = Isar.getInstance()!; final Album a = Album( remoteId: dto.id, name: dto.albumName, createdAt: dto.createdAt, modifiedAt: dto.updatedAt, shared: dto.shared, ); a.owner.value = await db.users.getById(dto.ownerId); if (dto.albumThumbnailAssetId != null) { a.thumbnail.value = await db.assets .where() .remoteIdEqualTo(dto.albumThumbnailAssetId) .findFirst(); } if (dto.sharedUsers.isNotEmpty) { final users = await db.users .getAllById(dto.sharedUsers.map((e) => e.id).toList(growable: false)); a.sharedUsers.addAll(users.cast()); } if (dto.assets.isNotEmpty) { final assets = await db.assets.getAllByRemoteId(dto.assets.map((e) => e.id)); a.assets.addAll(assets); } return a; } @override String toString() => name; } extension AssetsHelper on IsarCollection { Future store(Album a) async { await put(a); await a.owner.save(); await a.thumbnail.save(); await a.sharedUsers.save(); await a.assets.save(); } } extension AssetPathEntityHelper on AssetPathEntity { Future> getAssets({ int start = 0, int end = 0x7fffffffffffffff, Set? excludedAssets, }) async { final assetEntities = await getAssetListRange(start: start, end: end); if (excludedAssets != null) { return assetEntities .where((e) => !excludedAssets.contains(e.id)) .map(Asset.local) .toList(); } return assetEntities.map(Asset.local).toList(); } } extension AlbumResponseDtoHelper on AlbumResponseDto { List getAssets() => assets.map(Asset.remote).toList(); }