mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
feat(mobile) duplicated asset upload handling mechanism (#853)
This commit is contained in:
parent
f1af17bf4d
commit
6159c83fd2
@ -24,6 +24,7 @@
|
|||||||
"backup_controller_page_backup_selected": "Ausgewählt: ",
|
"backup_controller_page_backup_selected": "Ausgewählt: ",
|
||||||
"backup_controller_page_backup_sub": "Gesicherte Fotos und Videos",
|
"backup_controller_page_backup_sub": "Gesicherte Fotos und Videos",
|
||||||
"backup_controller_page_cancel": "Abbrechen",
|
"backup_controller_page_cancel": "Abbrechen",
|
||||||
|
"backup_background_service_default_notification": "Suche nach neuen assets…",
|
||||||
"backup_controller_page_created": "Erstellt: {}",
|
"backup_controller_page_created": "Erstellt: {}",
|
||||||
"backup_controller_page_desc_backup": "Aktiviere die Sicherung um Elemente automatisch auf den Server zu laden.",
|
"backup_controller_page_desc_backup": "Aktiviere die Sicherung um Elemente automatisch auf den Server zu laden.",
|
||||||
"backup_controller_page_excluded": "Ausgeschlossen: ",
|
"backup_controller_page_excluded": "Ausgeschlossen: ",
|
||||||
@ -123,4 +124,4 @@
|
|||||||
"version_announcement_overlay_text_2": "Bitte nehm dir die Zeit und lese das ",
|
"version_announcement_overlay_text_2": "Bitte nehm dir die Zeit und lese das ",
|
||||||
"version_announcement_overlay_text_3": " und achte darauf, dass deine docker-compose und .env Dateien aktuell sind, vor allem wenn du ein System für automatische Updates benutzt (z.B. Watchtower).",
|
"version_announcement_overlay_text_3": " und achte darauf, dass deine docker-compose und .env Dateien aktuell sind, vor allem wenn du ein System für automatische Updates benutzt (z.B. Watchtower).",
|
||||||
"version_announcement_overlay_title": "Neue Server-Version verfügbar \uD83C\uDF89"
|
"version_announcement_overlay_title": "Neue Server-Version verfügbar \uD83C\uDF89"
|
||||||
}
|
}
|
||||||
|
@ -25,3 +25,7 @@ const String backgroundBackupInfoBox = "immichBackgroundBackupInfoBox"; // Box
|
|||||||
const String backupFailedSince = "immichBackupFailedSince"; // Key 1
|
const String backupFailedSince = "immichBackupFailedSince"; // Key 1
|
||||||
const String backupRequireWifi = "immichBackupRequireWifi"; // Key 2
|
const String backupRequireWifi = "immichBackupRequireWifi"; // Key 2
|
||||||
const String backupRequireCharging = "immichBackupRequireCharging"; // Key 3
|
const String backupRequireCharging = "immichBackupRequireCharging"; // Key 3
|
||||||
|
|
||||||
|
// Duplicate asset
|
||||||
|
const String duplicatedAssetsBox = "immichDuplicatedAssetsBox"; // Box
|
||||||
|
const String duplicatedAssetsKey = "immichDuplicatedAssetsKey"; // Key 1
|
||||||
|
@ -10,6 +10,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/constants/locales.dart';
|
import 'package:immich_mobile/constants/locales.dart';
|
||||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/backup/models/hive_duplicated_assets.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
||||||
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
||||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||||
@ -30,12 +31,14 @@ void main() async {
|
|||||||
|
|
||||||
Hive.registerAdapter(HiveSavedLoginInfoAdapter());
|
Hive.registerAdapter(HiveSavedLoginInfoAdapter());
|
||||||
Hive.registerAdapter(HiveBackupAlbumsAdapter());
|
Hive.registerAdapter(HiveBackupAlbumsAdapter());
|
||||||
|
Hive.registerAdapter(HiveDuplicatedAssetsAdapter());
|
||||||
|
|
||||||
await Hive.openBox(userInfoBox);
|
await Hive.openBox(userInfoBox);
|
||||||
await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
|
await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
|
||||||
await Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox);
|
await Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox);
|
||||||
await Hive.openBox(hiveGithubReleaseInfoBox);
|
await Hive.openBox(hiveGithubReleaseInfoBox);
|
||||||
await Hive.openBox(userSettingInfoBox);
|
await Hive.openBox(userSettingInfoBox);
|
||||||
|
await Hive.openBox<HiveDuplicatedAssets>(duplicatedAssetsBox);
|
||||||
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
const SystemUiOverlayStyle(
|
const SystemUiOverlayStyle(
|
||||||
|
@ -14,6 +14,7 @@ import 'package:immich_mobile/modules/backup/background_service/localization.dar
|
|||||||
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/backup/models/hive_duplicated_assets.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
||||||
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
||||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||||
@ -316,10 +317,13 @@ class BackgroundService {
|
|||||||
|
|
||||||
Hive.registerAdapter(HiveSavedLoginInfoAdapter());
|
Hive.registerAdapter(HiveSavedLoginInfoAdapter());
|
||||||
Hive.registerAdapter(HiveBackupAlbumsAdapter());
|
Hive.registerAdapter(HiveBackupAlbumsAdapter());
|
||||||
|
Hive.registerAdapter(HiveDuplicatedAssetsAdapter());
|
||||||
|
|
||||||
await Hive.openBox(userInfoBox);
|
await Hive.openBox(userInfoBox);
|
||||||
await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
|
await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
|
||||||
await Hive.openBox(userSettingInfoBox);
|
await Hive.openBox(userSettingInfoBox);
|
||||||
await Hive.openBox(backgroundBackupInfoBox);
|
await Hive.openBox(backgroundBackupInfoBox);
|
||||||
|
await Hive.openBox<HiveDuplicatedAssets>(duplicatedAssetsBox);
|
||||||
|
|
||||||
ApiService apiService = ApiService();
|
ApiService apiService = ApiService();
|
||||||
apiService.setEndpoint(Hive.box(userInfoBox).get(serverEndpointKey));
|
apiService.setEndpoint(Hive.box(userInfoBox).get(serverEndpointKey));
|
||||||
@ -410,7 +414,7 @@ class BackgroundService {
|
|||||||
final bool ok = await backupService.backupAsset(
|
final bool ok = await backupService.backupAsset(
|
||||||
toUpload,
|
toUpload,
|
||||||
_cancellationToken!,
|
_cancellationToken!,
|
||||||
notifyTotalProgress ? _onAssetUploaded : (assetId, deviceId) {},
|
notifyTotalProgress ? _onAssetUploaded : (assetId, deviceId, isDup) {},
|
||||||
notifySingleProgress ? _onProgress : (sent, total) {},
|
notifySingleProgress ? _onProgress : (sent, total) {},
|
||||||
notifySingleProgress ? _onSetCurrentBackupAsset : (asset) {},
|
notifySingleProgress ? _onSetCurrentBackupAsset : (asset) {},
|
||||||
_onBackupError,
|
_onBackupError,
|
||||||
@ -429,7 +433,7 @@ class BackgroundService {
|
|||||||
return "$percent% ($_uploadedAssetsCount/$_assetsToUploadCount)";
|
return "$percent% ($_uploadedAssetsCount/$_assetsToUploadCount)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAssetUploaded(String deviceAssetId, String deviceId) {
|
void _onAssetUploaded(String deviceAssetId, String deviceId, bool isDup) {
|
||||||
debugPrint("Uploaded $deviceAssetId from $deviceId");
|
debugPrint("Uploaded $deviceAssetId from $deviceId");
|
||||||
_uploadedAssetsCount++;
|
_uploadedAssetsCount++;
|
||||||
_updateNotification(
|
_updateNotification(
|
||||||
|
@ -45,8 +45,10 @@ class ErrorUploadAsset extends Equatable {
|
|||||||
List<Object> get props {
|
List<Object> get props {
|
||||||
return [
|
return [
|
||||||
id,
|
id,
|
||||||
|
createdAt,
|
||||||
fileName,
|
fileName,
|
||||||
fileType,
|
fileType,
|
||||||
|
asset,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
|
part 'hive_duplicated_assets.model.g.dart';
|
||||||
|
|
||||||
|
@HiveType(typeId: 2)
|
||||||
|
class HiveDuplicatedAssets {
|
||||||
|
@HiveField(0, defaultValue: [])
|
||||||
|
List<String> duplicatedAssetIds;
|
||||||
|
|
||||||
|
HiveDuplicatedAssets({
|
||||||
|
required this.duplicatedAssetIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
HiveDuplicatedAssets copyWith({
|
||||||
|
List<String>? duplicatedAssetIds,
|
||||||
|
}) {
|
||||||
|
return HiveDuplicatedAssets(
|
||||||
|
duplicatedAssetIds: duplicatedAssetIds ?? this.duplicatedAssetIds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'duplicatedAssetIds': duplicatedAssetIds,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory HiveDuplicatedAssets.fromMap(Map<String, dynamic> map) {
|
||||||
|
return HiveDuplicatedAssets(
|
||||||
|
duplicatedAssetIds: List<String>.from(map['duplicatedAssetIds']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory HiveDuplicatedAssets.fromJson(String source) =>
|
||||||
|
HiveDuplicatedAssets.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'HiveDuplicatedAssets(duplicatedAssetIds: $duplicatedAssetIds)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
final listEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
|
return other is HiveDuplicatedAssets &&
|
||||||
|
listEquals(other.duplicatedAssetIds, duplicatedAssetIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => duplicatedAssetIds.hashCode;
|
||||||
|
}
|
Binary file not shown.
@ -10,6 +10,7 @@ import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
|
|||||||
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/backup/models/hive_duplicated_assets.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
|
import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
|
||||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||||
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
||||||
@ -296,6 +297,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
/// Those assets are unique and are used as the total assets
|
/// Those assets are unique and are used as the total assets
|
||||||
///
|
///
|
||||||
Future<void> _updateBackupAssetCount() async {
|
Future<void> _updateBackupAssetCount() async {
|
||||||
|
Set<String> duplicatedAssetIds = _backupService.getDuplicatedAssetIds();
|
||||||
Set<AssetEntity> assetsFromSelectedAlbums = {};
|
Set<AssetEntity> assetsFromSelectedAlbums = {};
|
||||||
Set<AssetEntity> assetsFromExcludedAlbums = {};
|
Set<AssetEntity> assetsFromExcludedAlbums = {};
|
||||||
|
|
||||||
@ -326,9 +328,15 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
// Find asset that were backup from selected albums
|
// Find asset that were backup from selected albums
|
||||||
Set<String> selectedAlbumsBackupAssets =
|
Set<String> selectedAlbumsBackupAssets =
|
||||||
Set.from(allUniqueAssets.map((e) => e.id));
|
Set.from(allUniqueAssets.map((e) => e.id));
|
||||||
|
|
||||||
selectedAlbumsBackupAssets
|
selectedAlbumsBackupAssets
|
||||||
.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
|
.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
|
||||||
|
|
||||||
|
// Remove duplicated asset from all unique assets
|
||||||
|
allUniqueAssets.removeWhere(
|
||||||
|
(asset) => duplicatedAssetIds.contains(asset.id),
|
||||||
|
);
|
||||||
|
|
||||||
if (allUniqueAssets.isEmpty) {
|
if (allUniqueAssets.isEmpty) {
|
||||||
debugPrint("No Asset On Device");
|
debugPrint("No Asset On Device");
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
@ -455,14 +463,26 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAssetUploaded(String deviceAssetId, String deviceId) {
|
void _onAssetUploaded(
|
||||||
state = state.copyWith(
|
String deviceAssetId,
|
||||||
selectedAlbumsBackupAssetsIds: {
|
String deviceId,
|
||||||
...state.selectedAlbumsBackupAssetsIds,
|
bool isDuplicated,
|
||||||
deviceAssetId
|
) {
|
||||||
},
|
if (isDuplicated) {
|
||||||
allAssetsInDatabase: [...state.allAssetsInDatabase, deviceAssetId],
|
state = state.copyWith(
|
||||||
);
|
allUniqueAssets: state.allUniqueAssets
|
||||||
|
.where((asset) => asset.id != deviceAssetId)
|
||||||
|
.toSet(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
state = state.copyWith(
|
||||||
|
selectedAlbumsBackupAssetsIds: {
|
||||||
|
...state.selectedAlbumsBackupAssetsIds,
|
||||||
|
deviceAssetId
|
||||||
|
},
|
||||||
|
allAssetsInDatabase: [...state.allAssetsInDatabase, deviceAssetId],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (state.allUniqueAssets.length -
|
if (state.allUniqueAssets.length -
|
||||||
state.selectedAlbumsBackupAssetsIds.length ==
|
state.selectedAlbumsBackupAssetsIds.length ==
|
||||||
@ -564,6 +584,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
albums.lastExcludedBackupTime,
|
albums.lastExcludedBackupTime,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
await Hive.openBox<HiveDuplicatedAssets>(duplicatedAssetsBox);
|
||||||
final Box backgroundBox = await Hive.openBox(backgroundBackupInfoBox);
|
final Box backgroundBox = await Hive.openBox(backgroundBackupInfoBox);
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
backupProgress: previous,
|
backupProgress: previous,
|
||||||
@ -608,6 +629,13 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
|
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
if (Hive.isBoxOpen(duplicatedAssetsBox)) {
|
||||||
|
await Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox).close();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (Hive.isBoxOpen(backgroundBackupInfoBox)) {
|
if (Hive.isBoxOpen(backgroundBackupInfoBox)) {
|
||||||
await Hive.box(backgroundBackupInfoBox).close();
|
await Hive.box(backgroundBackupInfoBox).close();
|
||||||
|
@ -19,6 +19,8 @@ import 'package:http_parser/http_parser.dart';
|
|||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:cancellation_token_http/http.dart' as http;
|
import 'package:cancellation_token_http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../models/hive_duplicated_assets.model.dart';
|
||||||
|
|
||||||
final backupServiceProvider = Provider(
|
final backupServiceProvider = Provider(
|
||||||
(ref) => BackupService(
|
(ref) => BackupService(
|
||||||
ref.watch(apiServiceProvider),
|
ref.watch(apiServiceProvider),
|
||||||
@ -41,6 +43,29 @@ class BackupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _saveDuplicatedAssetIdToLocalStorage(List<String> deviceAssetIds) {
|
||||||
|
HiveDuplicatedAssets duplicatedAssets =
|
||||||
|
Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox)
|
||||||
|
.get(duplicatedAssetsKey) ??
|
||||||
|
HiveDuplicatedAssets(duplicatedAssetIds: []);
|
||||||
|
|
||||||
|
duplicatedAssets.duplicatedAssetIds =
|
||||||
|
{...duplicatedAssets.duplicatedAssetIds, ...deviceAssetIds}.toList();
|
||||||
|
|
||||||
|
Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox)
|
||||||
|
.put(duplicatedAssetsKey, duplicatedAssets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get duplicated asset id from Hive storage
|
||||||
|
Set<String> getDuplicatedAssetIds() {
|
||||||
|
HiveDuplicatedAssets duplicatedAssets =
|
||||||
|
Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox)
|
||||||
|
.get(duplicatedAssetsKey) ??
|
||||||
|
HiveDuplicatedAssets(duplicatedAssetIds: []);
|
||||||
|
|
||||||
|
return duplicatedAssets.duplicatedAssetIds.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns all assets newer than the last successful backup per album
|
/// Returns all assets newer than the last successful backup per album
|
||||||
Future<List<AssetEntity>> buildUploadCandidates(
|
Future<List<AssetEntity>> buildUploadCandidates(
|
||||||
HiveBackupAlbums backupAlbums,
|
HiveBackupAlbums backupAlbums,
|
||||||
@ -140,34 +165,47 @@ class BackupService {
|
|||||||
Future<List<AssetEntity>> removeAlreadyUploadedAssets(
|
Future<List<AssetEntity>> removeAlreadyUploadedAssets(
|
||||||
List<AssetEntity> candidates,
|
List<AssetEntity> candidates,
|
||||||
) async {
|
) async {
|
||||||
final String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
|
if (candidates.isEmpty) {
|
||||||
if (candidates.length < 10) {
|
return candidates;
|
||||||
final List<CheckDuplicateAssetResponseDto?> duplicateResponse =
|
}
|
||||||
await Future.wait(
|
final Set<String> duplicatedAssetIds = getDuplicatedAssetIds();
|
||||||
candidates.map(
|
candidates = duplicatedAssetIds.isEmpty
|
||||||
(e) => _apiService.assetApi.checkDuplicateAsset(
|
? candidates
|
||||||
CheckDuplicateAssetDto(deviceAssetId: e.id, deviceId: deviceId),
|
: candidates
|
||||||
),
|
.whereNot((asset) => duplicatedAssetIds.contains(asset.id))
|
||||||
|
.toList();
|
||||||
|
if (candidates.isEmpty) {
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
final Set<String> existing = {};
|
||||||
|
try {
|
||||||
|
final String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
|
||||||
|
final CheckExistingAssetsResponseDto? duplicates =
|
||||||
|
await _apiService.assetApi.checkExistingAssets(
|
||||||
|
CheckExistingAssetsDto(
|
||||||
|
deviceAssetIds: candidates.map((e) => e.id).toList(),
|
||||||
|
deviceId: deviceId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return candidates
|
if (duplicates != null) {
|
||||||
.whereIndexed((i, e) => duplicateResponse[i]?.isExist == false)
|
existing.addAll(duplicates.existingIds);
|
||||||
.toList();
|
}
|
||||||
} else {
|
} on ApiException {
|
||||||
final List<String>? allAssetsInDatabase = await getDeviceBackupAsset();
|
// workaround for older server versions or when checking for too many assets at once
|
||||||
|
final List<String>? allAssetsInDatabase = await getDeviceBackupAsset();
|
||||||
if (allAssetsInDatabase == null) {
|
if (allAssetsInDatabase != null) {
|
||||||
return candidates;
|
existing.addAll(allAssetsInDatabase);
|
||||||
}
|
}
|
||||||
final Set<String> inDb = allAssetsInDatabase.toSet();
|
|
||||||
return candidates.whereNot((e) => inDb.contains(e.id)).toList();
|
|
||||||
}
|
}
|
||||||
|
return existing.isEmpty
|
||||||
|
? candidates
|
||||||
|
: candidates.whereNot((e) => existing.contains(e.id)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> backupAsset(
|
Future<bool> backupAsset(
|
||||||
Iterable<AssetEntity> assetList,
|
Iterable<AssetEntity> assetList,
|
||||||
http.CancellationToken cancelToken,
|
http.CancellationToken cancelToken,
|
||||||
Function(String, String) singleAssetDoneCb,
|
Function(String, String, bool) uploadSuccessCb,
|
||||||
Function(int, int) uploadProgressCb,
|
Function(int, int) uploadProgressCb,
|
||||||
Function(CurrentUploadAsset) setCurrentUploadAssetCb,
|
Function(CurrentUploadAsset) setCurrentUploadAssetCb,
|
||||||
Function(ErrorUploadAsset) errorCb,
|
Function(ErrorUploadAsset) errorCb,
|
||||||
@ -176,6 +214,7 @@ class BackupService {
|
|||||||
String savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
String savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
||||||
File? file;
|
File? file;
|
||||||
bool anyErrors = false;
|
bool anyErrors = false;
|
||||||
|
final List<String> duplicatedAssetIds = [];
|
||||||
|
|
||||||
for (var entity in assetList) {
|
for (var entity in assetList) {
|
||||||
try {
|
try {
|
||||||
@ -235,8 +274,13 @@ class BackupService {
|
|||||||
|
|
||||||
var response = await req.send(cancellationToken: cancelToken);
|
var response = await req.send(cancellationToken: cancelToken);
|
||||||
|
|
||||||
if (response.statusCode == 201) {
|
if (response.statusCode == 200) {
|
||||||
singleAssetDoneCb(entity.id, deviceId);
|
// asset is a duplicate (already exists on the server)
|
||||||
|
duplicatedAssetIds.add(entity.id);
|
||||||
|
uploadSuccessCb(entity.id, deviceId, true);
|
||||||
|
} else if (response.statusCode == 201) {
|
||||||
|
// stored a new asset on the server
|
||||||
|
uploadSuccessCb(entity.id, deviceId, false);
|
||||||
} else {
|
} else {
|
||||||
var data = await response.stream.bytesToString();
|
var data = await response.stream.bytesToString();
|
||||||
var error = jsonDecode(data);
|
var error = jsonDecode(data);
|
||||||
@ -260,7 +304,8 @@ class BackupService {
|
|||||||
}
|
}
|
||||||
} on http.CancelledException {
|
} on http.CancelledException {
|
||||||
debugPrint("Backup was cancelled by the user");
|
debugPrint("Backup was cancelled by the user");
|
||||||
return false;
|
anyErrors = true;
|
||||||
|
break;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("ERROR backupAsset: ${e.toString()}");
|
debugPrint("ERROR backupAsset: ${e.toString()}");
|
||||||
anyErrors = true;
|
anyErrors = true;
|
||||||
@ -271,6 +316,9 @@ class BackupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (duplicatedAssetIds.isNotEmpty) {
|
||||||
|
_saveDuplicatedAssetIdToLocalStorage(duplicatedAssetIds);
|
||||||
|
}
|
||||||
return !anyErrors;
|
return !anyErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,7 +419,6 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
ActionChip(
|
ActionChip(
|
||||||
avatar: Icon(
|
avatar: Icon(
|
||||||
Icons.info,
|
Icons.info,
|
||||||
size: 24,
|
|
||||||
color: Colors.red[400],
|
color: Colors.red[400],
|
||||||
),
|
),
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
|
@ -19,6 +19,8 @@ doc/AssetTypeEnum.md
|
|||||||
doc/AuthenticationApi.md
|
doc/AuthenticationApi.md
|
||||||
doc/CheckDuplicateAssetDto.md
|
doc/CheckDuplicateAssetDto.md
|
||||||
doc/CheckDuplicateAssetResponseDto.md
|
doc/CheckDuplicateAssetResponseDto.md
|
||||||
|
doc/CheckExistingAssetsDto.md
|
||||||
|
doc/CheckExistingAssetsResponseDto.md
|
||||||
doc/CreateAlbumDto.md
|
doc/CreateAlbumDto.md
|
||||||
doc/CreateDeviceInfoDto.md
|
doc/CreateDeviceInfoDto.md
|
||||||
doc/CreateProfileImageResponseDto.md
|
doc/CreateProfileImageResponseDto.md
|
||||||
@ -93,6 +95,8 @@ lib/model/asset_response_dto.dart
|
|||||||
lib/model/asset_type_enum.dart
|
lib/model/asset_type_enum.dart
|
||||||
lib/model/check_duplicate_asset_dto.dart
|
lib/model/check_duplicate_asset_dto.dart
|
||||||
lib/model/check_duplicate_asset_response_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/create_album_dto.dart
|
lib/model/create_album_dto.dart
|
||||||
lib/model/create_device_info_dto.dart
|
lib/model/create_device_info_dto.dart
|
||||||
lib/model/create_profile_image_response_dto.dart
|
lib/model/create_profile_image_response_dto.dart
|
||||||
@ -133,3 +137,5 @@ lib/model/user_count_response_dto.dart
|
|||||||
lib/model/user_response_dto.dart
|
lib/model/user_response_dto.dart
|
||||||
lib/model/validate_access_token_response_dto.dart
|
lib/model/validate_access_token_response_dto.dart
|
||||||
pubspec.yaml
|
pubspec.yaml
|
||||||
|
test/check_existing_assets_dto_test.dart
|
||||||
|
test/check_existing_assets_response_dto_test.dart
|
||||||
|
Binary file not shown.
Binary file not shown.
BIN
mobile/openapi/doc/CheckExistingAssetsDto.md
Normal file
BIN
mobile/openapi/doc/CheckExistingAssetsDto.md
Normal file
Binary file not shown.
BIN
mobile/openapi/doc/CheckExistingAssetsResponseDto.md
Normal file
BIN
mobile/openapi/doc/CheckExistingAssetsResponseDto.md
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
mobile/openapi/lib/model/check_existing_assets_dto.dart
Normal file
BIN
mobile/openapi/lib/model/check_existing_assets_dto.dart
Normal file
Binary file not shown.
BIN
mobile/openapi/lib/model/check_existing_assets_response_dto.dart
Normal file
BIN
mobile/openapi/lib/model/check_existing_assets_response_dto.dart
Normal file
Binary file not shown.
BIN
mobile/openapi/test/check_existing_assets_dto_test.dart
Normal file
BIN
mobile/openapi/test/check_existing_assets_dto_test.dart
Normal file
Binary file not shown.
BIN
mobile/openapi/test/check_existing_assets_response_dto_test.dart
Normal file
BIN
mobile/openapi/test/check_existing_assets_response_dto_test.dart
Normal file
Binary file not shown.
@ -137,6 +137,7 @@ describe('Album service', () => {
|
|||||||
getAssetWithNoEXIF: jest.fn(),
|
getAssetWithNoEXIF: jest.fn(),
|
||||||
getAssetWithNoThumbnail: jest.fn(),
|
getAssetWithNoThumbnail: jest.fn(),
|
||||||
getAssetWithNoSmartInfo: jest.fn(),
|
getAssetWithNoSmartInfo: jest.fn(),
|
||||||
|
getExistingAssets: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
sut = new AlbumService(albumRepositoryMock, assetRepositoryMock);
|
sut = new AlbumService(albumRepositoryMock, assetRepositoryMock);
|
||||||
|
@ -10,6 +10,9 @@ import { AssetCountByTimeBucket } from './response-dto/asset-count-by-time-group
|
|||||||
import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';
|
import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';
|
||||||
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
||||||
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
||||||
|
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
|
||||||
|
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
|
||||||
|
import { In } from 'typeorm/find-options/operator/In';
|
||||||
|
|
||||||
export interface IAssetRepository {
|
export interface IAssetRepository {
|
||||||
create(
|
create(
|
||||||
@ -32,6 +35,7 @@ export interface IAssetRepository {
|
|||||||
getAssetWithNoThumbnail(): Promise<AssetEntity[]>;
|
getAssetWithNoThumbnail(): Promise<AssetEntity[]>;
|
||||||
getAssetWithNoEXIF(): Promise<AssetEntity[]>;
|
getAssetWithNoEXIF(): Promise<AssetEntity[]>;
|
||||||
getAssetWithNoSmartInfo(): Promise<AssetEntity[]>;
|
getAssetWithNoSmartInfo(): Promise<AssetEntity[]>;
|
||||||
|
getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<CheckExistingAssetsResponseDto>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ASSET_REPOSITORY = 'ASSET_REPOSITORY';
|
export const ASSET_REPOSITORY = 'ASSET_REPOSITORY';
|
||||||
@ -279,4 +283,17 @@ export class AssetRepository implements IAssetRepository {
|
|||||||
relations: ['exifInfo'],
|
relations: ['exifInfo'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<CheckExistingAssetsResponseDto> {
|
||||||
|
const existingAssets = await this.assetRepository.find({
|
||||||
|
select: {deviceAssetId: true},
|
||||||
|
where: {
|
||||||
|
deviceAssetId: In(checkDuplicateAssetDto.deviceAssetIds),
|
||||||
|
deviceId: checkDuplicateAssetDto.deviceId,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return new CheckExistingAssetsResponseDto(existingAssets.map(a => a.deviceAssetId));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ import { GetAssetCountByTimeBucketDto } from './dto/get-asset-count-by-time-buck
|
|||||||
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
||||||
import { QueryFailedError } from 'typeorm';
|
import { QueryFailedError } from 'typeorm';
|
||||||
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
||||||
|
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
|
||||||
|
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ -74,6 +76,7 @@ export class AssetController {
|
|||||||
@GetAuthUser() authUser: AuthUserDto,
|
@GetAuthUser() authUser: AuthUserDto,
|
||||||
@UploadedFile() file: Express.Multer.File,
|
@UploadedFile() file: Express.Multer.File,
|
||||||
@Body(ValidationPipe) assetInfo: CreateAssetDto,
|
@Body(ValidationPipe) assetInfo: CreateAssetDto,
|
||||||
|
@Response({ passthrough: true }) res: Res,
|
||||||
): Promise<AssetFileUploadResponseDto> {
|
): Promise<AssetFileUploadResponseDto> {
|
||||||
const checksum = await this.assetService.calculateChecksum(file.path);
|
const checksum = await this.assetService.calculateChecksum(file.path);
|
||||||
|
|
||||||
@ -111,6 +114,7 @@ export class AssetController {
|
|||||||
|
|
||||||
if (err instanceof QueryFailedError && (err as any).constraint === 'UQ_userid_checksum') {
|
if (err instanceof QueryFailedError && (err as any).constraint === 'UQ_userid_checksum') {
|
||||||
const existedAsset = await this.assetService.getAssetByChecksum(authUser.id, checksum);
|
const existedAsset = await this.assetService.getAssetByChecksum(authUser.id, checksum);
|
||||||
|
res.status(200); // normal POST is 201. we use 200 to indicate the asset already exists
|
||||||
return new AssetFileUploadResponseDto(existedAsset.id);
|
return new AssetFileUploadResponseDto(existedAsset.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,4 +258,16 @@ export class AssetController {
|
|||||||
): Promise<CheckDuplicateAssetResponseDto> {
|
): Promise<CheckDuplicateAssetResponseDto> {
|
||||||
return await this.assetService.checkDuplicatedAsset(authUser, checkDuplicateAssetDto);
|
return await this.assetService.checkDuplicatedAsset(authUser, checkDuplicateAssetDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
*/
|
||||||
|
@Post('/exist')
|
||||||
|
@HttpCode(200)
|
||||||
|
async checkExistingAssets(
|
||||||
|
@GetAuthUser() authUser: AuthUserDto,
|
||||||
|
@Body(ValidationPipe) checkExistingAssetsDto: CheckExistingAssetsDto,
|
||||||
|
): Promise<CheckExistingAssetsResponseDto> {
|
||||||
|
return await this.assetService.checkExistingAssets(authUser, checkExistingAssetsDto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,7 @@ describe('AssetService', () => {
|
|||||||
getAssetWithNoEXIF: jest.fn(),
|
getAssetWithNoEXIF: jest.fn(),
|
||||||
getAssetWithNoThumbnail: jest.fn(),
|
getAssetWithNoThumbnail: jest.fn(),
|
||||||
getAssetWithNoSmartInfo: jest.fn(),
|
getAssetWithNoSmartInfo: jest.fn(),
|
||||||
|
getExistingAssets: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
sui = new AssetService(assetRepositoryMock, a);
|
sui = new AssetService(assetRepositoryMock, a);
|
||||||
|
@ -37,6 +37,8 @@ import { GetAssetCountByTimeBucketDto } from './dto/get-asset-count-by-time-buck
|
|||||||
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
||||||
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
||||||
import { timeUtils } from '@app/common/utils';
|
import { timeUtils } from '@app/common/utils';
|
||||||
|
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
|
||||||
|
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
|
||||||
|
|
||||||
const fileInfo = promisify(stat);
|
const fileInfo = promisify(stat);
|
||||||
|
|
||||||
@ -466,6 +468,13 @@ export class AssetService {
|
|||||||
return new CheckDuplicateAssetResponseDto(isDuplicated, res?.id);
|
return new CheckDuplicateAssetResponseDto(isDuplicated, res?.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async checkExistingAssets(
|
||||||
|
authUser: AuthUserDto,
|
||||||
|
checkExistingAssetsDto: CheckExistingAssetsDto,
|
||||||
|
): Promise<CheckExistingAssetsResponseDto> {
|
||||||
|
return this._assetRepository.getExistingAssets(authUser.id, checkExistingAssetsDto);
|
||||||
|
}
|
||||||
|
|
||||||
async getAssetCountByTimeBucket(
|
async getAssetCountByTimeBucket(
|
||||||
authUser: AuthUserDto,
|
authUser: AuthUserDto,
|
||||||
getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto,
|
getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto,
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
import { IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
|
export class CheckExistingAssetsDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
deviceAssetIds!: string[];
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
deviceId!: string;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
export class CheckExistingAssetsResponseDto {
|
||||||
|
constructor(existingIds: string[]) {
|
||||||
|
this.existingIds = existingIds;
|
||||||
|
}
|
||||||
|
existingIds: string[];
|
||||||
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
@ -452,6 +452,38 @@ export interface CheckDuplicateAssetResponseDto {
|
|||||||
*/
|
*/
|
||||||
'id'?: string;
|
'id'?: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CheckExistingAssetsDto
|
||||||
|
*/
|
||||||
|
export interface CheckExistingAssetsDto {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<string>}
|
||||||
|
* @memberof CheckExistingAssetsDto
|
||||||
|
*/
|
||||||
|
'deviceAssetIds': Array<string>;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof CheckExistingAssetsDto
|
||||||
|
*/
|
||||||
|
'deviceId': string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CheckExistingAssetsResponseDto
|
||||||
|
*/
|
||||||
|
export interface CheckExistingAssetsResponseDto {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<string>}
|
||||||
|
* @memberof CheckExistingAssetsResponseDto
|
||||||
|
*/
|
||||||
|
'existingIds': Array<string>;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
@ -2334,6 +2366,46 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
|||||||
options: localVarRequestOptions,
|
options: localVarRequestOptions,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
* @summary
|
||||||
|
* @param {CheckExistingAssetsDto} checkExistingAssetsDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
checkExistingAssets: async (checkExistingAssetsDto: CheckExistingAssetsDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'checkExistingAssetsDto' is not null or undefined
|
||||||
|
assertParamExists('checkExistingAssets', 'checkExistingAssetsDto', checkExistingAssetsDto)
|
||||||
|
const localVarPath = `/asset/exist`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
// authentication bearer required
|
||||||
|
// http bearer authentication required
|
||||||
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
localVarRequestOptions.data = serializeDataIfNeeded(checkExistingAssetsDto, localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DeleteAssetDto} deleteAssetDto
|
* @param {DeleteAssetDto} deleteAssetDto
|
||||||
@ -2953,6 +3025,17 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
|||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.checkDuplicateAsset(checkDuplicateAssetDto, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.checkDuplicateAsset(checkDuplicateAssetDto, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
* @summary
|
||||||
|
* @param {CheckExistingAssetsDto} checkExistingAssetsDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async checkExistingAssets(checkExistingAssetsDto: CheckExistingAssetsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CheckExistingAssetsResponseDto>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.checkExistingAssets(checkExistingAssetsDto, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DeleteAssetDto} deleteAssetDto
|
* @param {DeleteAssetDto} deleteAssetDto
|
||||||
@ -3128,6 +3211,16 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
|||||||
checkDuplicateAsset(checkDuplicateAssetDto: CheckDuplicateAssetDto, options?: any): AxiosPromise<CheckDuplicateAssetResponseDto> {
|
checkDuplicateAsset(checkDuplicateAssetDto: CheckDuplicateAssetDto, options?: any): AxiosPromise<CheckDuplicateAssetResponseDto> {
|
||||||
return localVarFp.checkDuplicateAsset(checkDuplicateAssetDto, options).then((request) => request(axios, basePath));
|
return localVarFp.checkDuplicateAsset(checkDuplicateAssetDto, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
* @summary
|
||||||
|
* @param {CheckExistingAssetsDto} checkExistingAssetsDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
checkExistingAssets(checkExistingAssetsDto: CheckExistingAssetsDto, options?: any): AxiosPromise<CheckExistingAssetsResponseDto> {
|
||||||
|
return localVarFp.checkExistingAssets(checkExistingAssetsDto, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DeleteAssetDto} deleteAssetDto
|
* @param {DeleteAssetDto} deleteAssetDto
|
||||||
@ -3290,6 +3383,18 @@ export class AssetApi extends BaseAPI {
|
|||||||
return AssetApiFp(this.configuration).checkDuplicateAsset(checkDuplicateAssetDto, options).then((request) => request(this.axios, this.basePath));
|
return AssetApiFp(this.configuration).checkDuplicateAsset(checkDuplicateAssetDto, options).then((request) => request(this.axios, this.basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
* @summary
|
||||||
|
* @param {CheckExistingAssetsDto} checkExistingAssetsDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof AssetApi
|
||||||
|
*/
|
||||||
|
public checkExistingAssets(checkExistingAssetsDto: CheckExistingAssetsDto, options?: AxiosRequestConfig) {
|
||||||
|
return AssetApiFp(this.configuration).checkExistingAssets(checkExistingAssetsDto, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DeleteAssetDto} deleteAssetDto
|
* @param {DeleteAssetDto} deleteAssetDto
|
||||||
|
@ -200,12 +200,12 @@ async function fileUploader(asset: File, uploadType: UploadType) {
|
|||||||
}
|
}
|
||||||
// TODO: This should have a proper type
|
// TODO: This should have a proper type
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function handleUploadError(asset: File, respBody?: any) {
|
function handleUploadError(asset: File, respBody: any, extraMessage?: string) {
|
||||||
const extraMsg = respBody ? ' ' + respBody.message : '';
|
const extraMsg = respBody ? ' ' + respBody?.message : '';
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
type: NotificationType.Error,
|
type: NotificationType.Error,
|
||||||
message: `Cannot upload file ${asset.name}!${extraMsg}`,
|
message: `Cannot upload file ${asset.name} ${extraMsg}${extraMessage}`,
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user