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