mirror of https://github.com/immich-app/immich.git
refactor: user entity (#16655)
* refactor: user entity * fix: add users to album & user profile url * chore: rebase fixes * generate files * fix(mobile): timeline not reset on login * fix: test stub * refactor: rename user model (#16813) * refactor: rename user model * simplify import --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com> * chore: generate files * fix: use getAllAccessible instead of getAll --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com>pull/16823/head
parent
a75718ce99
commit
d1c8fe5303
@ -0,0 +1,24 @@
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
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);
|
||||
|
||||
Future<List<UserDto>> getAll({SortUserBy? sortBy});
|
||||
|
||||
Future<bool> updateAll(List<UserDto> users);
|
||||
|
||||
Future<UserDto> update(UserDto user);
|
||||
|
||||
Future<void> delete(List<int> ids);
|
||||
|
||||
Future<void> deleteAll();
|
||||
}
|
||||
|
||||
enum SortUserBy { id }
|
||||
@ -0,0 +1,157 @@
|
||||
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,
|
||||
pink,
|
||||
red,
|
||||
yellow,
|
||||
blue,
|
||||
green,
|
||||
purple,
|
||||
orange,
|
||||
gray,
|
||||
amber;
|
||||
|
||||
Color toColor({bool isDarkTheme = false}) => switch (this) {
|
||||
AvatarColor.primary =>
|
||||
isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF),
|
||||
AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182),
|
||||
AvatarColor.red => const Color.fromARGB(255, 239, 68, 68),
|
||||
AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8),
|
||||
AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246),
|
||||
AvatarColor.green => const Color.fromARGB(255, 22, 163, 74),
|
||||
AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234),
|
||||
AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12),
|
||||
AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99),
|
||||
AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6),
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Rename to User once Isar is removed
|
||||
class UserDto {
|
||||
final String uid;
|
||||
final String email;
|
||||
final String name;
|
||||
final bool isAdmin;
|
||||
final DateTime updatedAt;
|
||||
|
||||
final String? profileImagePath;
|
||||
final AvatarColor avatarColor;
|
||||
|
||||
final bool memoryEnabled;
|
||||
final bool inTimeline;
|
||||
|
||||
final bool isPartnerSharedBy;
|
||||
final bool isPartnerSharedWith;
|
||||
|
||||
final int quotaUsageInBytes;
|
||||
final int quotaSizeInBytes;
|
||||
|
||||
int get id => fastHash(uid);
|
||||
bool get hasQuota => quotaSizeInBytes > 0;
|
||||
|
||||
const UserDto({
|
||||
required this.uid,
|
||||
required this.email,
|
||||
required this.name,
|
||||
required this.isAdmin,
|
||||
required this.updatedAt,
|
||||
this.profileImagePath,
|
||||
this.avatarColor = AvatarColor.primary,
|
||||
this.memoryEnabled = true,
|
||||
this.inTimeline = false,
|
||||
this.isPartnerSharedBy = false,
|
||||
this.isPartnerSharedWith = false,
|
||||
this.quotaUsageInBytes = 0,
|
||||
this.quotaSizeInBytes = 0,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''User: {
|
||||
id: $id,
|
||||
uid: $uid,
|
||||
email: $email,
|
||||
name: $name,
|
||||
isAdmin: $isAdmin,
|
||||
updatedAt: $updatedAt,
|
||||
profileImagePath: ${profileImagePath ?? '<NA>'},
|
||||
avatarColor: $avatarColor,
|
||||
memoryEnabled: $memoryEnabled,
|
||||
inTimeline: $inTimeline,
|
||||
isPartnerSharedBy: $isPartnerSharedBy,
|
||||
isPartnerSharedWith: $isPartnerSharedWith,
|
||||
quotaUsageInBytes: $quotaUsageInBytes,
|
||||
quotaSizeInBytes: $quotaSizeInBytes,
|
||||
}''';
|
||||
}
|
||||
|
||||
UserDto copyWith({
|
||||
String? uid,
|
||||
String? email,
|
||||
String? name,
|
||||
bool? isAdmin,
|
||||
DateTime? updatedAt,
|
||||
String? profileImagePath,
|
||||
AvatarColor? avatarColor,
|
||||
bool? memoryEnabled,
|
||||
bool? inTimeline,
|
||||
bool? isPartnerSharedBy,
|
||||
bool? isPartnerSharedWith,
|
||||
int? quotaUsageInBytes,
|
||||
int? quotaSizeInBytes,
|
||||
}) =>
|
||||
UserDto(
|
||||
uid: uid ?? this.uid,
|
||||
email: email ?? this.email,
|
||||
name: name ?? this.name,
|
||||
isAdmin: isAdmin ?? this.isAdmin,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
profileImagePath: profileImagePath ?? this.profileImagePath,
|
||||
avatarColor: avatarColor ?? this.avatarColor,
|
||||
memoryEnabled: memoryEnabled ?? this.memoryEnabled,
|
||||
inTimeline: inTimeline ?? this.inTimeline,
|
||||
isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy,
|
||||
isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith,
|
||||
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
|
||||
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(covariant UserDto other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.uid == uid &&
|
||||
other.updatedAt.isAtSameMomentAs(updatedAt) &&
|
||||
other.avatarColor == avatarColor &&
|
||||
other.email == email &&
|
||||
other.name == name &&
|
||||
other.isPartnerSharedBy == isPartnerSharedBy &&
|
||||
other.isPartnerSharedWith == isPartnerSharedWith &&
|
||||
other.profileImagePath == profileImagePath &&
|
||||
other.isAdmin == isAdmin &&
|
||||
other.memoryEnabled == memoryEnabled &&
|
||||
other.inTimeline == inTimeline &&
|
||||
other.quotaUsageInBytes == quotaUsageInBytes &&
|
||||
other.quotaSizeInBytes == quotaSizeInBytes;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
uid.hashCode ^
|
||||
name.hashCode ^
|
||||
email.hashCode ^
|
||||
updatedAt.hashCode ^
|
||||
isAdmin.hashCode ^
|
||||
profileImagePath.hashCode ^
|
||||
avatarColor.hashCode ^
|
||||
memoryEnabled.hashCode ^
|
||||
inTimeline.hashCode ^
|
||||
isPartnerSharedBy.hashCode ^
|
||||
isPartnerSharedWith.hashCode ^
|
||||
quotaUsageInBytes.hashCode ^
|
||||
quotaSizeInBytes.hashCode;
|
||||
}
|
||||
@ -1,181 +0,0 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
part 'user.entity.g.dart';
|
||||
|
||||
@Collection(inheritance: false)
|
||||
class User {
|
||||
User({
|
||||
required this.id,
|
||||
required this.updatedAt,
|
||||
required this.email,
|
||||
required this.name,
|
||||
required this.isAdmin,
|
||||
this.isPartnerSharedBy = false,
|
||||
this.isPartnerSharedWith = false,
|
||||
this.profileImagePath = '',
|
||||
this.avatarColor = AvatarColorEnum.primary,
|
||||
this.memoryEnabled = true,
|
||||
this.inTimeline = false,
|
||||
this.quotaUsageInBytes = 0,
|
||||
this.quotaSizeInBytes = 0,
|
||||
});
|
||||
|
||||
Id get isarId => fastHash(id);
|
||||
|
||||
User.fromUserDto(
|
||||
UserAdminResponseDto dto,
|
||||
UserPreferencesResponseDto? preferences,
|
||||
) : id = dto.id,
|
||||
updatedAt = dto.updatedAt,
|
||||
email = dto.email,
|
||||
name = dto.name,
|
||||
isPartnerSharedBy = false,
|
||||
isPartnerSharedWith = false,
|
||||
profileImagePath = dto.profileImagePath,
|
||||
isAdmin = dto.isAdmin,
|
||||
memoryEnabled = preferences?.memories.enabled ?? false,
|
||||
avatarColor = dto.avatarColor.toAvatarColor(),
|
||||
inTimeline = false,
|
||||
quotaUsageInBytes = dto.quotaUsageInBytes ?? 0,
|
||||
quotaSizeInBytes = dto.quotaSizeInBytes ?? 0;
|
||||
|
||||
User.fromPartnerDto(PartnerResponseDto dto)
|
||||
: id = dto.id,
|
||||
updatedAt = DateTime.now(),
|
||||
email = dto.email,
|
||||
name = dto.name,
|
||||
isPartnerSharedBy = false,
|
||||
isPartnerSharedWith = false,
|
||||
profileImagePath = dto.profileImagePath,
|
||||
isAdmin = false,
|
||||
memoryEnabled = false,
|
||||
avatarColor = dto.avatarColor.toAvatarColor(),
|
||||
inTimeline = dto.inTimeline ?? false,
|
||||
quotaUsageInBytes = 0,
|
||||
quotaSizeInBytes = 0;
|
||||
|
||||
/// Base user dto used where the complete user object is not required
|
||||
User.fromSimpleUserDto(UserResponseDto dto)
|
||||
: id = dto.id,
|
||||
email = dto.email,
|
||||
name = dto.name,
|
||||
profileImagePath = dto.profileImagePath,
|
||||
avatarColor = dto.avatarColor.toAvatarColor(),
|
||||
// Fill the remaining fields with placeholders
|
||||
isAdmin = false,
|
||||
inTimeline = false,
|
||||
memoryEnabled = false,
|
||||
isPartnerSharedBy = false,
|
||||
isPartnerSharedWith = false,
|
||||
updatedAt = DateTime.now(),
|
||||
quotaUsageInBytes = 0,
|
||||
quotaSizeInBytes = 0;
|
||||
|
||||
@Index(unique: true, replace: false, type: IndexType.hash)
|
||||
String id;
|
||||
DateTime updatedAt;
|
||||
String email;
|
||||
String name;
|
||||
bool isPartnerSharedBy;
|
||||
bool isPartnerSharedWith;
|
||||
bool isAdmin;
|
||||
String profileImagePath;
|
||||
@Enumerated(EnumType.ordinal)
|
||||
AvatarColorEnum avatarColor;
|
||||
bool memoryEnabled;
|
||||
bool inTimeline;
|
||||
int quotaUsageInBytes;
|
||||
int quotaSizeInBytes;
|
||||
|
||||
bool get hasQuota => quotaSizeInBytes > 0;
|
||||
@Backlink(to: 'owner')
|
||||
final IsarLinks<Album> albums = IsarLinks<Album>();
|
||||
@Backlink(to: 'sharedUsers')
|
||||
final IsarLinks<Album> sharedAlbums = IsarLinks<Album>();
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
if (other is! User) return false;
|
||||
return id == other.id &&
|
||||
updatedAt.isAtSameMomentAs(other.updatedAt) &&
|
||||
avatarColor == other.avatarColor &&
|
||||
email == other.email &&
|
||||
name == other.name &&
|
||||
isPartnerSharedBy == other.isPartnerSharedBy &&
|
||||
isPartnerSharedWith == other.isPartnerSharedWith &&
|
||||
profileImagePath == other.profileImagePath &&
|
||||
isAdmin == other.isAdmin &&
|
||||
memoryEnabled == other.memoryEnabled &&
|
||||
inTimeline == other.inTimeline &&
|
||||
quotaUsageInBytes == other.quotaUsageInBytes &&
|
||||
quotaSizeInBytes == other.quotaSizeInBytes;
|
||||
}
|
||||
|
||||
@override
|
||||
@ignore
|
||||
int get hashCode =>
|
||||
id.hashCode ^
|
||||
updatedAt.hashCode ^
|
||||
email.hashCode ^
|
||||
name.hashCode ^
|
||||
isPartnerSharedBy.hashCode ^
|
||||
isPartnerSharedWith.hashCode ^
|
||||
profileImagePath.hashCode ^
|
||||
avatarColor.hashCode ^
|
||||
isAdmin.hashCode ^
|
||||
memoryEnabled.hashCode ^
|
||||
inTimeline.hashCode ^
|
||||
quotaUsageInBytes.hashCode ^
|
||||
quotaSizeInBytes.hashCode;
|
||||
}
|
||||
|
||||
enum AvatarColorEnum {
|
||||
// do not change this order or reuse indices for other purposes, adding is OK
|
||||
primary,
|
||||
pink,
|
||||
red,
|
||||
yellow,
|
||||
blue,
|
||||
green,
|
||||
purple,
|
||||
orange,
|
||||
gray,
|
||||
amber,
|
||||
}
|
||||
|
||||
extension AvatarColorEnumHelper on UserAvatarColor {
|
||||
AvatarColorEnum toAvatarColor() => switch (this) {
|
||||
UserAvatarColor.primary => AvatarColorEnum.primary,
|
||||
UserAvatarColor.pink => AvatarColorEnum.pink,
|
||||
UserAvatarColor.red => AvatarColorEnum.red,
|
||||
UserAvatarColor.yellow => AvatarColorEnum.yellow,
|
||||
UserAvatarColor.blue => AvatarColorEnum.blue,
|
||||
UserAvatarColor.green => AvatarColorEnum.green,
|
||||
UserAvatarColor.purple => AvatarColorEnum.purple,
|
||||
UserAvatarColor.orange => AvatarColorEnum.orange,
|
||||
UserAvatarColor.gray => AvatarColorEnum.gray,
|
||||
UserAvatarColor.amber => AvatarColorEnum.amber,
|
||||
_ => AvatarColorEnum.primary,
|
||||
};
|
||||
}
|
||||
|
||||
extension AvatarColorToColorHelper on AvatarColorEnum {
|
||||
Color toColor([bool isDarkTheme = false]) => switch (this) {
|
||||
AvatarColorEnum.primary =>
|
||||
isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF),
|
||||
AvatarColorEnum.pink => const Color.fromARGB(255, 244, 114, 182),
|
||||
AvatarColorEnum.red => const Color.fromARGB(255, 239, 68, 68),
|
||||
AvatarColorEnum.yellow => const Color.fromARGB(255, 234, 179, 8),
|
||||
AvatarColorEnum.blue => const Color.fromARGB(255, 59, 130, 246),
|
||||
AvatarColorEnum.green => const Color.fromARGB(255, 22, 163, 74),
|
||||
AvatarColorEnum.purple => const Color.fromARGB(255, 147, 51, 234),
|
||||
AvatarColorEnum.orange => const Color.fromARGB(255, 234, 88, 12),
|
||||
AvatarColorEnum.gray => const Color.fromARGB(255, 75, 85, 99),
|
||||
AvatarColorEnum.amber => const Color.fromARGB(255, 217, 119, 6),
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
part 'user.entity.g.dart';
|
||||
|
||||
@Collection(inheritance: false)
|
||||
class User {
|
||||
Id get isarId => fastHash(id);
|
||||
@Index(unique: true, replace: false, type: IndexType.hash)
|
||||
final String id;
|
||||
final DateTime updatedAt;
|
||||
final String email;
|
||||
final String name;
|
||||
final bool isPartnerSharedBy;
|
||||
final bool isPartnerSharedWith;
|
||||
final bool isAdmin;
|
||||
final String profileImagePath;
|
||||
@Enumerated(EnumType.ordinal)
|
||||
final AvatarColor avatarColor;
|
||||
final bool memoryEnabled;
|
||||
final bool inTimeline;
|
||||
final int quotaUsageInBytes;
|
||||
final int quotaSizeInBytes;
|
||||
|
||||
const User({
|
||||
required this.id,
|
||||
required this.updatedAt,
|
||||
required this.email,
|
||||
required this.name,
|
||||
required this.isAdmin,
|
||||
this.isPartnerSharedBy = false,
|
||||
this.isPartnerSharedWith = false,
|
||||
this.profileImagePath = '',
|
||||
this.avatarColor = AvatarColor.primary,
|
||||
this.memoryEnabled = true,
|
||||
this.inTimeline = false,
|
||||
this.quotaUsageInBytes = 0,
|
||||
this.quotaSizeInBytes = 0,
|
||||
});
|
||||
|
||||
static User fromDto(UserDto dto) => User(
|
||||
id: dto.uid,
|
||||
updatedAt: dto.updatedAt,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: dto.isAdmin,
|
||||
isPartnerSharedBy: dto.isPartnerSharedBy,
|
||||
isPartnerSharedWith: dto.isPartnerSharedWith,
|
||||
profileImagePath: dto.profileImagePath ?? "",
|
||||
avatarColor: dto.avatarColor,
|
||||
memoryEnabled: dto.memoryEnabled,
|
||||
inTimeline: dto.inTimeline,
|
||||
quotaUsageInBytes: dto.quotaUsageInBytes,
|
||||
quotaSizeInBytes: dto.quotaSizeInBytes,
|
||||
);
|
||||
|
||||
UserDto toDto() => UserDto(
|
||||
uid: id,
|
||||
email: email,
|
||||
name: name,
|
||||
isAdmin: isAdmin,
|
||||
updatedAt: updatedAt,
|
||||
profileImagePath: profileImagePath.isEmpty ? null : profileImagePath,
|
||||
avatarColor: avatarColor,
|
||||
memoryEnabled: memoryEnabled,
|
||||
inTimeline: inTimeline,
|
||||
isPartnerSharedBy: isPartnerSharedBy,
|
||||
isPartnerSharedWith: isPartnerSharedWith,
|
||||
quotaUsageInBytes: quotaUsageInBytes,
|
||||
quotaSizeInBytes: quotaSizeInBytes,
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||
as entity;
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
class IsarUserRepository extends IsarDatabaseRepository
|
||||
implements IUserRepository {
|
||||
final Isar _db;
|
||||
const IsarUserRepository(super.db) : _db = db;
|
||||
|
||||
@override
|
||||
Future<void> delete(List<int> ids) async {
|
||||
await transaction(() async {
|
||||
await _db.users.deleteAll(ids);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAll() async {
|
||||
await transaction(() async {
|
||||
await _db.users.clear();
|
||||
});
|
||||
}
|
||||
|
||||
@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
|
||||
.where()
|
||||
.optional(
|
||||
sortBy != null,
|
||||
(query) => switch (sortBy!) {
|
||||
SortUserBy.id => query.sortById(),
|
||||
},
|
||||
)
|
||||
.findAll())
|
||||
.map((u) => u.toDto())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UserDto?> getByUserId(String id) async {
|
||||
return (await _db.users.getById(id))?.toDto();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<UserDto?>> getByUserIds(List<String> ids) async {
|
||||
return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> insert(UserDto user) async {
|
||||
await transaction(() async {
|
||||
await _db.users.put(entity.User.fromDto(user));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UserDto> update(UserDto user) async {
|
||||
await transaction(() async {
|
||||
await _db.users.put(entity.User.fromDto(user));
|
||||
});
|
||||
return user;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> updateAll(List<UserDto> users) async {
|
||||
await transaction(() async {
|
||||
await _db.users.putAll(users.map(entity.User.fromDto).toList());
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
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,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: false,
|
||||
updatedAt: DateTime.now(),
|
||||
profileImagePath: dto.profileImagePath,
|
||||
avatarColor: dto.avatarColor.toAvatarColor(),
|
||||
);
|
||||
|
||||
static UserDto fromAdminDto(
|
||||
UserAdminResponseDto adminDto, [
|
||||
UserPreferencesResponseDto? preferenceDto,
|
||||
]) =>
|
||||
UserDto(
|
||||
uid: adminDto.id,
|
||||
email: adminDto.email,
|
||||
name: adminDto.name,
|
||||
isAdmin: adminDto.isAdmin,
|
||||
updatedAt: adminDto.updatedAt,
|
||||
profileImagePath: adminDto.profileImagePath,
|
||||
avatarColor: adminDto.avatarColor.toAvatarColor(),
|
||||
memoryEnabled: preferenceDto?.memories.enabled ?? true,
|
||||
inTimeline: false,
|
||||
isPartnerSharedBy: false,
|
||||
isPartnerSharedWith: false,
|
||||
quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0,
|
||||
quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0,
|
||||
);
|
||||
|
||||
static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto(
|
||||
uid: dto.id,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: false,
|
||||
updatedAt: DateTime.now(),
|
||||
profileImagePath: dto.profileImagePath,
|
||||
avatarColor: dto.avatarColor.toAvatarColor(),
|
||||
memoryEnabled: false,
|
||||
inTimeline: dto.inTimeline ?? false,
|
||||
isPartnerSharedBy: false,
|
||||
isPartnerSharedWith: false,
|
||||
quotaUsageInBytes: 0,
|
||||
quotaSizeInBytes: 0,
|
||||
);
|
||||
}
|
||||
|
||||
extension on UserAvatarColor {
|
||||
AvatarColor toAvatarColor() => switch (this) {
|
||||
UserAvatarColor.red => AvatarColor.red,
|
||||
UserAvatarColor.green => AvatarColor.green,
|
||||
UserAvatarColor.blue => AvatarColor.blue,
|
||||
UserAvatarColor.purple => AvatarColor.purple,
|
||||
UserAvatarColor.orange => AvatarColor.orange,
|
||||
UserAvatarColor.pink => AvatarColor.pink,
|
||||
UserAvatarColor.amber => AvatarColor.amber,
|
||||
UserAvatarColor.yellow => AvatarColor.yellow,
|
||||
UserAvatarColor.gray => AvatarColor.gray,
|
||||
UserAvatarColor.primary || _ => AvatarColor.primary,
|
||||
};
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
|
||||
abstract class IPartnerRepository {
|
||||
Future<List<User>> getSharedWith();
|
||||
Future<List<User>> getSharedBy();
|
||||
Stream<List<User>> watchSharedWith();
|
||||
Stream<List<User>> watchSharedBy();
|
||||
Future<List<UserDto>> getSharedWith();
|
||||
Future<List<UserDto>> getSharedBy();
|
||||
Stream<List<UserDto>> watchSharedWith();
|
||||
Stream<List<UserDto>> watchSharedBy();
|
||||
}
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
||||
|
||||
abstract interface class IUserRepository implements IDatabaseRepository {
|
||||
Future<User?> get(String id);
|
||||
|
||||
Future<User?> getByDbId(int id);
|
||||
|
||||
Future<List<User>> getByIds(List<String> ids);
|
||||
|
||||
Future<List<User>> getAll({bool self = true, UserSort? sortBy});
|
||||
|
||||
/// Returns all users whose assets can be accessed (self+partners)
|
||||
Future<List<User>> getAllAccessible();
|
||||
|
||||
Future<List<User>> upsertAll(List<User> users);
|
||||
|
||||
Future<User> update(User user);
|
||||
|
||||
Future<void> deleteById(List<int> ids);
|
||||
|
||||
Future<User> me();
|
||||
|
||||
Future<void> clearTable();
|
||||
}
|
||||
|
||||
enum UserSort { id }
|
||||
@ -1,9 +1,14 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
|
||||
final otherUsersProvider = FutureProvider.autoDispose<List<User>>((ref) {
|
||||
final otherUsersProvider =
|
||||
FutureProvider.autoDispose<List<UserDto>>((ref) async {
|
||||
UserService userService = ref.watch(userServiceProvider);
|
||||
final currentUser = ref.watch(currentUserProvider);
|
||||
|
||||
return userService.getUsers();
|
||||
final allUsers = await userService.getAll();
|
||||
allUsers.removeWhere((u) => currentUser?.id == u.id);
|
||||
return allUsers;
|
||||
});
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'store.provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
@Riverpod(keepAlive: true)
|
||||
IStoreRepository storeRepository(Ref ref) =>
|
||||
IsarStoreRepository(ref.watch(isarProvider));
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
StoreService storeService(Ref _) => StoreService.I;
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'user.provider.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
IUserRepository userRepository(Ref ref) =>
|
||||
IsarUserRepository(ref.watch(isarProvider));
|
||||
@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user.provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$userRepositoryHash() => r'1a2ac726bcc44397dcaecf449084fefd336696d4';
|
||||
|
||||
/// See also [userRepository].
|
||||
@ProviderFor(userRepository)
|
||||
final userRepositoryProvider = Provider<IUserRepository>.internal(
|
||||
userRepository,
|
||||
name: r'userRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$userRepositoryHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef UserRepositoryRef = ProviderRef<IUserRepository>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@ -1,73 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
final userRepositoryProvider =
|
||||
Provider((ref) => UserRepository(ref.watch(dbProvider)));
|
||||
|
||||
class UserRepository extends DatabaseRepository implements IUserRepository {
|
||||
UserRepository(super.db);
|
||||
|
||||
@override
|
||||
Future<List<User>> getByIds(List<String> ids) async =>
|
||||
(await db.users.getAllById(ids)).nonNulls.toList();
|
||||
|
||||
@override
|
||||
Future<User?> get(String id) => db.users.getById(id);
|
||||
|
||||
@override
|
||||
Future<List<User>> getAll({bool self = true, UserSort? sortBy}) {
|
||||
final baseQuery = db.users.where();
|
||||
final int userId = Store.get(StoreKey.currentUser).isarId;
|
||||
final QueryBuilder<User, User, QAfterWhereClause> afterWhere =
|
||||
self ? baseQuery.noOp() : baseQuery.isarIdNotEqualTo(userId);
|
||||
final QueryBuilder<User, User, QAfterSortBy> query = switch (sortBy) {
|
||||
null => afterWhere.noOp(),
|
||||
UserSort.id => afterWhere.sortById(),
|
||||
};
|
||||
return query.findAll();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> update(User user) async {
|
||||
await txn(() => db.users.put(user));
|
||||
return user;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> me() => Future.value(Store.get(StoreKey.currentUser));
|
||||
|
||||
@override
|
||||
Future<void> deleteById(List<int> ids) => txn(() => db.users.deleteAll(ids));
|
||||
|
||||
@override
|
||||
Future<List<User>> upsertAll(List<User> users) async {
|
||||
await txn(() => db.users.putAll(users));
|
||||
return users;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<User>> getAllAccessible() => db.users
|
||||
.filter()
|
||||
.isPartnerSharedWithEqualTo(true)
|
||||
.or()
|
||||
.isarIdEqualTo(Store.get(StoreKey.currentUser).isarId)
|
||||
.findAll();
|
||||
|
||||
@override
|
||||
Future<User?> getByDbId(int id) async {
|
||||
return await db.users.get(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearTable() async {
|
||||
await txn(() async {
|
||||
await db.users.clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,35 +1,35 @@
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
|
||||
abstract final class UserStub {
|
||||
const UserStub._();
|
||||
|
||||
static final admin = User(
|
||||
id: "admin",
|
||||
updatedAt: DateTime(2021),
|
||||
static final admin = UserDto(
|
||||
uid: "admin",
|
||||
email: "admin@test.com",
|
||||
name: "admin",
|
||||
isAdmin: true,
|
||||
profileImagePath: '',
|
||||
avatarColor: AvatarColorEnum.green,
|
||||
updatedAt: DateTime(2021),
|
||||
profileImagePath: null,
|
||||
avatarColor: AvatarColor.green,
|
||||
);
|
||||
|
||||
static final user1 = User(
|
||||
id: "user1",
|
||||
updatedAt: DateTime(2022),
|
||||
static final user1 = UserDto(
|
||||
uid: "user1",
|
||||
email: "user1@test.com",
|
||||
name: "user1",
|
||||
isAdmin: false,
|
||||
profileImagePath: '',
|
||||
avatarColor: AvatarColorEnum.red,
|
||||
updatedAt: DateTime(2022),
|
||||
profileImagePath: null,
|
||||
avatarColor: AvatarColor.red,
|
||||
);
|
||||
|
||||
static final user2 = User(
|
||||
id: "user2",
|
||||
updatedAt: DateTime(2023),
|
||||
static final user2 = UserDto(
|
||||
uid: "user2",
|
||||
email: "user2@test.com",
|
||||
name: "user2",
|
||||
isAdmin: false,
|
||||
profileImagePath: '',
|
||||
avatarColor: AvatarColorEnum.primary,
|
||||
updatedAt: DateTime(2023),
|
||||
profileImagePath: null,
|
||||
avatarColor: AvatarColor.primary,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockCurrentUserProvider extends StateNotifier<User?>
|
||||
class MockCurrentUserProvider extends StateNotifier<UserDto?>
|
||||
with Mock
|
||||
implements CurrentUserProvider {
|
||||
MockCurrentUserProvider() : super(null);
|
||||
|
||||
@override
|
||||
set state(User? user) => super.state = user;
|
||||
set state(UserDto? user) => super.state = user;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue