Source Code added

This commit is contained in:
Fr4nz D13trich 2026-02-02 15:06:40 +01:00
parent 800376eafd
commit 9efa9bc6dd
3912 changed files with 754770 additions and 2 deletions

View file

@ -0,0 +1,124 @@
enum AlbumAssetOrder {
// do not change this order!
asc,
desc,
}
enum AlbumUserRole {
// do not change this order!
editor,
viewer,
}
// Model for an album stored in the server
class RemoteAlbum {
final String id;
final String name;
final String ownerId;
final String description;
final DateTime createdAt;
final DateTime updatedAt;
final String? thumbnailAssetId;
final bool isActivityEnabled;
final AlbumAssetOrder order;
final int assetCount;
final String ownerName;
final bool isShared;
const RemoteAlbum({
required this.id,
required this.name,
required this.ownerId,
required this.description,
required this.createdAt,
required this.updatedAt,
this.thumbnailAssetId,
required this.isActivityEnabled,
required this.order,
required this.assetCount,
required this.ownerName,
required this.isShared,
});
@override
String toString() {
return '''Album {
id: $id,
name: $name,
ownerId: $ownerId,
description: $description,
createdAt: $createdAt,
updatedAt: $updatedAt,
isActivityEnabled: $isActivityEnabled,
order: $order,
thumbnailAssetId: ${thumbnailAssetId ?? "<NA>"}
assetCount: $assetCount
ownerName: $ownerName
isShared: $isShared
}''';
}
@override
bool operator ==(Object other) {
if (other is! RemoteAlbum) return false;
if (identical(this, other)) return true;
return id == other.id &&
name == other.name &&
ownerId == other.ownerId &&
description == other.description &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
thumbnailAssetId == other.thumbnailAssetId &&
isActivityEnabled == other.isActivityEnabled &&
order == other.order &&
assetCount == other.assetCount &&
ownerName == other.ownerName &&
isShared == other.isShared;
}
@override
int get hashCode {
return id.hashCode ^
name.hashCode ^
ownerId.hashCode ^
description.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
thumbnailAssetId.hashCode ^
isActivityEnabled.hashCode ^
order.hashCode ^
assetCount.hashCode ^
ownerName.hashCode ^
isShared.hashCode;
}
RemoteAlbum copyWith({
String? id,
String? name,
String? ownerId,
String? description,
DateTime? createdAt,
DateTime? updatedAt,
String? thumbnailAssetId,
bool? isActivityEnabled,
AlbumAssetOrder? order,
int? assetCount,
String? ownerName,
bool? isShared,
}) {
return RemoteAlbum(
id: id ?? this.id,
name: name ?? this.name,
ownerId: ownerId ?? this.ownerId,
description: description ?? this.description,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId,
isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
order: order ?? this.order,
assetCount: assetCount ?? this.assetCount,
ownerName: ownerName ?? this.ownerName,
isShared: isShared ?? this.isShared,
);
}
}

View file

@ -0,0 +1,87 @@
enum BackupSelection {
// Used to sort albums based on the backupSelection
// selected -> none -> excluded
// Do not change the order of these values
selected,
none,
excluded,
}
class LocalAlbum {
final String id;
final String name;
final DateTime updatedAt;
final bool isIosSharedAlbum;
final int assetCount;
final BackupSelection backupSelection;
final String? linkedRemoteAlbumId;
const LocalAlbum({
required this.id,
required this.name,
required this.updatedAt,
this.assetCount = 0,
this.backupSelection = BackupSelection.none,
this.isIosSharedAlbum = false,
this.linkedRemoteAlbumId,
});
LocalAlbum copyWith({
String? id,
String? name,
DateTime? updatedAt,
int? assetCount,
BackupSelection? backupSelection,
bool? isIosSharedAlbum,
String? linkedRemoteAlbumId,
}) {
return LocalAlbum(
id: id ?? this.id,
name: name ?? this.name,
updatedAt: updatedAt ?? this.updatedAt,
assetCount: assetCount ?? this.assetCount,
backupSelection: backupSelection ?? this.backupSelection,
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId,
);
}
@override
bool operator ==(Object other) {
if (other is! LocalAlbum) return false;
if (identical(this, other)) return true;
return other.id == id &&
other.name == name &&
other.updatedAt == updatedAt &&
other.assetCount == assetCount &&
other.backupSelection == backupSelection &&
other.isIosSharedAlbum == isIosSharedAlbum &&
other.linkedRemoteAlbumId == linkedRemoteAlbumId;
}
@override
int get hashCode {
return id.hashCode ^
name.hashCode ^
updatedAt.hashCode ^
assetCount.hashCode ^
backupSelection.hashCode ^
isIosSharedAlbum.hashCode ^
linkedRemoteAlbumId.hashCode;
}
@override
String toString() {
return '''LocalAlbum: {
id: $id,
name: $name,
updatedAt: $updatedAt,
assetCount: $assetCount,
backupSelection: $backupSelection,
isIosSharedAlbum: $isIosSharedAlbum
linkedRemoteAlbumId: $linkedRemoteAlbumId,
}''';
}
}

View file

@ -0,0 +1,62 @@
enum RemoteAssetMetadataKey {
mobileApp("mobile-app");
final String key;
const RemoteAssetMetadataKey(this.key);
}
abstract class RemoteAssetMetadataValue {
const RemoteAssetMetadataValue();
Map<String, dynamic> toJson();
}
class RemoteAssetMetadataItem {
final RemoteAssetMetadataKey key;
final RemoteAssetMetadataValue value;
const RemoteAssetMetadataItem({required this.key, required this.value});
Map<String, Object?> toJson() {
return {'key': key.key, 'value': value};
}
}
class RemoteAssetMobileAppMetadata extends RemoteAssetMetadataValue {
final String? cloudId;
final String? createdAt;
final String? adjustmentTime;
final String? latitude;
final String? longitude;
const RemoteAssetMobileAppMetadata({
this.cloudId,
this.createdAt,
this.adjustmentTime,
this.latitude,
this.longitude,
});
@override
Map<String, dynamic> toJson() {
final map = <String, Object?>{};
if (cloudId != null) {
map["iCloudId"] = cloudId;
}
if (createdAt != null) {
map["createdAt"] = createdAt;
}
if (adjustmentTime != null) {
map["adjustmentTime"] = adjustmentTime;
}
if (latitude != null) {
map["latitude"] = latitude;
}
if (longitude != null) {
map["longitude"] = longitude;
}
return map;
}
}

View file

@ -0,0 +1,109 @@
part 'local_asset.model.dart';
part 'remote_asset.model.dart';
enum AssetType {
// do not change this order!
other,
image,
video,
audio,
}
enum AssetState { local, remote, merged }
sealed class BaseAsset {
final String name;
final String? checksum;
final AssetType type;
final DateTime createdAt;
final DateTime updatedAt;
final int? width;
final int? height;
final int? durationInSeconds;
final bool isFavorite;
final String? livePhotoVideoId;
final bool isEdited;
const BaseAsset({
required this.name,
required this.checksum,
required this.type,
required this.createdAt,
required this.updatedAt,
this.width,
this.height,
this.durationInSeconds,
this.isFavorite = false,
this.livePhotoVideoId,
required this.isEdited,
});
bool get isImage => type == AssetType.image;
bool get isVideo => type == AssetType.video;
bool get isMotionPhoto => livePhotoVideoId != null;
Duration get duration {
final durationInSeconds = this.durationInSeconds;
if (durationInSeconds != null) {
return Duration(seconds: durationInSeconds);
}
return const Duration();
}
bool get hasRemote => storage == AssetState.remote || storage == AssetState.merged;
bool get hasLocal => storage == AssetState.local || storage == AssetState.merged;
bool get isLocalOnly => storage == AssetState.local;
bool get isRemoteOnly => storage == AssetState.remote;
// Overridden in subclasses
AssetState get storage;
String? get localId;
String? get remoteId;
String get heroTag;
@override
String toString() {
return '''BaseAsset {
name: $name,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"},
isFavorite: $isFavorite,
isEdited: $isEdited,
}''';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is BaseAsset) {
return name == other.name &&
type == other.type &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
width == other.width &&
height == other.height &&
durationInSeconds == other.durationInSeconds &&
isFavorite == other.isFavorite &&
isEdited == other.isEdited;
}
return false;
}
@override
int get hashCode {
return name.hashCode ^
type.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
width.hashCode ^
height.hashCode ^
durationInSeconds.hashCode ^
isFavorite.hashCode ^
isEdited.hashCode;
}
}

View file

@ -0,0 +1,133 @@
part of 'base_asset.model.dart';
class LocalAsset extends BaseAsset {
final String id;
final String? remoteAssetId;
final String? cloudId;
final int orientation;
final DateTime? adjustmentTime;
final double? latitude;
final double? longitude;
const LocalAsset({
required this.id,
String? remoteId,
this.cloudId,
required super.name,
super.checksum,
required super.type,
required super.createdAt,
required super.updatedAt,
super.width,
super.height,
super.durationInSeconds,
super.isFavorite = false,
super.livePhotoVideoId,
this.orientation = 0,
this.adjustmentTime,
this.latitude,
this.longitude,
required super.isEdited,
}) : remoteAssetId = remoteId;
@override
String? get localId => id;
@override
String? get remoteId => remoteAssetId;
@override
AssetState get storage => remoteId == null ? AssetState.local : AssetState.merged;
@override
String get heroTag => '${id}_${remoteId ?? checksum}';
bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0;
@override
String toString() {
return '''LocalAsset {
id: $id,
name: $name,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"},
remoteId: ${remoteId ?? "<NA>"},
cloudId: ${cloudId ?? "<NA>"},
checksum: ${checksum ?? "<NA>"},
isFavorite: $isFavorite,
orientation: $orientation,
adjustmentTime: $adjustmentTime,
latitude: ${latitude ?? "<NA>"},
longitude: ${longitude ?? "<NA>"},
}''';
}
// Not checking for remoteId here
@override
bool operator ==(Object other) {
if (other is! LocalAsset) return false;
if (identical(this, other)) return true;
return super == other &&
id == other.id &&
cloudId == other.cloudId &&
orientation == other.orientation &&
adjustmentTime == other.adjustmentTime &&
latitude == other.latitude &&
longitude == other.longitude;
}
@override
int get hashCode =>
super.hashCode ^
id.hashCode ^
remoteId.hashCode ^
orientation.hashCode ^
adjustmentTime.hashCode ^
latitude.hashCode ^
longitude.hashCode;
LocalAsset copyWith({
String? id,
String? remoteId,
String? cloudId,
String? name,
String? checksum,
AssetType? type,
DateTime? createdAt,
DateTime? updatedAt,
int? width,
int? height,
int? durationInSeconds,
bool? isFavorite,
int? orientation,
DateTime? adjustmentTime,
double? latitude,
double? longitude,
bool? isEdited,
}) {
return LocalAsset(
id: id ?? this.id,
remoteId: remoteId ?? this.remoteId,
cloudId: cloudId ?? this.cloudId,
name: name ?? this.name,
checksum: checksum ?? this.checksum,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
isFavorite: isFavorite ?? this.isFavorite,
orientation: orientation ?? this.orientation,
adjustmentTime: adjustmentTime ?? this.adjustmentTime,
latitude: latitude ?? this.latitude,
longitude: longitude ?? this.longitude,
isEdited: isEdited ?? this.isEdited,
);
}
}

View file

@ -0,0 +1,130 @@
part of 'base_asset.model.dart';
enum AssetVisibility { timeline, hidden, archive, locked }
// Model for an asset stored in the server
class RemoteAsset extends BaseAsset {
final String id;
final String? localAssetId;
final String? thumbHash;
final AssetVisibility visibility;
final String ownerId;
final String? stackId;
const RemoteAsset({
required this.id,
String? localId,
required super.name,
required this.ownerId,
required super.checksum,
required super.type,
required super.createdAt,
required super.updatedAt,
super.width,
super.height,
super.durationInSeconds,
super.isFavorite = false,
this.thumbHash,
this.visibility = AssetVisibility.timeline,
super.livePhotoVideoId,
this.stackId,
required super.isEdited,
}) : localAssetId = localId;
@override
String? get localId => localAssetId;
@override
String? get remoteId => id;
@override
AssetState get storage => localId == null ? AssetState.remote : AssetState.merged;
@override
String get heroTag => '${localId ?? checksum}_$id';
@override
String toString() {
return '''Asset {
id: $id,
name: $name,
ownerId: $ownerId,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"},
localId: ${localId ?? "<NA>"},
isFavorite: $isFavorite,
thumbHash: ${thumbHash ?? "<NA>"},
visibility: $visibility,
stackId: ${stackId ?? "<NA>"},
checksum: $checksum,
livePhotoVideoId: ${livePhotoVideoId ?? "<NA>"},
}''';
}
// Not checking for localId here
@override
bool operator ==(Object other) {
if (other is! RemoteAsset) return false;
if (identical(this, other)) return true;
return super == other &&
id == other.id &&
ownerId == other.ownerId &&
thumbHash == other.thumbHash &&
visibility == other.visibility &&
stackId == other.stackId;
}
@override
int get hashCode =>
super.hashCode ^
id.hashCode ^
ownerId.hashCode ^
localId.hashCode ^
thumbHash.hashCode ^
visibility.hashCode ^
stackId.hashCode;
RemoteAsset copyWith({
String? id,
String? localId,
String? name,
String? ownerId,
String? checksum,
AssetType? type,
DateTime? createdAt,
DateTime? updatedAt,
int? width,
int? height,
int? durationInSeconds,
bool? isFavorite,
String? thumbHash,
AssetVisibility? visibility,
String? livePhotoVideoId,
String? stackId,
bool? isEdited,
}) {
return RemoteAsset(
id: id ?? this.id,
localId: localId ?? this.localId,
name: name ?? this.name,
ownerId: ownerId ?? this.ownerId,
checksum: checksum ?? this.checksum,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
isFavorite: isFavorite ?? this.isFavorite,
thumbHash: thumbHash ?? this.thumbHash,
visibility: visibility ?? this.visibility,
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
stackId: stackId ?? this.stackId,
isEdited: isEdited ?? this.isEdited,
);
}
}

View file

@ -0,0 +1,98 @@
// Model for an asset face stored in the server
class AssetFace {
final String id;
final String assetId;
final String? personId;
final int imageWidth;
final int imageHeight;
final int boundingBoxX1;
final int boundingBoxY1;
final int boundingBoxX2;
final int boundingBoxY2;
final String sourceType;
const AssetFace({
required this.id,
required this.assetId,
this.personId,
required this.imageWidth,
required this.imageHeight,
required this.boundingBoxX1,
required this.boundingBoxY1,
required this.boundingBoxX2,
required this.boundingBoxY2,
required this.sourceType,
});
AssetFace copyWith({
String? id,
String? assetId,
String? personId,
int? imageWidth,
int? imageHeight,
int? boundingBoxX1,
int? boundingBoxY1,
int? boundingBoxX2,
int? boundingBoxY2,
String? sourceType,
}) {
return AssetFace(
id: id ?? this.id,
assetId: assetId ?? this.assetId,
personId: personId ?? this.personId,
imageWidth: imageWidth ?? this.imageWidth,
imageHeight: imageHeight ?? this.imageHeight,
boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1,
boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1,
boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2,
boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2,
sourceType: sourceType ?? this.sourceType,
);
}
@override
String toString() {
return '''AssetFace {
id: $id,
assetId: $assetId,
personId: ${personId ?? "<NA>"},
imageWidth: $imageWidth,
imageHeight: $imageHeight,
boundingBoxX1: $boundingBoxX1,
boundingBoxY1: $boundingBoxY1,
boundingBoxX2: $boundingBoxX2,
boundingBoxY2: $boundingBoxY2,
sourceType: $sourceType,
}''';
}
@override
bool operator ==(covariant AssetFace other) {
if (identical(this, other)) return true;
return other.id == id &&
other.assetId == assetId &&
other.personId == personId &&
other.imageWidth == imageWidth &&
other.imageHeight == imageHeight &&
other.boundingBoxX1 == boundingBoxX1 &&
other.boundingBoxY1 == boundingBoxY1 &&
other.boundingBoxX2 == boundingBoxX2 &&
other.boundingBoxY2 == boundingBoxY2 &&
other.sourceType == sourceType;
}
@override
int get hashCode {
return id.hashCode ^
assetId.hashCode ^
personId.hashCode ^
imageWidth.hashCode ^
imageHeight.hashCode ^
boundingBoxX1.hashCode ^
boundingBoxY1.hashCode ^
boundingBoxX2.hashCode ^
boundingBoxY2.hashCode ^
sourceType.hashCode;
}
}

View file

@ -0,0 +1,34 @@
import 'dart:typed_data';
class DeviceAsset {
final String assetId;
final Uint8List hash;
final DateTime modifiedTime;
const DeviceAsset({required this.assetId, required this.hash, required this.modifiedTime});
@override
bool operator ==(covariant DeviceAsset other) {
if (identical(this, other)) return true;
return other.assetId == assetId && other.hash == hash && other.modifiedTime == modifiedTime;
}
@override
int get hashCode {
return assetId.hashCode ^ hash.hashCode ^ modifiedTime.hashCode;
}
@override
String toString() {
return 'DeviceAsset(assetId: $assetId, hash: $hash, modifiedTime: $modifiedTime)';
}
DeviceAsset copyWith({String? assetId, Uint8List? hash, DateTime? modifiedTime}) {
return DeviceAsset(
assetId: assetId ?? this.assetId,
hash: hash ?? this.hash,
modifiedTime: modifiedTime ?? this.modifiedTime,
);
}
}

View file

@ -0,0 +1,37 @@
import 'package:immich_mobile/domain/utils/event_stream.dart';
// Timeline Events
class TimelineReloadEvent extends Event {
const TimelineReloadEvent();
}
class ScrollToTopEvent extends Event {
const ScrollToTopEvent();
}
class ScrollToDateEvent extends Event {
final DateTime date;
const ScrollToDateEvent(this.date);
}
// Asset Viewer Events
class ViewerOpenBottomSheetEvent extends Event {
final bool activitiesMode;
const ViewerOpenBottomSheetEvent({this.activitiesMode = false});
}
class ViewerReloadAssetEvent extends Event {
const ViewerReloadAssetEvent();
}
// Multi-Select Events
class MultiSelectToggleEvent extends Event {
final bool isEnabled;
const MultiSelectToggleEvent(this.isEnabled);
}
// Map Events
class MapMarkerReloadEvent extends Event {
const MapMarkerReloadEvent();
}

View file

@ -0,0 +1,186 @@
class ExifInfo {
final int? assetId;
final int? fileSize;
final String? description;
final bool isFlipped;
final String? orientation;
final String? timeZone;
final DateTime? dateTimeOriginal;
final int? rating;
// GPS
final double? latitude;
final double? longitude;
final String? city;
final String? state;
final String? country;
// Camera related
final String? make;
final String? model;
final String? lens;
final double? f;
final double? mm;
final int? iso;
final double? exposureSeconds;
bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0;
String get exposureTime {
if (exposureSeconds == null) {
return "";
}
if (exposureSeconds! < 1) {
return "1/${(1.0 / exposureSeconds!).round()} s";
}
return "${exposureSeconds!.toStringAsFixed(1)} s";
}
String get fNumber => f == null ? "" : f!.toStringAsFixed(1);
String get focalLength => mm == null ? "" : mm!.toStringAsFixed(3);
const ExifInfo({
this.assetId,
this.fileSize,
this.description,
this.orientation,
this.timeZone,
this.dateTimeOriginal,
this.rating,
this.isFlipped = false,
this.latitude,
this.longitude,
this.city,
this.state,
this.country,
this.make,
this.model,
this.lens,
this.f,
this.mm,
this.iso,
this.exposureSeconds,
});
@override
bool operator ==(covariant ExifInfo other) {
if (identical(this, other)) return true;
return other.fileSize == fileSize &&
other.description == description &&
other.isFlipped == isFlipped &&
other.orientation == orientation &&
other.timeZone == timeZone &&
other.dateTimeOriginal == dateTimeOriginal &&
other.rating == rating &&
other.latitude == latitude &&
other.longitude == longitude &&
other.city == city &&
other.state == state &&
other.country == country &&
other.make == make &&
other.model == model &&
other.lens == lens &&
other.f == f &&
other.mm == mm &&
other.iso == iso &&
other.exposureSeconds == exposureSeconds &&
other.assetId == assetId;
}
@override
int get hashCode {
return fileSize.hashCode ^
description.hashCode ^
orientation.hashCode ^
isFlipped.hashCode ^
timeZone.hashCode ^
dateTimeOriginal.hashCode ^
rating.hashCode ^
latitude.hashCode ^
longitude.hashCode ^
city.hashCode ^
state.hashCode ^
country.hashCode ^
make.hashCode ^
model.hashCode ^
lens.hashCode ^
f.hashCode ^
mm.hashCode ^
iso.hashCode ^
exposureSeconds.hashCode ^
assetId.hashCode;
}
@override
String toString() {
return '''{
fileSize: ${fileSize ?? 'NA'},
description: ${description ?? 'NA'},
orientation: ${orientation ?? 'NA'},
isFlipped: $isFlipped,
timeZone: ${timeZone ?? 'NA'},
dateTimeOriginal: ${dateTimeOriginal ?? 'NA'},
rating: ${rating ?? 'NA'},
latitude: ${latitude ?? 'NA'},
longitude: ${longitude ?? 'NA'},
city: ${city ?? 'NA'},
state: ${state ?? 'NA'},
country: ${country ?? '<NA>'},
make: ${make ?? 'NA'},
model: ${model ?? 'NA'},
lens: ${lens ?? 'NA'},
f: ${f ?? 'NA'},
mm: ${mm ?? '<NA>'},
iso: ${iso ?? 'NA'},
exposureSeconds: ${exposureSeconds ?? 'NA'},
}''';
}
ExifInfo copyWith({
int? assetId,
int? fileSize,
String? description,
String? orientation,
String? timeZone,
DateTime? dateTimeOriginal,
int? rating,
double? latitude,
double? longitude,
String? city,
String? state,
String? country,
bool? isFlipped,
String? make,
String? model,
String? lens,
double? f,
double? mm,
int? iso,
double? exposureSeconds,
}) {
return ExifInfo(
assetId: assetId ?? this.assetId,
fileSize: fileSize ?? this.fileSize,
description: description ?? this.description,
orientation: orientation ?? this.orientation,
timeZone: timeZone ?? this.timeZone,
dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal,
rating: rating ?? this.rating,
isFlipped: isFlipped ?? this.isFlipped,
latitude: latitude ?? this.latitude,
longitude: longitude ?? this.longitude,
city: city ?? this.city,
state: state ?? this.state,
country: country ?? this.country,
make: make ?? this.make,
model: model ?? this.model,
lens: lens ?? this.lens,
f: f ?? this.f,
mm: mm ?? this.mm,
iso: iso ?? this.iso,
exposureSeconds: exposureSeconds ?? this.exposureSeconds,
);
}
}

View file

@ -0,0 +1,49 @@
/// Log levels according to dart logging [Level]
enum LogLevel { all, finest, finer, fine, config, info, warning, severe, shout, off }
class LogMessage {
final String message;
final LogLevel level;
final DateTime createdAt;
final String? logger;
final String? error;
final String? stack;
const LogMessage({
required this.message,
required this.level,
required this.createdAt,
this.logger,
this.error,
this.stack,
});
@override
bool operator ==(covariant LogMessage other) {
if (identical(this, other)) return true;
return other.message == message &&
other.level == level &&
other.createdAt == createdAt &&
other.logger == logger &&
other.error == error &&
other.stack == stack;
}
@override
int get hashCode {
return message.hashCode ^ level.hashCode ^ createdAt.hashCode ^ logger.hashCode ^ error.hashCode ^ stack.hashCode;
}
@override
String toString() {
return '''LogMessage: {
message: $message,
level: $level,
createdAt: $createdAt,
logger: ${logger ?? '<NA>'},
error: ${error ?? '<NA>'},
stack: ${stack ?? '<NA>'},
}''';
}
}

View file

@ -0,0 +1,18 @@
import 'package:maplibre_gl/maplibre_gl.dart';
class Marker {
final LatLng location;
final String assetId;
const Marker({required this.location, required this.assetId});
@override
bool operator ==(covariant Marker other) {
if (identical(this, other)) return true;
return other.location == location && other.assetId == assetId;
}
@override
int get hashCode => location.hashCode ^ assetId.hashCode;
}

View file

@ -0,0 +1,169 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
enum MemoryTypeEnum {
// do not change this order!
onThisDay,
}
class MemoryData {
final int year;
const MemoryData({required this.year});
MemoryData copyWith({int? year}) {
return MemoryData(year: year ?? this.year);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{'year': year};
}
factory MemoryData.fromMap(Map<String, dynamic> map) {
return MemoryData(year: map['year'] as int);
}
String toJson() => json.encode(toMap());
factory MemoryData.fromJson(String source) => MemoryData.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'MemoryData(year: $year)';
@override
bool operator ==(covariant MemoryData other) {
if (identical(this, other)) return true;
return other.year == year;
}
@override
int get hashCode => year.hashCode;
}
// Model for a memory stored in the server
class DriftMemory {
final String id;
final DateTime createdAt;
final DateTime updatedAt;
final DateTime? deletedAt;
final String ownerId;
// enum
final MemoryTypeEnum type;
final MemoryData data;
final bool isSaved;
final DateTime memoryAt;
final DateTime? seenAt;
final DateTime? showAt;
final DateTime? hideAt;
final List<RemoteAsset> assets;
const DriftMemory({
required this.id,
required this.createdAt,
required this.updatedAt,
this.deletedAt,
required this.ownerId,
required this.type,
required this.data,
required this.isSaved,
required this.memoryAt,
this.seenAt,
this.showAt,
this.hideAt,
required this.assets,
});
DriftMemory copyWith({
String? id,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
String? ownerId,
MemoryTypeEnum? type,
MemoryData? data,
bool? isSaved,
DateTime? memoryAt,
DateTime? seenAt,
DateTime? showAt,
DateTime? hideAt,
List<RemoteAsset>? assets,
}) {
return DriftMemory(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
deletedAt: deletedAt ?? this.deletedAt,
ownerId: ownerId ?? this.ownerId,
type: type ?? this.type,
data: data ?? this.data,
isSaved: isSaved ?? this.isSaved,
memoryAt: memoryAt ?? this.memoryAt,
seenAt: seenAt ?? this.seenAt,
showAt: showAt ?? this.showAt,
hideAt: hideAt ?? this.hideAt,
assets: assets ?? this.assets,
);
}
@override
String toString() {
return '''Memory {
id: $id,
createdAt: $createdAt,
updatedAt: $updatedAt,
deletedAt: ${deletedAt ?? "<NA>"},
ownerId: $ownerId,
type: $type,
data: $data,
isSaved: $isSaved,
memoryAt: $memoryAt,
seenAt: ${seenAt ?? "<NA>"},
showAt: ${showAt ?? "<NA>"},
hideAt: ${hideAt ?? "<NA>"},
assets: $assets
}''';
}
@override
bool operator ==(covariant DriftMemory other) {
if (identical(this, other)) return true;
final listEquals = const DeepCollectionEquality().equals;
return other.id == id &&
other.createdAt == createdAt &&
other.updatedAt == updatedAt &&
other.deletedAt == deletedAt &&
other.ownerId == ownerId &&
other.type == type &&
other.data == data &&
other.isSaved == isSaved &&
other.memoryAt == memoryAt &&
other.seenAt == seenAt &&
other.showAt == showAt &&
other.hideAt == hideAt &&
listEquals(other.assets, assets);
}
@override
int get hashCode {
return id.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
deletedAt.hashCode ^
ownerId.hashCode ^
type.hashCode ^
data.hashCode ^
isSaved.hashCode ^
memoryAt.hashCode ^
seenAt.hashCode ^
showAt.hashCode ^
hideAt.hashCode ^
assets.hashCode;
}
}

View file

@ -0,0 +1,190 @@
import 'dart:convert';
// TODO: Remove PersonDto once Isar is removed
class PersonDto {
const PersonDto({
required this.id,
this.birthDate,
required this.isHidden,
required this.name,
required this.thumbnailPath,
this.updatedAt,
});
final String id;
final DateTime? birthDate;
final bool isHidden;
final String name;
final String thumbnailPath;
final DateTime? updatedAt;
@override
String toString() {
return 'Person(id: $id, birthDate: $birthDate, isHidden: $isHidden, name: $name, thumbnailPath: $thumbnailPath, updatedAt: $updatedAt)';
}
PersonDto copyWith({
String? id,
DateTime? birthDate,
bool? isHidden,
String? name,
String? thumbnailPath,
DateTime? updatedAt,
}) {
return PersonDto(
id: id ?? this.id,
birthDate: birthDate ?? this.birthDate,
isHidden: isHidden ?? this.isHidden,
name: name ?? this.name,
thumbnailPath: thumbnailPath ?? this.thumbnailPath,
updatedAt: updatedAt ?? this.updatedAt,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'id': id,
'birthDate': birthDate?.millisecondsSinceEpoch,
'isHidden': isHidden,
'name': name,
'thumbnailPath': thumbnailPath,
'updatedAt': updatedAt?.millisecondsSinceEpoch,
};
}
factory PersonDto.fromMap(Map<String, dynamic> map) {
return PersonDto(
id: map['id'] as String,
birthDate: map['birthDate'] != null ? DateTime.fromMillisecondsSinceEpoch(map['birthDate'] as int) : null,
isHidden: map['isHidden'] as bool,
name: map['name'] as String,
thumbnailPath: map['thumbnailPath'] as String,
updatedAt: map['updatedAt'] != null ? DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int) : null,
);
}
String toJson() => json.encode(toMap());
factory PersonDto.fromJson(String source) => PersonDto.fromMap(json.decode(source) as Map<String, dynamic>);
@override
bool operator ==(covariant PersonDto other) {
if (identical(this, other)) return true;
return other.id == id &&
other.birthDate == birthDate &&
other.isHidden == isHidden &&
other.name == name &&
other.thumbnailPath == thumbnailPath &&
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return id.hashCode ^
birthDate.hashCode ^
isHidden.hashCode ^
name.hashCode ^
thumbnailPath.hashCode ^
updatedAt.hashCode;
}
}
// Model for a person stored in the server
class DriftPerson {
final String id;
final DateTime createdAt;
final DateTime updatedAt;
final String ownerId;
final String name;
final String? faceAssetId;
final bool isFavorite;
final bool isHidden;
final String? color;
final DateTime? birthDate;
const DriftPerson({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.ownerId,
required this.name,
this.faceAssetId,
required this.isFavorite,
required this.isHidden,
required this.color,
this.birthDate,
});
DriftPerson copyWith({
String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? ownerId,
String? name,
String? faceAssetId,
bool? isFavorite,
bool? isHidden,
String? color,
DateTime? birthDate,
}) {
return DriftPerson(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
name: name ?? this.name,
faceAssetId: faceAssetId ?? this.faceAssetId,
isFavorite: isFavorite ?? this.isFavorite,
isHidden: isHidden ?? this.isHidden,
color: color ?? this.color,
birthDate: birthDate ?? this.birthDate,
);
}
@override
String toString() {
return '''Person {
id: $id,
createdAt: $createdAt,
updatedAt: $updatedAt,
ownerId: $ownerId,
name: $name,
faceAssetId: ${faceAssetId ?? "<NA>"},
isFavorite: $isFavorite,
isHidden: $isHidden,
color: ${color ?? "<NA>"},
birthDate: ${birthDate ?? "<NA>"}
}''';
}
@override
bool operator ==(covariant DriftPerson other) {
if (identical(this, other)) return true;
return other.id == id &&
other.createdAt == createdAt &&
other.updatedAt == updatedAt &&
other.ownerId == ownerId &&
other.name == name &&
other.faceAssetId == faceAssetId &&
other.isFavorite == isFavorite &&
other.isHidden == isHidden &&
other.color == color &&
other.birthDate == birthDate;
}
@override
int get hashCode {
return id.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
ownerId.hashCode ^
name.hashCode ^
faceAssetId.hashCode ^
isFavorite.hashCode ^
isHidden.hashCode ^
color.hashCode ^
birthDate.hashCode;
}
}

View file

@ -0,0 +1,32 @@
import 'package:collection/collection.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
class SearchResult {
final List<BaseAsset> assets;
final double scrollOffset;
final int? nextPage;
const SearchResult({required this.assets, this.scrollOffset = 0.0, this.nextPage});
SearchResult copyWith({List<BaseAsset>? assets, int? nextPage, double? scrollOffset}) {
return SearchResult(
assets: assets ?? this.assets,
nextPage: nextPage ?? this.nextPage,
scrollOffset: scrollOffset ?? this.scrollOffset,
);
}
@override
String toString() => 'SearchResult(assets: ${assets.length}, nextPage: $nextPage, scrollOffset: $scrollOffset)';
@override
bool operator ==(covariant SearchResult other) {
if (identical(this, other)) return true;
final listEquals = const DeepCollectionEquality().equals;
return listEquals(other.assets, assets) && other.nextPage == nextPage && other.scrollOffset == scrollOffset;
}
@override
int get hashCode => assets.hashCode ^ nextPage.hashCode ^ scrollOffset.hashCode;
}

View file

@ -0,0 +1,18 @@
import 'package:immich_mobile/domain/models/store.model.dart';
enum Setting<T> {
tilesPerRow<int>(StoreKey.tilesPerRow, 4),
groupAssetsBy<int>(StoreKey.groupAssetsBy, 0),
showStorageIndicator<bool>(StoreKey.storageIndicator, true),
loadOriginal<bool>(StoreKey.loadOriginal, false),
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, false),
autoPlayVideo<bool>(StoreKey.autoPlayVideo, true),
preferRemoteImage<bool>(StoreKey.preferRemoteImage, false),
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false),
enableBackup<bool>(StoreKey.enableBackup, false);
const Setting(this.storeKey, this.defaultValue);
final StoreKey<T> storeKey;
final T defaultValue;
}

View file

@ -0,0 +1,71 @@
// Model for a stack stored in the server
class Stack {
final String id;
final DateTime createdAt;
final DateTime updatedAt;
final String ownerId;
final String primaryAssetId;
const Stack({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.ownerId,
required this.primaryAssetId,
});
Stack copyWith({String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) {
return Stack(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
}
@override
String toString() {
return '''Stack {
id: $id,
createdAt: $createdAt,
updatedAt: $updatedAt,
ownerId: $ownerId,
primaryAssetId: $primaryAssetId
}''';
}
@override
bool operator ==(covariant Stack other) {
if (identical(this, other)) return true;
return other.id == id &&
other.createdAt == createdAt &&
other.updatedAt == updatedAt &&
other.ownerId == ownerId &&
other.primaryAssetId == primaryAssetId;
}
@override
int get hashCode {
return id.hashCode ^ createdAt.hashCode ^ updatedAt.hashCode ^ ownerId.hashCode ^ primaryAssetId.hashCode;
}
}
class StackResponse {
final String id;
final String primaryAssetId;
final List<String> assetIds;
const StackResponse({required this.id, required this.primaryAssetId, required this.assetIds});
@override
bool operator ==(covariant StackResponse other) {
if (identical(this, other)) return true;
return other.id == id && other.primaryAssetId == primaryAssetId && other.assetIds == assetIds;
}
@override
int get hashCode => id.hashCode ^ primaryAssetId.hashCode ^ assetIds.hashCode;
}

View file

@ -0,0 +1,125 @@
import 'package:immich_mobile/domain/models/user.model.dart';
/// Key for each possible value in the `Store`.
/// Defines the data type for each value
enum StoreKey<T> {
version<int>._(0),
assetETag<String>._(1),
currentUser<UserDto>._(2),
deviceIdHash<int>._(3),
deviceId<String>._(4),
backupFailedSince<DateTime>._(5),
backupRequireWifi<bool>._(6),
backupRequireCharging<bool>._(7),
backupTriggerDelay<int>._(8),
serverUrl<String>._(10),
accessToken<String>._(11),
serverEndpoint<String>._(12),
autoBackup<bool>._(13),
backgroundBackup<bool>._(14),
sslClientCertData<String>._(15),
sslClientPasswd<String>._(16),
// user settings from [AppSettingsEnum] below:
loadPreview<bool>._(100),
loadOriginal<bool>._(101),
themeMode<String>._(102),
tilesPerRow<int>._(103),
dynamicLayout<bool>._(104),
groupAssetsBy<int>._(105),
uploadErrorNotificationGracePeriod<int>._(106),
backgroundBackupTotalProgress<bool>._(107),
backgroundBackupSingleProgress<bool>._(108),
storageIndicator<bool>._(109),
thumbnailCacheSize<int>._(110),
imageCacheSize<int>._(111),
albumThumbnailCacheSize<int>._(112),
selectedAlbumSortOrder<int>._(113),
advancedTroubleshooting<bool>._(114),
logLevel<int>._(115),
preferRemoteImage<bool>._(116),
loopVideo<bool>._(117),
// map related settings
mapShowFavoriteOnly<bool>._(118),
mapRelativeDate<int>._(119),
selfSignedCert<bool>._(120),
mapIncludeArchived<bool>._(121),
ignoreIcloudAssets<bool>._(122),
selectedAlbumSortReverse<bool>._(123),
mapThemeMode<int>._(124),
mapwithPartners<bool>._(125),
enableHapticFeedback<bool>._(126),
customHeaders<String>._(127),
// theme settings
primaryColor<String>._(128),
dynamicTheme<bool>._(129),
colorfulInterface<bool>._(130),
syncAlbums<bool>._(131),
// Auto endpoint switching
autoEndpointSwitching<bool>._(132),
preferredWifiName<String>._(133),
localEndpoint<String>._(134),
externalEndpointList<String>._(135),
// Video settings
loadOriginalVideo<bool>._(136),
manageLocalMediaAndroid<bool>._(137),
// Read-only Mode settings
readonlyModeEnabled<bool>._(138),
autoPlayVideo<bool>._(139),
albumGridView<bool>._(140),
// Experimental stuff
photoManagerCustomFilter<bool>._(1000),
betaPromptShown<bool>._(1001),
betaTimeline<bool>._(1002),
enableBackup<bool>._(1003),
useWifiForUploadVideos<bool>._(1004),
useWifiForUploadPhotos<bool>._(1005),
needBetaMigration<bool>._(1006),
// TODO: Remove this after patching open-api
shouldResetSync<bool>._(1007),
// Free up space
cleanupKeepFavorites<bool>._(1008),
cleanupKeepMediaType<int>._(1009),
cleanupKeepAlbumIds<String>._(1010),
cleanupCutoffDaysAgo<int>._(1011),
cleanupDefaultsInitialized<bool>._(1012),
syncMigrationStatus<String>._(1013);
const StoreKey._(this.id);
final int id;
Type get type => T;
}
class StoreDto<T> {
final StoreKey<T> key;
final T? value;
const StoreDto(this.key, this.value);
@override
String toString() {
return '''
StoreDto: {
key: $key,
value: ${value ?? '<NA>'},
}''';
}
@override
bool operator ==(covariant StoreDto<T> other) {
if (identical(this, other)) return true;
return other.key == key && other.value == value;
}
@override
int get hashCode => key.hashCode ^ value.hashCode;
}

View file

@ -0,0 +1,12 @@
import 'package:openapi/api.dart';
class SyncEvent {
final SyncEntityType type;
final Object data;
final String ack;
const SyncEvent({required this.type, required this.data, required this.ack});
@override
String toString() => 'SyncEvent(type: $type, data: $data, ack: $ack)';
}

View file

@ -0,0 +1,31 @@
enum GroupAssetsBy { day, month, auto, none }
enum HeaderType { none, month, day, monthAndDay }
class Bucket {
final int assetCount;
const Bucket({required this.assetCount});
@override
bool operator ==(covariant Bucket other) {
return assetCount == other.assetCount;
}
@override
int get hashCode => assetCount.hashCode;
}
class TimeBucket extends Bucket {
final DateTime date;
const TimeBucket({required this.date, required super.assetCount});
@override
bool operator ==(covariant TimeBucket other) {
return super == other && date == other.date;
}
@override
int get hashCode => super.hashCode ^ date.hashCode;
}

View file

@ -0,0 +1,235 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert';
import 'dart:ui';
enum AvatarColor {
// do not change this order or reuse indices for other purposes, adding is OK
primary("primary"),
pink("pink"),
red("red"),
yellow("yellow"),
blue("blue"),
green("green"),
purple("purple"),
orange("orange"),
gray("gray"),
amber("amber");
final String value;
const AvatarColor(this.value);
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 id;
final String email;
final String name;
final bool isAdmin;
final DateTime? updatedAt;
final AvatarColor avatarColor;
final bool memoryEnabled;
final bool inTimeline;
final bool isPartnerSharedBy;
final bool isPartnerSharedWith;
final int quotaUsageInBytes;
final int quotaSizeInBytes;
bool get hasQuota => quotaSizeInBytes > 0;
final bool hasProfileImage;
final DateTime profileChangedAt;
const UserDto({
required this.id,
required this.email,
required this.name,
this.isAdmin = false,
this.updatedAt,
required this.profileChangedAt,
this.avatarColor = AvatarColor.primary,
this.memoryEnabled = true,
this.inTimeline = false,
this.isPartnerSharedBy = false,
this.isPartnerSharedWith = false,
this.hasProfileImage = false,
this.quotaUsageInBytes = 0,
this.quotaSizeInBytes = 0,
});
@override
String toString() {
return '''User: {
id: $id,
email: $email,
name: $name,
isAdmin: $isAdmin,
updatedAt: $updatedAt,
avatarColor: $avatarColor,
memoryEnabled: $memoryEnabled,
inTimeline: $inTimeline,
isPartnerSharedBy: $isPartnerSharedBy,
isPartnerSharedWith: $isPartnerSharedWith,
hasProfileImage: $hasProfileImage
profileChangedAt: $profileChangedAt
}''';
}
UserDto copyWith({
String? id,
String? email,
String? name,
bool? isAdmin,
DateTime? updatedAt,
AvatarColor? avatarColor,
bool? memoryEnabled,
bool? inTimeline,
bool? isPartnerSharedBy,
bool? isPartnerSharedWith,
bool? hasProfileImage,
DateTime? profileChangedAt,
int? quotaSizeInBytes,
int? quotaUsageInBytes,
}) => UserDto(
id: id ?? this.id,
email: email ?? this.email,
name: name ?? this.name,
isAdmin: isAdmin ?? this.isAdmin,
updatedAt: updatedAt ?? this.updatedAt,
avatarColor: avatarColor ?? this.avatarColor,
memoryEnabled: memoryEnabled ?? this.memoryEnabled,
inTimeline: inTimeline ?? this.inTimeline,
isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy,
isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith,
hasProfileImage: hasProfileImage ?? this.hasProfileImage,
profileChangedAt: profileChangedAt ?? this.profileChangedAt,
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
);
@override
bool operator ==(covariant UserDto other) {
if (identical(this, other)) return true;
return other.id == id &&
((updatedAt == null && other.updatedAt == null) ||
(updatedAt != null && other.updatedAt != null && other.updatedAt!.isAtSameMomentAs(updatedAt!))) &&
other.avatarColor == avatarColor &&
other.email == email &&
other.name == name &&
other.isPartnerSharedBy == isPartnerSharedBy &&
other.isPartnerSharedWith == isPartnerSharedWith &&
other.isAdmin == isAdmin &&
other.memoryEnabled == memoryEnabled &&
other.inTimeline == inTimeline &&
other.hasProfileImage == hasProfileImage &&
other.profileChangedAt.isAtSameMomentAs(profileChangedAt) &&
other.quotaSizeInBytes == quotaSizeInBytes &&
other.quotaUsageInBytes == quotaUsageInBytes;
}
@override
int get hashCode =>
id.hashCode ^
name.hashCode ^
email.hashCode ^
updatedAt.hashCode ^
isAdmin.hashCode ^
avatarColor.hashCode ^
memoryEnabled.hashCode ^
inTimeline.hashCode ^
isPartnerSharedBy.hashCode ^
isPartnerSharedWith.hashCode ^
hasProfileImage.hashCode ^
profileChangedAt.hashCode ^
quotaSizeInBytes.hashCode ^
quotaUsageInBytes.hashCode;
}
class PartnerUserDto {
final String id;
final String email;
final String name;
final bool inTimeline;
final String? profileImagePath;
const PartnerUserDto({
required this.id,
required this.email,
required this.name,
required this.inTimeline,
this.profileImagePath,
});
PartnerUserDto copyWith({String? id, String? email, String? name, bool? inTimeline, String? profileImagePath}) {
return PartnerUserDto(
id: id ?? this.id,
email: email ?? this.email,
name: name ?? this.name,
inTimeline: inTimeline ?? this.inTimeline,
profileImagePath: profileImagePath ?? this.profileImagePath,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'id': id,
'email': email,
'name': name,
'inTimeline': inTimeline,
'profileImagePath': profileImagePath,
};
}
factory PartnerUserDto.fromMap(Map<String, dynamic> map) {
return PartnerUserDto(
id: map['id'] as String,
email: map['email'] as String,
name: map['name'] as String,
inTimeline: map['inTimeline'] as bool,
profileImagePath: map['profileImagePath'] != null ? map['profileImagePath'] as String : null,
);
}
String toJson() => json.encode(toMap());
factory PartnerUserDto.fromJson(String source) => PartnerUserDto.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() {
return 'PartnerUserDto(id: $id, email: $email, name: $name, inTimeline: $inTimeline, profileImagePath: $profileImagePath)';
}
@override
bool operator ==(covariant PartnerUserDto other) {
if (identical(this, other)) return true;
return other.id == id &&
other.email == email &&
other.name == name &&
other.inTimeline == inTimeline &&
other.profileImagePath == profileImagePath;
}
@override
int get hashCode {
return id.hashCode ^ email.hashCode ^ name.hashCode ^ inTimeline.hashCode ^ profileImagePath.hashCode;
}
}

View file

@ -0,0 +1,267 @@
import 'package:immich_mobile/domain/models/user.model.dart';
enum UserMetadataKey {
// do not change this order!
onboarding,
preferences,
license,
}
class Onboarding {
final bool isOnboarded;
const Onboarding({required this.isOnboarded});
Onboarding copyWith({bool? isOnboarded}) {
return Onboarding(isOnboarded: isOnboarded ?? this.isOnboarded);
}
Map<String, Object?> toMap() {
final onboarding = <String, Object?>{};
onboarding["isOnboarded"] = isOnboarded;
return onboarding;
}
factory Onboarding.fromMap(Map<String, Object?> map) {
return Onboarding(isOnboarded: map["isOnboarded"] as bool);
}
@override
String toString() {
return '''Onboarding {
isOnboarded: $isOnboarded,
}''';
}
@override
bool operator ==(covariant Onboarding other) {
if (identical(this, other)) return true;
return isOnboarded == other.isOnboarded;
}
@override
int get hashCode => isOnboarded.hashCode;
}
class Preferences {
final bool foldersEnabled;
final bool memoriesEnabled;
final bool peopleEnabled;
final bool ratingsEnabled;
final bool sharedLinksEnabled;
final bool tagsEnabled;
final AvatarColor userAvatarColor;
final bool showSupportBadge;
const Preferences({
this.foldersEnabled = false,
this.memoriesEnabled = true,
this.peopleEnabled = true,
this.ratingsEnabled = false,
this.sharedLinksEnabled = true,
this.tagsEnabled = false,
this.userAvatarColor = AvatarColor.primary,
this.showSupportBadge = true,
});
Preferences copyWith({
bool? foldersEnabled,
bool? memoriesEnabled,
bool? peopleEnabled,
bool? ratingsEnabled,
bool? sharedLinksEnabled,
bool? tagsEnabled,
AvatarColor? userAvatarColor,
bool? showSupportBadge,
}) {
return Preferences(
foldersEnabled: foldersEnabled ?? this.foldersEnabled,
memoriesEnabled: memoriesEnabled ?? this.memoriesEnabled,
peopleEnabled: peopleEnabled ?? this.peopleEnabled,
ratingsEnabled: ratingsEnabled ?? this.ratingsEnabled,
sharedLinksEnabled: sharedLinksEnabled ?? this.sharedLinksEnabled,
tagsEnabled: tagsEnabled ?? this.tagsEnabled,
userAvatarColor: userAvatarColor ?? this.userAvatarColor,
showSupportBadge: showSupportBadge ?? this.showSupportBadge,
);
}
Map<String, Object?> toMap() {
final preferences = <String, Object?>{};
preferences["folders-Enabled"] = foldersEnabled;
preferences["memories-Enabled"] = memoriesEnabled;
preferences["people-Enabled"] = peopleEnabled;
preferences["ratings-Enabled"] = ratingsEnabled;
preferences["sharedLinks-Enabled"] = sharedLinksEnabled;
preferences["tags-Enabled"] = tagsEnabled;
preferences["avatar-Color"] = userAvatarColor.value;
preferences["purchase-ShowSupportBadge"] = showSupportBadge;
return preferences;
}
factory Preferences.fromMap(Map<String, Object?> map) {
return Preferences(
foldersEnabled: (map["folders"] as Map<String, Object?>?)?["enabled"] as bool? ?? false,
memoriesEnabled: (map["memories"] as Map<String, Object?>?)?["enabled"] as bool? ?? true,
peopleEnabled: (map["people"] as Map<String, Object?>?)?["enabled"] as bool? ?? true,
ratingsEnabled: (map["ratings"] as Map<String, Object?>?)?["enabled"] as bool? ?? false,
sharedLinksEnabled: (map["sharedLinks"] as Map<String, Object?>?)?["enabled"] as bool? ?? true,
tagsEnabled: (map["tags"] as Map<String, Object?>?)?["enabled"] as bool? ?? false,
userAvatarColor: AvatarColor.values.firstWhere(
(e) => e.value == (map["avatar"] as Map<String, Object?>?)?["color"] as String?,
orElse: () => AvatarColor.primary,
),
showSupportBadge: (map["purchase"] as Map<String, Object?>?)?["showSupportBadge"] as bool? ?? true,
);
}
@override
String toString() {
return '''Preferences: {
foldersEnabled: $foldersEnabled,
memoriesEnabled: $memoriesEnabled,
peopleEnabled: $peopleEnabled,
ratingsEnabled: $ratingsEnabled,
sharedLinksEnabled: $sharedLinksEnabled,
tagsEnabled: $tagsEnabled,
userAvatarColor: $userAvatarColor,
showSupportBadge: $showSupportBadge,
}''';
}
@override
bool operator ==(covariant Preferences other) {
if (identical(this, other)) return true;
return other.foldersEnabled == foldersEnabled &&
other.memoriesEnabled == memoriesEnabled &&
other.peopleEnabled == peopleEnabled &&
other.ratingsEnabled == ratingsEnabled &&
other.sharedLinksEnabled == sharedLinksEnabled &&
other.tagsEnabled == tagsEnabled &&
other.userAvatarColor == userAvatarColor &&
other.showSupportBadge == showSupportBadge;
}
@override
int get hashCode {
return foldersEnabled.hashCode ^
memoriesEnabled.hashCode ^
peopleEnabled.hashCode ^
ratingsEnabled.hashCode ^
sharedLinksEnabled.hashCode ^
tagsEnabled.hashCode ^
userAvatarColor.hashCode ^
showSupportBadge.hashCode;
}
}
class License {
final DateTime activatedAt;
final String activationKey;
final String licenseKey;
const License({required this.activatedAt, required this.activationKey, required this.licenseKey});
License copyWith({DateTime? activatedAt, String? activationKey, String? licenseKey}) {
return License(
activatedAt: activatedAt ?? this.activatedAt,
activationKey: activationKey ?? this.activationKey,
licenseKey: licenseKey ?? this.licenseKey,
);
}
Map<String, Object?> toMap() {
final license = <String, Object?>{};
license["activatedAt"] = activatedAt;
license["activationKey"] = activationKey;
license["licenseKey"] = licenseKey;
return license;
}
factory License.fromMap(Map<String, Object?> map) {
return License(
activatedAt: DateTime.parse(map["activatedAt"] as String),
activationKey: map["activationKey"] as String,
licenseKey: map["licenseKey"] as String,
);
}
@override
String toString() {
return '''License {
activatedAt: $activatedAt,
activationKey: $activationKey,
licenseKey: $licenseKey,
}''';
}
@override
bool operator ==(covariant License other) {
if (identical(this, other)) return true;
return activatedAt == other.activatedAt && activationKey == other.activationKey && licenseKey == other.licenseKey;
}
@override
int get hashCode => activatedAt.hashCode ^ activationKey.hashCode ^ licenseKey.hashCode;
}
// Model for a user metadata stored in the server
class UserMetadata {
final String userId;
final UserMetadataKey key;
final Onboarding? onboarding;
final Preferences? preferences;
final License? license;
const UserMetadata({required this.userId, required this.key, this.onboarding, this.preferences, this.license})
: assert(
onboarding != null || preferences != null || license != null,
'One of onboarding, preferences and license must be provided',
);
UserMetadata copyWith({
String? userId,
UserMetadataKey? key,
Onboarding? onboarding,
Preferences? preferences,
License? license,
}) {
return UserMetadata(
userId: userId ?? this.userId,
key: key ?? this.key,
onboarding: onboarding ?? this.onboarding,
preferences: preferences ?? this.preferences,
license: license ?? this.license,
);
}
@override
String toString() {
return '''UserMetadata: {
userId: $userId,
key: $key,
onboarding: ${onboarding ?? "<NA>"},
preferences: ${preferences ?? "<NA>"},
license: ${license ?? "<NA>"},
}''';
}
@override
bool operator ==(covariant UserMetadata other) {
if (identical(this, other)) return true;
return other.userId == userId &&
other.key == key &&
other.onboarding == onboarding &&
other.preferences == preferences &&
other.license == license;
}
@override
int get hashCode {
return userId.hashCode ^ key.hashCode ^ onboarding.hashCode ^ preferences.hashCode ^ license.hashCode;
}
}