mirror of
https://github.com/immich-app/immich.git
synced 2025-04-01 02:51:27 -05:00
refactor(mobile): remove int user id (#16814)
* refactor: user entity * chore: rebase fixes * refactor: remove int user Id * refactor: migrate store userId from int to string * refactor: rename uid to id * fix: migration * pr feedback --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
e96ffd43e7
commit
9cf3b88f80
38 changed files with 182 additions and 157 deletions
mobile
lib
domain
extensions
infrastructure
entities
repositories
utils
interfaces
pages
album
albums
common
providers
repositories
album.repository.dartasset.repository.dartasset_media.repository.dartetag.repository.darttimeline.repository.dart
services
utils
widgets
test
fixtures
modules/shared
services
|
@ -4,8 +4,6 @@ import 'package:immich_mobile/domain/models/user.model.dart';
|
|||
abstract interface class IUserRepository implements IDatabaseRepository {
|
||||
Future<bool> insert(UserDto user);
|
||||
|
||||
Future<UserDto?> get(int id);
|
||||
|
||||
Future<UserDto?> getByUserId(String id);
|
||||
|
||||
Future<List<UserDto?>> getByUserIds(List<String> ids);
|
||||
|
@ -16,7 +14,7 @@ abstract interface class IUserRepository implements IDatabaseRepository {
|
|||
|
||||
Future<UserDto> update(UserDto user);
|
||||
|
||||
Future<void> delete(List<int> ids);
|
||||
Future<void> delete(List<String> ids);
|
||||
|
||||
Future<void> deleteAll();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
|
||||
enum AvatarColor {
|
||||
// do not change this order or reuse indices for other purposes, adding is OK
|
||||
primary,
|
||||
|
@ -32,7 +30,7 @@ enum AvatarColor {
|
|||
|
||||
// TODO: Rename to User once Isar is removed
|
||||
class UserDto {
|
||||
final String uid;
|
||||
final String id;
|
||||
final String email;
|
||||
final String name;
|
||||
final bool isAdmin;
|
||||
|
@ -50,11 +48,10 @@ class UserDto {
|
|||
final int quotaUsageInBytes;
|
||||
final int quotaSizeInBytes;
|
||||
|
||||
int get id => fastHash(uid);
|
||||
bool get hasQuota => quotaSizeInBytes > 0;
|
||||
|
||||
const UserDto({
|
||||
required this.uid,
|
||||
required this.id,
|
||||
required this.email,
|
||||
required this.name,
|
||||
required this.isAdmin,
|
||||
|
@ -73,7 +70,6 @@ class UserDto {
|
|||
String toString() {
|
||||
return '''User: {
|
||||
id: $id,
|
||||
uid: $uid,
|
||||
email: $email,
|
||||
name: $name,
|
||||
isAdmin: $isAdmin,
|
||||
|
@ -90,7 +86,7 @@ quotaSizeInBytes: $quotaSizeInBytes,
|
|||
}
|
||||
|
||||
UserDto copyWith({
|
||||
String? uid,
|
||||
String? id,
|
||||
String? email,
|
||||
String? name,
|
||||
bool? isAdmin,
|
||||
|
@ -105,7 +101,7 @@ quotaSizeInBytes: $quotaSizeInBytes,
|
|||
int? quotaSizeInBytes,
|
||||
}) =>
|
||||
UserDto(
|
||||
uid: uid ?? this.uid,
|
||||
id: id ?? this.id,
|
||||
email: email ?? this.email,
|
||||
name: name ?? this.name,
|
||||
isAdmin: isAdmin ?? this.isAdmin,
|
||||
|
@ -124,7 +120,7 @@ quotaSizeInBytes: $quotaSizeInBytes,
|
|||
bool operator ==(covariant UserDto other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.uid == uid &&
|
||||
return other.id == id &&
|
||||
other.updatedAt.isAtSameMomentAs(updatedAt) &&
|
||||
other.avatarColor == avatarColor &&
|
||||
other.email == email &&
|
||||
|
@ -141,7 +137,7 @@ quotaSizeInBytes: $quotaSizeInBytes,
|
|||
|
||||
@override
|
||||
int get hashCode =>
|
||||
uid.hashCode ^
|
||||
id.hashCode ^
|
||||
name.hashCode ^
|
||||
email.hashCode ^
|
||||
updatedAt.hashCode ^
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:typed_data';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
|
||||
extension ListExtension<E> on List<E> {
|
||||
List<E> uniqueConsecutive({
|
||||
|
@ -62,11 +63,11 @@ extension AssetListExtension on Iterable<Asset> {
|
|||
void Function()? errorCallback,
|
||||
}) {
|
||||
if (owner == null) return [];
|
||||
final userId = owner.id;
|
||||
final bool onlyOwned = every((e) => e.ownerId == userId);
|
||||
final isarUserId = fastHash(owner.id);
|
||||
final bool onlyOwned = every((e) => e.ownerId == isarUserId);
|
||||
if (!onlyOwned) {
|
||||
if (errorCallback != null) errorCallback();
|
||||
return where((a) => a.ownerId == userId);
|
||||
return where((a) => a.ownerId == isarUserId);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class User {
|
|||
});
|
||||
|
||||
static User fromDto(UserDto dto) => User(
|
||||
id: dto.uid,
|
||||
id: dto.id,
|
||||
updatedAt: dto.updatedAt,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
|
@ -56,7 +56,7 @@ class User {
|
|||
);
|
||||
|
||||
UserDto toDto() => UserDto(
|
||||
uid: id,
|
||||
id: id,
|
||||
email: email,
|
||||
name: name,
|
||||
isAdmin: isAdmin,
|
||||
|
|
|
@ -78,7 +78,9 @@ class IsarStoreRepository extends IsarDatabaseRepository
|
|||
const (DateTime) => entity.intValue == null
|
||||
? null
|
||||
: DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
|
||||
const (UserDto) => await IsarUserRepository(_db).get(entity.intValue!),
|
||||
const (UserDto) => entity.strValue == null
|
||||
? null
|
||||
: await IsarUserRepository(_db).getByUserId(entity.strValue!),
|
||||
_ => null,
|
||||
} as T?;
|
||||
|
||||
|
@ -89,8 +91,8 @@ class IsarStoreRepository extends IsarDatabaseRepository
|
|||
const (bool) => ((value as bool) ? 1 : 0, null),
|
||||
const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null),
|
||||
const (UserDto) => (
|
||||
(await IsarUserRepository(_db).update(value as UserDto)).id,
|
||||
null,
|
||||
(await IsarUserRepository(_db).update(value as UserDto)).id,
|
||||
),
|
||||
_ => throw UnsupportedError(
|
||||
"Unsupported primitive type: ${key.type} for key: ${key.name}",
|
||||
|
|
|
@ -11,9 +11,9 @@ class IsarUserRepository extends IsarDatabaseRepository
|
|||
const IsarUserRepository(super.db) : _db = db;
|
||||
|
||||
@override
|
||||
Future<void> delete(List<int> ids) async {
|
||||
Future<void> delete(List<String> ids) async {
|
||||
await transaction(() async {
|
||||
await _db.users.deleteAll(ids);
|
||||
await _db.users.deleteAllById(ids);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -24,11 +24,6 @@ class IsarUserRepository extends IsarDatabaseRepository
|
|||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UserDto?> get(int id) async {
|
||||
return (await _db.users.get(id))?.toDto();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
|
||||
return (await _db.users
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:openapi/api.dart';
|
|||
abstract final class UserConverter {
|
||||
/// Base user dto used where the complete user object is not required
|
||||
static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto(
|
||||
uid: dto.id,
|
||||
id: dto.id,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: false,
|
||||
|
@ -18,7 +18,7 @@ abstract final class UserConverter {
|
|||
UserPreferencesResponseDto? preferenceDto,
|
||||
]) =>
|
||||
UserDto(
|
||||
uid: adminDto.id,
|
||||
id: adminDto.id,
|
||||
email: adminDto.email,
|
||||
name: adminDto.name,
|
||||
isAdmin: adminDto.isAdmin,
|
||||
|
@ -34,7 +34,7 @@ abstract final class UserConverter {
|
|||
);
|
||||
|
||||
static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto(
|
||||
uid: dto.id,
|
||||
id: dto.id,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: false,
|
||||
|
|
|
@ -19,7 +19,7 @@ abstract interface class IAssetRepository implements IDatabaseRepository {
|
|||
);
|
||||
|
||||
Future<List<Asset>> getAll({
|
||||
required int ownerId,
|
||||
required String ownerId,
|
||||
AssetState? state,
|
||||
AssetSort? sortBy,
|
||||
int? limit,
|
||||
|
@ -29,8 +29,8 @@ abstract interface class IAssetRepository implements IDatabaseRepository {
|
|||
|
||||
Future<List<Asset>> getByAlbum(
|
||||
Album album, {
|
||||
Iterable<int> notOwnedBy = const [],
|
||||
int? ownerId,
|
||||
Iterable<String> notOwnedBy = const [],
|
||||
String? ownerId,
|
||||
AssetState? state,
|
||||
AssetSort? sortBy,
|
||||
});
|
||||
|
@ -45,7 +45,7 @@ abstract interface class IAssetRepository implements IDatabaseRepository {
|
|||
|
||||
Future<List<Asset>> getMatches({
|
||||
required List<Asset> assets,
|
||||
required int ownerId,
|
||||
required String ownerId,
|
||||
AssetState? state,
|
||||
int limit = 100,
|
||||
});
|
||||
|
@ -64,10 +64,10 @@ abstract interface class IAssetRepository implements IDatabaseRepository {
|
|||
|
||||
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false});
|
||||
|
||||
Future<List<Asset>> getTrashAssets(int userId);
|
||||
Future<List<Asset>> getTrashAssets(String userId);
|
||||
|
||||
Future<List<Asset>> getRecentlyAddedAssets(int userId);
|
||||
Future<List<Asset>> getMotionAssets(int userId);
|
||||
Future<List<Asset>> getRecentlyAddedAssets(String userId);
|
||||
Future<List<Asset>> getMotionAssets(String userId);
|
||||
}
|
||||
|
||||
enum AssetSort { checksum, ownerIdChecksum }
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:immich_mobile/entities/etag.entity.dart';
|
|||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
||||
|
||||
abstract interface class IETagRepository implements IDatabaseRepository {
|
||||
Future<ETag?> get(int id);
|
||||
Future<ETag?> get(String id);
|
||||
|
||||
Future<ETag?> getById(String id);
|
||||
|
||||
|
|
|
@ -3,22 +3,25 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
|
||||
abstract class ITimelineRepository {
|
||||
Future<List<int>> getTimelineUserIds(int id);
|
||||
Future<List<String>> getTimelineUserIds(String id);
|
||||
|
||||
Stream<List<int>> watchTimelineUsers(int id);
|
||||
Stream<List<String>> watchTimelineUsers(String id);
|
||||
|
||||
Stream<RenderList> watchArchiveTimeline(int userId);
|
||||
Stream<RenderList> watchFavoriteTimeline(int userId);
|
||||
Stream<RenderList> watchTrashTimeline(int userId);
|
||||
Stream<RenderList> watchArchiveTimeline(String userId);
|
||||
Stream<RenderList> watchFavoriteTimeline(String userId);
|
||||
Stream<RenderList> watchTrashTimeline(String userId);
|
||||
Stream<RenderList> watchAlbumTimeline(
|
||||
Album album,
|
||||
GroupAssetsBy groupAssetsBy,
|
||||
);
|
||||
Stream<RenderList> watchAllVideosTimeline();
|
||||
|
||||
Stream<RenderList> watchHomeTimeline(int userId, GroupAssetsBy groupAssetsBy);
|
||||
Stream<RenderList> watchHomeTimeline(
|
||||
String userId,
|
||||
GroupAssetsBy groupAssetsBy,
|
||||
);
|
||||
Stream<RenderList> watchMultiUsersTimeline(
|
||||
List<int> userIds,
|
||||
List<String> userIds,
|
||||
GroupAssetsBy groupAssetsBy,
|
||||
);
|
||||
|
||||
|
@ -27,5 +30,5 @@ abstract class ITimelineRepository {
|
|||
GroupAssetsBy getGroupByOption,
|
||||
);
|
||||
|
||||
Stream<RenderList> watchAssetSelectionTimeline(int userId);
|
||||
Stream<RenderList> watchAssetSelectionTimeline(String userId);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
|||
final sharedUsersList = useState<Set<UserDto>>({});
|
||||
|
||||
addNewUsersHandler() {
|
||||
context.maybePop(sharedUsersList.value.map((e) => e.uid).toList());
|
||||
context.maybePop(sharedUsersList.value.map((e) => e.id).toList());
|
||||
}
|
||||
|
||||
buildTileIcon(UserDto user) {
|
||||
|
@ -151,7 +151,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
|
|||
onData: (users) {
|
||||
for (var sharedUsers in album.sharedUsers) {
|
||||
users.removeWhere(
|
||||
(u) => u.uid == sharedUsers.id || u.uid == album.ownerId,
|
||||
(u) => u.id == sharedUsers.id || u.id == album.ownerId,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
void handleUserClick(UserDto user) {
|
||||
var actions = [];
|
||||
|
||||
if (user.uid == userId) {
|
||||
if (user.id == userId) {
|
||||
actions = [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.exit_to_app_rounded),
|
||||
|
@ -170,10 +170,10 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
),
|
||||
trailing: userId == user.uid || isOwner
|
||||
trailing: userId == user.id || isOwner
|
||||
? const Icon(Icons.more_horiz_rounded)
|
||||
: const SizedBox(),
|
||||
onTap: userId == user.uid || isOwner
|
||||
onTap: userId == user.id || isOwner
|
||||
? () => handleUserClick(user)
|
||||
: null,
|
||||
);
|
||||
|
|
|
@ -33,7 +33,7 @@ class AlbumsPage extends HookConsumerWidget {
|
|||
final searchController = useTextEditingController();
|
||||
final debounceTimer = useRef<Timer?>(null);
|
||||
final filterMode = useState(QuickFilterMode.all);
|
||||
final userId = ref.watch(currentUserProvider)?.uid;
|
||||
final userId = ref.watch(currentUserProvider)?.id;
|
||||
final searchFocusNode = useFocusNode();
|
||||
|
||||
toggleViewMode() {
|
||||
|
|
|
@ -72,7 +72,7 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
|
||||
final activity = data[index];
|
||||
final canDelete = activity.user.id == user?.id ||
|
||||
album.ownerId == user?.uid;
|
||||
album.ownerId == user?.id;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
|
|
|
@ -153,7 +153,7 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
|||
|
||||
state = state.copyWith(
|
||||
deviceId: deviceId,
|
||||
userId: user.uid,
|
||||
userId: user.id,
|
||||
userEmail: user.email,
|
||||
isAuthenticated: true,
|
||||
name: user.name,
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:immich_mobile/providers/locale_provider.dart';
|
|||
import 'package:immich_mobile/services/timeline.service.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
|
||||
final singleUserTimelineProvider = StreamProvider.family<RenderList, int?>(
|
||||
final singleUserTimelineProvider = StreamProvider.family<RenderList, String?>(
|
||||
(ref, userId) {
|
||||
if (userId == null) {
|
||||
return const Stream.empty();
|
||||
|
@ -18,7 +18,8 @@ final singleUserTimelineProvider = StreamProvider.family<RenderList, int?>(
|
|||
dependencies: [localeProvider],
|
||||
);
|
||||
|
||||
final multiUsersTimelineProvider = StreamProvider.family<RenderList, List<int>>(
|
||||
final multiUsersTimelineProvider =
|
||||
StreamProvider.family<RenderList, List<String>>(
|
||||
(ref, userIds) {
|
||||
ref.watch(localeProvider);
|
||||
final timelineService = ref.watch(timelineServiceProvider);
|
||||
|
|
|
@ -34,7 +34,7 @@ final currentUserProvider =
|
|||
return CurrentUserProvider(ref.watch(userServiceProvider));
|
||||
});
|
||||
|
||||
class TimelineUserIdsProvider extends StateNotifier<List<int>> {
|
||||
class TimelineUserIdsProvider extends StateNotifier<List<String>> {
|
||||
TimelineUserIdsProvider(this._timelineService) : super([]) {
|
||||
_timelineService.getTimelineUserIds().then((users) => state = users);
|
||||
streamSub = _timelineService
|
||||
|
@ -42,7 +42,7 @@ class TimelineUserIdsProvider extends StateNotifier<List<int>> {
|
|||
.listen((users) => state = users);
|
||||
}
|
||||
|
||||
late final StreamSubscription<List<int>> streamSub;
|
||||
late final StreamSubscription<List<String>> streamSub;
|
||||
final TimelineService _timelineService;
|
||||
|
||||
@override
|
||||
|
@ -53,6 +53,6 @@ class TimelineUserIdsProvider extends StateNotifier<List<int>> {
|
|||
}
|
||||
|
||||
final timelineUsersIdsProvider =
|
||||
StateNotifierProvider<TimelineUserIdsProvider, List<int>>((ref) {
|
||||
StateNotifierProvider<TimelineUserIdsProvider, List<String>>((ref) {
|
||||
return TimelineUserIdsProvider(ref.watch(timelineServiceProvider));
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:immich_mobile/interfaces/album.interface.dart';
|
|||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
final albumRepositoryProvider =
|
||||
|
@ -43,14 +44,11 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
|
|||
if (shared != null) {
|
||||
query = query.sharedEqualTo(shared);
|
||||
}
|
||||
final isarUserId = fastHash(Store.get(StoreKey.currentUser).id);
|
||||
if (owner == true) {
|
||||
query = query.owner(
|
||||
(q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).id),
|
||||
);
|
||||
query = query.owner((q) => q.isarIdEqualTo(isarUserId));
|
||||
} else if (owner == false) {
|
||||
query = query.owner(
|
||||
(q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).id),
|
||||
);
|
||||
query = query.owner((q) => q.not().isarIdEqualTo(isarUserId));
|
||||
}
|
||||
if (remote == true) {
|
||||
query = query.localIdIsNull();
|
||||
|
@ -140,16 +138,13 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
|
|||
.filter()
|
||||
.nameContains(searchTerm, caseSensitive: false)
|
||||
.remoteIdIsNotNull();
|
||||
final isarUserId = fastHash(Store.get(StoreKey.currentUser).id);
|
||||
|
||||
switch (filterMode) {
|
||||
case QuickFilterMode.sharedWithMe:
|
||||
query = query.owner(
|
||||
(q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).id),
|
||||
);
|
||||
query = query.owner((q) => q.not().isarIdEqualTo(isarUserId));
|
||||
case QuickFilterMode.myAlbums:
|
||||
query = query.owner(
|
||||
(q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).id),
|
||||
);
|
||||
query = query.owner((q) => q.isarIdEqualTo(isarUserId));
|
||||
case QuickFilterMode.all:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
|||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
final assetRepositoryProvider =
|
||||
|
@ -22,20 +23,21 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
|
|||
@override
|
||||
Future<List<Asset>> getByAlbum(
|
||||
Album album, {
|
||||
Iterable<int> notOwnedBy = const [],
|
||||
int? ownerId,
|
||||
Iterable<String> notOwnedBy = const [],
|
||||
String? ownerId,
|
||||
AssetState? state,
|
||||
AssetSort? sortBy,
|
||||
}) {
|
||||
var query = album.assets.filter();
|
||||
final isarUserIds = notOwnedBy.map(fastHash).toList();
|
||||
if (notOwnedBy.length == 1) {
|
||||
query = query.not().ownerIdEqualTo(notOwnedBy.first);
|
||||
query = query.not().ownerIdEqualTo(isarUserIds.first);
|
||||
} else if (notOwnedBy.isNotEmpty) {
|
||||
query =
|
||||
query.not().anyOf(notOwnedBy, (q, int id) => q.ownerIdEqualTo(id));
|
||||
query.not().anyOf(isarUserIds, (q, int id) => q.ownerIdEqualTo(id));
|
||||
}
|
||||
if (ownerId != null) {
|
||||
query = query.ownerIdEqualTo(ownerId);
|
||||
query = query.ownerIdEqualTo(fastHash(ownerId));
|
||||
}
|
||||
|
||||
if (state != null) {
|
||||
|
@ -87,27 +89,28 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
|
|||
|
||||
@override
|
||||
Future<List<Asset>> getAll({
|
||||
required int ownerId,
|
||||
required String ownerId,
|
||||
AssetState? state,
|
||||
AssetSort? sortBy,
|
||||
int? limit,
|
||||
}) {
|
||||
final baseQuery = db.assets.where();
|
||||
final isarUserIds = fastHash(ownerId);
|
||||
final QueryBuilder<Asset, Asset, QAfterFilterCondition> filteredQuery =
|
||||
switch (state) {
|
||||
null => baseQuery.ownerIdEqualToAnyChecksum(ownerId).noOp(),
|
||||
null => baseQuery.ownerIdEqualToAnyChecksum(isarUserIds).noOp(),
|
||||
AssetState.local => baseQuery
|
||||
.remoteIdIsNull()
|
||||
.filter()
|
||||
.localIdIsNotNull()
|
||||
.ownerIdEqualTo(ownerId),
|
||||
.ownerIdEqualTo(isarUserIds),
|
||||
AssetState.remote => baseQuery
|
||||
.localIdIsNull()
|
||||
.filter()
|
||||
.remoteIdIsNotNull()
|
||||
.ownerIdEqualTo(ownerId),
|
||||
.ownerIdEqualTo(isarUserIds),
|
||||
AssetState.merged => baseQuery
|
||||
.ownerIdEqualToAnyChecksum(ownerId)
|
||||
.ownerIdEqualToAnyChecksum(isarUserIds)
|
||||
.filter()
|
||||
.remoteIdIsNotNull()
|
||||
.localIdIsNotNull(),
|
||||
|
@ -132,7 +135,7 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
|
|||
@override
|
||||
Future<List<Asset>> getMatches({
|
||||
required List<Asset> assets,
|
||||
required int ownerId,
|
||||
required String ownerId,
|
||||
AssetState? state,
|
||||
int limit = 100,
|
||||
}) {
|
||||
|
@ -147,7 +150,7 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
|
|||
AssetState.merged =>
|
||||
baseQuery.localIdIsNotNull().filter().remoteIdIsNotNull(),
|
||||
};
|
||||
return _getMatchesImpl(query, ownerId, assets, limit);
|
||||
return _getMatchesImpl(query, fastHash(ownerId), assets, limit);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -185,10 +188,10 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
|
|||
|
||||
@override
|
||||
Future<List<Asset?>> getAllByOwnerIdChecksum(
|
||||
List<int> ids,
|
||||
List<int> ownerIds,
|
||||
List<String> checksums,
|
||||
) =>
|
||||
db.assets.getAllByOwnerIdChecksum(ids, checksums);
|
||||
db.assets.getAllByOwnerIdChecksum(ownerIds, checksums);
|
||||
|
||||
@override
|
||||
Future<List<Asset>> getAllLocal() =>
|
||||
|
@ -224,30 +227,30 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<List<Asset>> getTrashAssets(int userId) {
|
||||
Future<List<Asset>> getTrashAssets(String userId) {
|
||||
return db.assets
|
||||
.where()
|
||||
.remoteIdIsNotNull()
|
||||
.filter()
|
||||
.ownerIdEqualTo(userId)
|
||||
.ownerIdEqualTo(fastHash(userId))
|
||||
.isTrashedEqualTo(true)
|
||||
.findAll();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Asset>> getRecentlyAddedAssets(int userId) {
|
||||
Future<List<Asset>> getRecentlyAddedAssets(String userId) {
|
||||
return db.assets
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(userId)
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.sortByFileCreatedAtDesc()
|
||||
.findAll();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Asset>> getMotionAssets(int userId) {
|
||||
Future<List<Asset>> getMotionAssets(String userId) {
|
||||
return db.assets
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(userId)
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.filter()
|
||||
.livePhotoVideoIdIsNotNull()
|
||||
.findAll();
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:photo_manager/photo_manager.dart' hide AssetType;
|
||||
|
||||
final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository());
|
||||
|
@ -24,7 +25,7 @@ class AssetMediaRepository implements IAssetMediaRepository {
|
|||
final Asset asset = Asset(
|
||||
checksum: "",
|
||||
localId: local.id,
|
||||
ownerId: Store.get(StoreKey.currentUser).id,
|
||||
ownerId: fastHash(Store.get(StoreKey.currentUser).id),
|
||||
fileCreatedAt: local.createDateTime,
|
||||
fileModifiedAt: local.modifiedDateTime,
|
||||
updatedAt: local.modifiedDateTime,
|
||||
|
|
|
@ -15,7 +15,7 @@ class ETagRepository extends DatabaseRepository implements IETagRepository {
|
|||
Future<List<String>> getAllIds() => db.eTags.where().idProperty().findAll();
|
||||
|
||||
@override
|
||||
Future<ETag?> get(int id) => db.eTags.get(id);
|
||||
Future<ETag?> get(String id) => db.eTags.getById(id);
|
||||
|
||||
@override
|
||||
Future<void> upsertAll(List<ETag> etags) => txn(() => db.eTags.putAll(etags));
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
|||
import 'package:immich_mobile/interfaces/timeline.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
|
@ -17,32 +18,32 @@ class TimelineRepository extends DatabaseRepository
|
|||
TimelineRepository(super.db);
|
||||
|
||||
@override
|
||||
Future<List<int>> getTimelineUserIds(int id) {
|
||||
Future<List<String>> getTimelineUserIds(String id) {
|
||||
return db.users
|
||||
.filter()
|
||||
.inTimelineEqualTo(true)
|
||||
.or()
|
||||
.isarIdEqualTo(id)
|
||||
.isarIdProperty()
|
||||
.idEqualTo(id)
|
||||
.idProperty()
|
||||
.findAll();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<int>> watchTimelineUsers(int id) {
|
||||
Stream<List<String>> watchTimelineUsers(String id) {
|
||||
return db.users
|
||||
.filter()
|
||||
.inTimelineEqualTo(true)
|
||||
.or()
|
||||
.isarIdEqualTo(id)
|
||||
.isarIdProperty()
|
||||
.idEqualTo(id)
|
||||
.idProperty()
|
||||
.watch();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchArchiveTimeline(int userId) {
|
||||
Stream<RenderList> watchArchiveTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(userId)
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.filter()
|
||||
.isArchivedEqualTo(true)
|
||||
.isTrashedEqualTo(false)
|
||||
|
@ -52,10 +53,10 @@ class TimelineRepository extends DatabaseRepository
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchFavoriteTimeline(int userId) {
|
||||
Stream<RenderList> watchFavoriteTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(userId)
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.filter()
|
||||
.isFavoriteEqualTo(true)
|
||||
.isTrashedEqualTo(false)
|
||||
|
@ -79,10 +80,10 @@ class TimelineRepository extends DatabaseRepository
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchTrashTimeline(int userId) {
|
||||
Stream<RenderList> watchTrashTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.filter()
|
||||
.ownerIdEqualTo(userId)
|
||||
.ownerIdEqualTo(fastHash(userId))
|
||||
.isTrashedEqualTo(true)
|
||||
.sortByFileCreatedAtDesc();
|
||||
|
||||
|
@ -103,12 +104,12 @@ class TimelineRepository extends DatabaseRepository
|
|||
|
||||
@override
|
||||
Stream<RenderList> watchHomeTimeline(
|
||||
int userId,
|
||||
String userId,
|
||||
GroupAssetsBy groupAssetByOption,
|
||||
) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(userId)
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.filter()
|
||||
.isArchivedEqualTo(false)
|
||||
.isTrashedEqualTo(false)
|
||||
|
@ -120,12 +121,13 @@ class TimelineRepository extends DatabaseRepository
|
|||
|
||||
@override
|
||||
Stream<RenderList> watchMultiUsersTimeline(
|
||||
List<int> userIds,
|
||||
List<String> userIds,
|
||||
GroupAssetsBy groupAssetByOption,
|
||||
) {
|
||||
final isarUserIds = userIds.map(fastHash).toList();
|
||||
final query = db.assets
|
||||
.where()
|
||||
.anyOf(userIds, (qb, userId) => qb.ownerIdEqualToAnyChecksum(userId))
|
||||
.anyOf(isarUserIds, (qb, id) => qb.ownerIdEqualToAnyChecksum(id))
|
||||
.filter()
|
||||
.isArchivedEqualTo(false)
|
||||
.isTrashedEqualTo(false)
|
||||
|
@ -143,12 +145,12 @@ class TimelineRepository extends DatabaseRepository
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchAssetSelectionTimeline(int userId) {
|
||||
Stream<RenderList> watchAssetSelectionTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
.remoteIdIsNotNull()
|
||||
.filter()
|
||||
.ownerIdEqualTo(userId)
|
||||
.ownerIdEqualTo(fastHash(userId))
|
||||
.isTrashedEqualTo(false)
|
||||
.stackPrimaryAssetIdIsNull()
|
||||
.sortByFileCreatedAtDesc();
|
||||
|
|
|
@ -28,6 +28,7 @@ import 'package:immich_mobile/repositories/asset.repository.dart';
|
|||
import 'package:immich_mobile/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/services/entity.service.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final albumServiceProvider = Provider(
|
||||
|
@ -208,7 +209,7 @@ class AlbumService {
|
|||
final Album album = await _albumApiRepository.create(
|
||||
albumName,
|
||||
assetIds: assets.map((asset) => asset.remoteId!),
|
||||
sharedUserIds: sharedUsers.map((user) => user.uid),
|
||||
sharedUserIds: sharedUsers.map((user) => user.id),
|
||||
);
|
||||
await _entityService.fillAlbumWithDatabaseEntities(album);
|
||||
return _albumRepository.create(album);
|
||||
|
@ -296,7 +297,7 @@ class AlbumService {
|
|||
Future<bool> deleteAlbum(Album album) async {
|
||||
try {
|
||||
final userId = _userService.getMyUser().id;
|
||||
if (album.owner.value?.isarId == userId) {
|
||||
if (album.owner.value?.isarId == fastHash(userId)) {
|
||||
await _albumApiRepository.delete(album.remoteId!);
|
||||
}
|
||||
if (album.shared) {
|
||||
|
@ -362,7 +363,7 @@ class AlbumService {
|
|||
try {
|
||||
await _albumApiRepository.removeUser(
|
||||
album.remoteId!,
|
||||
userId: user.uid,
|
||||
userId: user.id,
|
||||
);
|
||||
|
||||
album.sharedUsers.remove(entity.User.fromDto(user));
|
||||
|
|
|
@ -101,7 +101,7 @@ class AssetService {
|
|||
_getRemoteAssetChanges(List<UserDto> users, DateTime since) async {
|
||||
final dto = AssetDeltaSyncDto(
|
||||
updatedAfter: since,
|
||||
userIds: users.map((e) => e.uid).toList(),
|
||||
userIds: users.map((e) => e.id).toList(),
|
||||
);
|
||||
final changes = await _apiService.syncApi.getDeltaSync(dto);
|
||||
return changes == null || changes.needsFullSync
|
||||
|
@ -142,7 +142,7 @@ class AssetService {
|
|||
limit: chunkSize,
|
||||
updatedUntil: until,
|
||||
lastId: lastId,
|
||||
userId: user.uid,
|
||||
userId: user.id,
|
||||
);
|
||||
log.fine("Requesting $chunkSize assets from $lastId");
|
||||
final List<AssetResponseDto>? assets =
|
||||
|
|
|
@ -46,10 +46,10 @@ class PartnerService {
|
|||
|
||||
Future<bool> removePartner(UserDto partner) async {
|
||||
try {
|
||||
await _partnerApiRepository.delete(partner.uid);
|
||||
await _partnerApiRepository.delete(partner.id);
|
||||
await _userRepository.update(partner.copyWith(isPartnerSharedBy: false));
|
||||
} catch (e) {
|
||||
_log.warning("Failed to remove partner ${partner.uid}", e);
|
||||
_log.warning("Failed to remove partner ${partner.id}", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -57,11 +57,11 @@ class PartnerService {
|
|||
|
||||
Future<bool> addPartner(UserDto partner) async {
|
||||
try {
|
||||
await _partnerApiRepository.create(partner.uid);
|
||||
await _partnerApiRepository.create(partner.id);
|
||||
await _userRepository.update(partner.copyWith(isPartnerSharedBy: true));
|
||||
return true;
|
||||
} catch (e) {
|
||||
_log.warning("Failed to add partner ${partner.uid}", e);
|
||||
_log.warning("Failed to add partner ${partner.id}", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -72,14 +72,14 @@ class PartnerService {
|
|||
}) async {
|
||||
try {
|
||||
final dto = await _partnerApiRepository.update(
|
||||
partner.uid,
|
||||
partner.id,
|
||||
inTimeline: inTimeline,
|
||||
);
|
||||
await _userRepository
|
||||
.update(partner.copyWith(inTimeline: dto.inTimeline));
|
||||
return true;
|
||||
} catch (e) {
|
||||
_log.warning("Failed to update partner ${partner.uid}", e);
|
||||
_log.warning("Failed to update partner ${partner.id}", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import 'package:immich_mobile/services/hash.service.dart';
|
|||
import 'package:immich_mobile/utils/async_mutex.dart';
|
||||
import 'package:immich_mobile/utils/datetime_comparison.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final syncServiceProvider = Provider(
|
||||
|
@ -152,14 +153,14 @@ class SyncService {
|
|||
/// Syncs users from the server to the local database
|
||||
/// Returns `true`if there were any changes
|
||||
Future<bool> _syncUsersFromServer(List<UserDto> users) async {
|
||||
users.sortBy((u) => u.uid);
|
||||
users.sortBy((u) => u.id);
|
||||
final dbUsers = await _userRepository.getAll(sortBy: SortUserBy.id);
|
||||
final List<int> toDelete = [];
|
||||
final List<String> toDelete = [];
|
||||
final List<UserDto> toUpsert = [];
|
||||
final changes = diffSortedListsSync(
|
||||
users,
|
||||
dbUsers,
|
||||
compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
|
||||
compare: (UserDto a, UserDto b) => a.id.compareTo(b.id),
|
||||
both: (UserDto a, UserDto b) {
|
||||
if (!a.updatedAt.isAtSameMomentAs(b.updatedAt) ||
|
||||
a.isPartnerSharedBy != b.isPartnerSharedBy ||
|
||||
|
@ -319,12 +320,12 @@ class SyncService {
|
|||
}
|
||||
|
||||
Future<void> _updateUserAssetsETag(List<UserDto> users, DateTime time) {
|
||||
final etags = users.map((u) => ETag(id: u.uid, time: time)).toList();
|
||||
final etags = users.map((u) => ETag(id: u.id, time: time)).toList();
|
||||
return _eTagRepository.upsertAll(etags);
|
||||
}
|
||||
|
||||
Future<void> _clearUserAssetsETag(List<UserDto> users) {
|
||||
final ids = users.map((u) => u.uid).toList();
|
||||
final ids = users.map((u) => u.id).toList();
|
||||
return _eTagRepository.deleteByIds(ids);
|
||||
}
|
||||
|
||||
|
@ -408,7 +409,7 @@ class SyncService {
|
|||
sharedUsers,
|
||||
compare: (UserDto a, UserDto b) => a.id.compareTo(b.id),
|
||||
both: (a, b) => false,
|
||||
onlyFirst: (UserDto a) => userIdsToAdd.add(a.uid),
|
||||
onlyFirst: (UserDto a) => userIdsToAdd.add(a.id),
|
||||
onlySecond: (UserDto a) => usersToUnlink.add(a),
|
||||
);
|
||||
|
||||
|
@ -459,7 +460,8 @@ class SyncService {
|
|||
existing.addAll(foreign);
|
||||
|
||||
// delete assets in DB unless they belong to this user or part of some other shared album
|
||||
deleteCandidates.addAll(toUnlink.where((a) => a.ownerId != userId));
|
||||
final isarUserId = fastHash(userId);
|
||||
deleteCandidates.addAll(toUnlink.where((a) => a.ownerId != isarUserId));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -878,16 +880,16 @@ class SyncService {
|
|||
return null;
|
||||
}
|
||||
|
||||
users.sortBy((u) => u.uid);
|
||||
sharedBy.sortBy((u) => u.uid);
|
||||
sharedWith.sortBy((u) => u.uid);
|
||||
users.sortBy((u) => u.id);
|
||||
sharedBy.sortBy((u) => u.id);
|
||||
sharedWith.sortBy((u) => u.id);
|
||||
|
||||
final updatedSharedBy = <UserDto>[];
|
||||
|
||||
diffSortedListsSync(
|
||||
users,
|
||||
sharedBy,
|
||||
compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
|
||||
compare: (UserDto a, UserDto b) => a.id.compareTo(b.id),
|
||||
both: (UserDto a, UserDto b) {
|
||||
updatedSharedBy.add(a.copyWith(isPartnerSharedBy: true));
|
||||
return true;
|
||||
|
@ -901,7 +903,7 @@ class SyncService {
|
|||
diffSortedListsSync(
|
||||
updatedSharedBy,
|
||||
sharedWith,
|
||||
compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
|
||||
compare: (UserDto a, UserDto b) => a.id.compareTo(b.id),
|
||||
both: (UserDto a, UserDto b) {
|
||||
updatedSharedWith.add(
|
||||
a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true),
|
||||
|
|
|
@ -28,21 +28,21 @@ class TimelineService {
|
|||
this._userService,
|
||||
);
|
||||
|
||||
Future<List<int>> getTimelineUserIds() async {
|
||||
Future<List<String>> getTimelineUserIds() async {
|
||||
final me = _userService.getMyUser();
|
||||
return _timelineRepository.getTimelineUserIds(me.id);
|
||||
}
|
||||
|
||||
Stream<List<int>> watchTimelineUserIds() async* {
|
||||
Stream<List<String>> watchTimelineUserIds() async* {
|
||||
final me = _userService.getMyUser();
|
||||
yield* _timelineRepository.watchTimelineUsers(me.id);
|
||||
}
|
||||
|
||||
Stream<RenderList> watchHomeTimeline(int userId) {
|
||||
Stream<RenderList> watchHomeTimeline(String userId) {
|
||||
return _timelineRepository.watchHomeTimeline(userId, _getGroupByOption());
|
||||
}
|
||||
|
||||
Stream<RenderList> watchMultiUsersTimeline(List<int> userIds) {
|
||||
Stream<RenderList> watchMultiUsersTimeline(List<String> userIds) {
|
||||
return _timelineRepository.watchMultiUsersTimeline(
|
||||
userIds,
|
||||
_getGroupByOption(),
|
||||
|
@ -83,10 +83,10 @@ class TimelineService {
|
|||
GroupAssetsBy? groupBy,
|
||||
) {
|
||||
GroupAssetsBy groupOption = GroupAssetsBy.none;
|
||||
if (groupBy != null) {
|
||||
groupOption = groupBy;
|
||||
} else {
|
||||
if (groupBy == null) {
|
||||
groupOption = _getGroupByOption();
|
||||
} else {
|
||||
groupOption = groupBy;
|
||||
}
|
||||
|
||||
return _timelineRepository.getTimelineFromAssets(
|
||||
|
|
|
@ -6,13 +6,33 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
const int targetVersion = 8;
|
||||
const int targetVersion = 9;
|
||||
|
||||
Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
||||
final int version = Store.get(StoreKey.version, 1);
|
||||
|
||||
if (version < 9) {
|
||||
await Store.put(StoreKey.version, version);
|
||||
final value = await db.storeValues.get(StoreKey.currentUser.id);
|
||||
if (value != null) {
|
||||
final id = value.intValue;
|
||||
if (id == null) {
|
||||
return;
|
||||
}
|
||||
await db.writeTxn(() async {
|
||||
final user = await db.users.get(id);
|
||||
await db.storeValues
|
||||
.put(StoreValue(StoreKey.currentUser.id, strValue: user?.id));
|
||||
});
|
||||
}
|
||||
// Do not clear other entities
|
||||
return;
|
||||
}
|
||||
|
||||
if (version < targetVersion) {
|
||||
_migrateTo(db, targetVersion);
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ class AlbumThumbnailCard extends ConsumerWidget {
|
|||
// Add the owner name to the subtitle
|
||||
String? owner;
|
||||
if (showOwner) {
|
||||
if (album.ownerId == ref.read(currentUserProvider)?.uid) {
|
||||
if (album.ownerId == ref.read(currentUserProvider)?.id) {
|
||||
owner = 'album_thumbnail_owned'.tr();
|
||||
} else if (album.ownerName != null) {
|
||||
owner = 'album_thumbnail_shared_by'.tr(args: [album.ownerName!]);
|
||||
|
|
|
@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/server_info.provider.dart';
|
|||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/services/stack.service.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
|
||||
import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart';
|
||||
|
@ -49,7 +50,8 @@ class BottomGalleryBar extends ConsumerWidget {
|
|||
if (asset == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.id;
|
||||
final isOwner =
|
||||
asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? '');
|
||||
final showControls = ref.watch(showControlsProvider);
|
||||
final stackId = asset.stackId;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/theme_extensions.dart';
|
|||
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/services/asset.service.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
|
@ -81,7 +82,7 @@ class DescriptionInput extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
return TextField(
|
||||
enabled: owner?.id == asset.ownerId,
|
||||
enabled: fastHash(owner?.id ?? '') == asset.ownerId,
|
||||
focusNode: focusNode,
|
||||
onTap: () => isFocus.value = true,
|
||||
onChanged: (value) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import 'package:immich_mobile/providers/tab.provider.dart';
|
|||
import 'package:immich_mobile/providers/trash.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:immich_mobile/widgets/album/add_to_album_bottom_sheet.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/upload_dialog.dart';
|
||||
import 'package:immich_mobile/widgets/asset_viewer/top_control_app_bar.dart';
|
||||
|
@ -33,12 +34,13 @@ class GalleryAppBar extends ConsumerWidget {
|
|||
return const SizedBox();
|
||||
}
|
||||
final album = ref.watch(currentAlbumProvider);
|
||||
final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.id;
|
||||
final isOwner =
|
||||
asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? '');
|
||||
final showControls = ref.watch(showControlsProvider);
|
||||
|
||||
final isPartner = ref
|
||||
.watch(partnerSharedWithProvider)
|
||||
.map((e) => e.id)
|
||||
.map((e) => fastHash(e.id))
|
||||
.contains(asset.ownerId);
|
||||
|
||||
toggleFavorite(Asset asset) =>
|
||||
|
|
|
@ -8,7 +8,7 @@ import 'package:immich_mobile/services/api.service.dart';
|
|||
|
||||
Widget userAvatar(BuildContext context, UserDto u, {double? radius}) {
|
||||
final url =
|
||||
"${Store.get(StoreKey.serverEndpoint)}/users/${u.uid}/profile-image";
|
||||
"${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image";
|
||||
final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : "";
|
||||
return CircleAvatar(
|
||||
radius: radius,
|
||||
|
|
|
@ -27,7 +27,7 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
bool isDarkTheme = context.themeData.brightness == Brightness.dark;
|
||||
final profileImageUrl =
|
||||
'${Store.get(StoreKey.serverEndpoint)}/users/${user.uid}/profile-image?d=${Random().nextInt(1024)}';
|
||||
'${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}';
|
||||
|
||||
final textIcon = DefaultTextStyle(
|
||||
style: TextStyle(
|
||||
|
|
6
mobile/test/fixtures/user.stub.dart
vendored
6
mobile/test/fixtures/user.stub.dart
vendored
|
@ -4,7 +4,7 @@ abstract final class UserStub {
|
|||
const UserStub._();
|
||||
|
||||
static final admin = UserDto(
|
||||
uid: "admin",
|
||||
id: "admin",
|
||||
email: "admin@test.com",
|
||||
name: "admin",
|
||||
isAdmin: true,
|
||||
|
@ -14,7 +14,7 @@ abstract final class UserStub {
|
|||
);
|
||||
|
||||
static final user1 = UserDto(
|
||||
uid: "user1",
|
||||
id: "user1",
|
||||
email: "user1@test.com",
|
||||
name: "user1",
|
||||
isAdmin: false,
|
||||
|
@ -24,7 +24,7 @@ abstract final class UserStub {
|
|||
);
|
||||
|
||||
static final user2 = UserDto(
|
||||
uid: "user2",
|
||||
id: "user2",
|
||||
email: "user2@test.com",
|
||||
name: "user2",
|
||||
isAdmin: false,
|
||||
|
|
|
@ -66,7 +66,7 @@ void main() {
|
|||
final MockUserService userService = MockUserService();
|
||||
|
||||
final owner = UserDto(
|
||||
uid: "1",
|
||||
id: "1",
|
||||
updatedAt: DateTime.now(),
|
||||
email: "a@b.c",
|
||||
name: "first last",
|
||||
|
@ -110,7 +110,7 @@ void main() {
|
|||
);
|
||||
when(() => userService.getMyUser()).thenReturn(owner);
|
||||
when(() => eTagRepository.get(owner.id))
|
||||
.thenAnswer((_) async => ETag(id: owner.uid, time: DateTime.now()));
|
||||
.thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now()));
|
||||
when(() => eTagRepository.deleteByIds(["1"])).thenAnswer((_) async {});
|
||||
when(() => eTagRepository.upsertAll(any())).thenAnswer((_) async {});
|
||||
when(() => partnerRepository.getSharedWith()).thenAnswer((_) async => []);
|
||||
|
|
|
@ -149,7 +149,7 @@ void main() {
|
|||
() => albumApiRepository.create(
|
||||
"name",
|
||||
assetIds: [AssetStub.image1.remoteId!],
|
||||
sharedUserIds: [UserStub.user1.uid],
|
||||
sharedUserIds: [UserStub.user1.id],
|
||||
),
|
||||
).called(1);
|
||||
verify(
|
||||
|
@ -217,7 +217,7 @@ void main() {
|
|||
|
||||
final result = await sut.addUsers(
|
||||
AlbumStub.emptyAlbum,
|
||||
[UserStub.user2.uid],
|
||||
[UserStub.user2.id],
|
||||
);
|
||||
|
||||
expect(result, true);
|
||||
|
|
|
@ -41,7 +41,7 @@ void main() {
|
|||
[User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)],
|
||||
);
|
||||
|
||||
when(() => userRepository.get(any()))
|
||||
when(() => userRepository.getByUserId(any()))
|
||||
.thenAnswer((_) async => UserStub.admin);
|
||||
when(() => userRepository.getByUserId(any()))
|
||||
.thenAnswer((_) async => UserStub.admin);
|
||||
|
|
Loading…
Add table
Reference in a new issue