> allOffsets,
) {
writer.writeDateTime(offsets[0], object.createdAt);
- writer.writeDateTime(offsets[1], object.lastModifiedAssetTimestamp);
- writer.writeString(offsets[2], object.localId);
- writer.writeDateTime(offsets[3], object.modifiedAt);
- writer.writeString(offsets[4], object.name);
- writer.writeString(offsets[5], object.remoteId);
- writer.writeBool(offsets[6], object.shared);
+ writer.writeDateTime(offsets[1], object.endDate);
+ writer.writeDateTime(offsets[2], object.lastModifiedAssetTimestamp);
+ writer.writeString(offsets[3], object.localId);
+ writer.writeDateTime(offsets[4], object.modifiedAt);
+ writer.writeString(offsets[5], object.name);
+ writer.writeString(offsets[6], object.remoteId);
+ writer.writeBool(offsets[7], object.shared);
+ writer.writeDateTime(offsets[8], object.startDate);
}
Album _albumDeserialize(
@@ -164,12 +176,14 @@ Album _albumDeserialize(
) {
final object = Album(
createdAt: reader.readDateTime(offsets[0]),
- lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[1]),
- localId: reader.readStringOrNull(offsets[2]),
- modifiedAt: reader.readDateTime(offsets[3]),
- name: reader.readString(offsets[4]),
- remoteId: reader.readStringOrNull(offsets[5]),
- shared: reader.readBool(offsets[6]),
+ endDate: reader.readDateTimeOrNull(offsets[1]),
+ lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[2]),
+ localId: reader.readStringOrNull(offsets[3]),
+ modifiedAt: reader.readDateTime(offsets[4]),
+ name: reader.readString(offsets[5]),
+ remoteId: reader.readStringOrNull(offsets[6]),
+ shared: reader.readBool(offsets[7]),
+ startDate: reader.readDateTimeOrNull(offsets[8]),
);
object.id = id;
return object;
@@ -187,15 +201,19 @@ P _albumDeserializeProp(
case 1:
return (reader.readDateTimeOrNull(offset)) as P;
case 2:
- return (reader.readStringOrNull(offset)) as P;
+ return (reader.readDateTimeOrNull(offset)) as P;
case 3:
- return (reader.readDateTime(offset)) as P;
- case 4:
- return (reader.readString(offset)) as P;
- case 5:
return (reader.readStringOrNull(offset)) as P;
+ case 4:
+ return (reader.readDateTime(offset)) as P;
+ case 5:
+ return (reader.readString(offset)) as P;
case 6:
+ return (reader.readStringOrNull(offset)) as P;
+ case 7:
return (reader.readBool(offset)) as P;
+ case 8:
+ return (reader.readDateTimeOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
@@ -477,6 +495,75 @@ extension AlbumQueryFilter on QueryBuilder {
});
}
+ QueryBuilder endDateIsNull() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(const FilterCondition.isNull(
+ property: r'endDate',
+ ));
+ });
+ }
+
+ QueryBuilder endDateIsNotNull() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(const FilterCondition.isNotNull(
+ property: r'endDate',
+ ));
+ });
+ }
+
+ QueryBuilder endDateEqualTo(
+ DateTime? value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'endDate',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder endDateGreaterThan(
+ DateTime? value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'endDate',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder endDateLessThan(
+ DateTime? value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'endDate',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder endDateBetween(
+ DateTime? lower,
+ DateTime? upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'endDate',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+
QueryBuilder idEqualTo(Id value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
@@ -1084,6 +1171,75 @@ extension AlbumQueryFilter on QueryBuilder {
));
});
}
+
+ QueryBuilder startDateIsNull() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(const FilterCondition.isNull(
+ property: r'startDate',
+ ));
+ });
+ }
+
+ QueryBuilder startDateIsNotNull() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(const FilterCondition.isNotNull(
+ property: r'startDate',
+ ));
+ });
+ }
+
+ QueryBuilder startDateEqualTo(
+ DateTime? value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'startDate',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder startDateGreaterThan(
+ DateTime? value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'startDate',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder startDateLessThan(
+ DateTime? value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'startDate',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder startDateBetween(
+ DateTime? lower,
+ DateTime? upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'startDate',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
}
extension AlbumQueryObject on QueryBuilder {}
@@ -1241,6 +1397,18 @@ extension AlbumQuerySortBy on QueryBuilder {
});
}
+ QueryBuilder sortByEndDate() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addSortBy(r'endDate', Sort.asc);
+ });
+ }
+
+ QueryBuilder sortByEndDateDesc() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addSortBy(r'endDate', Sort.desc);
+ });
+ }
+
QueryBuilder sortByLastModifiedAssetTimestamp() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.asc);
@@ -1313,6 +1481,18 @@ extension AlbumQuerySortBy on QueryBuilder {
return query.addSortBy(r'shared', Sort.desc);
});
}
+
+ QueryBuilder sortByStartDate() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addSortBy(r'startDate', Sort.asc);
+ });
+ }
+
+ QueryBuilder sortByStartDateDesc() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addSortBy(r'startDate', Sort.desc);
+ });
+ }
}
extension AlbumQuerySortThenBy on QueryBuilder {
@@ -1328,6 +1508,18 @@ extension AlbumQuerySortThenBy on QueryBuilder {
});
}
+ QueryBuilder thenByEndDate() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addSortBy(r'endDate', Sort.asc);
+ });
+ }
+
+ QueryBuilder thenByEndDateDesc() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addSortBy(r'endDate', Sort.desc);
+ });
+ }
+
QueryBuilder thenById() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.asc);
@@ -1412,6 +1604,18 @@ extension AlbumQuerySortThenBy on QueryBuilder {
return query.addSortBy(r'shared', Sort.desc);
});
}
+
+ QueryBuilder thenByStartDate() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addSortBy(r'startDate', Sort.asc);
+ });
+ }
+
+ QueryBuilder thenByStartDateDesc() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addSortBy(r'startDate', Sort.desc);
+ });
+ }
}
extension AlbumQueryWhereDistinct on QueryBuilder {
@@ -1421,6 +1625,12 @@ extension AlbumQueryWhereDistinct on QueryBuilder {
});
}
+ QueryBuilder distinctByEndDate() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addDistinctBy(r'endDate');
+ });
+ }
+
QueryBuilder distinctByLastModifiedAssetTimestamp() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'lastModifiedAssetTimestamp');
@@ -1459,6 +1669,12 @@ extension AlbumQueryWhereDistinct on QueryBuilder {
return query.addDistinctBy(r'shared');
});
}
+
+ QueryBuilder distinctByStartDate() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addDistinctBy(r'startDate');
+ });
+ }
}
extension AlbumQueryProperty on QueryBuilder {
@@ -1474,6 +1690,12 @@ extension AlbumQueryProperty on QueryBuilder {
});
}
+ QueryBuilder endDateProperty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addPropertyName(r'endDate');
+ });
+ }
+
QueryBuilder
lastModifiedAssetTimestampProperty() {
return QueryBuilder.apply(this, (query) {
@@ -1510,4 +1732,10 @@ extension AlbumQueryProperty on QueryBuilder {
return query.addPropertyName(r'shared');
});
}
+
+ QueryBuilder startDateProperty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addPropertyName(r'startDate');
+ });
+ }
}
diff --git a/mobile/lib/shared/models/asset.dart b/mobile/lib/shared/models/asset.dart
index 66f2cc9f3..e1a3f24cd 100644
--- a/mobile/lib/shared/models/asset.dart
+++ b/mobile/lib/shared/models/asset.dart
@@ -153,7 +153,10 @@ class Asset {
String? stackParentId;
- int stackCount;
+ @ignore
+ int get stackChildrenCount => stackCount ?? 0;
+
+ int? stackCount;
/// `true` if this [Asset] is present on the device
@ignore
@@ -253,7 +256,11 @@ class Asset {
isFavorite != a.isFavorite ||
isArchived != a.isArchived ||
isTrashed != a.isTrashed ||
- stackCount != a.stackCount;
+ // no local stack count or different count from remote
+ ((stackCount == null && a.stackCount != null) ||
+ (stackCount != null &&
+ a.stackCount != null &&
+ stackCount != a.stackCount));
}
/// Returns a new [Asset] with values from this and merged & updated with [a]
@@ -269,6 +276,7 @@ class Asset {
width: a.width ?? width,
height: a.height ?? height,
exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo,
+ stackCount: a.stackCount ?? stackCount,
);
} else if (isRemote) {
return _copyWith(
@@ -299,7 +307,7 @@ class Asset {
height: a.height,
livePhotoVideoId: a.livePhotoVideoId,
stackParentId: a.stackParentId,
- stackCount: a.stackCount,
+ stackCount: a.stackCount ?? stackCount,
// isFavorite + isArchived are not set by device-only assets
isFavorite: a.isFavorite,
isArchived: a.isArchived,
diff --git a/mobile/lib/shared/models/asset.g.dart b/mobile/lib/shared/models/asset.g.dart
index 4f485dfb0..f589ba0c6 100644
--- a/mobile/lib/shared/models/asset.g.dart
+++ b/mobile/lib/shared/models/asset.g.dart
@@ -250,7 +250,7 @@ Asset _assetDeserialize(
localId: reader.readStringOrNull(offsets[10]),
ownerId: reader.readLong(offsets[11]),
remoteId: reader.readStringOrNull(offsets[12]),
- stackCount: reader.readLong(offsets[13]),
+ stackCount: reader.readLongOrNull(offsets[13]),
stackParentId: reader.readStringOrNull(offsets[14]),
type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[15])] ??
AssetType.other,
@@ -294,7 +294,7 @@ P _assetDeserializeProp(
case 12:
return (reader.readStringOrNull(offset)) as P;
case 13:
- return (reader.readLong(offset)) as P;
+ return (reader.readLongOrNull(offset)) as P;
case 14:
return (reader.readStringOrNull(offset)) as P;
case 15:
@@ -1825,8 +1825,24 @@ extension AssetQueryFilter on QueryBuilder {
});
}
+ QueryBuilder stackCountIsNull() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(const FilterCondition.isNull(
+ property: r'stackCount',
+ ));
+ });
+ }
+
+ QueryBuilder stackCountIsNotNull() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(const FilterCondition.isNotNull(
+ property: r'stackCount',
+ ));
+ });
+ }
+
QueryBuilder stackCountEqualTo(
- int value) {
+ int? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'stackCount',
@@ -1836,7 +1852,7 @@ extension AssetQueryFilter on QueryBuilder {
}
QueryBuilder stackCountGreaterThan(
- int value, {
+ int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
@@ -1849,7 +1865,7 @@ extension AssetQueryFilter on QueryBuilder {
}
QueryBuilder stackCountLessThan(
- int value, {
+ int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
@@ -1862,8 +1878,8 @@ extension AssetQueryFilter on QueryBuilder {
}
QueryBuilder stackCountBetween(
- int lower,
- int upper, {
+ int? lower,
+ int? upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
@@ -2854,7 +2870,7 @@ extension AssetQueryProperty on QueryBuilder {
});
}
- QueryBuilder stackCountProperty() {
+ QueryBuilder stackCountProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'stackCount');
});
diff --git a/mobile/lib/shared/models/exif_info.dart b/mobile/lib/shared/models/exif_info.dart
index 4609c0487..568e4ce13 100644
--- a/mobile/lib/shared/models/exif_info.dart
+++ b/mobile/lib/shared/models/exif_info.dart
@@ -1,6 +1,5 @@
import 'package:isar/isar.dart';
import 'package:openapi/api.dart';
-import 'package:immich_mobile/utils/builtin_extensions.dart';
part 'exif_info.g.dart';
@@ -165,7 +164,11 @@ double? _exposureTimeToSeconds(String? s) {
}
final parts = s.split("/");
if (parts.length == 2) {
- return parts[0].toDouble() / parts[1].toDouble();
+ final numerator = double.tryParse(parts[0]);
+ final denominator = double.tryParse(parts[1]);
+ if (numerator != null && denominator != null) {
+ return numerator / denominator;
+ }
}
return null;
}
diff --git a/mobile/lib/shared/providers/asset.provider.dart b/mobile/lib/shared/providers/asset.provider.dart
index 31a11639b..a408316f9 100644
--- a/mobile/lib/shared/providers/asset.provider.dart
+++ b/mobile/lib/shared/providers/asset.provider.dart
@@ -131,7 +131,7 @@ class AssetNotifier extends StateNotifier {
Future deleteAssets(
Iterable deleteAssets, {
- bool? force = false,
+ bool force = false,
}) async {
_deleteInProgress = true;
state = true;
@@ -139,25 +139,69 @@ class AssetNotifier extends StateNotifier {
final localDeleted = await _deleteLocalAssets(deleteAssets);
final remoteDeleted = await _deleteRemoteAssets(deleteAssets, force);
if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) {
- List? assetsToUpdate;
- // Local only assets are permanently deleted for now. So always remove them from db
- final dbIds = deleteAssets
- .where((a) => a.isLocal && !a.isRemote)
- .map((e) => e.id)
- .toList();
- if (force == null || !force) {
- assetsToUpdate = remoteDeleted.map((e) {
- e.isTrashed = true;
- return e;
- }).toList();
- } else {
- // Add all remote assets to be deleted from isar as since they are permanently deleted
- dbIds.addAll(remoteDeleted.map((e) => e.id));
- }
- await _db.writeTxn(() async {
- if (assetsToUpdate != null) {
- await _db.assets.putAll(assetsToUpdate);
+ final dbIds = [];
+ final dbUpdates = [];
+
+ // Local assets are removed
+ if (localDeleted.isNotEmpty) {
+ // Permanently remove local only assets from isar
+ dbIds.addAll(
+ deleteAssets
+ .where((a) => a.storage == AssetState.local)
+ .map((e) => e.id),
+ );
+
+ if (remoteDeleted.any((e) => e.isLocal)) {
+ // Force delete: Add all local assets including merged assets
+ if (force) {
+ dbIds.addAll(remoteDeleted.map((e) => e.id));
+ // Soft delete: Remove local Id from asset and trash it
+ } else {
+ dbUpdates.addAll(
+ remoteDeleted.map((e) {
+ e.localId = null;
+ e.isTrashed = true;
+ return e;
+ }),
+ );
+ }
}
+ }
+
+ // Handle remote deletion
+ if (remoteDeleted.isNotEmpty) {
+ if (force) {
+ // Remove remote only assets
+ dbIds.addAll(
+ deleteAssets
+ .where((a) => a.storage == AssetState.remote)
+ .map((e) => e.id),
+ );
+ // Local assets are not removed and there are merged assets
+ final hasLocal = remoteDeleted.any((e) => e.isLocal);
+ if (localDeleted.isEmpty && hasLocal) {
+ // Remove remote Id from local assets
+ dbUpdates.addAll(
+ remoteDeleted.map((e) {
+ e.remoteId = null;
+ // Remove from trashed if remote asset is removed
+ e.isTrashed = false;
+ return e;
+ }),
+ );
+ }
+ } else {
+ dbUpdates.addAll(
+ remoteDeleted.map((e) {
+ e.isTrashed = true;
+ return e;
+ }),
+ );
+ }
+ }
+
+ await _db.writeTxn(() async {
+ await _db.assets.putAll(dbUpdates);
await _db.exifInfos.deleteAll(dbIds);
await _db.assets.deleteAll(dbIds);
});
diff --git a/mobile/lib/shared/providers/websocket.provider.dart b/mobile/lib/shared/providers/websocket.provider.dart
index 1dda262f5..83fcb000f 100644
--- a/mobile/lib/shared/providers/websocket.provider.dart
+++ b/mobile/lib/shared/providers/websocket.provider.dart
@@ -1,5 +1,3 @@
-import 'dart:convert';
-
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
@@ -175,9 +173,8 @@ class WebsocketNotifier extends StateNotifier {
.where((c) => c.action == PendingAction.assetDelete)
.toList();
if (deleteChanges.isNotEmpty) {
- List remoteIds = deleteChanges
- .map((a) => jsonDecode(a.value.toString()).toString())
- .toList();
+ List remoteIds =
+ deleteChanges.map((a) => a.value.toString()).toList();
ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds);
state = state.copyWith(
pendingChanges: state.pendingChanges
@@ -188,21 +185,20 @@ class WebsocketNotifier extends StateNotifier {
}
_handleOnUploadSuccess(dynamic data) {
- final jsonString = jsonDecode(data.toString());
- final dto = AssetResponseDto.fromJson(jsonString);
+ final dto = AssetResponseDto.fromJson(data);
if (dto != null) {
final newAsset = Asset.remote(dto);
ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
}
}
- _handleOnConfigUpdate(dynamic data) {
+ _handleOnConfigUpdate(dynamic _) {
ref.read(serverInfoProvider.notifier).getServerFeatures();
ref.read(serverInfoProvider.notifier).getServerConfig();
}
// Refresh updated assets
- _handleServerUpdates(dynamic data) {
+ _handleServerUpdates(dynamic _) {
ref.read(assetProvider.notifier).getAllAsset();
}
diff --git a/mobile/lib/shared/services/sync.service.dart b/mobile/lib/shared/services/sync.service.dart
index b38b66633..34d84401a 100644
--- a/mobile/lib/shared/services/sync.service.dart
+++ b/mobile/lib/shared/services/sync.service.dart
@@ -173,7 +173,14 @@ class SyncService {
/// Deletes remote-only assets, updates merged assets to be local-only
Future handleRemoteAssetRemoval(List idsToDelete) {
return _db.writeTxn(() async {
- await _db.assets.remote(idsToDelete).filter().localIdIsNull().deleteAll();
+ final idsToRemove = await _db.assets
+ .remote(idsToDelete)
+ .filter()
+ .localIdIsNull()
+ .idProperty()
+ .findAll();
+ await _db.assets.deleteAll(idsToRemove);
+ await _db.exifInfos.deleteAll(idsToRemove);
final onlyLocal = await _db.assets.remote(idsToDelete).findAll();
if (onlyLocal.isNotEmpty) {
for (final Asset a in onlyLocal) {
diff --git a/mobile/lib/shared/ui/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/shared/ui/app_bar_dialog/app_bar_dialog.dart
new file mode 100644
index 000000000..b17fce86d
--- /dev/null
+++ b/mobile/lib/shared/ui/app_bar_dialog/app_bar_dialog.dart
@@ -0,0 +1,263 @@
+import 'package:auto_route/auto_route.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
+import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
+import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart';
+import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
+import 'package:immich_mobile/routing/router.dart';
+import 'package:immich_mobile/shared/providers/asset.provider.dart';
+import 'package:immich_mobile/shared/providers/user.provider.dart';
+import 'package:immich_mobile/shared/providers/websocket.provider.dart';
+import 'package:immich_mobile/shared/ui/app_bar_dialog/app_bar_profile_info.dart';
+import 'package:immich_mobile/shared/ui/app_bar_dialog/app_bar_server_info.dart';
+import 'package:immich_mobile/shared/ui/confirm_dialog.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+class ImmichAppBarDialog extends HookConsumerWidget {
+ const ImmichAppBarDialog({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ BackUpState backupState = ref.watch(backupProvider);
+ final theme = Theme.of(context);
+ bool isDarkTheme = theme.brightness == Brightness.dark;
+ bool isHorizontal = MediaQuery.of(context).size.width > 600;
+ final horizontalPadding = isHorizontal ? 100.0 : 20.0;
+ final user = ref.watch(currentUserProvider);
+
+ useEffect(
+ () {
+ ref.read(backupProvider.notifier).updateServerInfo();
+ return null;
+ },
+ [user],
+ );
+
+ buildTopRow() {
+ return Row(
+ children: [
+ InkWell(
+ onTap: () => Navigator.of(context).pop(),
+ child: const Icon(
+ Icons.close,
+ size: 20,
+ ),
+ ),
+ Expanded(
+ child: Align(
+ alignment: Alignment.center,
+ child: Text(
+ 'IMMICH',
+ style: TextStyle(
+ fontFamily: 'SnowburstOne',
+ fontWeight: FontWeight.bold,
+ color: Theme.of(context).primaryColor,
+ fontSize: 15,
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ buildActionButton(IconData icon, String text, Function() onTap) {
+ return ListTile(
+ dense: true,
+ visualDensity: VisualDensity.standard,
+ contentPadding: const EdgeInsets.only(left: 30),
+ minLeadingWidth: 40,
+ leading: SizedBox(
+ child: Icon(
+ icon,
+ color: theme.textTheme.labelMedium?.color,
+ size: 20,
+ ),
+ ),
+ title: Text(
+ text,
+ style:
+ theme.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
+ ).tr(),
+ onTap: onTap,
+ );
+ }
+
+ buildSettingButton() {
+ return buildActionButton(
+ Icons.settings_rounded,
+ "profile_drawer_settings",
+ () => AutoRouter.of(context).push(const SettingsRoute()),
+ );
+ }
+
+ buildAppLogButton() {
+ return buildActionButton(
+ Icons.assignment_outlined,
+ "profile_drawer_app_logs",
+ () => AutoRouter.of(context).push(const AppLogRoute()),
+ );
+ }
+
+ buildSignOutButton() {
+ return buildActionButton(
+ Icons.logout_rounded,
+ "profile_drawer_sign_out",
+ () async {
+ showDialog(
+ context: context,
+ builder: (BuildContext ctx) {
+ return ConfirmDialog(
+ title: "app_bar_signout_dialog_title",
+ content: "app_bar_signout_dialog_content",
+ ok: "app_bar_signout_dialog_ok",
+ onOk: () async {
+ await ref.watch(authenticationProvider.notifier).logout();
+
+ ref.read(manualUploadProvider.notifier).cancelBackup();
+ ref.watch(backupProvider.notifier).cancelBackup();
+ ref.watch(assetProvider.notifier).clearAllAsset();
+ ref.watch(websocketProvider.notifier).disconnect();
+ AutoRouter.of(context).replace(const LoginRoute());
+ },
+ );
+ },
+ );
+ },
+ );
+ }
+
+ Widget buildStorageInformation() {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3),
+ child: Container(
+ padding: const EdgeInsets.symmetric(vertical: 4),
+ decoration: BoxDecoration(
+ color: isDarkTheme
+ ? Theme.of(context).scaffoldBackgroundColor
+ : const Color.fromARGB(255, 225, 229, 240),
+ ),
+ child: ListTile(
+ minLeadingWidth: 50,
+ leading: Icon(
+ Icons.storage_rounded,
+ color: theme.primaryColor,
+ ),
+ title: const Text(
+ "backup_controller_page_server_storage",
+ style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
+ ).tr(),
+ isThreeLine: true,
+ subtitle: Padding(
+ padding: const EdgeInsets.only(top: 8.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 8.0),
+ child: LinearProgressIndicator(
+ minHeight: 5.0,
+ value: backupState.serverInfo.diskUsagePercentage / 100.0,
+ backgroundColor: Colors.grey,
+ color: theme.primaryColor,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 12.0),
+ child:
+ const Text('backup_controller_page_storage_format').tr(
+ args: [
+ backupState.serverInfo.diskUse,
+ backupState.serverInfo.diskSize,
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ buildFooter() {
+ return Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 20),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ InkWell(
+ onTap: () {
+ Navigator.of(context).pop();
+ launchUrl(
+ Uri.parse('https://immich.app'),
+ );
+ },
+ child: Text(
+ "profile_drawer_documentation",
+ style: Theme.of(context).textTheme.bodySmall,
+ ).tr(),
+ ),
+ const SizedBox(
+ width: 20,
+ child: Text(
+ "•",
+ textAlign: TextAlign.center,
+ ),
+ ),
+ InkWell(
+ onTap: () {
+ Navigator.of(context).pop();
+ launchUrl(
+ Uri.parse('https://github.com/immich-app/immich'),
+ );
+ },
+ child: Text(
+ "profile_drawer_github",
+ style: Theme.of(context).textTheme.bodySmall,
+ ).tr(),
+ ),
+ ],
+ ),
+ );
+ }
+
+ return Dialog(
+ clipBehavior: Clip.hardEdge,
+ alignment: Alignment.topCenter,
+ insetPadding: EdgeInsets.only(
+ top: isHorizontal ? 20 : 60,
+ left: horizontalPadding,
+ right: horizontalPadding,
+ bottom: isHorizontal ? 20 : 100,
+ ),
+ backgroundColor: theme.cardColor,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(20),
+ ),
+ child: SizedBox(
+ child: SingleChildScrollView(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Container(
+ padding: const EdgeInsets.all(20),
+ child: buildTopRow(),
+ ),
+ const AppBarProfileInfoBox(),
+ buildStorageInformation(),
+ const AppBarServerInfo(),
+ buildAppLogButton(),
+ buildSettingButton(),
+ buildSignOutButton(),
+ buildFooter(),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/mobile/lib/modules/home/ui/profile_drawer/profile_drawer_header.dart b/mobile/lib/shared/ui/app_bar_dialog/app_bar_profile_info.dart
similarity index 57%
rename from mobile/lib/modules/home/ui/profile_drawer/profile_drawer_header.dart
rename to mobile/lib/shared/ui/app_bar_dialog/app_bar_profile_info.dart
index 328d9cbc1..d58699d5c 100644
--- a/mobile/lib/modules/home/ui/profile_drawer/profile_drawer_header.dart
+++ b/mobile/lib/shared/ui/app_bar_dialog/app_bar_profile_info.dart
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:immich_mobile/modules/home/providers/upload_profile_image.provider.dart';
@@ -9,8 +8,8 @@ import 'package:immich_mobile/modules/login/models/authentication_state.model.da
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
-class ProfileDrawerHeader extends HookConsumerWidget {
- const ProfileDrawerHeader({
+class AppBarProfileInfoBox extends HookConsumerWidget {
+ const AppBarProfileInfoBox({
Key? key,
}) : super(key: key);
@@ -23,30 +22,24 @@ class ProfileDrawerHeader extends HookConsumerWidget {
final user = Store.tryGet(StoreKey.currentUser);
buildUserProfileImage() {
+ const immichImage = CircleAvatar(
+ radius: 20,
+ backgroundImage: AssetImage('assets/immich-logo-no-outline.png'),
+ backgroundColor: Colors.transparent,
+ );
+
if (authState.profileImagePath.isEmpty || user == null) {
- return const CircleAvatar(
- radius: 35,
- backgroundImage: AssetImage('assets/immich-logo-no-outline.png'),
- backgroundColor: Colors.transparent,
- );
+ return immichImage;
}
- var userImage = UserCircleAvatar(
- radius: 35,
- size: 66,
+ final userImage = UserCircleAvatar(
+ radius: 20,
+ size: 40,
user: user,
);
if (uploadProfileImageStatus == UploadProfileStatus.idle) {
- if (authState.profileImagePath.isNotEmpty) {
- return userImage;
- } else {
- return const CircleAvatar(
- radius: 33,
- backgroundImage: AssetImage('assets/immich-logo-no-outline.png'),
- backgroundColor: Colors.transparent,
- );
- }
+ return authState.profileImagePath.isNotEmpty ? userImage : immichImage;
}
if (uploadProfileImageStatus == UploadProfileStatus.success) {
@@ -54,18 +47,18 @@ class ProfileDrawerHeader extends HookConsumerWidget {
}
if (uploadProfileImageStatus == UploadProfileStatus.failure) {
- return const CircleAvatar(
- radius: 35,
- backgroundImage: AssetImage('assets/immich-logo-no-outline.png'),
- backgroundColor: Colors.transparent,
- );
+ return immichImage;
}
if (uploadProfileImageStatus == UploadProfileStatus.loading) {
- return const ImmichLoadingIndicator();
+ return const SizedBox(
+ height: 40,
+ width: 40,
+ child: ImmichLoadingIndicator(borderRadius: 20),
+ );
}
- return const SizedBox();
+ return immichImage;
}
pickUserProfileImage() async {
@@ -80,54 +73,45 @@ class ProfileDrawerHeader extends HookConsumerWidget {
await ref.watch(uploadProfileImageProvider.notifier).upload(image);
if (success) {
+ final profileImagePath =
+ ref.read(uploadProfileImageProvider).profileImagePath;
ref.watch(authenticationProvider.notifier).updateUserProfileImagePath(
- ref.read(uploadProfileImageProvider).profileImagePath,
+ profileImagePath,
);
+ if (user != null) {
+ user.profileImagePath = profileImagePath;
+ Store.put(StoreKey.currentUser, user);
+ }
}
}
}
- useEffect(
- () {
- // buildUserProfileImage();
- return null;
- },
- [],
- );
-
- return DrawerHeader(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: isDarkMode
- ? [
- const Color.fromARGB(255, 22, 25, 48),
- const Color.fromARGB(255, 13, 13, 13),
- const Color.fromARGB(255, 0, 0, 0),
- ]
- : [
- const Color.fromARGB(255, 216, 219, 238),
- const Color.fromARGB(255, 242, 242, 242),
- Colors.white,
- ],
- begin: Alignment.centerRight,
- end: Alignment.centerLeft,
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10.0),
+ child: Container(
+ width: double.infinity,
+ decoration: BoxDecoration(
+ color: Theme.of(context).brightness == Brightness.dark
+ ? Theme.of(context).scaffoldBackgroundColor
+ : const Color.fromARGB(255, 225, 229, 240),
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(10),
+ topRight: Radius.circular(10),
+ ),
),
- ),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- GestureDetector(
+ child: ListTile(
+ minLeadingWidth: 50,
+ leading: GestureDetector(
onTap: pickUserProfileImage,
child: Stack(
clipBehavior: Clip.none,
children: [
buildUserProfileImage(),
Positioned(
- bottom: 0,
- right: -5,
+ bottom: -5,
+ right: -8,
child: Material(
- color: isDarkMode ? Colors.grey[700] : Colors.grey[100],
+ color: isDarkMode ? Colors.blueGrey[800] : Colors.white,
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0),
@@ -135,7 +119,7 @@ class ProfileDrawerHeader extends HookConsumerWidget {
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(
- Icons.edit,
+ Icons.camera_alt_outlined,
color: Theme.of(context).primaryColor,
size: 14,
),
@@ -145,19 +129,21 @@ class ProfileDrawerHeader extends HookConsumerWidget {
],
),
),
- Text(
+ title: Text(
"${authState.firstName} ${authState.lastName}",
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
- fontSize: 24,
+ fontSize: 16,
),
),
- Text(
+ subtitle: Text(
authState.userEmail,
- style: Theme.of(context).textTheme.labelMedium,
+ style: Theme.of(context).textTheme.labelMedium?.copyWith(
+ fontSize: 12,
+ ),
),
- ],
+ ),
),
);
}
diff --git a/mobile/lib/shared/ui/app_bar_dialog/app_bar_server_info.dart b/mobile/lib/shared/ui/app_bar_dialog/app_bar_server_info.dart
new file mode 100644
index 000000000..8ef3c09b5
--- /dev/null
+++ b/mobile/lib/shared/ui/app_bar_dialog/app_bar_server_info.dart
@@ -0,0 +1,209 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/shared/models/server_info/server_info.model.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:immich_mobile/shared/providers/server_info.provider.dart';
+import 'package:immich_mobile/utils/url_helper.dart';
+import 'package:package_info_plus/package_info_plus.dart';
+
+class AppBarServerInfo extends HookConsumerWidget {
+ const AppBarServerInfo({
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ ServerInfo serverInfoState = ref.watch(serverInfoProvider);
+
+ final appInfo = useState({});
+
+ getPackageInfo() async {
+ PackageInfo packageInfo = await PackageInfo.fromPlatform();
+
+ appInfo.value = {
+ "version": packageInfo.version,
+ "buildNumber": packageInfo.buildNumber,
+ };
+ }
+
+ useEffect(
+ () {
+ getPackageInfo();
+ return null;
+ },
+ [],
+ );
+
+ return Padding(
+ padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
+ child: Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).brightness == Brightness.dark
+ ? Theme.of(context).scaffoldBackgroundColor
+ : const Color.fromARGB(255, 225, 229, 240),
+ borderRadius: const BorderRadius.only(
+ bottomLeft: Radius.circular(10),
+ bottomRight: Radius.circular(10),
+ ),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Text(
+ serverInfoState.isVersionMismatch
+ ? serverInfoState.versionMismatchErrorMessage
+ : "profile_drawer_client_server_up_to_date".tr(),
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 11,
+ color: Theme.of(context).primaryColor,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 10),
+ child: Divider(
+ color: Color.fromARGB(101, 201, 201, 201),
+ thickness: 1,
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(left: 10.0),
+ child: Text(
+ "server_info_box_app_version".tr(),
+ style: TextStyle(
+ fontSize: 11,
+ color: Theme.of(context).textTheme.labelSmall?.color,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ),
+ Expanded(
+ flex: 0,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 10.0),
+ child: Text(
+ "${appInfo.value["version"]} build.${appInfo.value["buildNumber"]}",
+ style: TextStyle(
+ fontSize: 11,
+ color: Theme.of(context)
+ .textTheme
+ .labelSmall
+ ?.color
+ ?.withOpacity(0.5),
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 10),
+ child: Divider(
+ color: Color.fromARGB(101, 201, 201, 201),
+ thickness: 1,
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(left: 10.0),
+ child: Text(
+ "server_info_box_server_version".tr(),
+ style: TextStyle(
+ fontSize: 11,
+ color: Theme.of(context).textTheme.labelSmall?.color,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ),
+ Expanded(
+ flex: 0,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 10.0),
+ child: Text(
+ serverInfoState.serverVersion.major > 0
+ ? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}"
+ : "?",
+ style: TextStyle(
+ fontSize: 11,
+ color: Theme.of(context)
+ .textTheme
+ .labelSmall
+ ?.color
+ ?.withOpacity(0.5),
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 10),
+ child: Divider(
+ color: Color.fromARGB(101, 201, 201, 201),
+ thickness: 1,
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(left: 10.0),
+ child: Text(
+ "server_info_box_server_url".tr(),
+ style: TextStyle(
+ fontSize: 11,
+ color: Theme.of(context).textTheme.labelSmall?.color,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ),
+ Expanded(
+ flex: 0,
+ child: Container(
+ width: 200,
+ padding: const EdgeInsets.only(right: 10.0),
+ child: Text(
+ getServerUrl() ?? '--',
+ style: TextStyle(
+ fontSize: 11,
+ color: Theme.of(context)
+ .textTheme
+ .labelSmall
+ ?.color
+ ?.withOpacity(0.5),
+ fontWeight: FontWeight.bold,
+ overflow: TextOverflow.ellipsis,
+ ),
+ textAlign: TextAlign.end,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/mobile/lib/shared/ui/immich_app_bar.dart b/mobile/lib/shared/ui/immich_app_bar.dart
new file mode 100644
index 000000000..ad8195354
--- /dev/null
+++ b/mobile/lib/shared/ui/immich_app_bar.dart
@@ -0,0 +1,192 @@
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/shared/models/store.dart';
+import 'package:immich_mobile/shared/ui/app_bar_dialog/app_bar_dialog.dart';
+import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
+import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
+import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
+
+import 'package:immich_mobile/routing/router.dart';
+import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
+import 'package:immich_mobile/shared/models/server_info/server_info.model.dart';
+import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
+import 'package:immich_mobile/shared/providers/server_info.provider.dart';
+
+class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
+ @override
+ Size get preferredSize => const Size.fromHeight(kToolbarHeight);
+ final Widget? action;
+
+ const ImmichAppBar({super.key, this.action});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final BackUpState backupState = ref.watch(backupProvider);
+ final bool isEnableAutoBackup =
+ backupState.backgroundBackup || backupState.autoBackup;
+ final ServerInfo serverInfoState = ref.watch(serverInfoProvider);
+ AuthenticationState authState = ref.watch(authenticationProvider);
+ final user = Store.tryGet(StoreKey.currentUser);
+ final isDarkMode = Theme.of(context).brightness == Brightness.dark;
+ const widgetSize = 30.0;
+
+ buildProfilePhoto() {
+ return InkWell(
+ onTap: () => showDialog(
+ context: context,
+ useRootNavigator: false,
+ builder: (ctx) => const ImmichAppBarDialog(),
+ ),
+ borderRadius: BorderRadius.circular(12),
+ child: authState.profileImagePath.isEmpty || user == null
+ ? const Icon(
+ Icons.face_outlined,
+ size: widgetSize,
+ )
+ : UserCircleAvatar(
+ radius: 15,
+ size: 27,
+ user: user,
+ ),
+ );
+ }
+
+ buildProfileIndicator() {
+ return Badge(
+ label: Container(
+ decoration: BoxDecoration(
+ color: Colors.black,
+ borderRadius: BorderRadius.circular(widgetSize / 2),
+ ),
+ child: const Icon(
+ Icons.info,
+ color: Color.fromARGB(255, 243, 188, 106),
+ size: widgetSize / 2,
+ ),
+ ),
+ backgroundColor: Colors.transparent,
+ alignment: Alignment.bottomRight,
+ isLabelVisible: serverInfoState.isVersionMismatch,
+ offset: const Offset(2, 2),
+ child: buildProfilePhoto(),
+ );
+ }
+
+ getBackupBadgeIcon() {
+ final iconColor = isDarkMode ? Colors.white : Colors.black;
+
+ if (isEnableAutoBackup) {
+ if (backupState.backupProgress == BackUpProgressEnum.inProgress) {
+ return Container(
+ padding: const EdgeInsets.all(3.5),
+ child: CircularProgressIndicator(
+ strokeWidth: 2,
+ strokeCap: StrokeCap.round,
+ valueColor: AlwaysStoppedAnimation(iconColor),
+ ),
+ );
+ } else if (backupState.backupProgress !=
+ BackUpProgressEnum.inBackground &&
+ backupState.backupProgress != BackUpProgressEnum.manualInProgress) {
+ return Icon(
+ Icons.check_outlined,
+ size: 9,
+ color: iconColor,
+ );
+ }
+ }
+
+ if (!isEnableAutoBackup) {
+ return Icon(
+ Icons.cloud_off_rounded,
+ size: 9,
+ color: iconColor,
+ );
+ }
+ }
+
+ buildBackupIndicator() {
+ final indicatorIcon = getBackupBadgeIcon();
+ final badgeBackground = isDarkMode ? Colors.blueGrey[800] : Colors.white;
+
+ return InkWell(
+ onTap: () => AutoRouter.of(context).push(const BackupControllerRoute()),
+ borderRadius: BorderRadius.circular(12),
+ child: Badge(
+ label: Container(
+ width: widgetSize / 2,
+ height: widgetSize / 2,
+ decoration: BoxDecoration(
+ color: badgeBackground,
+ border: Border.all(
+ color: isDarkMode ? Colors.black : Colors.grey,
+ ),
+ borderRadius: BorderRadius.circular(widgetSize / 2),
+ ),
+ child: indicatorIcon,
+ ),
+ backgroundColor: Colors.transparent,
+ alignment: Alignment.bottomRight,
+ isLabelVisible: indicatorIcon != null,
+ offset: const Offset(2, 2),
+ child: Icon(
+ Icons.backup_rounded,
+ size: widgetSize,
+ color: Theme.of(context).primaryColor,
+ ),
+ ),
+ );
+ }
+
+ return AppBar(
+ backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(5),
+ ),
+ ),
+ automaticallyImplyLeading: false,
+ centerTitle: false,
+ title: Builder(
+ builder: (BuildContext context) {
+ return Row(
+ children: [
+ Container(
+ padding: const EdgeInsets.only(top: 3),
+ width: 28,
+ height: 28,
+ child: Image.asset(
+ 'assets/immich-logo.png',
+ ),
+ ),
+ Container(
+ margin: const EdgeInsets.only(left: 10),
+ child: const Text(
+ 'IMMICH',
+ style: TextStyle(
+ fontFamily: 'SnowburstOne',
+ fontWeight: FontWeight.bold,
+ fontSize: 24,
+ ),
+ ),
+ ),
+ ],
+ );
+ },
+ ),
+ actions: [
+ if (action != null)
+ Padding(padding: const EdgeInsets.only(right: 20), child: action!),
+ Padding(
+ padding: const EdgeInsets.only(right: 20),
+ child: buildBackupIndicator(),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(right: 20),
+ child: buildProfileIndicator(),
+ ),
+ ],
+ );
+ }
+}
diff --git a/mobile/lib/shared/ui/immich_loading_indicator.dart b/mobile/lib/shared/ui/immich_loading_indicator.dart
index bd4ad0d3c..98ddb8f47 100644
--- a/mobile/lib/shared/ui/immich_loading_indicator.dart
+++ b/mobile/lib/shared/ui/immich_loading_indicator.dart
@@ -1,8 +1,11 @@
import 'package:flutter/material.dart';
class ImmichLoadingIndicator extends StatelessWidget {
+ final double? borderRadius;
+
const ImmichLoadingIndicator({
Key? key,
+ this.borderRadius,
}) : super(key: key);
@override
@@ -12,7 +15,7 @@ class ImmichLoadingIndicator extends StatelessWidget {
width: 60,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor.withAlpha(200),
- borderRadius: BorderRadius.circular(10),
+ borderRadius: BorderRadius.circular(borderRadius ?? 10),
),
padding: const EdgeInsets.all(15),
child: const CircularProgressIndicator(
diff --git a/mobile/lib/shared/ui/user_circle_avatar.dart b/mobile/lib/shared/ui/user_circle_avatar.dart
index 39f27dbd9..b70566d88 100644
--- a/mobile/lib/shared/ui/user_circle_avatar.dart
+++ b/mobile/lib/shared/ui/user_circle_avatar.dart
@@ -1,5 +1,6 @@
import 'dart:math';
+import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/shared/models/store.dart';
@@ -46,7 +47,7 @@ class UserCircleAvatar extends ConsumerWidget {
radius: radius,
child: user.profileImagePath == ""
? Text(
- user.firstName[0],
+ user.firstName[0].toUpperCase(),
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
@@ -54,19 +55,18 @@ class UserCircleAvatar extends ConsumerWidget {
)
: ClipRRect(
borderRadius: BorderRadius.circular(50),
- child: FadeInImage(
+ child: CachedNetworkImage(
fit: BoxFit.cover,
- placeholder: MemoryImage(kTransparentImage),
+ cacheKey: user.profileImagePath,
width: size,
height: size,
- image: NetworkImage(
- profileImageUrl,
- headers: {
- "Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
- },
- ),
- fadeInDuration: const Duration(milliseconds: 200),
- imageErrorBuilder: (context, error, stackTrace) =>
+ placeholder: (_, __) => Image.memory(kTransparentImage),
+ imageUrl: profileImageUrl,
+ httpHeaders: {
+ "Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
+ },
+ fadeInDuration: const Duration(milliseconds: 300),
+ errorWidget: (context, error, stackTrace) =>
Image.memory(kTransparentImage),
),
),
diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart
index 2056237cb..6fef55c2f 100644
--- a/mobile/lib/utils/image_url_builder.dart
+++ b/mobile/lib/utils/image_url_builder.dart
@@ -1,6 +1,7 @@
import 'package:immich_mobile/shared/models/album.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/models/store.dart';
+import 'package:isar/isar.dart';
import 'package:openapi/api.dart';
String getThumbnailUrl(
@@ -35,8 +36,10 @@ String getAlbumThumbnailUrl(
if (album.thumbnail.value?.remoteId == null) {
return '';
}
- return getThumbnailUrlForRemoteId(album.thumbnail.value!.remoteId!,
- type: type,);
+ return getThumbnailUrlForRemoteId(
+ album.thumbnail.value!.remoteId!,
+ type: type,
+ );
}
String getAlbumThumbNailCacheKey(
@@ -57,7 +60,9 @@ String getImageUrl(final Asset asset) {
}
String getImageCacheKey(final Asset asset) {
- return '${asset.id}_fullStage';
+ // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
+ final isFromDto = asset.id == Isar.autoIncrement;
+ return '${isFromDto ? asset.remoteId : asset.id}_fullStage';
}
String getThumbnailUrlForRemoteId(
diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES
index 85b96e647..6350677f1 100644
--- a/mobile/openapi/.openapi-generator/FILES
+++ b/mobile/openapi/.openapi-generator/FILES
@@ -40,8 +40,6 @@ doc/CLIPConfig.md
doc/CLIPMode.md
doc/CQMode.md
doc/ChangePasswordDto.md
-doc/CheckDuplicateAssetDto.md
-doc/CheckDuplicateAssetResponseDto.md
doc/CheckExistingAssetsDto.md
doc/CheckExistingAssetsResponseDto.md
doc/CitiesFile.md
@@ -96,6 +94,7 @@ doc/PeopleUpdateDto.md
doc/PeopleUpdateItem.md
doc/PersonApi.md
doc/PersonResponseDto.md
+doc/PersonStatisticsResponseDto.md
doc/PersonUpdateDto.md
doc/QueueStatusDto.md
doc/RecognitionConfig.md
@@ -116,6 +115,7 @@ doc/ServerInfoResponseDto.md
doc/ServerMediaTypesResponseDto.md
doc/ServerPingResponse.md
doc/ServerStatsResponseDto.md
+doc/ServerThemeDto.md
doc/ServerVersionResponseDto.md
doc/SharedLinkApi.md
doc/SharedLinkCreateDto.md
@@ -130,11 +130,13 @@ doc/SystemConfigFFmpegDto.md
doc/SystemConfigJobDto.md
doc/SystemConfigMachineLearningDto.md
doc/SystemConfigMapDto.md
+doc/SystemConfigNewVersionCheckDto.md
doc/SystemConfigOAuthDto.md
doc/SystemConfigPasswordLoginDto.md
doc/SystemConfigReverseGeocodingDto.md
doc/SystemConfigStorageTemplateDto.md
doc/SystemConfigTemplateStorageOptionDto.md
+doc/SystemConfigThemeDto.md
doc/SystemConfigThumbnailDto.md
doc/SystemConfigTrashDto.md
doc/TagApi.md
@@ -154,7 +156,6 @@ doc/UpdateTagDto.md
doc/UpdateUserDto.md
doc/UsageByUserDto.md
doc/UserApi.md
-doc/UserCountResponseDto.md
doc/UserResponseDto.md
doc/ValidateAccessTokenResponseDto.md
doc/VideoCodec.md
@@ -213,8 +214,6 @@ lib/model/auth_device_response_dto.dart
lib/model/bulk_id_response_dto.dart
lib/model/bulk_ids_dto.dart
lib/model/change_password_dto.dart
-lib/model/check_duplicate_asset_dto.dart
-lib/model/check_duplicate_asset_response_dto.dart
lib/model/check_existing_assets_dto.dart
lib/model/check_existing_assets_response_dto.dart
lib/model/cities_file.dart
@@ -267,6 +266,7 @@ lib/model/people_response_dto.dart
lib/model/people_update_dto.dart
lib/model/people_update_item.dart
lib/model/person_response_dto.dart
+lib/model/person_statistics_response_dto.dart
lib/model/person_update_dto.dart
lib/model/queue_status_dto.dart
lib/model/recognition_config.dart
@@ -285,6 +285,7 @@ lib/model/server_info_response_dto.dart
lib/model/server_media_types_response_dto.dart
lib/model/server_ping_response.dart
lib/model/server_stats_response_dto.dart
+lib/model/server_theme_dto.dart
lib/model/server_version_response_dto.dart
lib/model/shared_link_create_dto.dart
lib/model/shared_link_edit_dto.dart
@@ -297,11 +298,13 @@ lib/model/system_config_f_fmpeg_dto.dart
lib/model/system_config_job_dto.dart
lib/model/system_config_machine_learning_dto.dart
lib/model/system_config_map_dto.dart
+lib/model/system_config_new_version_check_dto.dart
lib/model/system_config_o_auth_dto.dart
lib/model/system_config_password_login_dto.dart
lib/model/system_config_reverse_geocoding_dto.dart
lib/model/system_config_storage_template_dto.dart
lib/model/system_config_template_storage_option_dto.dart
+lib/model/system_config_theme_dto.dart
lib/model/system_config_thumbnail_dto.dart
lib/model/system_config_trash_dto.dart
lib/model/tag_response_dto.dart
@@ -319,7 +322,6 @@ lib/model/update_stack_parent_dto.dart
lib/model/update_tag_dto.dart
lib/model/update_user_dto.dart
lib/model/usage_by_user_dto.dart
-lib/model/user_count_response_dto.dart
lib/model/user_response_dto.dart
lib/model/validate_access_token_response_dto.dart
lib/model/video_codec.dart
@@ -358,8 +360,6 @@ test/authentication_api_test.dart
test/bulk_id_response_dto_test.dart
test/bulk_ids_dto_test.dart
test/change_password_dto_test.dart
-test/check_duplicate_asset_dto_test.dart
-test/check_duplicate_asset_response_dto_test.dart
test/check_existing_assets_dto_test.dart
test/check_existing_assets_response_dto_test.dart
test/cities_file_test.dart
@@ -417,6 +417,7 @@ test/people_update_dto_test.dart
test/people_update_item_test.dart
test/person_api_test.dart
test/person_response_dto_test.dart
+test/person_statistics_response_dto_test.dart
test/person_update_dto_test.dart
test/queue_status_dto_test.dart
test/recognition_config_test.dart
@@ -437,6 +438,7 @@ test/server_info_response_dto_test.dart
test/server_media_types_response_dto_test.dart
test/server_ping_response_test.dart
test/server_stats_response_dto_test.dart
+test/server_theme_dto_test.dart
test/server_version_response_dto_test.dart
test/shared_link_api_test.dart
test/shared_link_create_dto_test.dart
@@ -451,11 +453,13 @@ test/system_config_f_fmpeg_dto_test.dart
test/system_config_job_dto_test.dart
test/system_config_machine_learning_dto_test.dart
test/system_config_map_dto_test.dart
+test/system_config_new_version_check_dto_test.dart
test/system_config_o_auth_dto_test.dart
test/system_config_password_login_dto_test.dart
test/system_config_reverse_geocoding_dto_test.dart
test/system_config_storage_template_dto_test.dart
test/system_config_template_storage_option_dto_test.dart
+test/system_config_theme_dto_test.dart
test/system_config_thumbnail_dto_test.dart
test/system_config_trash_dto_test.dart
test/tag_api_test.dart
@@ -475,7 +479,6 @@ test/update_tag_dto_test.dart
test/update_user_dto_test.dart
test/usage_by_user_dto_test.dart
test/user_api_test.dart
-test/user_count_response_dto_test.dart
test/user_response_dto_test.dart
test/validate_access_token_response_dto_test.dart
test/video_codec_test.dart
diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md
index 47d04b9bd..6ec603962 100644
--- a/mobile/openapi/README.md
+++ b/mobile/openapi/README.md
@@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
-- API version: 1.82.1
+- API version: 1.83.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen
## Requirements
@@ -88,7 +88,6 @@ Class | Method | HTTP request | Description
*AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /album/{id}/user/{userId} |
*AlbumApi* | [**updateAlbumInfo**](doc//AlbumApi.md#updatealbuminfo) | **PATCH** /album/{id} |
*AssetApi* | [**bulkUploadCheck**](doc//AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |
-*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
*AssetApi* | [**deleteAssets**](doc//AssetApi.md#deleteassets) | **DELETE** /asset |
*AssetApi* | [**downloadArchive**](doc//AssetApi.md#downloadarchive) | **POST** /asset/download/archive |
@@ -152,6 +151,7 @@ Class | Method | HTTP request | Description
*PersonApi* | [**getAllPeople**](doc//PersonApi.md#getallpeople) | **GET** /person |
*PersonApi* | [**getPerson**](doc//PersonApi.md#getperson) | **GET** /person/{id} |
*PersonApi* | [**getPersonAssets**](doc//PersonApi.md#getpersonassets) | **GET** /person/{id}/assets |
+*PersonApi* | [**getPersonStatistics**](doc//PersonApi.md#getpersonstatistics) | **GET** /person/{id}/statistics |
*PersonApi* | [**getPersonThumbnail**](doc//PersonApi.md#getpersonthumbnail) | **GET** /person/{id}/thumbnail |
*PersonApi* | [**mergePerson**](doc//PersonApi.md#mergeperson) | **POST** /person/{id}/merge |
*PersonApi* | [**updatePeople**](doc//PersonApi.md#updatepeople) | **PUT** /person |
@@ -165,6 +165,7 @@ Class | Method | HTTP request | Description
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats |
*ServerInfoApi* | [**getSupportedMediaTypes**](doc//ServerInfoApi.md#getsupportedmediatypes) | **GET** /server-info/media-types |
+*ServerInfoApi* | [**getTheme**](doc//ServerInfoApi.md#gettheme) | **GET** /server-info/theme |
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
*SharedLinkApi* | [**addSharedLinkAssets**](doc//SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-link/{id}/assets |
*SharedLinkApi* | [**createSharedLink**](doc//SharedLinkApi.md#createsharedlink) | **POST** /shared-link |
@@ -193,7 +194,6 @@ Class | Method | HTTP request | Description
*UserApi* | [**getMyUserInfo**](doc//UserApi.md#getmyuserinfo) | **GET** /user/me |
*UserApi* | [**getProfileImage**](doc//UserApi.md#getprofileimage) | **GET** /user/profile-image/{id} |
*UserApi* | [**getUserById**](doc//UserApi.md#getuserbyid) | **GET** /user/info/{id} |
-*UserApi* | [**getUserCount**](doc//UserApi.md#getusercount) | **GET** /user/count |
*UserApi* | [**restoreUser**](doc//UserApi.md#restoreuser) | **POST** /user/{id}/restore |
*UserApi* | [**updateUser**](doc//UserApi.md#updateuser) | **PUT** /user |
@@ -232,8 +232,6 @@ Class | Method | HTTP request | Description
- [CLIPMode](doc//CLIPMode.md)
- [CQMode](doc//CQMode.md)
- [ChangePasswordDto](doc//ChangePasswordDto.md)
- - [CheckDuplicateAssetDto](doc//CheckDuplicateAssetDto.md)
- - [CheckDuplicateAssetResponseDto](doc//CheckDuplicateAssetResponseDto.md)
- [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md)
- [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md)
- [CitiesFile](doc//CitiesFile.md)
@@ -283,6 +281,7 @@ Class | Method | HTTP request | Description
- [PeopleUpdateDto](doc//PeopleUpdateDto.md)
- [PeopleUpdateItem](doc//PeopleUpdateItem.md)
- [PersonResponseDto](doc//PersonResponseDto.md)
+ - [PersonStatisticsResponseDto](doc//PersonStatisticsResponseDto.md)
- [PersonUpdateDto](doc//PersonUpdateDto.md)
- [QueueStatusDto](doc//QueueStatusDto.md)
- [RecognitionConfig](doc//RecognitionConfig.md)
@@ -301,6 +300,7 @@ Class | Method | HTTP request | Description
- [ServerMediaTypesResponseDto](doc//ServerMediaTypesResponseDto.md)
- [ServerPingResponse](doc//ServerPingResponse.md)
- [ServerStatsResponseDto](doc//ServerStatsResponseDto.md)
+ - [ServerThemeDto](doc//ServerThemeDto.md)
- [ServerVersionResponseDto](doc//ServerVersionResponseDto.md)
- [SharedLinkCreateDto](doc//SharedLinkCreateDto.md)
- [SharedLinkEditDto](doc//SharedLinkEditDto.md)
@@ -313,11 +313,13 @@ Class | Method | HTTP request | Description
- [SystemConfigJobDto](doc//SystemConfigJobDto.md)
- [SystemConfigMachineLearningDto](doc//SystemConfigMachineLearningDto.md)
- [SystemConfigMapDto](doc//SystemConfigMapDto.md)
+ - [SystemConfigNewVersionCheckDto](doc//SystemConfigNewVersionCheckDto.md)
- [SystemConfigOAuthDto](doc//SystemConfigOAuthDto.md)
- [SystemConfigPasswordLoginDto](doc//SystemConfigPasswordLoginDto.md)
- [SystemConfigReverseGeocodingDto](doc//SystemConfigReverseGeocodingDto.md)
- [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md)
- [SystemConfigTemplateStorageOptionDto](doc//SystemConfigTemplateStorageOptionDto.md)
+ - [SystemConfigThemeDto](doc//SystemConfigThemeDto.md)
- [SystemConfigThumbnailDto](doc//SystemConfigThumbnailDto.md)
- [SystemConfigTrashDto](doc//SystemConfigTrashDto.md)
- [TagResponseDto](doc//TagResponseDto.md)
@@ -335,7 +337,6 @@ Class | Method | HTTP request | Description
- [UpdateTagDto](doc//UpdateTagDto.md)
- [UpdateUserDto](doc//UpdateUserDto.md)
- [UsageByUserDto](doc//UsageByUserDto.md)
- - [UserCountResponseDto](doc//UserCountResponseDto.md)
- [UserResponseDto](doc//UserResponseDto.md)
- [ValidateAccessTokenResponseDto](doc//ValidateAccessTokenResponseDto.md)
- [VideoCodec](doc//VideoCodec.md)
diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md
index 075c32bd9..d606e540f 100644
--- a/mobile/openapi/doc/AssetApi.md
+++ b/mobile/openapi/doc/AssetApi.md
@@ -10,7 +10,6 @@ All URIs are relative to */api*
Method | HTTP request | Description
------------- | ------------- | -------------
[**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |
-[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
[**deleteAssets**](AssetApi.md#deleteassets) | **DELETE** /asset |
[**downloadArchive**](AssetApi.md#downloadarchive) | **POST** /asset/download/archive |
@@ -99,65 +98,6 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
-# **checkDuplicateAsset**
-> CheckDuplicateAssetResponseDto checkDuplicateAsset(checkDuplicateAssetDto, key)
-
-
-
-Check duplicated asset before uploading - for Web upload used
-
-### Example
-```dart
-import 'package:openapi/api.dart';
-// TODO Configure API key authorization: cookie
-//defaultApiClient.getAuthentication('cookie').apiKey = 'YOUR_API_KEY';
-// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
-//defaultApiClient.getAuthentication('cookie').apiKeyPrefix = 'Bearer';
-// TODO Configure API key authorization: api_key
-//defaultApiClient.getAuthentication('api_key').apiKey = 'YOUR_API_KEY';
-// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
-//defaultApiClient.getAuthentication('api_key').apiKeyPrefix = 'Bearer';
-// TODO Configure HTTP Bearer authorization: bearer
-// Case 1. Use String Token
-//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
-// Case 2. Use Function which generate token.
-// String yourTokenGeneratorFunction() { ... }
-//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction);
-
-final api_instance = AssetApi();
-final checkDuplicateAssetDto = CheckDuplicateAssetDto(); // CheckDuplicateAssetDto |
-final key = key_example; // String |
-
-try {
- final result = api_instance.checkDuplicateAsset(checkDuplicateAssetDto, key);
- print(result);
-} catch (e) {
- print('Exception when calling AssetApi->checkDuplicateAsset: $e\n');
-}
-```
-
-### Parameters
-
-Name | Type | Description | Notes
-------------- | ------------- | ------------- | -------------
- **checkDuplicateAssetDto** | [**CheckDuplicateAssetDto**](CheckDuplicateAssetDto.md)| |
- **key** | **String**| | [optional]
-
-### Return type
-
-[**CheckDuplicateAssetResponseDto**](CheckDuplicateAssetResponseDto.md)
-
-### Authorization
-
-[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
-
-### HTTP request headers
-
- - **Content-Type**: application/json
- - **Accept**: application/json
-
-[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
-
# **checkExistingAssets**
> CheckExistingAssetsResponseDto checkExistingAssets(checkExistingAssetsDto)
@@ -729,7 +669,7 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getByTimeBucket**
-> List getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key)
+> List getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, key)
@@ -760,10 +700,11 @@ final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final isArchived = true; // bool |
final isFavorite = true; // bool |
final isTrashed = true; // bool |
+final withStacked = true; // bool |
final key = key_example; // String |
try {
- final result = api_instance.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key);
+ final result = api_instance.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, key);
print(result);
} catch (e) {
print('Exception when calling AssetApi->getByTimeBucket: $e\n');
@@ -782,6 +723,7 @@ Name | Type | Description | Notes
**isArchived** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional]
**isTrashed** | **bool**| | [optional]
+ **withStacked** | **bool**| | [optional]
**key** | **String**| | [optional]
### Return type
@@ -1132,7 +1074,7 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getTimeBuckets**
-> List getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key)
+> List getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, key)
@@ -1162,10 +1104,11 @@ final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final isArchived = true; // bool |
final isFavorite = true; // bool |
final isTrashed = true; // bool |
+final withStacked = true; // bool |
final key = key_example; // String |
try {
- final result = api_instance.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key);
+ final result = api_instance.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, key);
print(result);
} catch (e) {
print('Exception when calling AssetApi->getTimeBuckets: $e\n');
@@ -1183,6 +1126,7 @@ Name | Type | Description | Notes
**isArchived** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional]
**isTrashed** | **bool**| | [optional]
+ **withStacked** | **bool**| | [optional]
**key** | **String**| | [optional]
### Return type
diff --git a/mobile/openapi/doc/PersonApi.md b/mobile/openapi/doc/PersonApi.md
index 0f6f2030c..73a35f0f3 100644
--- a/mobile/openapi/doc/PersonApi.md
+++ b/mobile/openapi/doc/PersonApi.md
@@ -12,6 +12,7 @@ Method | HTTP request | Description
[**getAllPeople**](PersonApi.md#getallpeople) | **GET** /person |
[**getPerson**](PersonApi.md#getperson) | **GET** /person/{id} |
[**getPersonAssets**](PersonApi.md#getpersonassets) | **GET** /person/{id}/assets |
+[**getPersonStatistics**](PersonApi.md#getpersonstatistics) | **GET** /person/{id}/statistics |
[**getPersonThumbnail**](PersonApi.md#getpersonthumbnail) | **GET** /person/{id}/thumbnail |
[**mergePerson**](PersonApi.md#mergeperson) | **POST** /person/{id}/merge |
[**updatePeople**](PersonApi.md#updatepeople) | **PUT** /person |
@@ -183,6 +184,61 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+# **getPersonStatistics**
+> PersonStatisticsResponseDto getPersonStatistics(id)
+
+
+
+### Example
+```dart
+import 'package:openapi/api.dart';
+// TODO Configure API key authorization: cookie
+//defaultApiClient.getAuthentication('cookie').apiKey = 'YOUR_API_KEY';
+// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
+//defaultApiClient.getAuthentication('cookie').apiKeyPrefix = 'Bearer';
+// TODO Configure API key authorization: api_key
+//defaultApiClient.getAuthentication('api_key').apiKey = 'YOUR_API_KEY';
+// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
+//defaultApiClient.getAuthentication('api_key').apiKeyPrefix = 'Bearer';
+// TODO Configure HTTP Bearer authorization: bearer
+// Case 1. Use String Token
+//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
+// Case 2. Use Function which generate token.
+// String yourTokenGeneratorFunction() { ... }
+//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction);
+
+final api_instance = PersonApi();
+final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
+
+try {
+ final result = api_instance.getPersonStatistics(id);
+ print(result);
+} catch (e) {
+ print('Exception when calling PersonApi->getPersonStatistics: $e\n');
+}
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **String**| |
+
+### Return type
+
+[**PersonStatisticsResponseDto**](PersonStatisticsResponseDto.md)
+
+### Authorization
+
+[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
# **getPersonThumbnail**
> MultipartFile getPersonThumbnail(id)
diff --git a/mobile/openapi/doc/CheckDuplicateAssetDto.md b/mobile/openapi/doc/PersonStatisticsResponseDto.md
similarity index 76%
rename from mobile/openapi/doc/CheckDuplicateAssetDto.md
rename to mobile/openapi/doc/PersonStatisticsResponseDto.md
index 259cdfc81..2dda35b11 100644
--- a/mobile/openapi/doc/CheckDuplicateAssetDto.md
+++ b/mobile/openapi/doc/PersonStatisticsResponseDto.md
@@ -1,4 +1,4 @@
-# openapi.model.CheckDuplicateAssetDto
+# openapi.model.PersonStatisticsResponseDto
## Load the model package
```dart
@@ -8,8 +8,7 @@ import 'package:openapi/api.dart';
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
-**deviceAssetId** | **String** | |
-**deviceId** | **String** | |
+**assets** | **int** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
diff --git a/mobile/openapi/doc/SearchApi.md b/mobile/openapi/doc/SearchApi.md
index 6bb2c0e93..b5c6c9de8 100644
--- a/mobile/openapi/doc/SearchApi.md
+++ b/mobile/openapi/doc/SearchApi.md
@@ -151,7 +151,7 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **searchPerson**
-> List searchPerson(name)
+> List searchPerson(name, withHidden)
@@ -175,9 +175,10 @@ import 'package:openapi/api.dart';
final api_instance = SearchApi();
final name = name_example; // String |
+final withHidden = true; // bool |
try {
- final result = api_instance.searchPerson(name);
+ final result = api_instance.searchPerson(name, withHidden);
print(result);
} catch (e) {
print('Exception when calling SearchApi->searchPerson: $e\n');
@@ -189,6 +190,7 @@ try {
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**name** | **String**| |
+ **withHidden** | **bool**| | [optional]
### Return type
diff --git a/mobile/openapi/doc/ServerInfoApi.md b/mobile/openapi/doc/ServerInfoApi.md
index c196bf108..64a373cc0 100644
--- a/mobile/openapi/doc/ServerInfoApi.md
+++ b/mobile/openapi/doc/ServerInfoApi.md
@@ -15,6 +15,7 @@ Method | HTTP request | Description
[**getServerVersion**](ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
[**getStats**](ServerInfoApi.md#getstats) | **GET** /server-info/stats |
[**getSupportedMediaTypes**](ServerInfoApi.md#getsupportedmediatypes) | **GET** /server-info/media-types |
+[**getTheme**](ServerInfoApi.md#gettheme) | **GET** /server-info/theme |
[**pingServer**](ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
@@ -268,6 +269,43 @@ No authorization required
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+# **getTheme**
+> ServerThemeDto getTheme()
+
+
+
+### Example
+```dart
+import 'package:openapi/api.dart';
+
+final api_instance = ServerInfoApi();
+
+try {
+ final result = api_instance.getTheme();
+ print(result);
+} catch (e) {
+ print('Exception when calling ServerInfoApi->getTheme: $e\n');
+}
+```
+
+### Parameters
+This endpoint does not need any parameter.
+
+### Return type
+
+[**ServerThemeDto**](ServerThemeDto.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
# **pingServer**
> ServerPingResponse pingServer()
diff --git a/mobile/openapi/doc/UserCountResponseDto.md b/mobile/openapi/doc/ServerThemeDto.md
similarity index 83%
rename from mobile/openapi/doc/UserCountResponseDto.md
rename to mobile/openapi/doc/ServerThemeDto.md
index 65dab5686..506f60b79 100644
--- a/mobile/openapi/doc/UserCountResponseDto.md
+++ b/mobile/openapi/doc/ServerThemeDto.md
@@ -1,4 +1,4 @@
-# openapi.model.UserCountResponseDto
+# openapi.model.ServerThemeDto
## Load the model package
```dart
@@ -8,7 +8,7 @@ import 'package:openapi/api.dart';
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
-**userCount** | **int** | |
+**customCss** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
diff --git a/mobile/openapi/doc/SharedLinkApi.md b/mobile/openapi/doc/SharedLinkApi.md
index 34b8e1e71..873ffc582 100644
--- a/mobile/openapi/doc/SharedLinkApi.md
+++ b/mobile/openapi/doc/SharedLinkApi.md
@@ -185,7 +185,7 @@ This endpoint does not need any parameter.
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getMySharedLink**
-> SharedLinkResponseDto getMySharedLink(key)
+> SharedLinkResponseDto getMySharedLink(password, token, key)
@@ -208,10 +208,12 @@ import 'package:openapi/api.dart';
//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = SharedLinkApi();
+final password = password; // String |
+final token = token_example; // String |
final key = key_example; // String |
try {
- final result = api_instance.getMySharedLink(key);
+ final result = api_instance.getMySharedLink(password, token, key);
print(result);
} catch (e) {
print('Exception when calling SharedLinkApi->getMySharedLink: $e\n');
@@ -222,6 +224,8 @@ try {
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
+ **password** | **String**| | [optional]
+ **token** | **String**| | [optional]
**key** | **String**| | [optional]
### Return type
diff --git a/mobile/openapi/doc/SharedLinkCreateDto.md b/mobile/openapi/doc/SharedLinkCreateDto.md
index 852610ae1..8f845dfa4 100644
--- a/mobile/openapi/doc/SharedLinkCreateDto.md
+++ b/mobile/openapi/doc/SharedLinkCreateDto.md
@@ -14,6 +14,7 @@ Name | Type | Description | Notes
**assetIds** | **List** | | [optional] [default to const []]
**description** | **String** | | [optional]
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
+**password** | **String** | | [optional]
**showMetadata** | **bool** | | [optional] [default to true]
**type** | [**SharedLinkType**](SharedLinkType.md) | |
diff --git a/mobile/openapi/doc/SharedLinkEditDto.md b/mobile/openapi/doc/SharedLinkEditDto.md
index ccd0d3b54..36af31b47 100644
--- a/mobile/openapi/doc/SharedLinkEditDto.md
+++ b/mobile/openapi/doc/SharedLinkEditDto.md
@@ -13,6 +13,7 @@ Name | Type | Description | Notes
**changeExpiryTime** | **bool** | Few clients cannot send null to set the expiryTime to never. Setting this flag and not sending expiryAt is considered as null instead. Clients that can send null values can ignore this. | [optional]
**description** | **String** | | [optional]
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
+**password** | **String** | | [optional]
**showMetadata** | **bool** | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
diff --git a/mobile/openapi/doc/SharedLinkResponseDto.md b/mobile/openapi/doc/SharedLinkResponseDto.md
index 24b76c86c..89f7c7ac8 100644
--- a/mobile/openapi/doc/SharedLinkResponseDto.md
+++ b/mobile/openapi/doc/SharedLinkResponseDto.md
@@ -17,7 +17,9 @@ Name | Type | Description | Notes
**expiresAt** | [**DateTime**](DateTime.md) | |
**id** | **String** | |
**key** | **String** | |
+**password** | **String** | |
**showMetadata** | **bool** | |
+**token** | **String** | | [optional]
**type** | [**SharedLinkType**](SharedLinkType.md) | |
**userId** | **String** | |
diff --git a/mobile/openapi/doc/SystemConfigDto.md b/mobile/openapi/doc/SystemConfigDto.md
index a5b8db773..98a626640 100644
--- a/mobile/openapi/doc/SystemConfigDto.md
+++ b/mobile/openapi/doc/SystemConfigDto.md
@@ -12,10 +12,12 @@ Name | Type | Description | Notes
**job** | [**SystemConfigJobDto**](SystemConfigJobDto.md) | |
**machineLearning** | [**SystemConfigMachineLearningDto**](SystemConfigMachineLearningDto.md) | |
**map** | [**SystemConfigMapDto**](SystemConfigMapDto.md) | |
+**newVersionCheck** | [**SystemConfigNewVersionCheckDto**](SystemConfigNewVersionCheckDto.md) | |
**oauth** | [**SystemConfigOAuthDto**](SystemConfigOAuthDto.md) | |
**passwordLogin** | [**SystemConfigPasswordLoginDto**](SystemConfigPasswordLoginDto.md) | |
**reverseGeocoding** | [**SystemConfigReverseGeocodingDto**](SystemConfigReverseGeocodingDto.md) | |
**storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) | |
+**theme** | [**SystemConfigThemeDto**](SystemConfigThemeDto.md) | |
**thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) | |
**trash** | [**SystemConfigTrashDto**](SystemConfigTrashDto.md) | |
diff --git a/mobile/openapi/doc/CheckDuplicateAssetResponseDto.md b/mobile/openapi/doc/SystemConfigNewVersionCheckDto.md
similarity index 75%
rename from mobile/openapi/doc/CheckDuplicateAssetResponseDto.md
rename to mobile/openapi/doc/SystemConfigNewVersionCheckDto.md
index 0f49eb22a..18b922de2 100644
--- a/mobile/openapi/doc/CheckDuplicateAssetResponseDto.md
+++ b/mobile/openapi/doc/SystemConfigNewVersionCheckDto.md
@@ -1,4 +1,4 @@
-# openapi.model.CheckDuplicateAssetResponseDto
+# openapi.model.SystemConfigNewVersionCheckDto
## Load the model package
```dart
@@ -8,8 +8,7 @@ import 'package:openapi/api.dart';
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
-**id** | **String** | | [optional]
-**isExist** | **bool** | |
+**enabled** | **bool** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
diff --git a/mobile/openapi/doc/SystemConfigThemeDto.md b/mobile/openapi/doc/SystemConfigThemeDto.md
new file mode 100644
index 000000000..bcdbb690c
--- /dev/null
+++ b/mobile/openapi/doc/SystemConfigThemeDto.md
@@ -0,0 +1,15 @@
+# openapi.model.SystemConfigThemeDto
+
+## Load the model package
+```dart
+import 'package:openapi/api.dart';
+```
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**customCss** | **String** | |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/mobile/openapi/doc/UserApi.md b/mobile/openapi/doc/UserApi.md
index 165a54335..fb88d53bd 100644
--- a/mobile/openapi/doc/UserApi.md
+++ b/mobile/openapi/doc/UserApi.md
@@ -16,7 +16,6 @@ Method | HTTP request | Description
[**getMyUserInfo**](UserApi.md#getmyuserinfo) | **GET** /user/me |
[**getProfileImage**](UserApi.md#getprofileimage) | **GET** /user/profile-image/{id} |
[**getUserById**](UserApi.md#getuserbyid) | **GET** /user/info/{id} |
-[**getUserCount**](UserApi.md#getusercount) | **GET** /user/count |
[**restoreUser**](UserApi.md#restoreuser) | **POST** /user/{id}/restore |
[**updateUser**](UserApi.md#updateuser) | **PUT** /user |
@@ -402,61 +401,6 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
-# **getUserCount**
-> UserCountResponseDto getUserCount(admin)
-
-
-
-### Example
-```dart
-import 'package:openapi/api.dart';
-// TODO Configure API key authorization: cookie
-//defaultApiClient.getAuthentication('cookie').apiKey = 'YOUR_API_KEY';
-// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
-//defaultApiClient.getAuthentication('cookie').apiKeyPrefix = 'Bearer';
-// TODO Configure API key authorization: api_key
-//defaultApiClient.getAuthentication('api_key').apiKey = 'YOUR_API_KEY';
-// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
-//defaultApiClient.getAuthentication('api_key').apiKeyPrefix = 'Bearer';
-// TODO Configure HTTP Bearer authorization: bearer
-// Case 1. Use String Token
-//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
-// Case 2. Use Function which generate token.
-// String yourTokenGeneratorFunction() { ... }
-//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction);
-
-final api_instance = UserApi();
-final admin = true; // bool |
-
-try {
- final result = api_instance.getUserCount(admin);
- print(result);
-} catch (e) {
- print('Exception when calling UserApi->getUserCount: $e\n');
-}
-```
-
-### Parameters
-
-Name | Type | Description | Notes
-------------- | ------------- | ------------- | -------------
- **admin** | **bool**| | [optional] [default to false]
-
-### Return type
-
-[**UserCountResponseDto**](UserCountResponseDto.md)
-
-### Authorization
-
-[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
-
-### HTTP request headers
-
- - **Content-Type**: Not defined
- - **Accept**: application/json
-
-[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
-
# **restoreUser**
> UserResponseDto restoreUser(id)
diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart
index e72c1da16..7a621b7f4 100644
--- a/mobile/openapi/lib/api.dart
+++ b/mobile/openapi/lib/api.dart
@@ -77,8 +77,6 @@ part 'model/clip_config.dart';
part 'model/clip_mode.dart';
part 'model/cq_mode.dart';
part 'model/change_password_dto.dart';
-part 'model/check_duplicate_asset_dto.dart';
-part 'model/check_duplicate_asset_response_dto.dart';
part 'model/check_existing_assets_dto.dart';
part 'model/check_existing_assets_response_dto.dart';
part 'model/cities_file.dart';
@@ -128,6 +126,7 @@ part 'model/people_response_dto.dart';
part 'model/people_update_dto.dart';
part 'model/people_update_item.dart';
part 'model/person_response_dto.dart';
+part 'model/person_statistics_response_dto.dart';
part 'model/person_update_dto.dart';
part 'model/queue_status_dto.dart';
part 'model/recognition_config.dart';
@@ -146,6 +145,7 @@ part 'model/server_info_response_dto.dart';
part 'model/server_media_types_response_dto.dart';
part 'model/server_ping_response.dart';
part 'model/server_stats_response_dto.dart';
+part 'model/server_theme_dto.dart';
part 'model/server_version_response_dto.dart';
part 'model/shared_link_create_dto.dart';
part 'model/shared_link_edit_dto.dart';
@@ -158,11 +158,13 @@ part 'model/system_config_f_fmpeg_dto.dart';
part 'model/system_config_job_dto.dart';
part 'model/system_config_machine_learning_dto.dart';
part 'model/system_config_map_dto.dart';
+part 'model/system_config_new_version_check_dto.dart';
part 'model/system_config_o_auth_dto.dart';
part 'model/system_config_password_login_dto.dart';
part 'model/system_config_reverse_geocoding_dto.dart';
part 'model/system_config_storage_template_dto.dart';
part 'model/system_config_template_storage_option_dto.dart';
+part 'model/system_config_theme_dto.dart';
part 'model/system_config_thumbnail_dto.dart';
part 'model/system_config_trash_dto.dart';
part 'model/tag_response_dto.dart';
@@ -180,7 +182,6 @@ part 'model/update_stack_parent_dto.dart';
part 'model/update_tag_dto.dart';
part 'model/update_user_dto.dart';
part 'model/usage_by_user_dto.dart';
-part 'model/user_count_response_dto.dart';
part 'model/user_response_dto.dart';
part 'model/validate_access_token_response_dto.dart';
part 'model/video_codec.dart';
diff --git a/mobile/openapi/lib/api/asset_api.dart b/mobile/openapi/lib/api/asset_api.dart
index 5935f5676..866d11a37 100644
--- a/mobile/openapi/lib/api/asset_api.dart
+++ b/mobile/openapi/lib/api/asset_api.dart
@@ -68,66 +68,6 @@ class AssetApi {
return null;
}
- /// Check duplicated asset before uploading - for Web upload used
- ///
- /// Note: This method returns the HTTP [Response].
- ///
- /// Parameters:
- ///
- /// * [CheckDuplicateAssetDto] checkDuplicateAssetDto (required):
- ///
- /// * [String] key:
- Future checkDuplicateAssetWithHttpInfo(CheckDuplicateAssetDto checkDuplicateAssetDto, { String? key, }) async {
- // ignore: prefer_const_declarations
- final path = r'/asset/check';
-
- // ignore: prefer_final_locals
- Object? postBody = checkDuplicateAssetDto;
-
- final queryParams = [];
- final headerParams = {};
- final formParams = {};
-
- if (key != null) {
- queryParams.addAll(_queryParams('', 'key', key));
- }
-
- const contentTypes = ['application/json'];
-
-
- return apiClient.invokeAPI(
- path,
- 'POST',
- queryParams,
- postBody,
- headerParams,
- formParams,
- contentTypes.isEmpty ? null : contentTypes.first,
- );
- }
-
- /// Check duplicated asset before uploading - for Web upload used
- ///
- /// Parameters:
- ///
- /// * [CheckDuplicateAssetDto] checkDuplicateAssetDto (required):
- ///
- /// * [String] key:
- Future checkDuplicateAsset(CheckDuplicateAssetDto checkDuplicateAssetDto, { String? key, }) async {
- final response = await checkDuplicateAssetWithHttpInfo(checkDuplicateAssetDto, key: key, );
- if (response.statusCode >= HttpStatus.badRequest) {
- throw ApiException(response.statusCode, await _decodeBodyBytes(response));
- }
- // When a remote server returns no body with a status of 204, we shall not decode it.
- // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
- // FormatException when trying to decode an empty string.
- if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
- return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'CheckDuplicateAssetResponseDto',) as CheckDuplicateAssetResponseDto;
-
- }
- return null;
- }
-
/// Checks if multiple assets exist on the server and returns all existing - used by background backup
///
/// Note: This method returns the HTTP [Response].
@@ -712,8 +652,10 @@ class AssetApi {
///
/// * [bool] isTrashed:
///
+ /// * [bool] withStacked:
+ ///
/// * [String] key:
- Future getByTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async {
+ Future getByTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, bool? withStacked, String? key, }) async {
// ignore: prefer_const_declarations
final path = r'/asset/time-bucket';
@@ -742,6 +684,9 @@ class AssetApi {
}
if (isTrashed != null) {
queryParams.addAll(_queryParams('', 'isTrashed', isTrashed));
+ }
+ if (withStacked != null) {
+ queryParams.addAll(_queryParams('', 'withStacked', withStacked));
}
queryParams.addAll(_queryParams('', 'timeBucket', timeBucket));
if (key != null) {
@@ -780,9 +725,11 @@ class AssetApi {
///
/// * [bool] isTrashed:
///
+ /// * [bool] withStacked:
+ ///
/// * [String] key:
- Future?> getByTimeBucket(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async {
- final response = await getByTimeBucketWithHttpInfo(size, timeBucket, userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, );
+ Future?> getByTimeBucket(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, bool? withStacked, String? key, }) async {
+ final response = await getByTimeBucketWithHttpInfo(size, timeBucket, userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, withStacked: withStacked, key: key, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -1145,8 +1092,10 @@ class AssetApi {
///
/// * [bool] isTrashed:
///
+ /// * [bool] withStacked:
+ ///
/// * [String] key:
- Future getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async {
+ Future getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, bool? withStacked, String? key, }) async {
// ignore: prefer_const_declarations
final path = r'/asset/time-buckets';
@@ -1176,6 +1125,9 @@ class AssetApi {
if (isTrashed != null) {
queryParams.addAll(_queryParams('', 'isTrashed', isTrashed));
}
+ if (withStacked != null) {
+ queryParams.addAll(_queryParams('', 'withStacked', withStacked));
+ }
if (key != null) {
queryParams.addAll(_queryParams('', 'key', key));
}
@@ -1210,9 +1162,11 @@ class AssetApi {
///
/// * [bool] isTrashed:
///
+ /// * [bool] withStacked:
+ ///
/// * [String] key:
- Future?> getTimeBuckets(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async {
- final response = await getTimeBucketsWithHttpInfo(size, userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, );
+ Future?> getTimeBuckets(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, bool? withStacked, String? key, }) async {
+ final response = await getTimeBucketsWithHttpInfo(size, userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, withStacked: withStacked, key: key, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
diff --git a/mobile/openapi/lib/api/person_api.dart b/mobile/openapi/lib/api/person_api.dart
index 0d5b60182..e4ab011a6 100644
--- a/mobile/openapi/lib/api/person_api.dart
+++ b/mobile/openapi/lib/api/person_api.dart
@@ -166,6 +166,54 @@ class PersonApi {
return null;
}
+ /// Performs an HTTP 'GET /person/{id}/statistics' operation and returns the [Response].
+ /// Parameters:
+ ///
+ /// * [String] id (required):
+ Future getPersonStatisticsWithHttpInfo(String id,) async {
+ // ignore: prefer_const_declarations
+ final path = r'/person/{id}/statistics'
+ .replaceAll('{id}', id);
+
+ // ignore: prefer_final_locals
+ Object? postBody;
+
+ final queryParams = [];
+ final headerParams = {};
+ final formParams = {};
+
+ const contentTypes = [];
+
+
+ return apiClient.invokeAPI(
+ path,
+ 'GET',
+ queryParams,
+ postBody,
+ headerParams,
+ formParams,
+ contentTypes.isEmpty ? null : contentTypes.first,
+ );
+ }
+
+ /// Parameters:
+ ///
+ /// * [String] id (required):
+ Future getPersonStatistics(String id,) async {
+ final response = await getPersonStatisticsWithHttpInfo(id,);
+ if (response.statusCode >= HttpStatus.badRequest) {
+ throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+ }
+ // When a remote server returns no body with a status of 204, we shall not decode it.
+ // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+ // FormatException when trying to decode an empty string.
+ if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+ return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'PersonStatisticsResponseDto',) as PersonStatisticsResponseDto;
+
+ }
+ return null;
+ }
+
/// Performs an HTTP 'GET /person/{id}/thumbnail' operation and returns the [Response].
/// Parameters:
///
diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart
index 870c5dcd3..7c01d5e9b 100644
--- a/mobile/openapi/lib/api/search_api.dart
+++ b/mobile/openapi/lib/api/search_api.dart
@@ -220,7 +220,9 @@ class SearchApi {
/// Parameters:
///
/// * [String] name (required):
- Future searchPersonWithHttpInfo(String name,) async {
+ ///
+ /// * [bool] withHidden:
+ Future searchPersonWithHttpInfo(String name, { bool? withHidden, }) async {
// ignore: prefer_const_declarations
final path = r'/search/person';
@@ -232,6 +234,9 @@ class SearchApi {
final formParams = {};
queryParams.addAll(_queryParams('', 'name', name));
+ if (withHidden != null) {
+ queryParams.addAll(_queryParams('', 'withHidden', withHidden));
+ }
const contentTypes = [];
@@ -250,8 +255,10 @@ class SearchApi {
/// Parameters:
///
/// * [String] name (required):
- Future?> searchPerson(String name,) async {
- final response = await searchPersonWithHttpInfo(name,);
+ ///
+ /// * [bool] withHidden:
+ Future?> searchPerson(String name, { bool? withHidden, }) async {
+ final response = await searchPersonWithHttpInfo(name, withHidden: withHidden, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
diff --git a/mobile/openapi/lib/api/server_info_api.dart b/mobile/openapi/lib/api/server_info_api.dart
index 51bcbfa4b..b8ea50ba8 100644
--- a/mobile/openapi/lib/api/server_info_api.dart
+++ b/mobile/openapi/lib/api/server_info_api.dart
@@ -262,6 +262,47 @@ class ServerInfoApi {
return null;
}
+ /// Performs an HTTP 'GET /server-info/theme' operation and returns the [Response].
+ Future getThemeWithHttpInfo() async {
+ // ignore: prefer_const_declarations
+ final path = r'/server-info/theme';
+
+ // ignore: prefer_final_locals
+ Object? postBody;
+
+ final queryParams = [];
+ final headerParams = {};
+ final formParams = {};
+
+ const contentTypes = [];
+
+
+ return apiClient.invokeAPI(
+ path,
+ 'GET',
+ queryParams,
+ postBody,
+ headerParams,
+ formParams,
+ contentTypes.isEmpty ? null : contentTypes.first,
+ );
+ }
+
+ Future getTheme() async {
+ final response = await getThemeWithHttpInfo();
+ if (response.statusCode >= HttpStatus.badRequest) {
+ throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+ }
+ // When a remote server returns no body with a status of 204, we shall not decode it.
+ // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+ // FormatException when trying to decode an empty string.
+ if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+ return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ServerThemeDto',) as ServerThemeDto;
+
+ }
+ return null;
+ }
+
/// Performs an HTTP 'GET /server-info/ping' operation and returns the [Response].
Future pingServerWithHttpInfo() async {
// ignore: prefer_const_declarations
diff --git a/mobile/openapi/lib/api/shared_link_api.dart b/mobile/openapi/lib/api/shared_link_api.dart
index 029f7bc8a..661da45eb 100644
--- a/mobile/openapi/lib/api/shared_link_api.dart
+++ b/mobile/openapi/lib/api/shared_link_api.dart
@@ -173,8 +173,12 @@ class SharedLinkApi {
/// Performs an HTTP 'GET /shared-link/me' operation and returns the [Response].
/// Parameters:
///
+ /// * [String] password:
+ ///
+ /// * [String] token:
+ ///
/// * [String] key:
- Future getMySharedLinkWithHttpInfo({ String? key, }) async {
+ Future getMySharedLinkWithHttpInfo({ String? password, String? token, String? key, }) async {
// ignore: prefer_const_declarations
final path = r'/shared-link/me';
@@ -185,6 +189,12 @@ class SharedLinkApi {
final headerParams = {};
final formParams = {};
+ if (password != null) {
+ queryParams.addAll(_queryParams('', 'password', password));
+ }
+ if (token != null) {
+ queryParams.addAll(_queryParams('', 'token', token));
+ }
if (key != null) {
queryParams.addAll(_queryParams('', 'key', key));
}
@@ -205,9 +215,13 @@ class SharedLinkApi {
/// Parameters:
///
+ /// * [String] password:
+ ///
+ /// * [String] token:
+ ///
/// * [String] key:
- Future getMySharedLink({ String? key, }) async {
- final response = await getMySharedLinkWithHttpInfo( key: key, );
+ Future getMySharedLink({ String? password, String? token, String? key, }) async {
+ final response = await getMySharedLinkWithHttpInfo( password: password, token: token, key: key, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
diff --git a/mobile/openapi/lib/api/user_api.dart b/mobile/openapi/lib/api/user_api.dart
index 110528d40..23f25492c 100644
--- a/mobile/openapi/lib/api/user_api.dart
+++ b/mobile/openapi/lib/api/user_api.dart
@@ -357,57 +357,6 @@ class UserApi {
return null;
}
- /// Performs an HTTP 'GET /user/count' operation and returns the [Response].
- /// Parameters:
- ///
- /// * [bool] admin:
- Future getUserCountWithHttpInfo({ bool? admin, }) async {
- // ignore: prefer_const_declarations
- final path = r'/user/count';
-
- // ignore: prefer_final_locals
- Object? postBody;
-
- final queryParams = [];
- final headerParams = {};
- final formParams = {};
-
- if (admin != null) {
- queryParams.addAll(_queryParams('', 'admin', admin));
- }
-
- const contentTypes = [];
-
-
- return apiClient.invokeAPI(
- path,
- 'GET',
- queryParams,
- postBody,
- headerParams,
- formParams,
- contentTypes.isEmpty ? null : contentTypes.first,
- );
- }
-
- /// Parameters:
- ///
- /// * [bool] admin:
- Future getUserCount({ bool? admin, }) async {
- final response = await getUserCountWithHttpInfo( admin: admin, );
- if (response.statusCode >= HttpStatus.badRequest) {
- throw ApiException(response.statusCode, await _decodeBodyBytes(response));
- }
- // When a remote server returns no body with a status of 204, we shall not decode it.
- // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
- // FormatException when trying to decode an empty string.
- if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
- return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserCountResponseDto',) as UserCountResponseDto;
-
- }
- return null;
- }
-
/// Performs an HTTP 'POST /user/{id}/restore' operation and returns the [Response].
/// Parameters:
///
diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart
index 34b9a431d..fa16b0d60 100644
--- a/mobile/openapi/lib/api_client.dart
+++ b/mobile/openapi/lib/api_client.dart
@@ -245,10 +245,6 @@ class ApiClient {
return CQModeTypeTransformer().decode(value);
case 'ChangePasswordDto':
return ChangePasswordDto.fromJson(value);
- case 'CheckDuplicateAssetDto':
- return CheckDuplicateAssetDto.fromJson(value);
- case 'CheckDuplicateAssetResponseDto':
- return CheckDuplicateAssetResponseDto.fromJson(value);
case 'CheckExistingAssetsDto':
return CheckExistingAssetsDto.fromJson(value);
case 'CheckExistingAssetsResponseDto':
@@ -347,6 +343,8 @@ class ApiClient {
return PeopleUpdateItem.fromJson(value);
case 'PersonResponseDto':
return PersonResponseDto.fromJson(value);
+ case 'PersonStatisticsResponseDto':
+ return PersonStatisticsResponseDto.fromJson(value);
case 'PersonUpdateDto':
return PersonUpdateDto.fromJson(value);
case 'QueueStatusDto':
@@ -383,6 +381,8 @@ class ApiClient {
return ServerPingResponse.fromJson(value);
case 'ServerStatsResponseDto':
return ServerStatsResponseDto.fromJson(value);
+ case 'ServerThemeDto':
+ return ServerThemeDto.fromJson(value);
case 'ServerVersionResponseDto':
return ServerVersionResponseDto.fromJson(value);
case 'SharedLinkCreateDto':
@@ -407,6 +407,8 @@ class ApiClient {
return SystemConfigMachineLearningDto.fromJson(value);
case 'SystemConfigMapDto':
return SystemConfigMapDto.fromJson(value);
+ case 'SystemConfigNewVersionCheckDto':
+ return SystemConfigNewVersionCheckDto.fromJson(value);
case 'SystemConfigOAuthDto':
return SystemConfigOAuthDto.fromJson(value);
case 'SystemConfigPasswordLoginDto':
@@ -417,6 +419,8 @@ class ApiClient {
return SystemConfigStorageTemplateDto.fromJson(value);
case 'SystemConfigTemplateStorageOptionDto':
return SystemConfigTemplateStorageOptionDto.fromJson(value);
+ case 'SystemConfigThemeDto':
+ return SystemConfigThemeDto.fromJson(value);
case 'SystemConfigThumbnailDto':
return SystemConfigThumbnailDto.fromJson(value);
case 'SystemConfigTrashDto':
@@ -451,8 +455,6 @@ class ApiClient {
return UpdateUserDto.fromJson(value);
case 'UsageByUserDto':
return UsageByUserDto.fromJson(value);
- case 'UserCountResponseDto':
- return UserCountResponseDto.fromJson(value);
case 'UserResponseDto':
return UserResponseDto.fromJson(value);
case 'ValidateAccessTokenResponseDto':
diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart
index e580ca5a2..746118681 100644
--- a/mobile/openapi/lib/model/asset_response_dto.dart
+++ b/mobile/openapi/lib/model/asset_response_dto.dart
@@ -118,7 +118,7 @@ class AssetResponseDto {
List stack;
- int stackCount;
+ int? stackCount;
String? stackParentId;
@@ -194,7 +194,7 @@ class AssetResponseDto {
(resized.hashCode) +
(smartInfo == null ? 0 : smartInfo!.hashCode) +
(stack.hashCode) +
- (stackCount.hashCode) +
+ (stackCount == null ? 0 : stackCount!.hashCode) +
(stackParentId == null ? 0 : stackParentId!.hashCode) +
(tags.hashCode) +
(thumbhash == null ? 0 : thumbhash!.hashCode) +
@@ -248,7 +248,11 @@ class AssetResponseDto {
// json[r'smartInfo'] = null;
}
json[r'stack'] = this.stack;
+ if (this.stackCount != null) {
json[r'stackCount'] = this.stackCount;
+ } else {
+ // json[r'stackCount'] = null;
+ }
if (this.stackParentId != null) {
json[r'stackParentId'] = this.stackParentId;
} else {
@@ -299,7 +303,7 @@ class AssetResponseDto {
resized: mapValueOfType(json, r'resized')!,
smartInfo: SmartInfoResponseDto.fromJson(json[r'smartInfo']),
stack: AssetResponseDto.listFromJson(json[r'stack']),
- stackCount: mapValueOfType(json, r'stackCount')!,
+ stackCount: mapValueOfType(json, r'stackCount'),
stackParentId: mapValueOfType(json, r'stackParentId'),
tags: TagResponseDto.listFromJson(json[r'tags']),
thumbhash: mapValueOfType(json, r'thumbhash'),
diff --git a/mobile/openapi/lib/model/check_duplicate_asset_dto.dart b/mobile/openapi/lib/model/check_duplicate_asset_dto.dart
deleted file mode 100644
index 3d2a3cd57..000000000
--- a/mobile/openapi/lib/model/check_duplicate_asset_dto.dart
+++ /dev/null
@@ -1,106 +0,0 @@
-//
-// AUTO-GENERATED FILE, DO NOT MODIFY!
-//
-// @dart=2.12
-
-// ignore_for_file: unused_element, unused_import
-// ignore_for_file: always_put_required_named_parameters_first
-// ignore_for_file: constant_identifier_names
-// ignore_for_file: lines_longer_than_80_chars
-
-part of openapi.api;
-
-class CheckDuplicateAssetDto {
- /// Returns a new [CheckDuplicateAssetDto] instance.
- CheckDuplicateAssetDto({
- required this.deviceAssetId,
- required this.deviceId,
- });
-
- String deviceAssetId;
-
- String deviceId;
-
- @override
- bool operator ==(Object other) => identical(this, other) || other is CheckDuplicateAssetDto &&
- other.deviceAssetId == deviceAssetId &&
- other.deviceId == deviceId;
-
- @override
- int get hashCode =>
- // ignore: unnecessary_parenthesis
- (deviceAssetId.hashCode) +
- (deviceId.hashCode);
-
- @override
- String toString() => 'CheckDuplicateAssetDto[deviceAssetId=$deviceAssetId, deviceId=$deviceId]';
-
- Map toJson() {
- final json = {};
- json[r'deviceAssetId'] = this.deviceAssetId;
- json[r'deviceId'] = this.deviceId;
- return json;
- }
-
- /// Returns a new [CheckDuplicateAssetDto] instance and imports its values from
- /// [value] if it's a [Map], null otherwise.
- // ignore: prefer_constructors_over_static_methods
- static CheckDuplicateAssetDto? fromJson(dynamic value) {
- if (value is Map) {
- final json = value.cast();
-
- return CheckDuplicateAssetDto(
- deviceAssetId: mapValueOfType(json, r'deviceAssetId')!,
- deviceId: mapValueOfType(json, r'deviceId')!,
- );
- }
- return null;
- }
-
- static List listFromJson(dynamic json, {bool growable = false,}) {
- final result = [];
- if (json is List && json.isNotEmpty) {
- for (final row in json) {
- final value = CheckDuplicateAssetDto.fromJson(row);
- if (value != null) {
- result.add(value);
- }
- }
- }
- return result.toList(growable: growable);
- }
-
- static Map mapFromJson(dynamic json) {
- final map = {};
- if (json is Map && json.isNotEmpty) {
- json = json.cast(); // ignore: parameter_assignments
- for (final entry in json.entries) {
- final value = CheckDuplicateAssetDto.fromJson(entry.value);
- if (value != null) {
- map[entry.key] = value;
- }
- }
- }
- return map;
- }
-
- // maps a json object with a list of CheckDuplicateAssetDto-objects as value to a dart map
- static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
- final map = >{};
- if (json is Map && json.isNotEmpty) {
- // ignore: parameter_assignments
- json = json.cast();
- for (final entry in json.entries) {
- map[entry.key] = CheckDuplicateAssetDto.listFromJson(entry.value, growable: growable,);
- }
- }
- return map;
- }
-
- /// The list of required keys that must be present in a JSON.
- static const requiredKeys = {
- 'deviceAssetId',
- 'deviceId',
- };
-}
-
diff --git a/mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart b/mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart
deleted file mode 100644
index f16ba2e9e..000000000
--- a/mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart
+++ /dev/null
@@ -1,115 +0,0 @@
-//
-// AUTO-GENERATED FILE, DO NOT MODIFY!
-//
-// @dart=2.12
-
-// ignore_for_file: unused_element, unused_import
-// ignore_for_file: always_put_required_named_parameters_first
-// ignore_for_file: constant_identifier_names
-// ignore_for_file: lines_longer_than_80_chars
-
-part of openapi.api;
-
-class CheckDuplicateAssetResponseDto {
- /// Returns a new [CheckDuplicateAssetResponseDto] instance.
- CheckDuplicateAssetResponseDto({
- this.id,
- required this.isExist,
- });
-
- ///
- /// Please note: This property should have been non-nullable! Since the specification file
- /// does not include a default value (using the "default:" property), however, the generated
- /// source code must fall back to having a nullable type.
- /// Consider adding a "default:" property in the specification file to hide this note.
- ///
- String? id;
-
- bool isExist;
-
- @override
- bool operator ==(Object other) => identical(this, other) || other is CheckDuplicateAssetResponseDto &&
- other.id == id &&
- other.isExist == isExist;
-
- @override
- int get hashCode =>
- // ignore: unnecessary_parenthesis
- (id == null ? 0 : id!.hashCode) +
- (isExist.hashCode);
-
- @override
- String toString() => 'CheckDuplicateAssetResponseDto[id=$id, isExist=$isExist]';
-
- Map toJson() {
- final json = {};
- if (this.id != null) {
- json[r'id'] = this.id;
- } else {
- // json[r'id'] = null;
- }
- json[r'isExist'] = this.isExist;
- return json;
- }
-
- /// Returns a new [CheckDuplicateAssetResponseDto] instance and imports its values from
- /// [value] if it's a [Map], null otherwise.
- // ignore: prefer_constructors_over_static_methods
- static CheckDuplicateAssetResponseDto? fromJson(dynamic value) {
- if (value is Map) {
- final json = value.cast();
-
- return CheckDuplicateAssetResponseDto(
- id: mapValueOfType(json, r'id'),
- isExist: mapValueOfType(json, r'isExist')!,
- );
- }
- return null;
- }
-
- static List listFromJson(dynamic json, {bool growable = false,}) {
- final result = [];
- if (json is List && json.isNotEmpty) {
- for (final row in json) {
- final value = CheckDuplicateAssetResponseDto.fromJson(row);
- if (value != null) {
- result.add(value);
- }
- }
- }
- return result.toList(growable: growable);
- }
-
- static Map mapFromJson(dynamic json) {
- final map = {};
- if (json is Map && json.isNotEmpty) {
- json = json.cast(); // ignore: parameter_assignments
- for (final entry in json.entries) {
- final value = CheckDuplicateAssetResponseDto.fromJson(entry.value);
- if (value != null) {
- map[entry.key] = value;
- }
- }
- }
- return map;
- }
-
- // maps a json object with a list of CheckDuplicateAssetResponseDto-objects as value to a dart map
- static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
- final map = >{};
- if (json is Map && json.isNotEmpty) {
- // ignore: parameter_assignments
- json = json.cast();
- for (final entry in json.entries) {
- map[entry.key] = CheckDuplicateAssetResponseDto.listFromJson(entry.value, growable: growable,);
- }
- }
- return map;
- }
-
- /// The list of required keys that must be present in a JSON.
- static const requiredKeys = {
- 'isExist',
- };
-}
-
diff --git a/mobile/openapi/lib/model/person_statistics_response_dto.dart b/mobile/openapi/lib/model/person_statistics_response_dto.dart
new file mode 100644
index 000000000..b4fbef723
--- /dev/null
+++ b/mobile/openapi/lib/model/person_statistics_response_dto.dart
@@ -0,0 +1,98 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.12
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class PersonStatisticsResponseDto {
+ /// Returns a new [PersonStatisticsResponseDto] instance.
+ PersonStatisticsResponseDto({
+ required this.assets,
+ });
+
+ int assets;
+
+ @override
+ bool operator ==(Object other) => identical(this, other) || other is PersonStatisticsResponseDto &&
+ other.assets == assets;
+
+ @override
+ int get hashCode =>
+ // ignore: unnecessary_parenthesis
+ (assets.hashCode);
+
+ @override
+ String toString() => 'PersonStatisticsResponseDto[assets=$assets]';
+
+ Map toJson() {
+ final json = {};
+ json[r'assets'] = this.assets;
+ return json;
+ }
+
+ /// Returns a new [PersonStatisticsResponseDto] instance and imports its values from
+ /// [value] if it's a [Map], null otherwise.
+ // ignore: prefer_constructors_over_static_methods
+ static PersonStatisticsResponseDto? fromJson(dynamic value) {
+ if (value is Map) {
+ final json = value.cast();
+
+ return PersonStatisticsResponseDto(
+ assets: mapValueOfType(json, r'assets')!,
+ );
+ }
+ return null;
+ }
+
+ static List listFromJson(dynamic json, {bool growable = false,}) {
+ final result = [];
+ if (json is List && json.isNotEmpty) {
+ for (final row in json) {
+ final value = PersonStatisticsResponseDto.fromJson(row);
+ if (value != null) {
+ result.add(value);
+ }
+ }
+ }
+ return result.toList(growable: growable);
+ }
+
+ static Map mapFromJson(dynamic json) {
+ final map = {};
+ if (json is Map && json.isNotEmpty) {
+ json = json.cast(); // ignore: parameter_assignments
+ for (final entry in json.entries) {
+ final value = PersonStatisticsResponseDto.fromJson(entry.value);
+ if (value != null) {
+ map[entry.key] = value;
+ }
+ }
+ }
+ return map;
+ }
+
+ // maps a json object with a list of PersonStatisticsResponseDto-objects as value to a dart map
+ static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
+ final map = >{};
+ if (json is Map && json.isNotEmpty) {
+ // ignore: parameter_assignments
+ json = json.cast();
+ for (final entry in json.entries) {
+ map[entry.key] = PersonStatisticsResponseDto.listFromJson(entry.value, growable: growable,);
+ }
+ }
+ return map;
+ }
+
+ /// The list of required keys that must be present in a JSON.
+ static const requiredKeys = {
+ 'assets',
+ };
+}
+
diff --git a/mobile/openapi/lib/model/server_theme_dto.dart b/mobile/openapi/lib/model/server_theme_dto.dart
new file mode 100644
index 000000000..f31596033
--- /dev/null
+++ b/mobile/openapi/lib/model/server_theme_dto.dart
@@ -0,0 +1,98 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.12
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class ServerThemeDto {
+ /// Returns a new [ServerThemeDto] instance.
+ ServerThemeDto({
+ required this.customCss,
+ });
+
+ String customCss;
+
+ @override
+ bool operator ==(Object other) => identical(this, other) || other is ServerThemeDto &&
+ other.customCss == customCss;
+
+ @override
+ int get hashCode =>
+ // ignore: unnecessary_parenthesis
+ (customCss.hashCode);
+
+ @override
+ String toString() => 'ServerThemeDto[customCss=$customCss]';
+
+ Map toJson() {
+ final json = {};
+ json[r'customCss'] = this.customCss;
+ return json;
+ }
+
+ /// Returns a new [ServerThemeDto] instance and imports its values from
+ /// [value] if it's a [Map], null otherwise.
+ // ignore: prefer_constructors_over_static_methods
+ static ServerThemeDto? fromJson(dynamic value) {
+ if (value is Map) {
+ final json = value.cast();
+
+ return ServerThemeDto(
+ customCss: mapValueOfType(json, r'customCss')!,
+ );
+ }
+ return null;
+ }
+
+ static List listFromJson(dynamic json, {bool growable = false,}) {
+ final result = [];
+ if (json is List && json.isNotEmpty) {
+ for (final row in json) {
+ final value = ServerThemeDto.fromJson(row);
+ if (value != null) {
+ result.add(value);
+ }
+ }
+ }
+ return result.toList(growable: growable);
+ }
+
+ static Map mapFromJson(dynamic json) {
+ final map = {};
+ if (json is Map && json.isNotEmpty) {
+ json = json.cast(); // ignore: parameter_assignments
+ for (final entry in json.entries) {
+ final value = ServerThemeDto.fromJson(entry.value);
+ if (value != null) {
+ map[entry.key] = value;
+ }
+ }
+ }
+ return map;
+ }
+
+ // maps a json object with a list of ServerThemeDto-objects as value to a dart map
+ static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
+ final map =