Merge branch 'main' into feature/readonly-sharing

# Conflicts:
#	mobile/openapi/.openapi-generator/FILES
#	mobile/openapi/README.md
#	mobile/openapi/lib/api.dart
#	mobile/openapi/lib/api_client.dart
#	server/src/services/album.service.spec.ts
This commit is contained in:
mgabor
2024-04-17 12:59:50 +02:00
257 changed files with 7638 additions and 8458 deletions

View File

@@ -511,5 +511,7 @@
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
"viewer_remove_from_stack": "Remove from Stack",
"viewer_stack_use_as_main_asset": "Use as Main Asset",
"viewer_unstack": "Un-Stack"
}
"viewer_unstack": "Un-Stack",
"haptic_feedback_title": "Haptic Feedback",
"haptic_feedback_switch": "Enable haptic feedback"
}

View File

@@ -20,6 +20,7 @@ import 'package:immich_mobile/modules/asset_viewer/ui/gallery_app_bar.dart';
import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart';
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart';
import 'package:immich_mobile/shared/ui/immich_thumbnail.dart';
import 'package:immich_mobile/shared/ui/photo_view/photo_view_gallery.dart';
@@ -303,7 +304,9 @@ class GalleryViewerPage extends HookConsumerWidget {
scrollDirection: Axis.horizontal,
onPageChanged: (value) async {
final next = currentIndex.value < value ? value + 1 : value - 1;
HapticFeedback.selectionClick();
ref.read(hapticFeedbackProvider.notifier).selectionClick();
currentIndex.value = value;
stackIndex.value = -1;
isPlayingVideo.value = false;

View File

@@ -1,13 +1,13 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/models/available_album.model.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/shared/ui/immich_toast.dart';
class AlbumInfoCard extends HookConsumerWidget {
@@ -21,6 +21,7 @@ class AlbumInfoCard extends HookConsumerWidget {
ref.watch(backupProvider).selectedBackupAlbums.contains(album);
final bool isExcluded =
ref.watch(backupProvider).excludedBackupAlbums.contains(album);
final isDarkTheme = context.isDarkTheme;
ColorFilter selectedFilter = ColorFilter.mode(
@@ -78,7 +79,7 @@ class AlbumInfoCard extends HookConsumerWidget {
return GestureDetector(
onTap: () {
HapticFeedback.selectionClick();
ref.read(hapticFeedbackProvider.notifier).selectionClick();
if (isSelected) {
ref.read(backupProvider.notifier).removeAlbumForBackup(album);
@@ -87,7 +88,7 @@ class AlbumInfoCard extends HookConsumerWidget {
}
},
onDoubleTap: () {
HapticFeedback.selectionClick();
ref.read(hapticFeedbackProvider.notifier).selectionClick();
if (isExcluded) {
// Remove from exclude album list

View File

@@ -1,6 +1,5 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -8,6 +7,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/models/available_album.model.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/shared/ui/immich_toast.dart';
class AlbumInfoListTile extends HookConsumerWidget {
@@ -68,7 +68,7 @@ class AlbumInfoListTile extends HookConsumerWidget {
return GestureDetector(
onDoubleTap: () {
HapticFeedback.selectionClick();
ref.watch(hapticFeedbackProvider.notifier).selectionClick();
if (isExcluded) {
// Remove from exclude album list
@@ -93,7 +93,7 @@ class AlbumInfoListTile extends HookConsumerWidget {
tileColor: buildTileColor(),
contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
onTap: () {
HapticFeedback.selectionClick();
ref.read(hapticFeedbackProvider.notifier).selectionClick();
if (isSelected) {
ref.read(backupProvider.notifier).removeAlbumForBackup(album);
} else {

View File

@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
class GroupDividerTitle extends HookConsumerWidget {
const GroupDividerTitle({
@@ -38,7 +38,7 @@ class GroupDividerTitle extends HookConsumerWidget {
);
void handleTitleIconClick() {
HapticFeedback.heavyImpact();
ref.read(hapticFeedbackProvider.notifier).heavyImpact();
if (selected) {
onDeselect();
} else {

View File

@@ -6,7 +6,7 @@ import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/collection_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart';
@@ -15,6 +15,7 @@ import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart';
import 'package:immich_mobile/modules/home/ui/control_bottom_app_bar.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'asset_grid_data_structure.dart';
@@ -27,7 +28,7 @@ typedef ImmichAssetGridSelectionListener = void Function(
Set<Asset>,
);
class ImmichAssetGridView extends StatefulWidget {
class ImmichAssetGridView extends ConsumerStatefulWidget {
final RenderList renderList;
final int assetsPerRow;
final double margin;
@@ -69,12 +70,12 @@ class ImmichAssetGridView extends StatefulWidget {
});
@override
State<StatefulWidget> createState() {
createState() {
return ImmichAssetGridViewState();
}
}
class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
class ImmichAssetGridViewState extends ConsumerState<ImmichAssetGridView> {
final ItemScrollController _itemScrollController = ItemScrollController();
final ScrollOffsetController _scrollOffsetController =
ScrollOffsetController();
@@ -314,7 +315,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
final now = Timeline.now;
if (now > (_hapticFeedbackTS + feedbackInterval)) {
_hapticFeedbackTS = now;
HapticFeedback.mediumImpact();
ref.read(hapticFeedbackProvider.notifier).mediumImpact();
}
}
}

View File

@@ -1,14 +1,15 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/shared/ui/immich_thumbnail.dart';
import 'package:immich_mobile/utils/storage_indicator.dart';
import 'package:isar/isar.dart';
class ThumbnailImage extends StatelessWidget {
class ThumbnailImage extends ConsumerWidget {
final Asset asset;
final int index;
final Asset Function(int index) loadAsset;
@@ -37,7 +38,7 @@ class ThumbnailImage extends StatelessWidget {
});
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final assetContainerColor = context.isDarkTheme
? Colors.blueGrey
: context.themeData.primaryColorLight;
@@ -186,7 +187,7 @@ class ThumbnailImage extends StatelessWidget {
},
onLongPress: () {
onSelect?.call();
HapticFeedback.heavyImpact();
ref.read(hapticFeedbackProvider.notifier).heavyImpact();
},
child: Stack(
children: [

View File

@@ -2,7 +2,6 @@ import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_udid/flutter_udid.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
@@ -92,7 +91,6 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
serverUrl: serverUrl,
);
} catch (e) {
HapticFeedback.vibrate();
debugPrint("Error logging in $e");
return false;
}

View File

@@ -1,10 +1,10 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart';
import 'package:immich_mobile/modules/memories/providers/memory.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart';
class MemoryLane extends HookConsumerWidget {
@@ -33,7 +33,9 @@ class MemoryLane extends HookConsumerWidget {
return GestureDetector(
onTap: () {
HapticFeedback.heavyImpact();
ref
.read(hapticFeedbackProvider.notifier)
.heavyImpact();
context.pushRoute(
MemoryRoute(
memories: memories,

View File

@@ -9,6 +9,7 @@ import 'package:immich_mobile/modules/memories/ui/memory_card.dart';
import 'package:immich_mobile/modules/memories/ui/memory_epilogue.dart';
import 'package:immich_mobile/modules/memories/ui/memory_progress_indicator.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart';
@RoutePage()
@@ -127,7 +128,7 @@ class MemoryPage extends HookConsumerWidget {
}
Future<void> onAssetChanged(int otherIndex) async {
HapticFeedback.selectionClick();
ref.read(hapticFeedbackProvider.notifier).selectionClick();
currentAssetPage.value = otherIndex;
updateProgressText();
// Wait for page change animation to finish
@@ -169,7 +170,7 @@ class MemoryPage extends HookConsumerWidget {
scrollDirection: Axis.vertical,
controller: memoryPageController,
onPageChanged: (pageNumber) {
HapticFeedback.mediumImpact();
ref.read(hapticFeedbackProvider.notifier).mediumImpact();
if (pageNumber < memories.length) {
currentMemoryIndex.value = pageNumber;
currentMemory.value = memories[pageNumber];

View File

@@ -58,6 +58,7 @@ enum AppSettingsEnum<T> {
null,
false,
),
enableHapticFeedback<bool>(StoreKey.enableHapticFeedback, null, true),
;
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);

View File

@@ -23,36 +23,46 @@ class LanguageSettings extends HookConsumerWidget {
return ListView(
padding: const EdgeInsets.all(16),
children: [
DropdownMenu(
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
),
contentPadding: const EdgeInsets.only(left: 16),
),
menuStyle: MenuStyle(
shape: MaterialStatePropertyAll<OutlinedBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
),
),
menuHeight: context.height * 0.5,
hintText: "Languages",
label: const Text('Languages'),
dropdownMenuEntries: locales.keys
.map(
(countryName) => DropdownMenuEntry(
value: locales[countryName],
label: countryName,
LayoutBuilder(
builder: (context, constraints) {
return DropdownMenu(
width: constraints.maxWidth,
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
),
)
.toList(),
controller: textController,
onSelected: (value) {
if (value != null) {
selectedLocale.value = value;
}
contentPadding: const EdgeInsets.only(left: 16),
),
menuStyle: MenuStyle(
shape: MaterialStatePropertyAll<OutlinedBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
),
backgroundColor: MaterialStatePropertyAll<Color>(
context.isDarkTheme
? Colors.grey[900]!
: context.scaffoldBackgroundColor,
),
),
menuHeight: context.height * 0.5,
hintText: "Languages",
label: const Text('Languages'),
dropdownMenuEntries: locales.keys
.map(
(countryName) => DropdownMenuEntry(
value: locales[countryName],
label: countryName,
),
)
.toList(),
controller: textController,
onSelected: (value) {
if (value != null) {
selectedLocale.value = value;
}
},
);
},
),
const SizedBox(height: 16),

View File

@@ -0,0 +1,38 @@
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/settings/services/app_settings.service.dart';
import 'package:immich_mobile/modules/settings/ui/settings_sub_title.dart';
import 'package:immich_mobile/modules/settings/ui/settings_switch_list_tile.dart';
import 'package:immich_mobile/modules/settings/utils/app_settings_update_hook.dart';
class HapticSetting extends HookConsumerWidget {
const HapticSetting({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final hapticFeedbackSetting =
useAppSettingsState(AppSettingsEnum.enableHapticFeedback);
final isHapticFeedbackEnabled =
useValueNotifier(hapticFeedbackSetting.value);
onHapticFeedbackChange(bool isEnabled) {
hapticFeedbackSetting.value = isEnabled;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SettingsSubTitle(title: "haptic_feedback_title".tr()),
SettingsSwitchListTile(
valueNotifier: isHapticFeedbackEnabled,
title: 'haptic_feedback_switch'.tr(),
onChanged: onHapticFeedbackChange,
),
],
);
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/modules/settings/ui/preference_settings/haptic_setting.dart';
import 'package:immich_mobile/modules/settings/ui/preference_settings/theme_setting.dart';
import 'package:immich_mobile/modules/settings/ui/settings_sub_page_scaffold.dart';
@@ -11,6 +12,7 @@ class PreferenceSetting extends StatelessWidget {
Widget build(BuildContext context) {
const preferenceSettings = [
ThemeSetting(),
HapticSetting(),
];
return const SettingsSubPageScaffold(settings: preferenceSettings);

View File

@@ -191,6 +191,7 @@ enum StoreKey<T> {
selectedAlbumSortReverse<bool>(123, type: bool),
mapThemeMode<int>(124, type: int),
mapwithPartners<bool>(125, type: bool),
enableHapticFeedback<bool>(126, type: bool),
;
const StoreKey(

View File

@@ -0,0 +1,56 @@
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
final hapticFeedbackProvider =
StateNotifierProvider<HapticNotifier, void>((ref) {
return HapticNotifier(ref);
});
class HapticNotifier extends StateNotifier<void> {
void build() {}
final Ref _ref;
HapticNotifier(this._ref) : super(null);
selectionClick() {
if (_ref
.read(appSettingsServiceProvider)
.getSetting(AppSettingsEnum.enableHapticFeedback)) {
HapticFeedback.selectionClick();
}
}
lightImpact() {
if (_ref
.read(appSettingsServiceProvider)
.getSetting(AppSettingsEnum.enableHapticFeedback)) {
HapticFeedback.lightImpact();
}
}
mediumImpact() {
if (_ref
.read(appSettingsServiceProvider)
.getSetting(AppSettingsEnum.enableHapticFeedback)) {
HapticFeedback.mediumImpact();
}
}
heavyImpact() {
if (_ref
.read(appSettingsServiceProvider)
.getSetting(AppSettingsEnum.enableHapticFeedback)) {
HapticFeedback.heavyImpact();
}
}
vibrate() {
if (_ref
.read(appSettingsServiceProvider)
.getSetting(AppSettingsEnum.enableHapticFeedback)) {
HapticFeedback.vibrate();
}
}
}

View File

@@ -1,13 +1,13 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart';
import 'package:immich_mobile/modules/home/providers/multiselect.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart';
import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/shared/providers/tab.provider.dart';
@RoutePage()
@@ -53,7 +53,7 @@ class TabControllerPage extends HookConsumerWidget {
scrollToTopNotifierProvider.scrollToTop();
}
HapticFeedback.selectionClick();
ref.read(hapticFeedbackProvider.notifier).selectionClick();
tabsRouter.setActiveIndex(index);
ref.read(tabProvider.notifier).state = TabEnum.values[index];
},
@@ -107,7 +107,7 @@ class TabControllerPage extends HookConsumerWidget {
scrollToTopNotifierProvider.scrollToTop();
}
HapticFeedback.selectionClick();
ref.read(hapticFeedbackProvider.notifier).selectionClick();
tabsRouter.setActiveIndex(index);
ref.read(tabProvider.notifier).state = TabEnum.values[index];
},

View File

@@ -26,6 +26,7 @@ doc/AssetBulkUploadCheckDto.md
doc/AssetBulkUploadCheckItem.md
doc/AssetBulkUploadCheckResponseDto.md
doc/AssetBulkUploadCheckResult.md
doc/AssetDeltaSyncResponseDto.md
doc/AssetFaceResponseDto.md
doc/AssetFaceUpdateDto.md
doc/AssetFaceUpdateItem.md
@@ -151,6 +152,7 @@ doc/SharedLinkType.md
doc/SignUpDto.md
doc/SmartInfoResponseDto.md
doc/SmartSearchDto.md
doc/SyncApi.md
doc/SystemConfigApi.md
doc/SystemConfigDto.md
doc/SystemConfigFFmpegDto.md
@@ -221,6 +223,7 @@ lib/api/person_api.dart
lib/api/search_api.dart
lib/api/server_info_api.dart
lib/api/shared_link_api.dart
lib/api/sync_api.dart
lib/api/system_config_api.dart
lib/api/tag_api.dart
lib/api/timeline_api.dart
@@ -253,6 +256,7 @@ lib/model/asset_bulk_upload_check_dto.dart
lib/model/asset_bulk_upload_check_item.dart
lib/model/asset_bulk_upload_check_response_dto.dart
lib/model/asset_bulk_upload_check_result.dart
lib/model/asset_delta_sync_response_dto.dart
lib/model/asset_face_response_dto.dart
lib/model/asset_face_update_dto.dart
lib/model/asset_face_update_item.dart
@@ -435,6 +439,7 @@ test/asset_bulk_upload_check_dto_test.dart
test/asset_bulk_upload_check_item_test.dart
test/asset_bulk_upload_check_response_dto_test.dart
test/asset_bulk_upload_check_result_test.dart
test/asset_delta_sync_response_dto_test.dart
test/asset_face_response_dto_test.dart
test/asset_face_update_dto_test.dart
test/asset_face_update_item_test.dart
@@ -560,6 +565,7 @@ test/shared_link_type_test.dart
test/sign_up_dto_test.dart
test/smart_info_response_dto_test.dart
test/smart_search_dto_test.dart
test/sync_api_test.dart
test/system_config_api_test.dart
test/system_config_dto_test.dart
test/system_config_f_fmpeg_dto_test.dart

View File

@@ -192,6 +192,8 @@ Class | Method | HTTP request | Description
*SharedLinkApi* | [**removeSharedLink**](doc//SharedLinkApi.md#removesharedlink) | **DELETE** /shared-link/{id} |
*SharedLinkApi* | [**removeSharedLinkAssets**](doc//SharedLinkApi.md#removesharedlinkassets) | **DELETE** /shared-link/{id}/assets |
*SharedLinkApi* | [**updateSharedLink**](doc//SharedLinkApi.md#updatesharedlink) | **PATCH** /shared-link/{id} |
*SyncApi* | [**getAllForUserFullSync**](doc//SyncApi.md#getallforuserfullsync) | **GET** /sync/full-sync |
*SyncApi* | [**getDeltaSync**](doc//SyncApi.md#getdeltasync) | **GET** /sync/delta-sync |
*SystemConfigApi* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config |
*SystemConfigApi* | [**getConfigDefaults**](doc//SystemConfigApi.md#getconfigdefaults) | **GET** /system-config/defaults |
*SystemConfigApi* | [**getMapStyle**](doc//SystemConfigApi.md#getmapstyle) | **GET** /system-config/map/style.json |
@@ -243,6 +245,7 @@ Class | Method | HTTP request | Description
- [AssetBulkUploadCheckItem](doc//AssetBulkUploadCheckItem.md)
- [AssetBulkUploadCheckResponseDto](doc//AssetBulkUploadCheckResponseDto.md)
- [AssetBulkUploadCheckResult](doc//AssetBulkUploadCheckResult.md)
- [AssetDeltaSyncResponseDto](doc//AssetDeltaSyncResponseDto.md)
- [AssetFaceResponseDto](doc//AssetFaceResponseDto.md)
- [AssetFaceUpdateDto](doc//AssetFaceUpdateDto.md)
- [AssetFaceUpdateItem](doc//AssetFaceUpdateItem.md)

View File

@@ -0,0 +1,17 @@
# openapi.model.AssetDeltaSyncResponseDto
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**deleted** | **List<String>** | | [default to const []]
**needsFullSync** | **bool** | |
**upserted** | [**List<AssetResponseDto>**](AssetResponseDto.md) | | [default to const []]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

135
mobile/openapi/doc/SyncApi.md generated Normal file
View File

@@ -0,0 +1,135 @@
# openapi.api.SyncApi
## Load the API package
```dart
import 'package:openapi/api.dart';
```
All URIs are relative to */api*
Method | HTTP request | Description
------------- | ------------- | -------------
[**getAllForUserFullSync**](SyncApi.md#getallforuserfullsync) | **GET** /sync/full-sync |
[**getDeltaSync**](SyncApi.md#getdeltasync) | **GET** /sync/delta-sync |
# **getAllForUserFullSync**
> List<AssetResponseDto> getAllForUserFullSync(limit, updatedUntil, lastCreationDate, lastId, userId)
### Example
```dart
import 'package:openapi/api.dart';
// TODO Configure API key authorization: cookie
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
// TODO Configure API key authorization: api_key
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
// TODO Configure HTTP Bearer authorization: bearer
// Case 1. Use String Token
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
// Case 2. Use Function which generate token.
// String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = SyncApi();
final limit = 56; // int |
final updatedUntil = 2013-10-20T19:20:30+01:00; // DateTime |
final lastCreationDate = 2013-10-20T19:20:30+01:00; // DateTime |
final lastId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final userId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
try {
final result = api_instance.getAllForUserFullSync(limit, updatedUntil, lastCreationDate, lastId, userId);
print(result);
} catch (e) {
print('Exception when calling SyncApi->getAllForUserFullSync: $e\n');
}
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**limit** | **int**| |
**updatedUntil** | **DateTime**| |
**lastCreationDate** | **DateTime**| | [optional]
**lastId** | **String**| | [optional]
**userId** | **String**| | [optional]
### Return type
[**List<AssetResponseDto>**](AssetResponseDto.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)
# **getDeltaSync**
> AssetDeltaSyncResponseDto getDeltaSync(updatedAfter, userIds)
### Example
```dart
import 'package:openapi/api.dart';
// TODO Configure API key authorization: cookie
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
// TODO Configure API key authorization: api_key
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
// TODO Configure HTTP Bearer authorization: bearer
// Case 1. Use String Token
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
// Case 2. Use Function which generate token.
// String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = SyncApi();
final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final userIds = []; // List<String> |
try {
final result = api_instance.getDeltaSync(updatedAfter, userIds);
print(result);
} catch (e) {
print('Exception when calling SyncApi->getDeltaSync: $e\n');
}
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**updatedAfter** | **DateTime**| |
**userIds** | [**List<String>**](String.md)| | [default to const []]
### Return type
[**AssetDeltaSyncResponseDto**](AssetDeltaSyncResponseDto.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)

View File

@@ -46,6 +46,7 @@ part 'api/person_api.dart';
part 'api/search_api.dart';
part 'api/server_info_api.dart';
part 'api/shared_link_api.dart';
part 'api/sync_api.dart';
part 'api/system_config_api.dart';
part 'api/tag_api.dart';
part 'api/timeline_api.dart';
@@ -71,6 +72,7 @@ part 'model/asset_bulk_upload_check_dto.dart';
part 'model/asset_bulk_upload_check_item.dart';
part 'model/asset_bulk_upload_check_response_dto.dart';
part 'model/asset_bulk_upload_check_result.dart';
part 'model/asset_delta_sync_response_dto.dart';
part 'model/asset_face_response_dto.dart';
part 'model/asset_face_update_dto.dart';
part 'model/asset_face_update_item.dart';

150
mobile/openapi/lib/api/sync_api.dart generated Normal file
View File

@@ -0,0 +1,150 @@
//
// 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 SyncApi {
SyncApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
final ApiClient apiClient;
/// Performs an HTTP 'GET /sync/full-sync' operation and returns the [Response].
/// Parameters:
///
/// * [int] limit (required):
///
/// * [DateTime] updatedUntil (required):
///
/// * [DateTime] lastCreationDate:
///
/// * [String] lastId:
///
/// * [String] userId:
Future<Response> getAllForUserFullSyncWithHttpInfo(int limit, DateTime updatedUntil, { DateTime? lastCreationDate, String? lastId, String? userId, }) async {
// ignore: prefer_const_declarations
final path = r'/sync/full-sync';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (lastCreationDate != null) {
queryParams.addAll(_queryParams('', 'lastCreationDate', lastCreationDate));
}
if (lastId != null) {
queryParams.addAll(_queryParams('', 'lastId', lastId));
}
queryParams.addAll(_queryParams('', 'limit', limit));
queryParams.addAll(_queryParams('', 'updatedUntil', updatedUntil));
if (userId != null) {
queryParams.addAll(_queryParams('', 'userId', userId));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [int] limit (required):
///
/// * [DateTime] updatedUntil (required):
///
/// * [DateTime] lastCreationDate:
///
/// * [String] lastId:
///
/// * [String] userId:
Future<List<AssetResponseDto>?> getAllForUserFullSync(int limit, DateTime updatedUntil, { DateTime? lastCreationDate, String? lastId, String? userId, }) async {
final response = await getAllForUserFullSyncWithHttpInfo(limit, updatedUntil, lastCreationDate: lastCreationDate, lastId: lastId, userId: userId, );
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) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<AssetResponseDto>') as List)
.cast<AssetResponseDto>()
.toList(growable: false);
}
return null;
}
/// Performs an HTTP 'GET /sync/delta-sync' operation and returns the [Response].
/// Parameters:
///
/// * [DateTime] updatedAfter (required):
///
/// * [List<String>] userIds (required):
Future<Response> getDeltaSyncWithHttpInfo(DateTime updatedAfter, List<String> userIds,) async {
// ignore: prefer_const_declarations
final path = r'/sync/delta-sync';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter));
queryParams.addAll(_queryParams('multi', 'userIds', userIds));
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [DateTime] updatedAfter (required):
///
/// * [List<String>] userIds (required):
Future<AssetDeltaSyncResponseDto?> getDeltaSync(DateTime updatedAfter, List<String> userIds,) async {
final response = await getDeltaSyncWithHttpInfo(updatedAfter, userIds,);
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), 'AssetDeltaSyncResponseDto',) as AssetDeltaSyncResponseDto;
}
return null;
}
}

View File

@@ -220,6 +220,8 @@ class ApiClient {
return AssetBulkUploadCheckResponseDto.fromJson(value);
case 'AssetBulkUploadCheckResult':
return AssetBulkUploadCheckResult.fromJson(value);
case 'AssetDeltaSyncResponseDto':
return AssetDeltaSyncResponseDto.fromJson(value);
case 'AssetFaceResponseDto':
return AssetFaceResponseDto.fromJson(value);
case 'AssetFaceUpdateDto':

View File

@@ -0,0 +1,116 @@
//
// 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 AssetDeltaSyncResponseDto {
/// Returns a new [AssetDeltaSyncResponseDto] instance.
AssetDeltaSyncResponseDto({
this.deleted = const [],
required this.needsFullSync,
this.upserted = const [],
});
List<String> deleted;
bool needsFullSync;
List<AssetResponseDto> upserted;
@override
bool operator ==(Object other) => identical(this, other) || other is AssetDeltaSyncResponseDto &&
_deepEquality.equals(other.deleted, deleted) &&
other.needsFullSync == needsFullSync &&
_deepEquality.equals(other.upserted, upserted);
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(deleted.hashCode) +
(needsFullSync.hashCode) +
(upserted.hashCode);
@override
String toString() => 'AssetDeltaSyncResponseDto[deleted=$deleted, needsFullSync=$needsFullSync, upserted=$upserted]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'deleted'] = this.deleted;
json[r'needsFullSync'] = this.needsFullSync;
json[r'upserted'] = this.upserted;
return json;
}
/// Returns a new [AssetDeltaSyncResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static AssetDeltaSyncResponseDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return AssetDeltaSyncResponseDto(
deleted: json[r'deleted'] is Iterable
? (json[r'deleted'] as Iterable).cast<String>().toList(growable: false)
: const [],
needsFullSync: mapValueOfType<bool>(json, r'needsFullSync')!,
upserted: AssetResponseDto.listFromJson(json[r'upserted']),
);
}
return null;
}
static List<AssetDeltaSyncResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <AssetDeltaSyncResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = AssetDeltaSyncResponseDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, AssetDeltaSyncResponseDto> mapFromJson(dynamic json) {
final map = <String, AssetDeltaSyncResponseDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = AssetDeltaSyncResponseDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of AssetDeltaSyncResponseDto-objects as value to a dart map
static Map<String, List<AssetDeltaSyncResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<AssetDeltaSyncResponseDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = AssetDeltaSyncResponseDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'deleted',
'needsFullSync',
'upserted',
};
}

View File

@@ -0,0 +1,37 @@
//
// 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
import 'package:openapi/api.dart';
import 'package:test/test.dart';
// tests for AssetDeltaSyncResponseDto
void main() {
// final instance = AssetDeltaSyncResponseDto();
group('test AssetDeltaSyncResponseDto', () {
// List<String> deleted (default value: const [])
test('to test the property `deleted`', () async {
// TODO
});
// bool needsFullSync
test('to test the property `needsFullSync`', () async {
// TODO
});
// List<AssetResponseDto> upserted (default value: const [])
test('to test the property `upserted`', () async {
// TODO
});
});
}

31
mobile/openapi/test/sync_api_test.dart generated Normal file
View File

@@ -0,0 +1,31 @@
//
// 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
import 'package:openapi/api.dart';
import 'package:test/test.dart';
/// tests for SyncApi
void main() {
// final instance = SyncApi();
group('tests for SyncApi', () {
//Future<List<AssetResponseDto>> getAllForUserFullSync(int limit, DateTime updatedUntil, { DateTime lastCreationDate, String lastId, String userId }) async
test('test getAllForUserFullSync', () async {
// TODO
});
//Future<AssetDeltaSyncResponseDto> getDeltaSync(DateTime updatedAfter, List<String> userIds) async
test('test getDeltaSync', () async {
// TODO
});
});
}

View File

@@ -12,7 +12,8 @@ dependencies:
sdk: flutter
path_provider_ios:
# TODO: change to stable after 3.16 support
# TODO: upgrade to stable after 3.0.1 is released. 3.0.0 is broken
# https://github.com/fluttercandies/flutter_photo_manager/pull/990#issuecomment-2058066427
photo_manager: ^3.0.0-dev.5
photo_manager_image_provider: ^2.1.0
flutter_hooks: ^0.20.4