From ae7e582ec825fc554c157f39035cd30e4facd0c0 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 13 Jul 2022 07:23:48 -0500 Subject: [PATCH] Refactor mobile to use OpenApi generated SDK (#336) --- README.md | 11 +- mobile/analysis_options.yaml | 10 +- mobile/assets/i18n/en-US.json | 4 +- mobile/lib/main.dart | 21 +- .../image_viewer_page_state.provider.dart | 18 +- .../services/image_viewer.service.dart | 30 +- .../asset_viewer/ui/exif_bottom_sheet.dart | 33 +- .../asset_viewer/ui/remote_photo_view.dart | 70 ++-- .../asset_viewer/ui/top_control_app_bar.dart | 29 +- .../asset_viewer/views/image_viewer_page.dart | 35 +- .../asset_viewer/views/video_viewer_page.dart | 24 +- .../backup/models/backup_state.model.dart | 12 +- .../check_duplicate_asset_response.model.dart | 48 --- .../backup/providers/backup.provider.dart | 69 ++-- .../backup/services/backup.service.dart | 108 +++--- .../modules/backup/ui/album_info_card.dart | 34 +- .../modules/backup/ui/backup_info_card.dart | 12 +- .../backup/views/album_preview_page.dart | 26 +- .../views/backup_album_selection_page.dart | 50 ++- .../backup/views/backup_controller_page.dart | 128 ++++--- .../views/failed_backup_status_page.dart | 22 +- .../models/delete_asset_response.model.dart | 55 --- .../models/get_all_asset_response.model.dart | 50 +-- .../home/models/home_page_state.model.dart | 30 +- .../providers/home_page_state.provider.dart | 29 +- .../upload_profile_image.provider.dart | 18 +- .../modules/home/services/asset.service.dart | 127 ++----- .../home/ui/control_bottom_app_bar.dart | 16 +- .../lib/modules/home/ui/daily_title_text.dart | 16 +- .../home/ui/disable_multi_select_button.dart | 21 +- .../modules/home/ui/draggable_scrollbar.dart | 114 +++--- mobile/lib/modules/home/ui/image_grid.dart | 6 +- .../modules/home/ui/immich_sliver_appbar.dart | 9 +- .../modules/home/ui/monthly_title_text.dart | 4 +- .../lib/modules/home/ui/profile_drawer.dart | 43 ++- .../lib/modules/home/ui/thumbnail_image.dart | 28 +- mobile/lib/modules/home/views/home_page.dart | 15 +- .../models/authentication_state.model.dart | 52 +-- .../models/hive_saved_login_info.model.dart | 11 +- .../providers/authentication.provider.dart | 151 ++++---- .../login/ui/change_password_form.dart | 53 +-- mobile/lib/modules/login/ui/login_form.dart | 122 +++--- .../search/models/curated_location.model.dart | 84 ----- .../search/models/curated_object.model.dart | 85 ----- .../search_result_page_state.model.dart | 13 +- .../providers/search_page_state.provider.dart | 7 +- .../search_result_page.provider.dart | 36 +- .../search/services/search.service.dart | 69 ++-- mobile/lib/modules/search/ui/search_bar.dart | 15 +- .../search/ui/thumbnail_with_info.dart | 13 +- .../lib/modules/search/views/search_page.dart | 41 +- .../search/views/search_result_page.dart | 46 ++- .../asset_selection_page_result.model.dart | 41 +- .../models/asset_selection_state.model.dart | 69 +--- .../sharing/models/shared_album.model.dart | 117 ------ .../providers/album_title.provider.dart | 3 +- .../providers/album_viewer.provider.dart | 5 +- .../providers/asset_selection.provider.dart | 59 +-- .../providers/shared_album.provider.dart | 18 +- .../suggested_shared_users.provider.dart | 6 +- .../services/shared_album.service.dart | 143 ++++--- .../ui/album_action_outlined_button.dart | 17 +- .../sharing/ui/album_title_text_field.dart | 5 +- .../sharing/ui/album_viewer_appbar.dart | 10 +- .../ui/album_viewer_editable_title.dart | 27 +- .../sharing/ui/album_viewer_thumbnail.dart | 32 +- .../sharing/ui/asset_grid_by_month.dart | 4 +- .../modules/sharing/ui/month_group_title.dart | 18 +- .../sharing/ui/selection_thumbnail_image.dart | 14 +- .../ui/shared_album_thumbnail_image.dart | 8 +- .../sharing/ui/sharing_sliver_appbar.dart | 6 +- .../sharing/views/album_viewer_page.dart | 75 ++-- .../views/create_shared_album_page.dart | 138 +++---- ...lect_additional_user_for_sharing_page.dart | 43 ++- .../views/select_user_for_sharing_page.dart | 59 +-- .../modules/sharing/views/sharing_page.dart | 26 +- mobile/lib/routing/auth_guard.dart | 22 +- mobile/lib/routing/router.dart | 11 +- mobile/lib/routing/router.gr.dart | 12 +- .../lib/routing/tab_navigation_observer.dart | 4 +- .../lib/shared/models/device_info.model.dart | 100 ----- mobile/lib/shared/models/exif.model.dart | 212 ----------- .../lib/shared/models/immich_asset.model.dart | 110 ------ .../models/immich_asset_with_exif.model.dart | 135 ------- .../lib/shared/models/mapbox_info.model.dart | 55 --- .../lib/shared/models/server_info.model.dart | 99 ----- .../models/server_info_state.model.dart | 42 +-- .../shared/models/server_version.model.dart | 73 ---- mobile/lib/shared/models/user.model.dart | 76 ---- .../lib/shared/providers/asset.provider.dart | 38 +- .../providers/release_info.provider.dart | 3 +- .../providers/server_info.provider.dart | 19 +- .../shared/providers/websocket.provider.dart | 16 +- mobile/lib/shared/services/api.service.dart | 30 ++ .../shared/services/device_info.service.dart | 7 +- .../services/local_storage.service.dart | 21 -- .../lib/shared/services/network.service.dart | 9 +- .../shared/services/server_info.service.dart | 38 +- mobile/lib/shared/services/user.service.dart | 79 ++-- ...ch_sliver_persistent_app_bar_delegate.dart | 5 +- mobile/lib/shared/views/splash_screen.dart | 30 +- .../lib/shared/views/tab_controller_page.dart | 23 +- .../views/version_announcement_overlay.dart | 59 +-- mobile/openapi/.gitignore | 17 + mobile/openapi/.openapi-generator-ignore | 23 ++ mobile/openapi/.openapi-generator/FILES | 100 +++++ mobile/openapi/.openapi-generator/VERSION | 1 + mobile/openapi/.travis.yml | 14 + mobile/openapi/README.md | Bin 0 -> 7409 bytes mobile/openapi/analysis_options.yaml | 0 mobile/openapi/doc/AddAssetsDto.md | Bin 0 -> 437 bytes mobile/openapi/doc/AddUsersDto.md | Bin 0 -> 441 bytes mobile/openapi/doc/AdminSignupResponseDto.md | Bin 0 -> 541 bytes mobile/openapi/doc/AlbumApi.md | Bin 0 -> 13123 bytes mobile/openapi/doc/AlbumResponseDto.md | Bin 0 -> 761 bytes mobile/openapi/doc/AssetApi.md | Bin 0 -> 18438 bytes .../openapi/doc/AssetFileUploadResponseDto.md | Bin 0 -> 418 bytes mobile/openapi/doc/AssetResponseDto.md | Bin 0 -> 1027 bytes mobile/openapi/doc/AssetTypeEnum.md | Bin 0 -> 379 bytes mobile/openapi/doc/AuthenticationApi.md | Bin 0 -> 3637 bytes mobile/openapi/doc/CheckDuplicateAssetDto.md | Bin 0 -> 457 bytes .../doc/CheckDuplicateAssetResponseDto.md | Bin 0 -> 425 bytes mobile/openapi/doc/CreateAlbumDto.md | Bin 0 -> 562 bytes mobile/openapi/doc/CreateDeviceInfoDto.md | Bin 0 -> 525 bytes .../doc/CreateProfileImageResponseDto.md | Bin 0 -> 465 bytes mobile/openapi/doc/CreateUserDto.md | Bin 0 -> 505 bytes .../doc/CuratedLocationsResponseDto.md | Bin 0 -> 550 bytes .../openapi/doc/CuratedObjectsResponseDto.md | Bin 0 -> 550 bytes mobile/openapi/doc/DeleteAssetDto.md | Bin 0 -> 434 bytes mobile/openapi/doc/DeleteAssetResponseDto.md | Bin 0 -> 479 bytes mobile/openapi/doc/DeleteAssetStatus.md | Bin 0 -> 383 bytes mobile/openapi/doc/DeviceInfoApi.md | Bin 0 -> 3247 bytes mobile/openapi/doc/DeviceInfoResponseDto.md | Bin 0 -> 602 bytes mobile/openapi/doc/DeviceTypeEnum.md | Bin 0 -> 380 bytes mobile/openapi/doc/ExifResponseDto.md | Bin 0 -> 1263 bytes mobile/openapi/doc/LoginCredentialDto.md | Bin 0 -> 445 bytes mobile/openapi/doc/LoginResponseDto.md | Bin 0 -> 744 bytes mobile/openapi/doc/RemoveAssetsDto.md | Bin 0 -> 440 bytes mobile/openapi/doc/SearchAssetDto.md | Bin 0 -> 414 bytes mobile/openapi/doc/ServerInfoApi.md | Bin 0 -> 2790 bytes mobile/openapi/doc/ServerInfoResponseDto.md | Bin 0 -> 630 bytes mobile/openapi/doc/ServerPingResponse.md | Bin 0 -> 422 bytes mobile/openapi/doc/ServerVersionReponseDto.md | Bin 0 -> 494 bytes mobile/openapi/doc/SignUpDto.md | Bin 0 -> 501 bytes mobile/openapi/doc/SmartInfoResponseDto.md | Bin 0 -> 558 bytes mobile/openapi/doc/UpdateAlbumDto.md | Bin 0 -> 444 bytes mobile/openapi/doc/UpdateDeviceInfoDto.md | Bin 0 -> 525 bytes mobile/openapi/doc/UpdateUserDto.md | Bin 0 -> 679 bytes mobile/openapi/doc/UserApi.md | Bin 0 -> 8726 bytes mobile/openapi/doc/UserCountResponseDto.md | Bin 0 -> 416 bytes mobile/openapi/doc/UserResponseDto.md | Bin 0 -> 645 bytes .../doc/ValidateAccessTokenResponseDto.md | Bin 0 -> 428 bytes mobile/openapi/git_push.sh | 57 +++ mobile/openapi/lib/api.dart | Bin 0 -> 2673 bytes mobile/openapi/lib/api/album_api.dart | Bin 0 -> 14449 bytes mobile/openapi/lib/api/asset_api.dart | Bin 0 -> 22949 bytes .../openapi/lib/api/authentication_api.dart | Bin 0 -> 5132 bytes mobile/openapi/lib/api/device_info_api.dart | Bin 0 -> 3841 bytes mobile/openapi/lib/api/server_info_api.dart | Bin 0 -> 4598 bytes mobile/openapi/lib/api/user_api.dart | Bin 0 -> 11424 bytes mobile/openapi/lib/api_client.dart | Bin 0 -> 12268 bytes mobile/openapi/lib/api_exception.dart | Bin 0 -> 834 bytes mobile/openapi/lib/api_helper.dart | Bin 0 -> 3446 bytes mobile/openapi/lib/auth/api_key_auth.dart | Bin 0 -> 1122 bytes mobile/openapi/lib/auth/authentication.dart | Bin 0 -> 505 bytes mobile/openapi/lib/auth/http_basic_auth.dart | Bin 0 -> 740 bytes mobile/openapi/lib/auth/http_bearer_auth.dart | Bin 0 -> 1217 bytes mobile/openapi/lib/auth/oauth.dart | Bin 0 -> 593 bytes mobile/openapi/lib/model/add_assets_dto.dart | Bin 0 -> 3369 bytes mobile/openapi/lib/model/add_users_dto.dart | Bin 0 -> 3414 bytes .../lib/model/admin_signup_response_dto.dart | Bin 0 -> 4390 bytes .../openapi/lib/model/album_response_dto.dart | Bin 0 -> 5357 bytes .../model/asset_file_upload_response_dto.dart | Bin 0 -> 3506 bytes .../openapi/lib/model/asset_response_dto.dart | Bin 0 -> 8545 bytes mobile/openapi/lib/model/asset_type_enum.dart | Bin 0 -> 2851 bytes .../lib/model/check_duplicate_asset_dto.dart | Bin 0 -> 3802 bytes .../check_duplicate_asset_response_dto.dart | Bin 0 -> 3642 bytes .../openapi/lib/model/create_album_dto.dart | Bin 0 -> 4056 bytes .../lib/model/create_device_info_dto.dart | Bin 0 -> 4439 bytes .../create_profile_image_response_dto.dart | Bin 0 -> 3954 bytes mobile/openapi/lib/model/create_user_dto.dart | Bin 0 -> 4026 bytes .../model/curated_locations_response_dto.dart | Bin 0 -> 4538 bytes .../model/curated_objects_response_dto.dart | Bin 0 -> 4522 bytes .../openapi/lib/model/delete_asset_dto.dart | Bin 0 -> 3344 bytes .../lib/model/delete_asset_response_dto.dart | Bin 0 -> 3661 bytes .../lib/model/delete_asset_status.dart | Bin 0 -> 2738 bytes .../lib/model/device_info_response_dto.dart | Bin 0 -> 4685 bytes .../openapi/lib/model/device_type_enum.dart | Bin 0 -> 2755 bytes .../openapi/lib/model/exif_response_dto.dart | Bin 0 -> 10335 bytes .../lib/model/login_credential_dto.dart | Bin 0 -> 3626 bytes .../openapi/lib/model/login_response_dto.dart | Bin 0 -> 5342 bytes .../openapi/lib/model/remove_assets_dto.dart | Bin 0 -> 3429 bytes .../openapi/lib/model/search_asset_dto.dart | Bin 0 -> 3362 bytes .../lib/model/server_info_response_dto.dart | Bin 0 -> 5260 bytes .../lib/model/server_ping_response.dart | Bin 0 -> 3358 bytes .../lib/model/server_version_reponse_dto.dart | Bin 0 -> 4091 bytes mobile/openapi/lib/model/sign_up_dto.dart | Bin 0 -> 3946 bytes .../lib/model/smart_info_response_dto.dart | Bin 0 -> 4535 bytes .../openapi/lib/model/update_album_dto.dart | Bin 0 -> 3582 bytes .../lib/model/update_device_info_dto.dart | Bin 0 -> 4439 bytes mobile/openapi/lib/model/update_user_dto.dart | Bin 0 -> 7550 bytes .../lib/model/user_count_response_dto.dart | Bin 0 -> 3464 bytes .../openapi/lib/model/user_response_dto.dart | Bin 0 -> 5202 bytes .../validate_access_token_response_dto.dart | Bin 0 -> 3678 bytes mobile/openapi/pubspec.lock | 355 ++++++++++++++++++ mobile/openapi/pubspec.yaml | 16 + mobile/openapi/test/add_assets_dto_test.dart | Bin 0 -> 592 bytes mobile/openapi/test/add_users_dto_test.dart | Bin 0 -> 599 bytes .../test/admin_signup_response_dto_test.dart | Bin 0 -> 980 bytes mobile/openapi/test/album_api_test.dart | Bin 0 -> 1767 bytes .../openapi/test/album_response_dto_test.dart | Bin 0 -> 1374 bytes mobile/openapi/test/asset_api_test.dart | Bin 0 -> 2626 bytes .../asset_file_upload_response_dto_test.dart | Bin 0 -> 590 bytes .../openapi/test/asset_response_dto_test.dart | Bin 0 -> 2136 bytes mobile/openapi/test/asset_type_enum_test.dart | Bin 0 -> 423 bytes .../openapi/test/authentication_api_test.dart | Bin 0 -> 877 bytes .../test/check_duplicate_asset_dto_test.dart | Bin 0 -> 701 bytes ...eck_duplicate_asset_response_dto_test.dart | Bin 0 -> 610 bytes .../openapi/test/create_album_dto_test.dart | Bin 0 -> 852 bytes .../test/create_device_info_dto_test.dart | Bin 0 -> 793 bytes ...reate_profile_image_response_dto_test.dart | Bin 0 -> 724 bytes mobile/openapi/test/create_user_dto_test.dart | Bin 0 -> 862 bytes .../curated_locations_response_dto_test.dart | Bin 0 -> 1003 bytes .../curated_objects_response_dto_test.dart | Bin 0 -> 1001 bytes .../openapi/test/delete_asset_dto_test.dart | Bin 0 -> 588 bytes .../test/delete_asset_response_dto_test.dart | Bin 0 -> 686 bytes .../test/delete_asset_status_test.dart | Bin 0 -> 431 bytes mobile/openapi/test/device_info_api_test.dart | Bin 0 -> 775 bytes .../test/device_info_response_dto_test.dart | Bin 0 -> 1204 bytes .../openapi/test/device_type_enum_test.dart | Bin 0 -> 425 bytes .../openapi/test/exif_response_dto_test.dart | Bin 0 -> 2495 bytes .../test/login_credential_dto_test.dart | Bin 0 -> 673 bytes .../openapi/test/login_response_dto_test.dart | Bin 0 -> 1319 bytes .../openapi/test/remove_assets_dto_test.dart | Bin 0 -> 601 bytes .../openapi/test/search_asset_dto_test.dart | Bin 0 -> 570 bytes mobile/openapi/test/server_info_api_test.dart | Bin 0 -> 835 bytes .../test/server_info_response_dto_test.dart | Bin 0 -> 1237 bytes .../test/server_ping_response_test.dart | Bin 0 -> 568 bytes .../test/server_version_reponse_dto_test.dart | Bin 0 -> 862 bytes mobile/openapi/test/sign_up_dto_test.dart | Bin 0 -> 850 bytes .../test/smart_info_response_dto_test.dart | Bin 0 -> 828 bytes .../openapi/test/update_album_dto_test.dart | Bin 0 -> 667 bytes .../test/update_device_info_dto_test.dart | Bin 0 -> 793 bytes mobile/openapi/test/update_user_dto_test.dart | Bin 0 -> 1193 bytes mobile/openapi/test/user_api_test.dart | Bin 0 -> 1375 bytes .../test/user_count_response_dto_test.dart | Bin 0 -> 583 bytes .../openapi/test/user_response_dto_test.dart | Bin 0 -> 1296 bytes ...lidate_access_token_response_dto_test.dart | Bin 0 -> 616 bytes mobile/pubspec.lock | 7 + mobile/pubspec.yaml | 7 + .../src/api-v1/asset/asset.controller.ts | 13 +- .../immich/src/api-v1/asset/asset.service.ts | 11 +- .../api-v1/asset/dto/asset-file-upload.dto.ts | 4 +- .../src/api-v1/asset/dto/create-asset.dto.ts | 2 + .../asset/dto/get-all-asset-query.dto.ts | 6 - .../asset/dto/get-all-asset-response.dto.ts | 8 - .../asset/dto/get-new-asset-query.dto.ts | 6 - .../src/api-v1/asset/dto/update-asset.dto.ts | 4 - .../src/api-v1/asset/dto/update-exif.dto.ts | 4 - .../asset/response-dto/asset-response.dto.ts | 3 + .../response-dto/delete-asset-response.dto.ts | 13 + .../asset/response-dto/exif-response.dto.ts | 40 +- .../validate-asset-token-response.dto,.ts | 5 +- .../device-info/dto/create-device-info.dto.ts | 2 + .../device-info/dto/update-device-info.dto.ts | 15 +- .../create-device-info-response.dto.ts | 7 +- .../response-dto/server-info-response.dto.ts | 10 + .../server-version-response.dto.ts | 5 + .../api-v1/server-info/server-info.service.ts | 2 +- .../user/dto/create-profile-image.dto.ts | 3 +- .../response-dto/user-count-response.dto.ts | 4 +- .../immich/src/api-v1/user/user.controller.ts | 6 +- server/immich-openapi-specs.json | 2 +- server/package.json | 4 +- web/src/api/open-api/api.ts | 252 ++++++++----- .../lib/components/forms/login-form.svelte | 4 - 276 files changed, 2473 insertions(+), 3003 deletions(-) delete mode 100644 mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart delete mode 100644 mobile/lib/modules/home/models/delete_asset_response.model.dart delete mode 100644 mobile/lib/modules/search/models/curated_location.model.dart delete mode 100644 mobile/lib/modules/search/models/curated_object.model.dart delete mode 100644 mobile/lib/modules/sharing/models/shared_album.model.dart delete mode 100644 mobile/lib/shared/models/device_info.model.dart delete mode 100644 mobile/lib/shared/models/exif.model.dart delete mode 100644 mobile/lib/shared/models/immich_asset.model.dart delete mode 100644 mobile/lib/shared/models/immich_asset_with_exif.model.dart delete mode 100644 mobile/lib/shared/models/mapbox_info.model.dart delete mode 100644 mobile/lib/shared/models/server_info.model.dart delete mode 100644 mobile/lib/shared/models/server_version.model.dart delete mode 100644 mobile/lib/shared/models/user.model.dart create mode 100644 mobile/lib/shared/services/api.service.dart delete mode 100644 mobile/lib/shared/services/local_storage.service.dart create mode 100644 mobile/openapi/.gitignore create mode 100644 mobile/openapi/.openapi-generator-ignore create mode 100644 mobile/openapi/.openapi-generator/FILES create mode 100644 mobile/openapi/.openapi-generator/VERSION create mode 100644 mobile/openapi/.travis.yml create mode 100644 mobile/openapi/README.md create mode 100644 mobile/openapi/analysis_options.yaml create mode 100644 mobile/openapi/doc/AddAssetsDto.md create mode 100644 mobile/openapi/doc/AddUsersDto.md create mode 100644 mobile/openapi/doc/AdminSignupResponseDto.md create mode 100644 mobile/openapi/doc/AlbumApi.md create mode 100644 mobile/openapi/doc/AlbumResponseDto.md create mode 100644 mobile/openapi/doc/AssetApi.md create mode 100644 mobile/openapi/doc/AssetFileUploadResponseDto.md create mode 100644 mobile/openapi/doc/AssetResponseDto.md create mode 100644 mobile/openapi/doc/AssetTypeEnum.md create mode 100644 mobile/openapi/doc/AuthenticationApi.md create mode 100644 mobile/openapi/doc/CheckDuplicateAssetDto.md create mode 100644 mobile/openapi/doc/CheckDuplicateAssetResponseDto.md create mode 100644 mobile/openapi/doc/CreateAlbumDto.md create mode 100644 mobile/openapi/doc/CreateDeviceInfoDto.md create mode 100644 mobile/openapi/doc/CreateProfileImageResponseDto.md create mode 100644 mobile/openapi/doc/CreateUserDto.md create mode 100644 mobile/openapi/doc/CuratedLocationsResponseDto.md create mode 100644 mobile/openapi/doc/CuratedObjectsResponseDto.md create mode 100644 mobile/openapi/doc/DeleteAssetDto.md create mode 100644 mobile/openapi/doc/DeleteAssetResponseDto.md create mode 100644 mobile/openapi/doc/DeleteAssetStatus.md create mode 100644 mobile/openapi/doc/DeviceInfoApi.md create mode 100644 mobile/openapi/doc/DeviceInfoResponseDto.md create mode 100644 mobile/openapi/doc/DeviceTypeEnum.md create mode 100644 mobile/openapi/doc/ExifResponseDto.md create mode 100644 mobile/openapi/doc/LoginCredentialDto.md create mode 100644 mobile/openapi/doc/LoginResponseDto.md create mode 100644 mobile/openapi/doc/RemoveAssetsDto.md create mode 100644 mobile/openapi/doc/SearchAssetDto.md create mode 100644 mobile/openapi/doc/ServerInfoApi.md create mode 100644 mobile/openapi/doc/ServerInfoResponseDto.md create mode 100644 mobile/openapi/doc/ServerPingResponse.md create mode 100644 mobile/openapi/doc/ServerVersionReponseDto.md create mode 100644 mobile/openapi/doc/SignUpDto.md create mode 100644 mobile/openapi/doc/SmartInfoResponseDto.md create mode 100644 mobile/openapi/doc/UpdateAlbumDto.md create mode 100644 mobile/openapi/doc/UpdateDeviceInfoDto.md create mode 100644 mobile/openapi/doc/UpdateUserDto.md create mode 100644 mobile/openapi/doc/UserApi.md create mode 100644 mobile/openapi/doc/UserCountResponseDto.md create mode 100644 mobile/openapi/doc/UserResponseDto.md create mode 100644 mobile/openapi/doc/ValidateAccessTokenResponseDto.md create mode 100644 mobile/openapi/git_push.sh create mode 100644 mobile/openapi/lib/api.dart create mode 100644 mobile/openapi/lib/api/album_api.dart create mode 100644 mobile/openapi/lib/api/asset_api.dart create mode 100644 mobile/openapi/lib/api/authentication_api.dart create mode 100644 mobile/openapi/lib/api/device_info_api.dart create mode 100644 mobile/openapi/lib/api/server_info_api.dart create mode 100644 mobile/openapi/lib/api/user_api.dart create mode 100644 mobile/openapi/lib/api_client.dart create mode 100644 mobile/openapi/lib/api_exception.dart create mode 100644 mobile/openapi/lib/api_helper.dart create mode 100644 mobile/openapi/lib/auth/api_key_auth.dart create mode 100644 mobile/openapi/lib/auth/authentication.dart create mode 100644 mobile/openapi/lib/auth/http_basic_auth.dart create mode 100644 mobile/openapi/lib/auth/http_bearer_auth.dart create mode 100644 mobile/openapi/lib/auth/oauth.dart create mode 100644 mobile/openapi/lib/model/add_assets_dto.dart create mode 100644 mobile/openapi/lib/model/add_users_dto.dart create mode 100644 mobile/openapi/lib/model/admin_signup_response_dto.dart create mode 100644 mobile/openapi/lib/model/album_response_dto.dart create mode 100644 mobile/openapi/lib/model/asset_file_upload_response_dto.dart create mode 100644 mobile/openapi/lib/model/asset_response_dto.dart create mode 100644 mobile/openapi/lib/model/asset_type_enum.dart create mode 100644 mobile/openapi/lib/model/check_duplicate_asset_dto.dart create mode 100644 mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart create mode 100644 mobile/openapi/lib/model/create_album_dto.dart create mode 100644 mobile/openapi/lib/model/create_device_info_dto.dart create mode 100644 mobile/openapi/lib/model/create_profile_image_response_dto.dart create mode 100644 mobile/openapi/lib/model/create_user_dto.dart create mode 100644 mobile/openapi/lib/model/curated_locations_response_dto.dart create mode 100644 mobile/openapi/lib/model/curated_objects_response_dto.dart create mode 100644 mobile/openapi/lib/model/delete_asset_dto.dart create mode 100644 mobile/openapi/lib/model/delete_asset_response_dto.dart create mode 100644 mobile/openapi/lib/model/delete_asset_status.dart create mode 100644 mobile/openapi/lib/model/device_info_response_dto.dart create mode 100644 mobile/openapi/lib/model/device_type_enum.dart create mode 100644 mobile/openapi/lib/model/exif_response_dto.dart create mode 100644 mobile/openapi/lib/model/login_credential_dto.dart create mode 100644 mobile/openapi/lib/model/login_response_dto.dart create mode 100644 mobile/openapi/lib/model/remove_assets_dto.dart create mode 100644 mobile/openapi/lib/model/search_asset_dto.dart create mode 100644 mobile/openapi/lib/model/server_info_response_dto.dart create mode 100644 mobile/openapi/lib/model/server_ping_response.dart create mode 100644 mobile/openapi/lib/model/server_version_reponse_dto.dart create mode 100644 mobile/openapi/lib/model/sign_up_dto.dart create mode 100644 mobile/openapi/lib/model/smart_info_response_dto.dart create mode 100644 mobile/openapi/lib/model/update_album_dto.dart create mode 100644 mobile/openapi/lib/model/update_device_info_dto.dart create mode 100644 mobile/openapi/lib/model/update_user_dto.dart create mode 100644 mobile/openapi/lib/model/user_count_response_dto.dart create mode 100644 mobile/openapi/lib/model/user_response_dto.dart create mode 100644 mobile/openapi/lib/model/validate_access_token_response_dto.dart create mode 100644 mobile/openapi/pubspec.lock create mode 100644 mobile/openapi/pubspec.yaml create mode 100644 mobile/openapi/test/add_assets_dto_test.dart create mode 100644 mobile/openapi/test/add_users_dto_test.dart create mode 100644 mobile/openapi/test/admin_signup_response_dto_test.dart create mode 100644 mobile/openapi/test/album_api_test.dart create mode 100644 mobile/openapi/test/album_response_dto_test.dart create mode 100644 mobile/openapi/test/asset_api_test.dart create mode 100644 mobile/openapi/test/asset_file_upload_response_dto_test.dart create mode 100644 mobile/openapi/test/asset_response_dto_test.dart create mode 100644 mobile/openapi/test/asset_type_enum_test.dart create mode 100644 mobile/openapi/test/authentication_api_test.dart create mode 100644 mobile/openapi/test/check_duplicate_asset_dto_test.dart create mode 100644 mobile/openapi/test/check_duplicate_asset_response_dto_test.dart create mode 100644 mobile/openapi/test/create_album_dto_test.dart create mode 100644 mobile/openapi/test/create_device_info_dto_test.dart create mode 100644 mobile/openapi/test/create_profile_image_response_dto_test.dart create mode 100644 mobile/openapi/test/create_user_dto_test.dart create mode 100644 mobile/openapi/test/curated_locations_response_dto_test.dart create mode 100644 mobile/openapi/test/curated_objects_response_dto_test.dart create mode 100644 mobile/openapi/test/delete_asset_dto_test.dart create mode 100644 mobile/openapi/test/delete_asset_response_dto_test.dart create mode 100644 mobile/openapi/test/delete_asset_status_test.dart create mode 100644 mobile/openapi/test/device_info_api_test.dart create mode 100644 mobile/openapi/test/device_info_response_dto_test.dart create mode 100644 mobile/openapi/test/device_type_enum_test.dart create mode 100644 mobile/openapi/test/exif_response_dto_test.dart create mode 100644 mobile/openapi/test/login_credential_dto_test.dart create mode 100644 mobile/openapi/test/login_response_dto_test.dart create mode 100644 mobile/openapi/test/remove_assets_dto_test.dart create mode 100644 mobile/openapi/test/search_asset_dto_test.dart create mode 100644 mobile/openapi/test/server_info_api_test.dart create mode 100644 mobile/openapi/test/server_info_response_dto_test.dart create mode 100644 mobile/openapi/test/server_ping_response_test.dart create mode 100644 mobile/openapi/test/server_version_reponse_dto_test.dart create mode 100644 mobile/openapi/test/sign_up_dto_test.dart create mode 100644 mobile/openapi/test/smart_info_response_dto_test.dart create mode 100644 mobile/openapi/test/update_album_dto_test.dart create mode 100644 mobile/openapi/test/update_device_info_dto_test.dart create mode 100644 mobile/openapi/test/update_user_dto_test.dart create mode 100644 mobile/openapi/test/user_api_test.dart create mode 100644 mobile/openapi/test/user_count_response_dto_test.dart create mode 100644 mobile/openapi/test/user_response_dto_test.dart create mode 100644 mobile/openapi/test/validate_access_token_response_dto_test.dart delete mode 100644 server/apps/immich/src/api-v1/asset/dto/get-all-asset-query.dto.ts delete mode 100644 server/apps/immich/src/api-v1/asset/dto/get-all-asset-response.dto.ts delete mode 100644 server/apps/immich/src/api-v1/asset/dto/get-new-asset-query.dto.ts delete mode 100644 server/apps/immich/src/api-v1/asset/dto/update-asset.dto.ts delete mode 100644 server/apps/immich/src/api-v1/asset/dto/update-exif.dto.ts create mode 100644 server/apps/immich/src/api-v1/asset/response-dto/delete-asset-response.dto.ts diff --git a/README.md b/README.md index d2d55073b4..3a3b7f1d78 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ To *update* docker-compose with newest image (if you have started the docker-com docker-compose -f ./docker/docker-compose.yml pull && docker-compose -f ./docker/docker-compose.yml up ``` -The server will be running at `http://your-ip:2283/api` through `Nginx` +The server will be running at `http://your-ip:2283/api` ## Step 3: Register User @@ -225,6 +225,15 @@ make dev # required Makefile installed on the system. All servers and web container are hot reload for quick feedback loop. +## Note for developers +### 1 - OpenAPI +OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). When you add a new or modify an existing endpoint, you must run the generate command below to update the client SDK. + +```bash +npm run api:generate # Run from server directory +``` +You can find the generated client SDK in the [`web/src/api`](web/src/api) for Typescript SDK and [`mobile/openapi`](mobile/openapi) for Dart SDK. + # Support If you like the app, find it helpful, and want to support me to offset the cost of publishing to AppStores, you can sponsor the project with [**Github Sponsor**](https://github.com/sponsors/alextran1502), or a one time donation with the Buy Me a coffee link below. diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index ecf2f65ca3..b570c2b2e9 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -21,10 +21,18 @@ linter: # or a specific dart file by using the `// ignore: name_of_lint` and # `// ignore_for_file: name_of_lint` syntax on the line or in the file # producing the lint. + rules: # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule use_build_context_synchronously: false + require_trailing_commas: true + unrelated_type_equality_checks: true # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options +analyzer: + exclude: + - openapi/ + - openapi/test/ + - lib/generated_plugin_registrant.dart diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 7d568f60fb..4a239b09fa 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -71,6 +71,7 @@ "login_form_label_password": "Password", "login_form_password_hint": "password", "login_form_save_login": "Stay logged in", + "login_form_failed_login": "Error logging you in, check server url, email and password", "monthly_title_text_date_format": "MMMM y", "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", "profile_drawer_sign_out": "Sign Out", @@ -81,6 +82,7 @@ "search_result_page_new_search_hint": "New Search", "select_additional_user_for_sharing_page_suggestions": "Suggestions", "select_user_for_sharing_page_err_album": "Failed to create album", + "select_user_for_sharing_page_share_suggestions": "Suggestions", "share_add": "Add", "share_add_photos": "Add photos", "share_add_title": "Add a title", @@ -100,4 +102,4 @@ "version_announcement_overlay_text_2": "please take your time to visit the ", "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89" -} \ No newline at end of file +} diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 5e0e51a77d..c14f2f39f6 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -46,12 +46,15 @@ void main() async { Locale('de', 'DE') ]; - runApp(EasyLocalization( + runApp( + EasyLocalization( supportedLocales: locales, path: 'assets/i18n', useFallbackTranslations: true, fallbackLocale: locales.first, - child: const ProviderScope(child: ImmichApp()))); + child: const ProviderScope(child: ImmichApp()), + ), + ); } class ImmichApp extends ConsumerStatefulWidget { @@ -111,6 +114,7 @@ class ImmichAppState extends ConsumerState @override initState() { super.initState(); + initApp().then((_) => debugPrint("App Init Completed")); } @@ -120,10 +124,9 @@ class ImmichAppState extends ConsumerState super.dispose(); } - final _immichRouter = AppRouter(); - @override Widget build(BuildContext context) { + var router = ref.watch(appRouterProvider); ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo(); return MaterialApp( @@ -142,7 +145,8 @@ class ImmichAppState extends ConsumerState primarySwatch: Colors.indigo, fontFamily: 'WorkSans', snackBarTheme: const SnackBarThemeData( - contentTextStyle: TextStyle(fontFamily: 'WorkSans')), + contentTextStyle: TextStyle(fontFamily: 'WorkSans'), + ), scaffoldBackgroundColor: immichBackgroundColor, appBarTheme: const AppBarTheme( backgroundColor: immichBackgroundColor, @@ -152,9 +156,10 @@ class ImmichAppState extends ConsumerState systemOverlayStyle: SystemUiOverlayStyle.dark, ), ), - routeInformationParser: _immichRouter.defaultRouteParser(), - routerDelegate: _immichRouter.delegate( - navigatorObservers: () => [TabNavigationObserver(ref: ref)]), + routeInformationParser: router.defaultRouteParser(), + routerDelegate: router.delegate( + navigatorObservers: () => [TabNavigationObserver(ref: ref)], + ), ), const ImmichLoadingOverlay(), const VersionAnnouncementOverlay(), diff --git a/mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart b/mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart index a9e12b848e..4bed6eb5f1 100644 --- a/mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart +++ b/mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart @@ -3,17 +3,20 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart'; import 'package:immich_mobile/modules/asset_viewer/services/image_viewer.service.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; +import 'package:openapi/api.dart'; class ImageViewerStateNotifier extends StateNotifier { - final ImageViewerService _imageViewerService = ImageViewerService(); + final ImageViewerService _imageViewerService; - ImageViewerStateNotifier() - : super(ImageViewerPageState( - downloadAssetStatus: DownloadAssetStatus.idle)); + ImageViewerStateNotifier(this._imageViewerService) + : super( + ImageViewerPageState( + downloadAssetStatus: DownloadAssetStatus.idle, + ), + ); - void downloadAsset(ImmichAsset asset, BuildContext context) async { + void downloadAsset(AssetResponseDto asset, BuildContext context) async { state = state.copyWith(downloadAssetStatus: DownloadAssetStatus.loading); bool isSuccess = await _imageViewerService.downloadAssetToDevice(asset); @@ -43,4 +46,5 @@ class ImageViewerStateNotifier extends StateNotifier { final imageViewerStateProvider = StateNotifierProvider( - ((ref) => ImageViewerStateNotifier())); + ((ref) => ImageViewerStateNotifier(ref.watch(imageViewerServiceProvider))), +); diff --git a/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart b/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart index 9ee9e8a67a..771b0873d8 100644 --- a/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart +++ b/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart @@ -1,33 +1,35 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; -import 'package:hive_flutter/hive_flutter.dart'; -import 'package:immich_mobile/constants/hive_box.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; +import 'package:openapi/api.dart'; import 'package:path/path.dart' as p; -import 'package:http/http.dart' as http; import 'package:photo_manager/photo_manager.dart'; import 'package:path_provider/path_provider.dart'; +final imageViewerServiceProvider = + Provider((ref) => ImageViewerService(ref.watch(apiServiceProvider))); + class ImageViewerService { - Future downloadAssetToDevice(ImmichAsset asset) async { + final ApiService _apiService; + ImageViewerService(this._apiService); + + Future downloadAssetToDevice(AssetResponseDto asset) async { try { String fileName = p.basename(asset.originalPath); - var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); - Uri filePath = Uri.parse( - "$savedEndpoint/asset/download?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false"); - var res = await http.get( - filePath, - headers: { - "Authorization": "Bearer ${Hive.box(userInfoBox).get(accessTokenKey)}" - }, + var res = await _apiService.assetApi.downloadFileWithHttpInfo( + asset.deviceAssetId, + asset.deviceId, + isThumb: false, + isWeb: false, ); final AssetEntity? entity; - if (asset.type == 'IMAGE') { + if (asset.type == AssetTypeEnum.IMAGE) { entity = await PhotoManager.editor.saveImage( res.bodyBytes, title: p.basename(asset.originalPath), diff --git a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart index 892ef04c88..952be9448e 100644 --- a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart +++ b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart @@ -2,13 +2,12 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/shared/models/immich_asset_with_exif.model.dart'; -import 'package:intl/intl.dart'; +import 'package:openapi/api.dart'; import 'package:path/path.dart' as p; import 'package:latlong2/latlong.dart'; class ExifBottomSheet extends ConsumerWidget { - final ImmichAssetWithExif assetDetail; + final AssetResponseDto assetDetail; const ExifBottomSheet({Key? key, required this.assetDetail}) : super(key: key); @@ -26,8 +25,10 @@ class ExifBottomSheet extends ConsumerWidget { ), child: FlutterMap( options: MapOptions( - center: LatLng(assetDetail.exifInfo!.latitude!, - assetDetail.exifInfo!.longitude!), + center: LatLng( + assetDetail.exifInfo?.latitude?.toDouble() ?? 0, + assetDetail.exifInfo?.longitude?.toDouble() ?? 0, + ), zoom: 16.0, ), layers: [ @@ -46,10 +47,13 @@ class ExifBottomSheet extends ConsumerWidget { markers: [ Marker( anchorPos: AnchorPos.align(AnchorAlign.top), - point: LatLng(assetDetail.exifInfo!.latitude!, - assetDetail.exifInfo!.longitude!), + point: LatLng( + assetDetail.exifInfo?.latitude?.toDouble() ?? 0, + assetDetail.exifInfo?.longitude?.toDouble() ?? 0, + ), builder: (ctx) => const Image( - image: AssetImage('assets/location-pin.png')), + image: AssetImage('assets/location-pin.png'), + ), ), ], ), @@ -63,7 +67,10 @@ class ExifBottomSheet extends ConsumerWidget { return Text( "${assetDetail.exifInfo!.city}, ${assetDetail.exifInfo!.state}", style: TextStyle( - fontSize: 12, color: Colors.grey[200], fontWeight: FontWeight.bold), + fontSize: 12, + color: Colors.grey[200], + fontWeight: FontWeight.bold, + ), ); } @@ -74,7 +81,7 @@ class ExifBottomSheet extends ConsumerWidget { if (assetDetail.exifInfo?.dateTimeOriginal != null) Text( DateFormat('date_format'.tr()).format( - DateTime.parse(assetDetail.exifInfo!.dateTimeOriginal!), + assetDetail.exifInfo!.dateTimeOriginal!, ), style: TextStyle( color: Colors.grey[400], @@ -151,7 +158,8 @@ class ExifBottomSheet extends ConsumerWidget { ), subtitle: assetDetail.exifInfo?.exifImageHeight != null ? Text( - "${assetDetail.exifInfo?.exifImageHeight} x ${assetDetail.exifInfo?.exifImageWidth} ${assetDetail.exifInfo?.fileSizeInByte!}B ") + "${assetDetail.exifInfo?.exifImageHeight} x ${assetDetail.exifInfo?.exifImageWidth} ${assetDetail.exifInfo?.fileSizeInByte!}B ", + ) : null, ), if (assetDetail.exifInfo?.make != null) @@ -166,7 +174,8 @@ class ExifBottomSheet extends ConsumerWidget { style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text( - "ƒ/${assetDetail.exifInfo?.fNumber} 1/${(1 / (assetDetail.exifInfo?.exposureTime ?? 1)).toStringAsFixed(0)} ${assetDetail.exifInfo?.focalLength}mm ISO${assetDetail.exifInfo?.iso} "), + "ƒ/${assetDetail.exifInfo?.fNumber} 1/${(1 / (assetDetail.exifInfo?.exposureTime ?? 1)).toStringAsFixed(0)} ${assetDetail.exifInfo?.focalLength}mm ISO${assetDetail.exifInfo?.iso} ", + ), ), ], ), diff --git a/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart b/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart index 3a9bce99be..384eae20c4 100644 --- a/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart +++ b/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart @@ -17,16 +17,20 @@ class _RemotePhotoViewState extends State { bool allowMoving = _status == _RemoteImageStatus.full; return PhotoView( - imageProvider: _imageProvider, - minScale: PhotoViewComputedScale.contained, - maxScale: allowMoving ? 1.0 : PhotoViewComputedScale.contained, - enablePanAlways: true, - scaleStateChangedCallback: _scaleStateChanged, - onScaleEnd: _onScaleListener); + imageProvider: _imageProvider, + minScale: PhotoViewComputedScale.contained, + maxScale: allowMoving ? 1.0 : PhotoViewComputedScale.contained, + enablePanAlways: true, + scaleStateChangedCallback: _scaleStateChanged, + onScaleEnd: _onScaleListener, + ); } - void _onScaleListener(BuildContext context, ScaleEndDetails details, - PhotoViewControllerValue controllerValue) { + void _onScaleListener( + BuildContext context, + ScaleEndDetails details, + PhotoViewControllerValue controllerValue, + ) { // Disable swipe events when zoomed in if (_zoomedIn) return; @@ -42,12 +46,17 @@ class _RemotePhotoViewState extends State { } CachedNetworkImageProvider _authorizedImageProvider(String url) { - return CachedNetworkImageProvider(url, - headers: {"Authorization": widget.authToken}, cacheKey: url); + return CachedNetworkImageProvider( + url, + headers: {"Authorization": widget.authToken}, + cacheKey: url, + ); } void _performStateTransition( - _RemoteImageStatus newStatus, CachedNetworkImageProvider provider) { + _RemoteImageStatus newStatus, + CachedNetworkImageProvider provider, + ) { // Transition to same status is forbidden if (_status == newStatus) return; // Transition full -> thumbnail is forbidden @@ -67,19 +76,22 @@ class _RemotePhotoViewState extends State { _authorizedImageProvider(widget.thumbnailUrl); _imageProvider = thumbnailProvider; - thumbnailProvider - .resolve(const ImageConfiguration()) - .addListener(ImageStreamListener((ImageInfo imageInfo, _) { - _performStateTransition(_RemoteImageStatus.thumbnail, thumbnailProvider); - })); + thumbnailProvider.resolve(const ImageConfiguration()).addListener( + ImageStreamListener((ImageInfo imageInfo, _) { + _performStateTransition( + _RemoteImageStatus.thumbnail, + thumbnailProvider, + ); + }), + ); CachedNetworkImageProvider fullProvider = _authorizedImageProvider(widget.imageUrl); - fullProvider - .resolve(const ImageConfiguration()) - .addListener(ImageStreamListener((ImageInfo imageInfo, _) { - _performStateTransition(_RemoteImageStatus.full, fullProvider); - })); + fullProvider.resolve(const ImageConfiguration()).addListener( + ImageStreamListener((ImageInfo imageInfo, _) { + _performStateTransition(_RemoteImageStatus.full, fullProvider); + }), + ); } @override @@ -90,14 +102,14 @@ class _RemotePhotoViewState extends State { } class RemotePhotoView extends StatefulWidget { - const RemotePhotoView( - {Key? key, - required this.thumbnailUrl, - required this.imageUrl, - required this.authToken, - required this.onSwipeDown, - required this.onSwipeUp}) - : super(key: key); + const RemotePhotoView({ + Key? key, + required this.thumbnailUrl, + required this.imageUrl, + required this.authToken, + required this.onSwipeDown, + required this.onSwipeUp, + }) : super(key: key); final String thumbnailUrl; final String imageUrl; diff --git a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart index 39e8ecf6a2..eba66a2691 100644 --- a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart +++ b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart @@ -3,17 +3,17 @@ import 'dart:developer'; 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/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget { - const TopControlAppBar( - {Key? key, - required this.asset, - required this.onMoreInfoPressed, - required this.onDownloadPressed}) - : super(key: key); + const TopControlAppBar({ + Key? key, + required this.asset, + required this.onMoreInfoPressed, + required this.onDownloadPressed, + }) : super(key: key); - final ImmichAsset asset; + final AssetResponseDto asset; final Function onMoreInfoPressed; final Function onDownloadPressed; @@ -54,12 +54,13 @@ class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget { : const Icon(Icons.favorite_border_rounded), ), IconButton( - iconSize: iconSize, - splashRadius: iconSize, - onPressed: () { - onMoreInfoPressed(); - }, - icon: const Icon(Icons.more_horiz_rounded)) + iconSize: iconSize, + splashRadius: iconSize, + onPressed: () { + onMoreInfoPressed(); + }, + icon: const Icon(Icons.more_horiz_rounded), + ) ], ); } diff --git a/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart b/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart index a59fc825ce..d88b8a9dcf 100644 --- a/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart +++ b/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart @@ -11,17 +11,16 @@ import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/remote_photo_view.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart'; import 'package:immich_mobile/modules/home/services/asset.service.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; -import 'package:immich_mobile/shared/models/immich_asset_with_exif.model.dart'; +import 'package:openapi/api.dart'; // ignore: must_be_immutable class ImageViewerPage extends HookConsumerWidget { final String imageUrl; final String heroTag; final String thumbnailUrl; - final ImmichAsset asset; + final AssetResponseDto asset; - ImmichAssetWithExif? assetDetail; + AssetResponseDto? assetDetail; ImageViewerPage({ Key? key, @@ -54,10 +53,13 @@ class ImageViewerPage extends HookConsumerWidget { ); } - useEffect(() { - getAssetExif(); - return null; - }, []); + useEffect( + () { + getAssetExif(); + return null; + }, + [], + ); return Scaffold( backgroundColor: Colors.black, @@ -75,14 +77,15 @@ class ImageViewerPage extends HookConsumerWidget { children: [ Center( child: Hero( - tag: heroTag, - child: RemotePhotoView( - thumbnailUrl: thumbnailUrl, - imageUrl: imageUrl, - authToken: "Bearer ${box.get(accessTokenKey)}", - onSwipeDown: () => AutoRouter.of(context).pop(), - onSwipeUp: () => showInfo(), - )), + tag: heroTag, + child: RemotePhotoView( + thumbnailUrl: thumbnailUrl, + imageUrl: imageUrl, + authToken: "Bearer ${box.get(accessTokenKey)}", + onSwipeDown: () => AutoRouter.of(context).pop(), + onSwipeUp: () => showInfo(), + ), + ), ), if (downloadAssetStatus == DownloadAssetStatus.loading) const Center( diff --git a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart index 99be903dcb..4a2e4908ee 100644 --- a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart +++ b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart @@ -12,15 +12,14 @@ import 'package:immich_mobile/modules/asset_viewer/ui/download_loading_indicator import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart'; import 'package:immich_mobile/modules/home/services/asset.service.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; -import 'package:immich_mobile/shared/models/immich_asset_with_exif.model.dart'; +import 'package:openapi/api.dart'; import 'package:video_player/video_player.dart'; // ignore: must_be_immutable class VideoViewerPage extends HookConsumerWidget { final String videoUrl; - final ImmichAsset asset; - ImmichAssetWithExif? assetDetail; + final AssetResponseDto asset; + AssetResponseDto? assetDetail; VideoViewerPage({Key? key, required this.videoUrl, required this.asset}) : super(key: key); @@ -49,10 +48,13 @@ class VideoViewerPage extends HookConsumerWidget { await ref.watch(assetServiceProvider).getAssetById(asset.id); } - useEffect(() { - getAssetExif(); - return null; - }, []); + useEffect( + () { + getAssetExif(); + return null; + }, + [], + ); return Scaffold( backgroundColor: Colors.black, @@ -116,8 +118,10 @@ class _VideoThumbnailPlayerState extends State { Future initializePlayer() async { try { - videoPlayerController = VideoPlayerController.network(widget.url, - httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"}); + videoPlayerController = VideoPlayerController.network( + widget.url, + httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"}, + ); await videoPlayerController.initialize(); _createChewieController(); diff --git a/mobile/lib/modules/backup/models/backup_state.model.dart b/mobile/lib/modules/backup/models/backup_state.model.dart index c3c66f08c8..8f41b0200a 100644 --- a/mobile/lib/modules/backup/models/backup_state.model.dart +++ b/mobile/lib/modules/backup/models/backup_state.model.dart @@ -1,10 +1,10 @@ import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; +import 'package:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:immich_mobile/modules/backup/models/available_album.model.dart'; import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart'; -import 'package:immich_mobile/shared/models/server_info.model.dart'; enum BackUpProgressEnum { idle, inProgress, done } @@ -14,7 +14,7 @@ class BackUpState { final List allAssetsInDatabase; final double progressInPercentage; final CancellationToken cancelToken; - final ServerInfo serverInfo; + final ServerInfoResponseDto serverInfo; /// All available albums on the device final List availableAlbums; @@ -49,7 +49,7 @@ class BackUpState { List? allAssetsInDatabase, double? progressInPercentage, CancellationToken? cancelToken, - ServerInfo? serverInfo, + ServerInfoResponseDto? serverInfo, List? availableAlbums, Set? selectedBackupAlbums, Set? excludedBackupAlbums, @@ -93,8 +93,10 @@ class BackUpState { collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) && collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) && collectionEquals(other.allUniqueAssets, allUniqueAssets) && - collectionEquals(other.selectedAlbumsBackupAssetsIds, - selectedAlbumsBackupAssetsIds) && + collectionEquals( + other.selectedAlbumsBackupAssetsIds, + selectedAlbumsBackupAssetsIds, + ) && other.currentUploadAsset == currentUploadAsset; } diff --git a/mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart b/mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart deleted file mode 100644 index 2fc30ea61f..0000000000 --- a/mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'dart:convert'; - -class CheckDuplicateAssetResponse { - final bool isExist; - CheckDuplicateAssetResponse({ - required this.isExist, - }); - - CheckDuplicateAssetResponse copyWith({ - bool? isExist, - }) { - return CheckDuplicateAssetResponse( - isExist: isExist ?? this.isExist, - ); - } - - Map toMap() { - final result = {}; - - result.addAll({'isExist': isExist}); - - return result; - } - - factory CheckDuplicateAssetResponse.fromMap(Map map) { - return CheckDuplicateAssetResponse( - isExist: map['isExist'] ?? false, - ); - } - - String toJson() => json.encode(toMap()); - - factory CheckDuplicateAssetResponse.fromJson(String source) => - CheckDuplicateAssetResponse.fromMap(json.decode(source)); - - @override - String toString() => 'CheckDuplicateAssetResponse(isExist: $isExist)'; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is CheckDuplicateAssetResponse && other.isExist == isExist; - } - - @override - int get hashCode => isExist.hashCode; -} diff --git a/mobile/lib/modules/backup/providers/backup.provider.dart b/mobile/lib/modules/backup/providers/backup.provider.dart index 2f6970f55e..e71a8dbfd8 100644 --- a/mobile/lib/modules/backup/providers/backup.provider.dart +++ b/mobile/lib/modules/backup/providers/backup.provider.dart @@ -12,8 +12,8 @@ import 'package:immich_mobile/modules/backup/providers/error_backup_list.provide import 'package:immich_mobile/modules/backup/services/backup.service.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/shared/models/server_info.model.dart'; import 'package:immich_mobile/shared/services/server_info.service.dart'; +import 'package:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; class BackupNotifier extends StateNotifier { @@ -28,12 +28,12 @@ class BackupNotifier extends StateNotifier { allAssetsInDatabase: const [], progressInPercentage: 0, cancelToken: CancellationToken(), - serverInfo: ServerInfo( + serverInfo: ServerInfoResponseDto( diskAvailable: "0", diskAvailableRaw: 0, diskSize: "0", diskSizeRaw: 0, - diskUsagePercentage: 0.0, + diskUsagePercentage: 0, diskUse: "0", diskUseRaw: 0, ), @@ -113,7 +113,9 @@ class BackupNotifier extends StateNotifier { // Get all albums on the device List availableAlbums = []; List albums = await PhotoManager.getAssetPathList( - hasAll: true, type: RequestType.common); + hasAll: true, + type: RequestType.common, + ); for (AssetPathEntity album in albums) { AvailableAlbum availableAlbum = AvailableAlbum(albumEntity: album); @@ -156,7 +158,10 @@ class BackupNotifier extends StateNotifier { // Get album that contains all assets var list = await PhotoManager.getAssetPathList( - hasAll: true, onlyAll: true, type: RequestType.common); + hasAll: true, + onlyAll: true, + type: RequestType.common, + ); AssetPathEntity albumHasAllAssets = list.first; backupAlbumInfoBox.put( @@ -175,13 +180,15 @@ class BackupNotifier extends StateNotifier { for (var selectedAlbumId in backupAlbumInfo!.selectedAlbumIds) { var albumAsset = await AssetPathEntity.fromId(selectedAlbumId); state = state.copyWith( - selectedBackupAlbums: {...state.selectedBackupAlbums, albumAsset}); + selectedBackupAlbums: {...state.selectedBackupAlbums, albumAsset}, + ); } for (var excludedAlbumId in backupAlbumInfo.excludedAlbumsIds) { var albumAsset = await AssetPathEntity.fromId(excludedAlbumId); state = state.copyWith( - excludedBackupAlbums: {...state.excludedBackupAlbums, albumAsset}); + excludedBackupAlbums: {...state.excludedBackupAlbums, albumAsset}, + ); } } catch (e) { debugPrint("[ERROR] Failed to generate album from id $e"); @@ -211,8 +218,11 @@ class BackupNotifier extends StateNotifier { Set allUniqueAssets = assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); - List allAssetsInDatabase = - await _backupService.getDeviceBackupAsset(); + var allAssetsInDatabase = await _backupService.getDeviceBackupAsset(); + + if (allAssetsInDatabase == null) { + return; + } // Find asset that were backup from selected albums Set selectedAlbumsBackupAssets = @@ -328,23 +338,27 @@ class BackupNotifier extends StateNotifier { void cancelBackup() { state.cancelToken.cancel(); state = state.copyWith( - backupProgress: BackUpProgressEnum.idle, progressInPercentage: 0.0); + backupProgress: BackUpProgressEnum.idle, + progressInPercentage: 0.0, + ); } void _onAssetUploaded(String deviceAssetId, String deviceId) { - state = state.copyWith(selectedAlbumsBackupAssetsIds: { - ...state.selectedAlbumsBackupAssetsIds, - deviceAssetId - }, allAssetsInDatabase: [ - ...state.allAssetsInDatabase, - deviceAssetId - ]); + state = state.copyWith( + selectedAlbumsBackupAssetsIds: { + ...state.selectedAlbumsBackupAssetsIds, + deviceAssetId + }, + allAssetsInDatabase: [...state.allAssetsInDatabase, deviceAssetId], + ); if (state.allUniqueAssets.length - state.selectedAlbumsBackupAssetsIds.length == 0) { state = state.copyWith( - backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0); + backupProgress: BackUpProgressEnum.done, + progressInPercentage: 0.0, + ); } _updateServerInfo(); @@ -352,24 +366,19 @@ class BackupNotifier extends StateNotifier { void _onUploadProgress(int sent, int total) { state = state.copyWith( - progressInPercentage: (sent.toDouble() / total.toDouble() * 100)); + progressInPercentage: (sent.toDouble() / total.toDouble() * 100), + ); } Future _updateServerInfo() async { var serverInfo = await _serverInfoService.getServerInfo(); // Update server info - state = state.copyWith( - serverInfo: ServerInfo( - diskSize: serverInfo.diskSize, - diskUse: serverInfo.diskUse, - diskAvailable: serverInfo.diskAvailable, - diskSizeRaw: serverInfo.diskSizeRaw, - diskUseRaw: serverInfo.diskUseRaw, - diskAvailableRaw: serverInfo.diskAvailableRaw, - diskUsagePercentage: serverInfo.diskUsagePercentage, - ), - ); + if (serverInfo != null) { + state = state.copyWith( + serverInfo: serverInfo, + ); + } } void resumeBackup() { diff --git a/mobile/lib/modules/backup/services/backup.service.dart b/mobile/lib/modules/backup/services/backup.service.dart index c1b1576ab8..59696028f4 100644 --- a/mobile/lib/modules/backup/services/backup.service.dart +++ b/mobile/lib/modules/backup/services/backup.service.dart @@ -2,59 +2,38 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; -import 'package:immich_mobile/modules/backup/models/check_duplicate_asset_response.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/shared/services/network.service.dart'; -import 'package:immich_mobile/shared/models/device_info.model.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/utils/files_helper.dart'; +import 'package:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:http_parser/http_parser.dart'; import 'package:path/path.dart' as p; import 'package:cancellation_token_http/http.dart' as http; -final backupServiceProvider = - Provider((ref) => BackupService(ref.watch(networkServiceProvider))); +final backupServiceProvider = Provider( + (ref) => BackupService( + ref.watch(apiServiceProvider), + ), +); class BackupService { - final NetworkService _networkService; + final ApiService _apiService; + BackupService(this._apiService); - BackupService(this._networkService); - - Future> getDeviceBackupAsset() async { - String deviceId = Hive.box(userInfoBox).get(deviceIdKey); - - Response response = - await _networkService.getRequest(url: "asset/$deviceId"); - List result = jsonDecode(response.toString()); - - return result.cast(); - } - - Future checkDuplicateAsset(String deviceAssetId) async { + Future?> getDeviceBackupAsset() async { String deviceId = Hive.box(userInfoBox).get(deviceIdKey); try { - Response response = - await _networkService.postRequest(url: "asset/check", data: { - "deviceId": deviceId, - "deviceAssetId": deviceAssetId, - }); - - if (response.statusCode == 200) { - var result = CheckDuplicateAssetResponse.fromJson(response.toString()); - - return result.isExist; - } else { - return false; - } + return await _apiService.assetApi.getUserAssetsByDeviceId(deviceId); } catch (e) { - return false; + debugPrint('Error [getDeviceBackupAsset] ${e.toString()}'); + return null; } } @@ -99,9 +78,11 @@ class BackupService { var box = Hive.box(userInfoBox); var req = MultipartRequest( - 'POST', Uri.parse('$savedEndpoint/asset/upload'), - onProgress: ((bytes, totalBytes) => - uploadProgressCb(bytes, totalBytes))); + 'POST', + Uri.parse('$savedEndpoint/asset/upload'), + onProgress: ((bytes, totalBytes) => + uploadProgressCb(bytes, totalBytes)), + ); req.headers["Authorization"] = "Bearer ${box.get(accessTokenKey)}"; req.fields['deviceAssetId'] = entity.id; @@ -133,16 +114,19 @@ class BackupService { var error = jsonDecode(data); debugPrint( - "Error(${error['statusCode']}) uploading ${entity.id} | $originalFileName | Created on ${entity.createDateTime} | ${error['error']}"); + "Error(${error['statusCode']}) uploading ${entity.id} | $originalFileName | Created on ${entity.createDateTime} | ${error['error']}", + ); - errorCb(ErrorUploadAsset( - asset: entity, - id: entity.id, - createdAt: entity.createDateTime, - fileName: originalFileName, - fileType: _getAssetType(entity.type), - errorMessage: error['error'], - )); + errorCb( + ErrorUploadAsset( + asset: entity, + id: entity.id, + createdAt: entity.createDateTime, + fileName: originalFileName, + fileType: _getAssetType(entity.type), + errorMessage: error['error'], + ), + ); continue; } } @@ -160,8 +144,6 @@ class BackupService { } } - void sendBackupRequest(AssetEntity entity) {} - String _getAssetType(AssetType assetType) { switch (assetType) { case AssetType.audio: @@ -175,15 +157,29 @@ class BackupService { } } - Future setAutoBackup( - bool status, String deviceId, String deviceType) async { - var res = await _networkService.patchRequest(url: 'device-info', data: { - "isAutoBackup": status, - "deviceId": deviceId, - "deviceType": deviceType, - }); + Future setAutoBackup( + bool status, + String deviceId, + DeviceTypeEnum deviceType, + ) async { + try { + var updatedDeviceInfo = await _apiService.deviceInfoApi.updateDeviceInfo( + UpdateDeviceInfoDto( + deviceId: deviceId, + deviceType: deviceType, + isAutoBackup: status, + ), + ); - return DeviceInfoRemote.fromJson(res.toString()); + if (updatedDeviceInfo == null) { + throw Exception("Error updating device info"); + } + + return updatedDeviceInfo; + } catch (e) { + debugPrint("Error setAutoBackup: ${e.toString()}"); + throw Error(); + } } } diff --git a/mobile/lib/modules/backup/ui/album_info_card.dart b/mobile/lib/modules/backup/ui/album_info_card.dart index 90a1e8976d..370db0c3b2 100644 --- a/mobile/lib/modules/backup/ui/album_info_card.dart +++ b/mobile/lib/modules/backup/ui/album_info_card.dart @@ -26,7 +26,9 @@ class AlbumInfoCard extends HookConsumerWidget { ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo); ColorFilter selectedFilter = ColorFilter.mode( - Theme.of(context).primaryColor.withAlpha(100), BlendMode.darken); + Theme.of(context).primaryColor.withAlpha(100), + BlendMode.darken, + ); ColorFilter excludedFilter = ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); ColorFilter unselectedFilter = @@ -40,7 +42,10 @@ class AlbumInfoCard extends HookConsumerWidget { label: const Text( "album_info_card_backup_album_included", style: TextStyle( - fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ).tr(), backgroundColor: Theme.of(context).primaryColor, ); @@ -51,7 +56,10 @@ class AlbumInfoCard extends HookConsumerWidget { label: const Text( "album_info_card_backup_album_excluded", style: TextStyle( - fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ).tr(), backgroundColor: Colors.red[300], ); @@ -138,15 +146,16 @@ class AlbumInfoCard extends HookConsumerWidget { height: 200, decoration: BoxDecoration( borderRadius: const BorderRadius.only( - topLeft: Radius.circular(12), - topRight: Radius.circular(12)), + topLeft: Radius.circular(12), + topRight: Radius.circular(12), + ), image: DecorationImage( colorFilter: _buildImageFilter(), image: imageData != null ? MemoryImage(imageData!) : const AssetImage( - 'assets/immich-logo-no-outline.png') - as ImageProvider, + 'assets/immich-logo-no-outline.png', + ) as ImageProvider, fit: BoxFit.cover, ), ), @@ -174,9 +183,10 @@ class AlbumInfoCard extends HookConsumerWidget { Text( albumInfo.name, style: TextStyle( - fontSize: 14, - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold), + fontSize: 14, + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + ), ), Padding( padding: const EdgeInsets.only(top: 2.0), @@ -186,7 +196,9 @@ class AlbumInfoCard extends HookConsumerWidget { ? " (${'backup_all'.tr()})" : ""), style: TextStyle( - fontSize: 12, color: Colors.grey[600]), + fontSize: 12, + color: Colors.grey[600], + ), ), ) ], diff --git a/mobile/lib/modules/backup/ui/backup_info_card.dart b/mobile/lib/modules/backup/ui/backup_info_card.dart index 300fa1feba..6bc832a5d4 100644 --- a/mobile/lib/modules/backup/ui/backup_info_card.dart +++ b/mobile/lib/modules/backup/ui/backup_info_card.dart @@ -5,12 +5,12 @@ class BackupInfoCard extends StatelessWidget { final String title; final String subtitle; final String info; - const BackupInfoCard( - {Key? key, - required this.title, - required this.subtitle, - required this.info}) - : super(key: key); + const BackupInfoCard({ + Key? key, + required this.title, + required this.subtitle, + required this.info, + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/mobile/lib/modules/backup/views/album_preview_page.dart b/mobile/lib/modules/backup/views/album_preview_page.dart index 561d177b7b..2266fe8bcc 100644 --- a/mobile/lib/modules/backup/views/album_preview_page.dart +++ b/mobile/lib/modules/backup/views/album_preview_page.dart @@ -20,10 +20,13 @@ class AlbumPreviewPage extends HookConsumerWidget { await album.getAssetListRange(start: 0, end: album.assetCount); } - useEffect(() { - _getAssetsInAlbum(); - return null; - }, []); + useEffect( + () { + _getAssetsInAlbum(); + return null; + }, + [], + ); return Scaffold( appBar: AppBar( @@ -39,9 +42,10 @@ class AlbumPreviewPage extends HookConsumerWidget { child: Text( "ID ${album.id}", style: TextStyle( - fontSize: 10, - color: Colors.grey[600], - fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.grey[600], + fontWeight: FontWeight.bold, + ), ), ), ], @@ -59,9 +63,11 @@ class AlbumPreviewPage extends HookConsumerWidget { ), itemCount: assets.value.length, itemBuilder: (context, index) { - Future thumbData = assets.value[index] - .thumbnailDataWithSize(const ThumbnailSize(200, 200), - quality: 50); + Future thumbData = + assets.value[index].thumbnailDataWithSize( + const ThumbnailSize(200, 200), + quality: 50, + ); return FutureBuilder( future: thumbData, diff --git a/mobile/lib/modules/backup/views/backup_album_selection_page.dart b/mobile/lib/modules/backup/views/backup_album_selection_page.dart index 27cf556842..50a5be8537 100644 --- a/mobile/lib/modules/backup/views/backup_album_selection_page.dart +++ b/mobile/lib/modules/backup/views/backup_album_selection_page.dart @@ -17,10 +17,13 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums; final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums; - useEffect(() { - ref.read(backupProvider.notifier).getBackupInfo(); - return null; - }, []); + useEffect( + () { + ref.read(backupProvider.notifier).getBackupInfo(); + return null; + }, + [], + ); _buildAlbumSelectionList() { if (availableAlbums.isEmpty) { @@ -42,8 +45,9 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ? const EdgeInsets.only(left: 16.00) : const EdgeInsets.all(0), child: AlbumInfoCard( - imageData: thumbnailData, - albumInfo: availableAlbums[index].albumEntity), + imageData: thumbnailData, + albumInfo: availableAlbums[index].albumEntity, + ), ); }), ), @@ -73,13 +77,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { child: Chip( visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5)), + borderRadius: BorderRadius.circular(5), + ), label: Text( album.name, style: const TextStyle( - fontSize: 10, - color: Colors.white, - fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ), backgroundColor: Theme.of(context).primaryColor, deleteIconColor: Colors.white, @@ -109,13 +115,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { child: Chip( visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5)), + borderRadius: BorderRadius.circular(5), + ), label: Text( album.name, style: const TextStyle( - fontSize: 10, - color: Colors.white, - fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ), backgroundColor: Colors.red[300], deleteIconColor: Colors.white, @@ -185,9 +193,10 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { title: Text( "backup_album_selection_page_total_assets", style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: Colors.grey[700]), + fontWeight: FontWeight.bold, + fontSize: 14, + color: Colors.grey[700], + ), ).tr(), trailing: Text( ref @@ -234,7 +243,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { builder: (BuildContext context) { return AlertDialog( shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), + borderRadius: BorderRadius.circular(12), + ), elevation: 5, title: Text( 'backup_album_selection_page_selection_info', @@ -250,7 +260,9 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { Text( 'backup_album_selection_page_assets_scatter', style: TextStyle( - fontSize: 14, color: Colors.grey[700]), + fontSize: 14, + color: Colors.grey[700], + ), ).tr(), ], ), diff --git a/mobile/lib/modules/backup/views/backup_controller_page.dart b/mobile/lib/modules/backup/views/backup_controller_page.dart index a778e986d4..65377b8538 100644 --- a/mobile/lib/modules/backup/views/backup_controller_page.dart +++ b/mobile/lib/modules/backup/views/backup_controller_page.dart @@ -11,7 +11,6 @@ import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/providers/websocket.provider.dart'; import 'package:immich_mobile/modules/backup/ui/backup_info_card.dart'; -import 'package:intl/intl.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; class BackupControllerPage extends HookConsumerWidget { @@ -27,16 +26,19 @@ class BackupControllerPage extends HookConsumerWidget { ? false : true; - useEffect(() { - if (backupState.backupProgress != BackUpProgressEnum.inProgress) { - ref.watch(backupProvider.notifier).getBackupInfo(); - } + useEffect( + () { + if (backupState.backupProgress != BackUpProgressEnum.inProgress) { + ref.watch(backupProvider.notifier).getBackupInfo(); + } - ref - .watch(websocketProvider.notifier) - .stopListenToEvent('on_upload_success'); - return null; - }, []); + ref + .watch(websocketProvider.notifier) + .stopListenToEvent('on_upload_success'); + return null; + }, + [], + ); Widget _buildStorageInformation() { return ListTile( @@ -68,10 +70,11 @@ class BackupControllerPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(top: 12.0), child: const Text('backup_controller_page_storage_format').tr( - args: [ - backupState.serverInfo.diskUse, - backupState.serverInfo.diskSize - ]), + args: [ + backupState.serverInfo.diskUse, + backupState.serverInfo.diskSize + ], + ), ), ], ), @@ -129,8 +132,10 @@ class BackupControllerPage extends HookConsumerWidget { .setAutoBackup(true); } }, - child: Text(backupBtnText, - style: const TextStyle(fontWeight: FontWeight.bold)), + child: Text( + backupBtnText, + style: const TextStyle(fontWeight: FontWeight.bold), + ), ), ) ], @@ -157,9 +162,10 @@ class BackupControllerPage extends HookConsumerWidget { child: Text( text.trim().substring(0, text.length - 2), style: TextStyle( - color: Theme.of(context).primaryColor, - fontSize: 12, - fontWeight: FontWeight.bold), + color: Theme.of(context).primaryColor, + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), ); } else { @@ -168,9 +174,10 @@ class BackupControllerPage extends HookConsumerWidget { child: Text( "backup_controller_page_none_selected".tr(), style: TextStyle( - color: Theme.of(context).primaryColor, - fontSize: 12, - fontWeight: FontWeight.bold), + color: Theme.of(context).primaryColor, + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), ); } @@ -190,9 +197,10 @@ class BackupControllerPage extends HookConsumerWidget { child: Text( text.trim().substring(0, text.length - 2), style: TextStyle( - color: Colors.red[300], - fontSize: 12, - fontWeight: FontWeight.bold), + color: Colors.red[300], + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), ); } else { @@ -213,9 +221,10 @@ class BackupControllerPage extends HookConsumerWidget { borderOnForeground: false, child: ListTile( minVerticalPadding: 15, - title: const Text("backup_controller_page_albums", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)) - .tr(), + title: const Text( + "backup_controller_page_albums", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + ).tr(), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -284,9 +293,9 @@ class BackupControllerPage extends HookConsumerWidget { fontWeight: FontWeight.bold, fontSize: 11, ), - ).tr(args: [ - ref.watch(errorBackupListProvider).length.toString() - ]), + ).tr( + args: [ref.watch(errorBackupListProvider).length.toString()], + ), backgroundColor: Colors.white, onPressed: () { AutoRouter.of(context).push(const FailedBackupStatusRoute()); @@ -331,12 +340,16 @@ class BackupControllerPage extends HookConsumerWidget { child: const Text( 'backup_controller_page_filename', style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 10.0), - ).tr(args: [ - backupState.currentUploadAsset.fileName, - backupState.currentUploadAsset.fileType - .toLowerCase() - ]), + fontWeight: FontWeight.bold, + fontSize: 10.0, + ), + ).tr( + args: [ + backupState.currentUploadAsset.fileName, + backupState.currentUploadAsset.fileType + .toLowerCase() + ], + ), ), ), ], @@ -352,16 +365,20 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.all(6.0), child: const Text( "backup_controller_page_created", - style: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 10.0), - ).tr(args: [ - DateFormat.yMMMMd('en_US').format( - DateTime.parse( - backupState.currentUploadAsset.createdAt - .toString(), - ), - ) - ]), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 10.0, + ), + ).tr( + args: [ + DateFormat.yMMMMd('en_US').format( + DateTime.parse( + backupState.currentUploadAsset.createdAt + .toString(), + ), + ) + ], + ), ), ), ], @@ -406,14 +423,15 @@ class BackupControllerPage extends HookConsumerWidget { style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ).tr(), leading: IconButton( - onPressed: () { - ref.watch(websocketProvider.notifier).listenUploadEvent(); - AutoRouter.of(context).pop(true); - }, - splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - )), + onPressed: () { + ref.watch(websocketProvider.notifier).listenUploadEvent(); + AutoRouter.of(context).pop(true); + }, + splashRadius: 24, + icon: const Icon( + Icons.arrow_back_ios_rounded, + ), + ), ), body: Padding( padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32), diff --git a/mobile/lib/modules/backup/views/failed_backup_status_page.dart b/mobile/lib/modules/backup/views/failed_backup_status_page.dart index 203ea0fad5..c78592fa2c 100644 --- a/mobile/lib/modules/backup/views/failed_backup_status_page.dart +++ b/mobile/lib/modules/backup/views/failed_backup_status_page.dart @@ -19,13 +19,14 @@ class FailedBackupStatusPage extends HookConsumerWidget { style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), leading: IconButton( - onPressed: () { - AutoRouter.of(context).pop(true); - }, - splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - )), + onPressed: () { + AutoRouter.of(context).pop(true); + }, + splashRadius: 24, + icon: const Icon( + Icons.arrow_back_ios_rounded, + ), + ), ), body: ListView.builder( shrinkWrap: true, @@ -92,9 +93,10 @@ class FailedBackupStatusPage extends HookConsumerWidget { ), ), style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Colors.grey[700]), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.grey[700], + ), ), Icon( Icons.error, diff --git a/mobile/lib/modules/home/models/delete_asset_response.model.dart b/mobile/lib/modules/home/models/delete_asset_response.model.dart deleted file mode 100644 index 6e9105941f..0000000000 --- a/mobile/lib/modules/home/models/delete_asset_response.model.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:convert'; - -class DeleteAssetResponse { - final String id; - final String status; - - DeleteAssetResponse({ - required this.id, - required this.status, - }); - - DeleteAssetResponse copyWith({ - String? id, - String? status, - }) { - return DeleteAssetResponse( - id: id ?? this.id, - status: status ?? this.status, - ); - } - - Map toMap() { - return { - 'id': id, - 'status': status, - }; - } - - factory DeleteAssetResponse.fromMap(Map map) { - return DeleteAssetResponse( - id: map['id'] ?? '', - status: map['status'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory DeleteAssetResponse.fromJson(String source) => - DeleteAssetResponse.fromMap(json.decode(source)); - - @override - String toString() => 'DeleteAssetResponse(id: $id, status: $status)'; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is DeleteAssetResponse && - other.id == id && - other.status == status; - } - - @override - int get hashCode => id.hashCode ^ status.hashCode; -} diff --git a/mobile/lib/modules/home/models/get_all_asset_response.model.dart b/mobile/lib/modules/home/models/get_all_asset_response.model.dart index 86b340ae36..f02d830b2a 100644 --- a/mobile/lib/modules/home/models/get_all_asset_response.model.dart +++ b/mobile/lib/modules/home/models/get_all_asset_response.model.dart @@ -1,11 +1,9 @@ -import 'dart:convert'; - import 'package:flutter/foundation.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class ImmichAssetGroupByDate { final String date; - List assets; + List assets; ImmichAssetGroupByDate({ required this.date, required this.assets, @@ -13,7 +11,7 @@ class ImmichAssetGroupByDate { ImmichAssetGroupByDate copyWith({ String? date, - List? assets, + List? assets, }) { return ImmichAssetGroupByDate( date: date ?? this.date, @@ -21,26 +19,6 @@ class ImmichAssetGroupByDate { ); } - Map toMap() { - return { - 'date': date, - 'assets': assets.map((x) => x.toMap()).toList(), - }; - } - - factory ImmichAssetGroupByDate.fromMap(Map map) { - return ImmichAssetGroupByDate( - date: map['date'] ?? '', - assets: List.from( - map['assets']?.map((x) => ImmichAsset.fromMap(x))), - ); - } - - String toJson() => json.encode(toMap()); - - factory ImmichAssetGroupByDate.fromJson(String source) => - ImmichAssetGroupByDate.fromMap(json.decode(source)); - @override String toString() => 'ImmichAssetGroupByDate(date: $date, assets: $assets)'; @@ -79,28 +57,6 @@ class GetAllAssetResponse { ); } - Map toMap() { - return { - 'count': count, - 'data': data.map((x) => x.toMap()).toList(), - 'nextPageKey': nextPageKey, - }; - } - - factory GetAllAssetResponse.fromMap(Map map) { - return GetAllAssetResponse( - count: map['count']?.toInt() ?? 0, - data: List.from( - map['data']?.map((x) => ImmichAssetGroupByDate.fromMap(x))), - nextPageKey: map['nextPageKey'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory GetAllAssetResponse.fromJson(String source) => - GetAllAssetResponse.fromMap(json.decode(source)); - @override String toString() => 'GetAllAssetResponse(count: $count, data: $data, nextPageKey: $nextPageKey)'; diff --git a/mobile/lib/modules/home/models/home_page_state.model.dart b/mobile/lib/modules/home/models/home_page_state.model.dart index 95621fc991..1701ac3ffb 100644 --- a/mobile/lib/modules/home/models/home_page_state.model.dart +++ b/mobile/lib/modules/home/models/home_page_state.model.dart @@ -1,12 +1,10 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class HomePageState { final bool isMultiSelectEnable; - final Set selectedItems; + final Set selectedItems; final Set selectedDateGroup; HomePageState({ required this.isMultiSelectEnable, @@ -16,7 +14,7 @@ class HomePageState { HomePageState copyWith({ bool? isMultiSelectEnable, - Set? selectedItems, + Set? selectedItems, Set? selectedDateGroup, }) { return HomePageState( @@ -26,28 +24,6 @@ class HomePageState { ); } - Map toMap() { - return { - 'isMultiSelectEnable': isMultiSelectEnable, - 'selectedItems': selectedItems.map((x) => x.toMap()).toList(), - 'selectedDateGroup': selectedDateGroup.toList(), - }; - } - - factory HomePageState.fromMap(Map map) { - return HomePageState( - isMultiSelectEnable: map['isMultiSelectEnable'] ?? false, - selectedItems: Set.from( - map['selectedItems']?.map((x) => ImmichAsset.fromMap(x))), - selectedDateGroup: Set.from(map['selectedDateGroup']), - ); - } - - String toJson() => json.encode(toMap()); - - factory HomePageState.fromJson(String source) => - HomePageState.fromMap(json.decode(source)); - @override String toString() => 'HomePageState(isMultiSelectEnable: $isMultiSelectEnable, selectedItems: $selectedItems, selectedDateGroup: $selectedDateGroup)'; diff --git a/mobile/lib/modules/home/providers/home_page_state.provider.dart b/mobile/lib/modules/home/providers/home_page_state.provider.dart index 6a9fb71760..f81c7199a0 100644 --- a/mobile/lib/modules/home/providers/home_page_state.provider.dart +++ b/mobile/lib/modules/home/providers/home_page_state.provider.dart @@ -1,6 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/home/models/home_page_state.model.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class HomePageStateNotifier extends StateNotifier { HomePageStateNotifier() @@ -14,7 +14,8 @@ class HomePageStateNotifier extends StateNotifier { void addSelectedDateGroup(String dateGroupTitle) { state = state.copyWith( - selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle}); + selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle}, + ); } void removeSelectedDateGroup(String dateGroupTitle) { @@ -25,36 +26,39 @@ class HomePageStateNotifier extends StateNotifier { state = state.copyWith(selectedDateGroup: currentDateGroup); } - void enableMultiSelect(Set selectedItems) { + void enableMultiSelect(Set selectedItems) { state = state.copyWith(isMultiSelectEnable: true, selectedItems: selectedItems); } void disableMultiSelect() { state = state.copyWith( - isMultiSelectEnable: false, selectedItems: {}, selectedDateGroup: {}); + isMultiSelectEnable: false, + selectedItems: {}, + selectedDateGroup: {}, + ); } - void addSingleSelectedItem(ImmichAsset asset) { + void addSingleSelectedItem(AssetResponseDto asset) { state = state.copyWith(selectedItems: {...state.selectedItems, asset}); } - void addMultipleSelectedItems(List assets) { + void addMultipleSelectedItems(List assets) { state = state.copyWith(selectedItems: {...state.selectedItems, ...assets}); } - void removeSingleSelectedItem(ImmichAsset asset) { - Set currentList = state.selectedItems; + void removeSingleSelectedItem(AssetResponseDto asset) { + Set currentList = state.selectedItems; currentList.removeWhere((e) => e.id == asset.id); state = state.copyWith(selectedItems: currentList); } - void removeMultipleSelectedItem(List assets) { - Set currentList = state.selectedItems; + void removeMultipleSelectedItem(List assets) { + Set currentList = state.selectedItems; - for (ImmichAsset asset in assets) { + for (AssetResponseDto asset in assets) { currentList.removeWhere((e) => e.id == asset.id); } @@ -64,4 +68,5 @@ class HomePageStateNotifier extends StateNotifier { final homePageStateProvider = StateNotifierProvider( - ((ref) => HomePageStateNotifier())); + ((ref) => HomePageStateNotifier()), +); diff --git a/mobile/lib/modules/home/providers/upload_profile_image.provider.dart b/mobile/lib/modules/home/providers/upload_profile_image.provider.dart index f8d21d782c..66060da383 100644 --- a/mobile/lib/modules/home/providers/upload_profile_image.provider.dart +++ b/mobile/lib/modules/home/providers/upload_profile_image.provider.dart @@ -73,10 +73,12 @@ class UploadProfileImageState { class UploadProfileImageNotifier extends StateNotifier { UploadProfileImageNotifier(this._userSErvice) - : super(UploadProfileImageState( - profileImagePath: '', - status: UploadProfileStatus.idle, - )); + : super( + UploadProfileImageState( + profileImagePath: '', + status: UploadProfileStatus.idle, + ), + ); final UserService _userSErvice; @@ -88,8 +90,9 @@ class UploadProfileImageNotifier if (res != null) { debugPrint("Succesfully upload profile image"); state = state.copyWith( - status: UploadProfileStatus.success, - profileImagePath: res.profileImagePath); + status: UploadProfileStatus.success, + profileImagePath: res.profileImagePath, + ); return true; } @@ -100,4 +103,5 @@ class UploadProfileImageNotifier final uploadProfileImageProvider = StateNotifierProvider( - ((ref) => UploadProfileImageNotifier(ref.watch(userServiceProvider)))); + ((ref) => UploadProfileImageNotifier(ref.watch(userServiceProvider))), +); diff --git a/mobile/lib/modules/home/services/asset.service.dart b/mobile/lib/modules/home/services/asset.service.dart index 16546f1dc7..3e44d8b165 100644 --- a/mobile/lib/modules/home/services/asset.service.dart +++ b/mobile/lib/modules/home/services/asset.service.dart @@ -1,120 +1,51 @@ -import 'dart:convert'; +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/home/models/delete_asset_response.model.dart'; -import 'package:immich_mobile/modules/home/models/get_all_asset_response.model.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; -import 'package:immich_mobile/shared/models/immich_asset_with_exif.model.dart'; -import 'package:immich_mobile/shared/services/network.service.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; +import 'package:openapi/api.dart'; -final assetServiceProvider = - Provider((ref) => AssetService(ref.watch(networkServiceProvider))); +final assetServiceProvider = Provider( + (ref) => AssetService( + ref.watch(apiServiceProvider), + ), +); class AssetService { - final NetworkService _networkService; - AssetService(this._networkService); + final ApiService _apiService; - Future?> getAllAsset() async { - var res = await _networkService.getRequest(url: "asset/"); + AssetService(this._apiService); + + Future?> getAllAsset() async { try { - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => ImmichAsset.fromMap(a))); - return result; + return await _apiService.assetApi.getAllAssets(); } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); - } - return null; - } - - Future getAllAssetWithPagination() async { - var res = await _networkService.getRequest(url: "asset/all"); - try { - Map decodedData = jsonDecode(res.toString()); - - GetAllAssetResponse result = GetAllAssetResponse.fromMap(decodedData); - return result; - } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); - } - return null; - } - - Future getOlderAsset(String? nextPageKey) async { - try { - var res = await _networkService.getRequest( - url: "asset/all?nextPageKey=$nextPageKey", - ); - - Map decodedData = jsonDecode(res.toString()); - - GetAllAssetResponse result = GetAllAssetResponse.fromMap(decodedData); - if (result.count != 0) { - return result; - } - } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); - } - return null; - } - - Future> getNewAsset(String latestDate) async { - try { - var res = await _networkService.getRequest( - url: "asset/new?latestDate=$latestDate", - ); - - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => ImmichAsset.fromMap(a))); - if (result.isNotEmpty) { - return result; - } - - return []; - } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); - return []; - } - } - - Future getAssetById(String assetId) async { - try { - var res = await _networkService.getRequest( - url: "asset/assetById/$assetId", - ); - - Map decodedData = jsonDecode(res.toString()); - - ImmichAssetWithExif result = ImmichAssetWithExif.fromMap(decodedData); - return result; - } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); + debugPrint("Error [getAllAsset] ${e.toString()}"); return null; } } - Future?> deleteAssets( - Set deleteAssets) async { + Future getAssetById(String assetId) async { try { - var payload = []; + return await _apiService.assetApi.getAssetById(assetId); + } catch (e) { + debugPrint("Error [getAssetById] ${e.toString()}"); + return null; + } + } + + Future?> deleteAssets( + Set deleteAssets, + ) async { + try { + List payload = []; for (var asset in deleteAssets) { payload.add(asset.id); } - var res = await _networkService - .deleteRequest(url: "asset/", data: {"ids": payload}); - - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => DeleteAssetResponse.fromMap(a))); - - return result; + return await _apiService.assetApi + .deleteAsset(DeleteAssetDto(ids: payload)); } catch (e) { debugPrint("Error getAllAsset ${e.toString()}"); return null; diff --git a/mobile/lib/modules/home/ui/control_bottom_app_bar.dart b/mobile/lib/modules/home/ui/control_bottom_app_bar.dart index 2ff56fb997..87b08e2313 100644 --- a/mobile/lib/modules/home/ui/control_bottom_app_bar.dart +++ b/mobile/lib/modules/home/ui/control_bottom_app_bar.dart @@ -15,7 +15,9 @@ class ControlBottomAppBar extends StatelessWidget { height: MediaQuery.of(context).size.height * 0.15, decoration: BoxDecoration( borderRadius: const BorderRadius.only( - topLeft: Radius.circular(15), topRight: Radius.circular(15)), + topLeft: Radius.circular(15), + topRight: Radius.circular(15), + ), color: Colors.grey[300]?.withOpacity(0.98), ), child: Column( @@ -48,12 +50,12 @@ class ControlBottomAppBar extends StatelessWidget { } class ControlBoxButton extends StatelessWidget { - const ControlBoxButton( - {Key? key, - required this.label, - required this.iconData, - required this.onPressed}) - : super(key: key); + const ControlBoxButton({ + Key? key, + required this.label, + required this.iconData, + required this.onPressed, + }) : super(key: key); final String label; final IconData iconData; diff --git a/mobile/lib/modules/home/ui/daily_title_text.dart b/mobile/lib/modules/home/ui/daily_title_text.dart index 15e941f9a0..ad2532b416 100644 --- a/mobile/lib/modules/home/ui/daily_title_text.dart +++ b/mobile/lib/modules/home/ui/daily_title_text.dart @@ -2,8 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; -import 'package:intl/intl.dart'; +import 'package:openapi/api.dart'; class DailyTitleText extends ConsumerWidget { const DailyTitleText({ @@ -13,14 +12,15 @@ class DailyTitleText extends ConsumerWidget { }) : super(key: key); final String isoDate; - final List assetGroup; + final List assetGroup; @override Widget build(BuildContext context, WidgetRef ref) { var currentYear = DateTime.now().year; var groupYear = DateTime.parse(isoDate).year; - var formatDateTemplate = - currentYear == groupYear ? "daily_title_text_date".tr() : "daily_title_text_date_year".tr(); + var formatDateTemplate = currentYear == groupYear + ? "daily_title_text_date".tr() + : "daily_title_text_date_year".tr(); var dateText = DateFormat(formatDateTemplate).format(DateTime.parse(isoDate)); var isMultiSelectEnable = @@ -74,7 +74,11 @@ class DailyTitleText extends ConsumerWidget { return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only( - top: 29.0, bottom: 29.0, left: 12.0, right: 12.0), + top: 29.0, + bottom: 29.0, + left: 12.0, + right: 12.0, + ), child: Row( children: [ Text( diff --git a/mobile/lib/modules/home/ui/disable_multi_select_button.dart b/mobile/lib/modules/home/ui/disable_multi_select_button.dart index 3a28d7fe86..9a75bd6376 100644 --- a/mobile/lib/modules/home/ui/disable_multi_select_button.dart +++ b/mobile/lib/modules/home/ui/disable_multi_select_button.dart @@ -29,15 +29,18 @@ class DisableMultiSelectButton extends ConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: TextButton.icon( - onPressed: () { - onPressed(); - }, - icon: const Icon(Icons.close_rounded), - label: Text( - '$selectedItemCount', - style: const TextStyle( - fontWeight: FontWeight.w600, fontSize: 18), - )), + onPressed: () { + onPressed(); + }, + icon: const Icon(Icons.close_rounded), + label: Text( + '$selectedItemCount', + style: const TextStyle( + fontWeight: FontWeight.w600, + fontSize: 18, + ), + ), + ), ), ), ), diff --git a/mobile/lib/modules/home/ui/draggable_scrollbar.dart b/mobile/lib/modules/home/ui/draggable_scrollbar.dart index b7e075492c..90e7d85e51 100644 --- a/mobile/lib/modules/home/ui/draggable_scrollbar.dart +++ b/mobile/lib/modules/home/ui/draggable_scrollbar.dart @@ -118,20 +118,24 @@ class DraggableScrollbar extends StatefulWidget { this.labelConstraints, }) : assert(child.scrollDirection == Axis.vertical), scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb), + heightScrollThumb * 0.6, + scrollThumbKey, + alwaysVisibleScrollThumb, + ), super(key: key); @override DraggableScrollbarState createState() => DraggableScrollbarState(); - static buildScrollThumbAndLabel( - {required Widget scrollThumb, - required Color backgroundColor, - required Animation? thumbAnimation, - required Animation? labelAnimation, - required Text? labelText, - required BoxConstraints? labelConstraints, - required bool alwaysVisibleScrollThumb}) { + static buildScrollThumbAndLabel({ + required Widget scrollThumb, + required Color backgroundColor, + required Animation? thumbAnimation, + required Animation? labelAnimation, + required Text? labelText, + required BoxConstraints? labelConstraints, + required bool alwaysVisibleScrollThumb, + }) { var scrollThumbAndLabel = labelText == null ? scrollThumb : Row( @@ -158,7 +162,10 @@ class DraggableScrollbar extends StatefulWidget { } static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + double width, + Key? scrollThumbKey, + bool alwaysVisibleScrollThumb, + ) { return ( Color backgroundColor, Animation thumbAnimation, @@ -198,7 +205,9 @@ class DraggableScrollbar extends StatefulWidget { } static ScrollThumbBuilder _thumbArrowBuilder( - Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + Key? scrollThumbKey, + bool alwaysVisibleScrollThumb, + ) { return ( Color backgroundColor, Animation thumbAnimation, @@ -234,7 +243,9 @@ class DraggableScrollbar extends StatefulWidget { } static ScrollThumbBuilder _thumbRRectBuilder( - Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + Key? scrollThumbKey, + bool alwaysVisibleScrollThumb, + ) { return ( Color backgroundColor, Animation thumbAnimation, @@ -372,42 +383,44 @@ class DraggableScrollbarState extends State } return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - //print("LayoutBuilder constraints=$constraints"); + builder: (BuildContext context, BoxConstraints constraints) { + //print("LayoutBuilder constraints=$constraints"); - return NotificationListener( - onNotification: (ScrollNotification notification) { - changePosition(notification); - return false; - }, - child: Stack( - children: [ - RepaintBoundary( - child: widget.child, - ), - RepaintBoundary( + return NotificationListener( + onNotification: (ScrollNotification notification) { + changePosition(notification); + return false; + }, + child: Stack( + children: [ + RepaintBoundary( + child: widget.child, + ), + RepaintBoundary( child: GestureDetector( - onVerticalDragStart: _onVerticalDragStart, - onVerticalDragUpdate: _onVerticalDragUpdate, - onVerticalDragEnd: _onVerticalDragEnd, - child: Container( - alignment: Alignment.topRight, - margin: EdgeInsets.only(top: _barOffset), - padding: widget.padding, - child: widget.scrollThumbBuilder( - widget.backgroundColor, - _thumbAnimation, - _labelAnimation, - widget.heightScrollThumb, - labelText: labelText, - labelConstraints: widget.labelConstraints, + onVerticalDragStart: _onVerticalDragStart, + onVerticalDragUpdate: _onVerticalDragUpdate, + onVerticalDragEnd: _onVerticalDragEnd, + child: Container( + alignment: Alignment.topRight, + margin: EdgeInsets.only(top: _barOffset), + padding: widget.padding, + child: widget.scrollThumbBuilder( + widget.backgroundColor, + _thumbAnimation, + _labelAnimation, + widget.heightScrollThumb, + labelText: labelText, + labelConstraints: widget.labelConstraints, + ), + ), ), ), - )), - ], - ), - ); - }); + ], + ), + ); + }, + ); } //scroll bar has received notification that it's view was scrolled @@ -498,7 +511,10 @@ class DraggableScrollbarState extends State } double viewDelta = getScrollViewDelta( - details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); + details.delta.dy, + barMaxScrollExtent, + viewMaxScrollExtent, + ); _viewOffset = widget.controller.position.pixels + viewDelta; if (_viewOffset < widget.controller.position.minScrollExtent) { @@ -579,7 +595,9 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); path.lineTo( - startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); + startPointX + arrowWidth / 2, + startPointY - arrowWidth / 2 + 1.0, + ); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -589,7 +607,9 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); path.lineTo( - startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); + startPointX + arrowWidth / 2, + startPointY + arrowWidth / 2 - 1.0, + ); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); diff --git a/mobile/lib/modules/home/ui/image_grid.dart b/mobile/lib/modules/home/ui/image_grid.dart index 3b8e11adb8..1a7f1b2d71 100644 --- a/mobile/lib/modules/home/ui/image_grid.dart +++ b/mobile/lib/modules/home/ui/image_grid.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/home/ui/thumbnail_image.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class ImageGrid extends ConsumerWidget { - final List assetGroup; + final List assetGroup; const ImageGrid({Key? key, required this.assetGroup}) : super(key: key); @@ -25,7 +25,7 @@ class ImageGrid extends ConsumerWidget { child: Stack( children: [ ThumbnailImage(asset: assetGroup[index]), - if (assetType != 'IMAGE') + if (assetType != AssetTypeEnum.IMAGE) Positioned( top: 5, right: 5, diff --git a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart index 1f46def56c..7e71cf93f4 100644 --- a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart +++ b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart @@ -31,7 +31,8 @@ class ImmichSliverAppBar extends ConsumerWidget { pinned: false, snap: false, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5))), + borderRadius: BorderRadius.all(Radius.circular(5)), + ), leading: Builder( builder: (BuildContext context) { return Stack( @@ -99,7 +100,8 @@ class ImmichSliverAppBar extends ConsumerWidget { child: CircularProgressIndicator( strokeWidth: 1, valueColor: AlwaysStoppedAnimation( - Theme.of(context).primaryColor), + Theme.of(context).primaryColor, + ), ), ), ), @@ -117,7 +119,8 @@ class ImmichSliverAppBar extends ConsumerWidget { Icons.cloud_off_rounded, size: 8, ), - child: const Icon(Icons.backup_rounded)), + child: const Icon(Icons.backup_rounded), + ), onPressed: () async { var onPop = await AutoRouter.of(context) .push(const BackupControllerRoute()); diff --git a/mobile/lib/modules/home/ui/monthly_title_text.dart b/mobile/lib/modules/home/ui/monthly_title_text.dart index ad9998c8f2..f319624604 100644 --- a/mobile/lib/modules/home/ui/monthly_title_text.dart +++ b/mobile/lib/modules/home/ui/monthly_title_text.dart @@ -1,6 +1,5 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; class MonthlyTitleText extends StatelessWidget { const MonthlyTitleText({ @@ -12,7 +11,8 @@ class MonthlyTitleText extends StatelessWidget { @override Widget build(BuildContext context) { - var monthTitleText = DateFormat("monthly_title_text_date_format".tr()).format(DateTime.parse(isoDate)); + var monthTitleText = DateFormat("monthly_title_text_date_format".tr()) + .format(DateTime.parse(isoDate)); return SliverToBoxAdapter( child: Padding( diff --git a/mobile/lib/modules/home/ui/profile_drawer.dart b/mobile/lib/modules/home/ui/profile_drawer.dart index 20ef4d139a..6154fdc315 100644 --- a/mobile/lib/modules/home/ui/profile_drawer.dart +++ b/mobile/lib/modules/home/ui/profile_drawer.dart @@ -55,7 +55,8 @@ class ProfileDrawer extends HookConsumerWidget { return CircleAvatar( radius: 35, backgroundImage: NetworkImage( - '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}'), + '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}', + ), backgroundColor: Colors.transparent, ); } else { @@ -71,7 +72,8 @@ class ProfileDrawer extends HookConsumerWidget { return CircleAvatar( radius: 35, backgroundImage: NetworkImage( - '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}'), + '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}', + ), backgroundColor: Colors.transparent, ); } @@ -93,7 +95,10 @@ class ProfileDrawer extends HookConsumerWidget { _pickUserProfileImage() async { final XFile? image = await ImagePicker().pickImage( - source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024); + source: ImageSource.gallery, + maxHeight: 1024, + maxWidth: 1024, + ); if (image != null) { var success = @@ -101,16 +106,20 @@ class ProfileDrawer extends HookConsumerWidget { if (success) { ref.watch(authenticationProvider.notifier).updateUserProfileImagePath( - ref.read(uploadProfileImageProvider).profileImagePath); + ref.read(uploadProfileImageProvider).profileImagePath, + ); } } } - useEffect(() { - _getPackageInfo(); - _buildUserProfileImage(); - return null; - }, []); + useEffect( + () { + _getPackageInfo(); + _buildUserProfileImage(); + return null; + }, + [], + ); return Drawer( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -186,9 +195,10 @@ class ProfileDrawer extends HookConsumerWidget { title: const Text( "profile_drawer_sign_out", style: TextStyle( - color: Colors.black54, - fontSize: 14, - fontWeight: FontWeight.bold), + color: Colors.black54, + fontSize: 14, + fontWeight: FontWeight.bold, + ), ).tr(), onTap: () async { bool res = @@ -231,9 +241,10 @@ class ProfileDrawer extends HookConsumerWidget { : "profile_drawer_client_server_up_to_date".tr(), textAlign: TextAlign.center, style: TextStyle( - fontSize: 11, - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.w600), + fontSize: 11, + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.w600, + ), ), ), const Divider(), @@ -271,7 +282,7 @@ class ProfileDrawer extends HookConsumerWidget { ), ), Text( - "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}", + "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch_}", style: TextStyle( fontSize: 11, color: Colors.grey[500], diff --git a/mobile/lib/modules/home/ui/thumbnail_image.dart b/mobile/lib/modules/home/ui/thumbnail_image.dart index ff5af246d6..f86e76505a 100644 --- a/mobile/lib/modules/home/ui/thumbnail_image.dart +++ b/mobile/lib/modules/home/ui/thumbnail_image.dart @@ -8,11 +8,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:openapi/api.dart'; class ThumbnailImage extends HookConsumerWidget { - final ImmichAsset asset; + final AssetResponseDto asset; const ThumbnailImage({Key? key, required this.asset}) : super(key: key); @@ -22,14 +22,13 @@ class ThumbnailImage extends HookConsumerWidget { var box = Hive.box(userInfoBox); var thumbnailRequestUrl = - '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true'; - + '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}'; var selectedAsset = ref.watch(homePageStateProvider).selectedItems; var isMultiSelectEnable = ref.watch(homePageStateProvider).isMultiSelectEnable; var deviceId = ref.watch(authenticationProvider).deviceId; - Widget _buildSelectionIcon(ImmichAsset asset) { + Widget _buildSelectionIcon(AssetResponseDto asset) { if (selectedAsset.contains(asset)) { return Icon( Icons.check_circle, @@ -61,7 +60,7 @@ class ThumbnailImage extends HookConsumerWidget { .watch(homePageStateProvider.notifier) .addSingleSelectedItem(asset); } else { - if (asset.type == 'IMAGE') { + if (asset.type == AssetTypeEnum.IMAGE) { AutoRouter.of(context).push( ImageViewerRoute( imageUrl: @@ -74,9 +73,10 @@ class ThumbnailImage extends HookConsumerWidget { } else { AutoRouter.of(context).push( VideoViewerRoute( - videoUrl: - '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}', - asset: asset), + videoUrl: + '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}', + asset: asset, + ), ); } } @@ -94,14 +94,16 @@ class ThumbnailImage extends HookConsumerWidget { decoration: BoxDecoration( border: isMultiSelectEnable && selectedAsset.contains(asset) ? Border.all( - color: Theme.of(context).primaryColorLight, width: 10) + color: Theme.of(context).primaryColorLight, + width: 10, + ) : const Border(), ), child: CachedNetworkImage( cacheKey: "${asset.id}-${cacheKey.value}", width: 300, height: 300, - memCacheHeight: asset.type == 'IMAGE' ? 250 : 400, + memCacheHeight: asset.type == AssetTypeEnum.IMAGE ? 250 : 400, fit: BoxFit.cover, imageUrl: thumbnailRequestUrl, httpHeaders: { @@ -112,9 +114,11 @@ class ThumbnailImage extends HookConsumerWidget { Transform.scale( scale: 0.2, child: CircularProgressIndicator( - value: downloadProgress.progress), + value: downloadProgress.progress, + ), ), errorWidget: (context, url, error) { + debugPrint("Error getting thumbnail $url = $error"); return Icon( Icons.image_not_supported_outlined, color: Theme.of(context).primaryColor, diff --git a/mobile/lib/modules/home/views/home_page.dart b/mobile/lib/modules/home/views/home_page.dart index 00c1aa8935..66a4834b75 100644 --- a/mobile/lib/modules/home/views/home_page.dart +++ b/mobile/lib/modules/home/views/home_page.dart @@ -26,12 +26,15 @@ class HomePage extends HookConsumerWidget { ref.watch(homePageStateProvider).isMultiSelectEnable; var homePageState = ref.watch(homePageStateProvider); - useEffect(() { - ref.read(websocketProvider.notifier).connect(); - ref.read(assetProvider.notifier).getAllAsset(); - ref.watch(serverInfoProvider.notifier).getServerVersion(); - return null; - }, []); + useEffect( + () { + ref.read(websocketProvider.notifier).connect(); + ref.read(assetProvider.notifier).getAllAsset(); + ref.watch(serverInfoProvider.notifier).getServerVersion(); + return null; + }, + [], + ); void reloadAllAsset() { ref.read(assetProvider.notifier).getAllAsset(); diff --git a/mobile/lib/modules/login/models/authentication_state.model.dart b/mobile/lib/modules/login/models/authentication_state.model.dart index 6603021bb8..4805d499bd 100644 --- a/mobile/lib/modules/login/models/authentication_state.model.dart +++ b/mobile/lib/modules/login/models/authentication_state.model.dart @@ -1,10 +1,8 @@ -import 'dart:convert'; - -import 'package:immich_mobile/shared/models/device_info.model.dart'; +import 'package:openapi/api.dart'; class AuthenticationState { final String deviceId; - final String deviceType; + final DeviceTypeEnum deviceType; final String userId; final String userEmail; final bool isAuthenticated; @@ -13,8 +11,7 @@ class AuthenticationState { final bool isAdmin; final bool shouldChangePassword; final String profileImagePath; - final DeviceInfoRemote deviceInfo; - + final DeviceInfoResponseDto deviceInfo; AuthenticationState({ required this.deviceId, required this.deviceType, @@ -31,7 +28,7 @@ class AuthenticationState { AuthenticationState copyWith({ String? deviceId, - String? deviceType, + DeviceTypeEnum? deviceType, String? userId, String? userEmail, bool? isAuthenticated, @@ -40,7 +37,7 @@ class AuthenticationState { bool? isAdmin, bool? shouldChangePassword, String? profileImagePath, - DeviceInfoRemote? deviceInfo, + DeviceInfoResponseDto? deviceInfo, }) { return AuthenticationState( deviceId: deviceId ?? this.deviceId, @@ -57,45 +54,6 @@ class AuthenticationState { ); } - Map toMap() { - final result = {}; - - result.addAll({'deviceId': deviceId}); - result.addAll({'deviceType': deviceType}); - result.addAll({'userId': userId}); - result.addAll({'userEmail': userEmail}); - result.addAll({'isAuthenticated': isAuthenticated}); - result.addAll({'firstName': firstName}); - result.addAll({'lastName': lastName}); - result.addAll({'isAdmin': isAdmin}); - result.addAll({'shouldChangePassword': shouldChangePassword}); - result.addAll({'profileImagePath': profileImagePath}); - result.addAll({'deviceInfo': deviceInfo.toMap()}); - - return result; - } - - factory AuthenticationState.fromMap(Map map) { - return AuthenticationState( - deviceId: map['deviceId'] ?? '', - deviceType: map['deviceType'] ?? '', - userId: map['userId'] ?? '', - userEmail: map['userEmail'] ?? '', - isAuthenticated: map['isAuthenticated'] ?? false, - firstName: map['firstName'] ?? '', - lastName: map['lastName'] ?? '', - isAdmin: map['isAdmin'] ?? false, - shouldChangePassword: map['shouldChangePassword'] ?? false, - profileImagePath: map['profileImagePath'] ?? '', - deviceInfo: DeviceInfoRemote.fromMap(map['deviceInfo']), - ); - } - - String toJson() => json.encode(toMap()); - - factory AuthenticationState.fromJson(String source) => - AuthenticationState.fromMap(json.decode(source)); - @override String toString() { return 'AuthenticationState(deviceId: $deviceId, deviceType: $deviceType, userId: $userId, userEmail: $userEmail, isAuthenticated: $isAuthenticated, firstName: $firstName, lastName: $lastName, isAdmin: $isAdmin, shouldChangePassword: $shouldChangePassword, profileImagePath: $profileImagePath, deviceInfo: $deviceInfo)'; diff --git a/mobile/lib/modules/login/models/hive_saved_login_info.model.dart b/mobile/lib/modules/login/models/hive_saved_login_info.model.dart index 471e5df66c..6d367d5978 100644 --- a/mobile/lib/modules/login/models/hive_saved_login_info.model.dart +++ b/mobile/lib/modules/login/models/hive_saved_login_info.model.dart @@ -16,9 +16,10 @@ class HiveSavedLoginInfo { @HiveField(3) bool isSaveLogin; - HiveSavedLoginInfo( - {required this.email, - required this.password, - required this.serverUrl, - required this.isSaveLogin}); + HiveSavedLoginInfo({ + required this.email, + required this.password, + required this.serverUrl, + required this.isSaveLogin, + }); } diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index 0634e5731a..885066e220 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -1,23 +1,23 @@ -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/modules/login/models/authentication_state.model.dart'; import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart'; -import 'package:immich_mobile/modules/login/models/login_response.model.dart'; import 'package:immich_mobile/modules/backup/services/backup.service.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/shared/services/device_info.service.dart'; -import 'package:immich_mobile/shared/services/network.service.dart'; -import 'package:immich_mobile/shared/models/device_info.model.dart'; +import 'package:openapi/api.dart'; class AuthenticationNotifier extends StateNotifier { AuthenticationNotifier( - this._deviceInfoService, this._backupService, this._networkService) - : super( + this._deviceInfoService, + this._backupService, + this._apiService, + ) : super( AuthenticationState( deviceId: "", - deviceType: "", + deviceType: DeviceTypeEnum.ANDROID, userId: "", userEmail: "", firstName: '', @@ -26,12 +26,11 @@ class AuthenticationNotifier extends StateNotifier { isAdmin: false, shouldChangePassword: false, isAuthenticated: false, - deviceInfo: DeviceInfoRemote( + deviceInfo: DeviceInfoResponseDto( id: 0, userId: "", deviceId: "", - deviceType: "", - notificationToken: "", + deviceType: DeviceTypeEnum.ANDROID, createdAt: "", isAutoBackup: false, ), @@ -40,10 +39,14 @@ class AuthenticationNotifier extends StateNotifier { final DeviceInfoService _deviceInfoService; final BackupService _backupService; - final NetworkService _networkService; + final ApiService _apiService; - Future login(String email, String password, String serverEndpoint, - bool isSavedLoginInfo) async { + Future login( + String email, + String password, + String serverEndpoint, + bool isSavedLoginInfo, + ) async { // Store server endpoint to Hive and test endpoint if (serverEndpoint[serverEndpoint.length - 1] == "/") { var validUrl = serverEndpoint.substring(0, serverEndpoint.length - 1); @@ -52,12 +55,12 @@ class AuthenticationNotifier extends StateNotifier { Hive.box(userInfoBox).put(serverEndpointKey, serverEndpoint); } + // Check Server URL validity try { - bool isServerEndpointVerified = await _networkService.pingServer(); - if (!isServerEndpointVerified) { - return false; - } + _apiService.setEndpoint(Hive.box(userInfoBox).get(serverEndpointKey)); + await _apiService.serverInfoApi.pingServer(); } catch (e) { + debugPrint('Invalid Server Endpoint Url $e'); return false; } @@ -72,56 +75,73 @@ class AuthenticationNotifier extends StateNotifier { // Make sign-in request try { - Response res = await _networkService.postRequest( - url: 'auth/login', data: {'email': email, 'password': password}); + var loginResponse = await _apiService.authenticationApi.login( + LoginCredentialDto( + email: email, + password: password, + ), + ); - var payload = LogInReponse.fromJson(res.toString()); + if (loginResponse == null) { + debugPrint('Login Response is null'); + return false; + } - Hive.box(userInfoBox).put(accessTokenKey, payload.accessToken); + Hive.box(userInfoBox).put(accessTokenKey, loginResponse.accessToken); state = state.copyWith( isAuthenticated: true, - userId: payload.userId, - userEmail: payload.userEmail, - firstName: payload.firstName, - lastName: payload.lastName, - profileImagePath: payload.profileImagePath, - isAdmin: payload.isAdmin, - shouldChangePassword: payload.shouldChangePassword, + userId: loginResponse.userId, + userEmail: loginResponse.userEmail, + firstName: loginResponse.firstName, + lastName: loginResponse.lastName, + profileImagePath: loginResponse.profileImagePath, + isAdmin: loginResponse.isAdmin, + shouldChangePassword: loginResponse.shouldChangePassword, ); + // Login Success - Set Access Token to API Client + _apiService.setAccessToken(loginResponse.accessToken); + if (isSavedLoginInfo) { // Save login info to local storage Hive.box(hiveLoginInfoBox).put( savedLoginInfoKey, HiveSavedLoginInfo( - email: email, - password: password, - isSaveLogin: true, - serverUrl: Hive.box(userInfoBox).get(serverEndpointKey)), + email: email, + password: password, + isSaveLogin: true, + serverUrl: Hive.box(userInfoBox).get(serverEndpointKey), + ), ); } else { Hive.box(hiveLoginInfoBox) .delete(savedLoginInfoKey); } } catch (e) { + debugPrint("Error logging in $e"); return false; } // Register device info try { - Response res = await _networkService.postRequest( - url: 'device-info', - data: { - 'deviceId': state.deviceId, - 'deviceType': state.deviceType, - }, + DeviceInfoResponseDto? deviceInfo = + await _apiService.deviceInfoApi.createDeviceInfo( + CreateDeviceInfoDto( + deviceId: state.deviceId, + deviceType: state.deviceType, + ), ); - DeviceInfoRemote deviceInfo = DeviceInfoRemote.fromJson(res.toString()); + if (deviceInfo == null) { + debugPrint('Device Info Response is null'); + return false; + } + state = state.copyWith(deviceInfo: deviceInfo); } catch (e) { debugPrint("ERROR Register Device Info: $e"); + return false; } return true; @@ -129,27 +149,7 @@ class AuthenticationNotifier extends StateNotifier { Future logout() async { Hive.box(userInfoBox).delete(accessTokenKey); - state = AuthenticationState( - deviceId: "", - deviceType: "", - userId: "", - userEmail: "", - firstName: '', - lastName: '', - profileImagePath: '', - shouldChangePassword: false, - isAuthenticated: false, - isAdmin: false, - deviceInfo: DeviceInfoRemote( - id: 0, - userId: "", - deviceId: "", - deviceType: "", - notificationToken: "", - createdAt: "", - isAutoBackup: false, - ), - ); + state = state.copyWith(isAuthenticated: false); return true; } @@ -157,11 +157,13 @@ class AuthenticationNotifier extends StateNotifier { setAutoBackup(bool backupState) async { var deviceInfo = await _deviceInfoService.getDeviceInfo(); var deviceId = deviceInfo["deviceId"]; - var deviceType = deviceInfo["deviceType"]; - DeviceInfoRemote deviceInfoRemote = + DeviceTypeEnum deviceType = deviceInfo["deviceType"]; + + DeviceInfoResponseDto updatedDeviceInfo = await _backupService.setAutoBackup(backupState, deviceId, deviceType); - state = state.copyWith(deviceInfo: deviceInfoRemote); + + state = state.copyWith(deviceInfo: updatedDeviceInfo); } updateUserProfileImagePath(String path) { @@ -169,19 +171,20 @@ class AuthenticationNotifier extends StateNotifier { } Future changePassword(String newPassword) async { - Response res = await _networkService.putRequest( - url: 'user', - data: { - 'id': state.userId, - 'password': newPassword, - 'shouldChangePassword': false, - }, - ); + try { + await _apiService.userApi.updateUser( + UpdateUserDto( + id: state.userId, + password: newPassword, + shouldChangePassword: false, + ), + ); - if (res.statusCode == 200) { state = state.copyWith(shouldChangePassword: false); + return true; - } else { + } catch (e) { + debugPrint("Error changing password $e"); return false; } } @@ -192,6 +195,6 @@ final authenticationProvider = return AuthenticationNotifier( ref.watch(deviceInfoServiceProvider), ref.watch(backupServiceProvider), - ref.watch(networkServiceProvider), + ref.watch(apiServiceProvider), ); }); diff --git a/mobile/lib/modules/login/ui/change_password_form.dart b/mobile/lib/modules/login/ui/change_password_form.dart index 4e3632e741..0351803d43 100644 --- a/mobile/lib/modules/login/ui/change_password_form.dart +++ b/mobile/lib/modules/login/ui/change_password_form.dart @@ -140,35 +140,36 @@ class ChangePasswordButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton( - style: ElevatedButton.styleFrom( - visualDensity: VisualDensity.standard, - primary: Theme.of(context).primaryColor, - onPrimary: Colors.grey[50], - elevation: 2, - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), - ), - onPressed: () async { - if (formKey.currentState!.validate()) { - var isSuccess = await ref - .watch(authenticationProvider.notifier) - .changePassword(passwordController.value.text); + style: ElevatedButton.styleFrom( + visualDensity: VisualDensity.standard, + primary: Theme.of(context).primaryColor, + onPrimary: Colors.grey[50], + elevation: 2, + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), + ), + onPressed: () async { + if (formKey.currentState!.validate()) { + var isSuccess = await ref + .watch(authenticationProvider.notifier) + .changePassword(passwordController.value.text); - if (isSuccess) { - bool res = - await ref.watch(authenticationProvider.notifier).logout(); + if (isSuccess) { + bool res = + await ref.watch(authenticationProvider.notifier).logout(); - if (res) { - ref.watch(backupProvider.notifier).cancelBackup(); - ref.watch(assetProvider.notifier).clearAllAsset(); - ref.watch(websocketProvider.notifier).disconnect(); - AutoRouter.of(context).replace(const LoginRoute()); - } + if (res) { + ref.watch(backupProvider.notifier).cancelBackup(); + ref.watch(assetProvider.notifier).clearAllAsset(); + ref.watch(websocketProvider.notifier).disconnect(); + AutoRouter.of(context).replace(const LoginRoute()); } } - }, - child: const Text( - "Change Password", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - )); + } + }, + child: const Text( + "Change Password", + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + ); } } diff --git a/mobile/lib/modules/login/ui/login_form.dart b/mobile/lib/modules/login/ui/login_form.dart index b109254a77..dad98505df 100644 --- a/mobile/lib/modules/login/ui/login_form.dart +++ b/mobile/lib/modules/login/ui/login_form.dart @@ -22,22 +22,25 @@ class LoginForm extends HookConsumerWidget { final passwordController = useTextEditingController.fromValue(TextEditingValue.empty); final serverEndpointController = - useTextEditingController(text: 'login_endpoint_hint'.tr()); + useTextEditingController(text: 'login_form_endpoint_hint'.tr()); final isSaveLoginInfo = useState(false); - useEffect(() { - var loginInfo = - Hive.box(hiveLoginInfoBox).get(savedLoginInfoKey); + useEffect( + () { + var loginInfo = Hive.box(hiveLoginInfoBox) + .get(savedLoginInfoKey); - if (loginInfo != null) { - usernameController.text = loginInfo.email; - passwordController.text = loginInfo.password; - serverEndpointController.text = loginInfo.serverUrl; - isSaveLoginInfo.value = loginInfo.isSaveLogin; - } + if (loginInfo != null) { + usernameController.text = loginInfo.email; + passwordController.text = loginInfo.password; + serverEndpointController.text = loginInfo.serverUrl; + isSaveLoginInfo.value = loginInfo.isSaveLogin; + } - return null; - }, []); + return null; + }, + [], + ); return Center( child: ConstrainedBox( @@ -71,14 +74,16 @@ class LoginForm extends HookConsumerWidget { dense: true, side: const BorderSide(color: Colors.grey, width: 1.5), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5)), + borderRadius: BorderRadius.circular(5), + ), enableFeedback: true, title: const Text( "login_form_save_login", style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.grey), + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), ).tr(), value: isSaveLoginInfo.value, onChanged: (switchValue) { @@ -108,7 +113,6 @@ class ServerEndpointInput extends StatelessWidget { : super(key: key); String? _validateInput(String? url) { - if (url?.startsWith(RegExp(r'https?://')) == true) { return null; } else { @@ -122,7 +126,7 @@ class ServerEndpointInput extends StatelessWidget { controller: controller, decoration: InputDecoration( labelText: 'login_form_endpoint_url'.tr(), - border: OutlineInputBorder(), + border: const OutlineInputBorder(), hintText: 'login_form_endpoint_hint'.tr(), ), validator: _validateInput, @@ -140,8 +144,9 @@ class EmailInput extends StatelessWidget { if (email == null || email == '') return null; if (email.endsWith(' ')) return 'login_form_err_trailing_whitespace'.tr(); if (email.startsWith(' ')) return 'login_form_err_leading_whitespace'.tr(); - if (email.contains(' ') || !email.contains('@')) + if (email.contains(' ') || !email.contains('@')) { return 'login_form_err_invalid_email'.tr(); + } return null; } @@ -151,7 +156,7 @@ class EmailInput extends StatelessWidget { controller: controller, decoration: InputDecoration( labelText: 'login_form_label_email'.tr(), - border: OutlineInputBorder(), + border: const OutlineInputBorder(), hintText: 'login_form_email_hint'.tr(), ), validator: _validateInput, @@ -171,9 +176,10 @@ class PasswordInput extends StatelessWidget { obscureText: true, controller: controller, decoration: InputDecoration( - labelText: 'login_form_label_password'.tr(), - border: OutlineInputBorder(), - hintText: 'login_form_password_hint'.tr()), + labelText: 'login_form_label_password'.tr(), + border: const OutlineInputBorder(), + hintText: 'login_form_password_hint'.tr(), + ), ); } } @@ -195,43 +201,47 @@ class LoginButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton( - style: ElevatedButton.styleFrom( - visualDensity: VisualDensity.standard, - primary: Theme.of(context).primaryColor, - onPrimary: Colors.grey[50], - elevation: 2, - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), - ), - onPressed: () async { - // This will remove current cache asset state of previous user login. - ref.watch(assetProvider.notifier).clearAllAsset(); + style: ElevatedButton.styleFrom( + visualDensity: VisualDensity.standard, + primary: Theme.of(context).primaryColor, + onPrimary: Colors.grey[50], + elevation: 2, + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), + ), + onPressed: () async { + // This will remove current cache asset state of previous user login. + ref.watch(assetProvider.notifier).clearAllAsset(); - var isAuthenticated = await ref - .watch(authenticationProvider.notifier) - .login(emailController.text, passwordController.text, - serverEndpointController.text, isSavedLoginInfo); + var isAuthenticated = + await ref.watch(authenticationProvider.notifier).login( + emailController.text, + passwordController.text, + serverEndpointController.text, + isSavedLoginInfo, + ); - if (isAuthenticated) { - // Resume backup (if enable) then navigate + if (isAuthenticated) { + // Resume backup (if enable) then navigate - if (ref.watch(authenticationProvider).shouldChangePassword && - !ref.watch(authenticationProvider).isAdmin) { - AutoRouter.of(context).push(const ChangePasswordRoute()); - } else { - ref.watch(backupProvider.notifier).resumeBackup(); - AutoRouter.of(context).pushNamed("/tab-controller-page"); - } + if (ref.watch(authenticationProvider).shouldChangePassword && + !ref.watch(authenticationProvider).isAdmin) { + AutoRouter.of(context).push(const ChangePasswordRoute()); } else { - ImmichToast.show( - context: context, - msg: "login_failed".tr(), - toastType: ToastType.error, - ); + ref.watch(backupProvider.notifier).resumeBackup(); + AutoRouter.of(context).pushNamed("/tab-controller-page"); } - }, - child: const Text( - "login_form_button_text", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr()); + } else { + ImmichToast.show( + context: context, + msg: "login_form_failed_login".tr(), + toastType: ToastType.error, + ); + } + }, + child: const Text( + "login_form_button_text", + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ).tr(), + ); } } diff --git a/mobile/lib/modules/search/models/curated_location.model.dart b/mobile/lib/modules/search/models/curated_location.model.dart deleted file mode 100644 index a3baebbdf4..0000000000 --- a/mobile/lib/modules/search/models/curated_location.model.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'dart:convert'; - -class CuratedLocation { - final String id; - final String city; - final String resizePath; - final String deviceAssetId; - final String deviceId; - - CuratedLocation({ - required this.id, - required this.city, - required this.resizePath, - required this.deviceAssetId, - required this.deviceId, - }); - - CuratedLocation copyWith({ - String? id, - String? city, - String? resizePath, - String? deviceAssetId, - String? deviceId, - }) { - return CuratedLocation( - id: id ?? this.id, - city: city ?? this.city, - resizePath: resizePath ?? this.resizePath, - deviceAssetId: deviceAssetId ?? this.deviceAssetId, - deviceId: deviceId ?? this.deviceId, - ); - } - - Map toMap() { - return { - 'id': id, - 'city': city, - 'resizePath': resizePath, - 'deviceAssetId': deviceAssetId, - 'deviceId': deviceId, - }; - } - - factory CuratedLocation.fromMap(Map map) { - return CuratedLocation( - id: map['id'] ?? '', - city: map['city'] ?? '', - resizePath: map['resizePath'] ?? '', - deviceAssetId: map['deviceAssetId'] ?? '', - deviceId: map['deviceId'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory CuratedLocation.fromJson(String source) => - CuratedLocation.fromMap(json.decode(source)); - - @override - String toString() { - return 'CuratedLocation(id: $id, city: $city, resizePath: $resizePath, deviceAssetId: $deviceAssetId, deviceId: $deviceId)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is CuratedLocation && - other.id == id && - other.city == city && - other.resizePath == resizePath && - other.deviceAssetId == deviceAssetId && - other.deviceId == deviceId; - } - - @override - int get hashCode { - return id.hashCode ^ - city.hashCode ^ - resizePath.hashCode ^ - deviceAssetId.hashCode ^ - deviceId.hashCode; - } -} diff --git a/mobile/lib/modules/search/models/curated_object.model.dart b/mobile/lib/modules/search/models/curated_object.model.dart deleted file mode 100644 index c389aaf92e..0000000000 --- a/mobile/lib/modules/search/models/curated_object.model.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'dart:convert'; - -class CuratedObject { - final String id; - final String object; - final String resizePath; - final String deviceAssetId; - final String deviceId; - CuratedObject({ - required this.id, - required this.object, - required this.resizePath, - required this.deviceAssetId, - required this.deviceId, - }); - - CuratedObject copyWith({ - String? id, - String? object, - String? resizePath, - String? deviceAssetId, - String? deviceId, - }) { - return CuratedObject( - id: id ?? this.id, - object: object ?? this.object, - resizePath: resizePath ?? this.resizePath, - deviceAssetId: deviceAssetId ?? this.deviceAssetId, - deviceId: deviceId ?? this.deviceId, - ); - } - - Map toMap() { - final result = {}; - - result.addAll({'id': id}); - result.addAll({'object': object}); - result.addAll({'resizePath': resizePath}); - result.addAll({'deviceAssetId': deviceAssetId}); - result.addAll({'deviceId': deviceId}); - - return result; - } - - factory CuratedObject.fromMap(Map map) { - return CuratedObject( - id: map['id'] ?? '', - object: map['object'] ?? '', - resizePath: map['resizePath'] ?? '', - deviceAssetId: map['deviceAssetId'] ?? '', - deviceId: map['deviceId'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory CuratedObject.fromJson(String source) => - CuratedObject.fromMap(json.decode(source)); - - @override - String toString() { - return 'CuratedObject(id: $id, object: $object, resizePath: $resizePath, deviceAssetId: $deviceAssetId, deviceId: $deviceId)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is CuratedObject && - other.id == id && - other.object == object && - other.resizePath == resizePath && - other.deviceAssetId == deviceAssetId && - other.deviceId == deviceId; - } - - @override - int get hashCode { - return id.hashCode ^ - object.hashCode ^ - resizePath.hashCode ^ - deviceAssetId.hashCode ^ - deviceId.hashCode; - } -} diff --git a/mobile/lib/modules/search/models/search_result_page_state.model.dart b/mobile/lib/modules/search/models/search_result_page_state.model.dart index 4911bb0e6f..1d79dff4b5 100644 --- a/mobile/lib/modules/search/models/search_result_page_state.model.dart +++ b/mobile/lib/modules/search/models/search_result_page_state.model.dart @@ -1,13 +1,13 @@ import 'dart:convert'; import 'package:collection/collection.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class SearchResultPageState { final bool isLoading; final bool isSuccess; final bool isError; - final List searchResult; + final List searchResult; SearchResultPageState({ required this.isLoading, @@ -20,7 +20,7 @@ class SearchResultPageState { bool? isLoading, bool? isSuccess, bool? isError, - List? searchResult, + List? searchResult, }) { return SearchResultPageState( isLoading: isLoading ?? this.isLoading, @@ -35,7 +35,7 @@ class SearchResultPageState { 'isLoading': isLoading, 'isSuccess': isSuccess, 'isError': isError, - 'searchResult': searchResult.map((x) => x.toMap()).toList(), + 'searchResult': searchResult.map((x) => x.toJson()).toList(), }; } @@ -44,8 +44,9 @@ class SearchResultPageState { isLoading: map['isLoading'] ?? false, isSuccess: map['isSuccess'] ?? false, isError: map['isError'] ?? false, - searchResult: List.from( - map['searchResult']?.map((x) => ImmichAsset.fromMap(x))), + searchResult: List.from( + map['searchResult']?.map((x) => AssetResponseDto.mapFromJson(x)), + ), ); } diff --git a/mobile/lib/modules/search/providers/search_page_state.provider.dart b/mobile/lib/modules/search/providers/search_page_state.provider.dart index dc61aad1a8..d66b6b280b 100644 --- a/mobile/lib/modules/search/providers/search_page_state.provider.dart +++ b/mobile/lib/modules/search/providers/search_page_state.provider.dart @@ -1,9 +1,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/search/models/curated_location.model.dart'; -import 'package:immich_mobile/modules/search/models/curated_object.model.dart'; import 'package:immich_mobile/modules/search/models/search_page_state.model.dart'; import 'package:immich_mobile/modules/search/services/search.service.dart'; +import 'package:openapi/api.dart'; class SearchPageStateNotifier extends StateNotifier { SearchPageStateNotifier(this._searchService) @@ -58,7 +57,7 @@ final searchPageStateProvider = }); final getCuratedLocationProvider = - FutureProvider.autoDispose>((ref) async { + FutureProvider.autoDispose>((ref) async { final SearchService searchService = ref.watch(searchServiceProvider); var curatedLocation = await searchService.getCuratedLocation(); @@ -66,7 +65,7 @@ final getCuratedLocationProvider = }); final getCuratedObjectProvider = - FutureProvider.autoDispose>((ref) async { + FutureProvider.autoDispose>((ref) async { final SearchService searchService = ref.watch(searchServiceProvider); var curatedObject = await searchService.getCuratedObjects(); diff --git a/mobile/lib/modules/search/providers/search_result_page.provider.dart b/mobile/lib/modules/search/providers/search_result_page.provider.dart index eb9f9c3daf..d6984a4b89 100644 --- a/mobile/lib/modules/search/providers/search_result_page.provider.dart +++ b/mobile/lib/modules/search/providers/search_result_page.provider.dart @@ -3,8 +3,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/search/models/search_result_page_state.model.dart'; import 'package:immich_mobile/modules/search/services/search.service.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; import 'package:intl/intl.dart'; +import 'package:openapi/api.dart'; class SearchResultPageNotifier extends StateNotifier { SearchResultPageNotifier(this._searchService) @@ -21,19 +21,29 @@ class SearchResultPageNotifier extends StateNotifier { void search(String searchTerm) async { state = state.copyWith( - searchResult: [], isError: false, isLoading: true, isSuccess: false); + searchResult: [], + isError: false, + isLoading: true, + isSuccess: false, + ); - List? assets = await _searchService.searchAsset(searchTerm); + List? assets = + await _searchService.searchAsset(searchTerm); if (assets != null) { state = state.copyWith( - searchResult: assets, - isError: false, - isLoading: false, - isSuccess: true); + searchResult: assets, + isError: false, + isLoading: false, + isSuccess: true, + ); } else { state = state.copyWith( - searchResult: [], isError: true, isLoading: false, isSuccess: false); + searchResult: [], + isError: true, + isLoading: false, + isSuccess: false, + ); } } } @@ -48,7 +58,11 @@ final searchResultGroupByDateTimeProvider = StateProvider((ref) { var assets = ref.watch(searchResultPageProvider).searchResult; assets.sortByCompare( - (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a)); - return assets.groupListsBy((element) => - DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt))); + (e) => DateTime.parse(e.createdAt), + (a, b) => b.compareTo(a), + ); + return assets.groupListsBy( + (element) => + DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt)), + ); }); diff --git a/mobile/lib/modules/search/services/search.service.dart b/mobile/lib/modules/search/services/search.service.dart index 06e6bab6f1..83e6122a80 100644 --- a/mobile/lib/modules/search/services/search.service.dart +++ b/mobile/lib/modules/search/services/search.service.dart @@ -1,79 +1,54 @@ -import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/search/models/curated_location.model.dart'; -import 'package:immich_mobile/modules/search/models/curated_object.model.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; -import 'package:immich_mobile/shared/services/network.service.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; +import 'package:openapi/api.dart'; -final searchServiceProvider = - Provider((ref) => SearchService(ref.watch(networkServiceProvider))); +final searchServiceProvider = Provider( + (ref) => SearchService( + ref.watch(apiServiceProvider), + ), +); class SearchService { - final NetworkService _networkService; - SearchService(this._networkService); + final ApiService _apiService; + SearchService(this._apiService); Future?> getUserSuggestedSearchTerms() async { try { - var res = await _networkService.getRequest(url: "asset/searchTerm"); - List decodedData = jsonDecode(res.toString()); - - return List.from(decodedData); + return await _apiService.assetApi.getAssetSearchTerms(); } catch (e) { debugPrint("[ERROR] [getUserSuggestedSearchTerms] ${e.toString()}"); return []; } } - Future?> searchAsset(String searchTerm) async { + Future?> searchAsset(String searchTerm) async { try { - var res = await _networkService.postRequest( - url: "asset/search", - data: {"searchTerm": searchTerm}, - ); - - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => ImmichAsset.fromMap(a))); - - return result; + return await _apiService.assetApi + .searchAsset(SearchAssetDto(searchTerm: searchTerm)); } catch (e) { debugPrint("[ERROR] [searchAsset] ${e.toString()}"); return null; } } - Future?> getCuratedLocation() async { + Future?> getCuratedLocation() async { try { - var res = await _networkService.getRequest(url: "asset/allLocation"); + var locations = await _apiService.assetApi.getCuratedLocations(); - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => CuratedLocation.fromMap(a))); - - return result; + return locations; } catch (e) { - debugPrint("[ERROR] [getCuratedLocation] ${e.toString()}"); - throw Error(); + debugPrint("Error [getCuratedLocation] ${e.toString()}"); + return []; } } - Future?> getCuratedObjects() async { + Future?> getCuratedObjects() async { try { - var res = await _networkService.getRequest(url: "asset/allObjects"); - - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => CuratedObject.fromMap(a))); - - return result; + return await _apiService.assetApi.getCuratedObjects(); } catch (e) { - debugPrint("[ERROR] [CuratedObject] ${e.toString()}"); - throw Error(); + debugPrint("Error [getCuratedObjects] ${e.toString()}"); + throw []; } } } diff --git a/mobile/lib/modules/search/ui/search_bar.dart b/mobile/lib/modules/search/ui/search_bar.dart index f1b67749af..f9883f65e0 100644 --- a/mobile/lib/modules/search/ui/search_bar.dart +++ b/mobile/lib/modules/search/ui/search_bar.dart @@ -5,9 +5,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; class SearchBar extends HookConsumerWidget with PreferredSizeWidget { - SearchBar( - {Key? key, required this.searchFocusNode, required this.onSubmitted}) - : super(key: key); + SearchBar({ + Key? key, + required this.searchFocusNode, + required this.onSubmitted, + }) : super(key: key); final FocusNode searchFocusNode; final Function(String) onSubmitted; @@ -26,7 +28,8 @@ class SearchBar extends HookConsumerWidget with PreferredSizeWidget { ref.watch(searchPageStateProvider.notifier).disableSearch(); searchTermController.clear(); }, - icon: const Icon(Icons.arrow_back_ios_rounded)) + icon: const Icon(Icons.arrow_back_ios_rounded), + ) : const Icon(Icons.search_rounded), title: TextField( controller: searchTermController, @@ -50,10 +53,10 @@ class SearchBar extends HookConsumerWidget with PreferredSizeWidget { }, decoration: InputDecoration( hintText: 'search_bar_hint'.tr(), - enabledBorder: UnderlineInputBorder( + enabledBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.transparent), ), - focusedBorder: UnderlineInputBorder( + focusedBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.transparent), ), ), diff --git a/mobile/lib/modules/search/ui/thumbnail_with_info.dart b/mobile/lib/modules/search/ui/thumbnail_with_info.dart index 36956b9a74..d65db0224c 100644 --- a/mobile/lib/modules/search/ui/thumbnail_with_info.dart +++ b/mobile/lib/modules/search/ui/thumbnail_with_info.dart @@ -2,15 +2,14 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:immich_mobile/constants/hive_box.dart'; -import 'package:immich_mobile/utils/capitalize_first_letter.dart'; class ThumbnailWithInfo extends StatelessWidget { - const ThumbnailWithInfo( - {Key? key, - required this.textInfo, - required this.imageUrl, - required this.onTap}) - : super(key: key); + const ThumbnailWithInfo({ + Key? key, + required this.textInfo, + required this.imageUrl, + required this.onTap, + }) : super(key: key); final String textInfo; final String imageUrl; diff --git a/mobile/lib/modules/search/views/search_page.dart b/mobile/lib/modules/search/views/search_page.dart index 9a2a5fb54d..832c641781 100644 --- a/mobile/lib/modules/search/views/search_page.dart +++ b/mobile/lib/modules/search/views/search_page.dart @@ -5,8 +5,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; -import 'package:immich_mobile/modules/search/models/curated_location.model.dart'; -import 'package:immich_mobile/modules/search/models/curated_object.model.dart'; import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; import 'package:immich_mobile/modules/search/ui/search_bar.dart'; import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart'; @@ -14,6 +12,7 @@ import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/utils/capitalize_first_letter.dart'; +import 'package:openapi/api.dart'; // ignore: must_be_immutable class SearchPage extends HookConsumerWidget { @@ -25,15 +24,18 @@ class SearchPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var box = Hive.box(userInfoBox); final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled; - AsyncValue> curatedLocation = + AsyncValue> curatedLocation = ref.watch(getCuratedLocationProvider); - AsyncValue> curatedObjects = + AsyncValue> curatedObjects = ref.watch(getCuratedObjectProvider); - useEffect(() { - searchFocusNode = FocusNode(); - return () => searchFocusNode.dispose(); - }, []); + useEffect( + () { + searchFocusNode = FocusNode(); + return () => searchFocusNode.dispose(); + }, + [], + ); _onSearchSubmitted(String searchTerm) async { searchFocusNode.unfocus(); @@ -58,16 +60,16 @@ class SearchPage extends HookConsumerWidget { scrollDirection: Axis.horizontal, itemCount: curatedLocation.value?.length, itemBuilder: ((context, index) { - CuratedLocation locationInfo = curatedLocations[index]; + var locationInfo = curatedLocations[index]; var thumbnailRequestUrl = - '${box.get(serverEndpointKey)}/asset/file?aid=${locationInfo.deviceAssetId}&did=${locationInfo.deviceId}&isThumb=true'; - + '${box.get(serverEndpointKey)}/asset/thumbnail/${locationInfo.id}'; return ThumbnailWithInfo( imageUrl: thumbnailRequestUrl, textInfo: locationInfo.city, onTap: () { AutoRouter.of(context).push( - SearchResultRoute(searchTerm: locationInfo.city)); + SearchResultRoute(searchTerm: locationInfo.city), + ); }, ); }), @@ -109,7 +111,7 @@ class SearchPage extends HookConsumerWidget { scrollDirection: Axis.horizontal, itemCount: curatedObjects.value?.length, itemBuilder: ((context, index) { - CuratedObject curatedObjectInfo = objects[index]; + var curatedObjectInfo = objects[index]; var thumbnailRequestUrl = '${box.get(serverEndpointKey)}/asset/file?aid=${curatedObjectInfo.deviceAssetId}&did=${curatedObjectInfo.deviceId}&isThumb=true'; @@ -117,9 +119,12 @@ class SearchPage extends HookConsumerWidget { imageUrl: thumbnailRequestUrl, textInfo: curatedObjectInfo.object, onTap: () { - AutoRouter.of(context).push(SearchResultRoute( + AutoRouter.of(context).push( + SearchResultRoute( searchTerm: curatedObjectInfo.object - .capitalizeFirstLetter())); + .capitalizeFirstLetter(), + ), + ); }, ); }), @@ -160,7 +165,7 @@ class SearchPage extends HookConsumerWidget { ListView( children: [ Padding( - padding: EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16.0), child: const Text( "search_page_places", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), @@ -168,8 +173,8 @@ class SearchPage extends HookConsumerWidget { ), _buildPlaces(), Padding( - padding: EdgeInsets.all(16.0), - child: const Text( + padding: const EdgeInsets.all(16.0), + child: const Text( "search_page_things", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ).tr(), diff --git a/mobile/lib/modules/search/views/search_result_page.dart b/mobile/lib/modules/search/views/search_result_page.dart index 7fd14abfa9..ffcd7c80b5 100644 --- a/mobile/lib/modules/search/views/search_result_page.dart +++ b/mobile/lib/modules/search/views/search_result_page.dart @@ -29,13 +29,18 @@ class SearchResultPage extends HookConsumerWidget { late FocusNode searchFocusNode; - useEffect(() { - searchFocusNode = FocusNode(); + useEffect( + () { + searchFocusNode = FocusNode(); - Future.delayed(Duration.zero, - () => ref.read(searchResultPageProvider.notifier).search(searchTerm)); - return () => searchFocusNode.dispose(); - }, []); + Future.delayed( + Duration.zero, + () => ref.read(searchResultPageProvider.notifier).search(searchTerm), + ); + return () => searchFocusNode.dispose(); + }, + [], + ); _onSearchSubmitted(String newSearchTerm) { debugPrint("Re-Search with $newSearchTerm"); @@ -69,10 +74,10 @@ class SearchResultPage extends HookConsumerWidget { }, decoration: InputDecoration( hintText: 'search_result_page_new_search_hint'.tr(), - enabledBorder: UnderlineInputBorder( + enabledBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.transparent), ), - focusedBorder: UnderlineInputBorder( + focusedBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.transparent), ), ), @@ -90,9 +95,10 @@ class SearchResultPage extends HookConsumerWidget { Text( currentSearchTerm.value, style: TextStyle( - color: Theme.of(context).primaryColor, - fontSize: 13, - fontWeight: FontWeight.bold), + color: Theme.of(context).primaryColor, + fontSize: 13, + fontWeight: FontWeight.bold, + ), maxLines: 1, ), Icon( @@ -116,9 +122,10 @@ class SearchResultPage extends HookConsumerWidget { if (searchResultPageState.isLoading) { return Center( - child: SpinKitDancingSquare( - color: Theme.of(context).primaryColor, - )); + child: SpinKitDancingSquare( + color: Theme.of(context).primaryColor, + ), + ); } if (searchResultPageState.isSuccess) { @@ -184,11 +191,12 @@ class SearchResultPage extends HookConsumerWidget { icon: const Icon(Icons.arrow_back_ios_rounded), ), title: GestureDetector( - onTap: () { - isNewSearch.value = true; - searchFocusNode.requestFocus(); - }, - child: isNewSearch.value ? _buildTextField() : _buildChip()), + onTap: () { + isNewSearch.value = true; + searchFocusNode.requestFocus(); + }, + child: isNewSearch.value ? _buildTextField() : _buildChip(), + ), centerTitle: false, ), body: GestureDetector( diff --git a/mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart b/mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart index faea8d0eca..e2fd8e3d12 100644 --- a/mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart +++ b/mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart @@ -1,12 +1,10 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class AssetSelectionPageResult { - final Set selectedNewAsset; - final Set selectedAdditionalAsset; + final Set selectedNewAsset; + final Set selectedAdditionalAsset; final bool isAlbumExist; AssetSelectionPageResult({ @@ -16,8 +14,8 @@ class AssetSelectionPageResult { }); AssetSelectionPageResult copyWith({ - Set? selectedNewAsset, - Set? selectedAdditionalAsset, + Set? selectedNewAsset, + Set? selectedAdditionalAsset, bool? isAlbumExist, }) { return AssetSelectionPageResult( @@ -28,35 +26,6 @@ class AssetSelectionPageResult { ); } - Map toMap() { - final result = {}; - - result.addAll( - {'selectedNewAsset': selectedNewAsset.map((x) => x.toMap()).toList()}); - result.addAll({ - 'selectedAdditionalAsset': - selectedAdditionalAsset.map((x) => x.toMap()).toList() - }); - result.addAll({'isAlbumExist': isAlbumExist}); - - return result; - } - - factory AssetSelectionPageResult.fromMap(Map map) { - return AssetSelectionPageResult( - selectedNewAsset: Set.from( - map['selectedNewAsset']?.map((x) => ImmichAsset.fromMap(x))), - selectedAdditionalAsset: Set.from( - map['selectedAdditionalAsset']?.map((x) => ImmichAsset.fromMap(x))), - isAlbumExist: map['isAlbumExist'] ?? false, - ); - } - - String toJson() => json.encode(toMap()); - - factory AssetSelectionPageResult.fromJson(String source) => - AssetSelectionPageResult.fromMap(json.decode(source)); - @override String toString() => 'AssetSelectionPageResult(selectedNewAsset: $selectedNewAsset, selectedAdditionalAsset: $selectedAdditionalAsset, isAlbumExist: $isAlbumExist)'; diff --git a/mobile/lib/modules/sharing/models/asset_selection_state.model.dart b/mobile/lib/modules/sharing/models/asset_selection_state.model.dart index 7520bc2a59..6a0a9160a1 100644 --- a/mobile/lib/modules/sharing/models/asset_selection_state.model.dart +++ b/mobile/lib/modules/sharing/models/asset_selection_state.model.dart @@ -1,14 +1,12 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class AssetSelectionState { final Set selectedMonths; - final Set selectedNewAssetsForAlbum; - final Set selectedAdditionalAssetsForAlbum; - final Set selectedAssetsInAlbumViewer; + final Set selectedNewAssetsForAlbum; + final Set selectedAdditionalAssetsForAlbum; + final Set selectedAssetsInAlbumViewer; final bool isMultiselectEnable; /// Indicate the asset selection page is navigated from existing album @@ -24,9 +22,9 @@ class AssetSelectionState { AssetSelectionState copyWith({ Set? selectedMonths, - Set? selectedNewAssetsForAlbum, - Set? selectedAdditionalAssetsForAlbum, - Set? selectedAssetsInAlbumViewer, + Set? selectedNewAssetsForAlbum, + Set? selectedAdditionalAssetsForAlbum, + Set? selectedAssetsInAlbumViewer, bool? isMultiselectEnable, bool? isAlbumExist, }) { @@ -43,49 +41,6 @@ class AssetSelectionState { ); } - Map toMap() { - final result = {}; - - result.addAll({'selectedMonths': selectedMonths.toList()}); - result.addAll({ - 'selectedNewAssetsForAlbum': - selectedNewAssetsForAlbum.map((x) => x.toMap()).toList() - }); - result.addAll({ - 'selectedAdditionalAssetsForAlbum': - selectedAdditionalAssetsForAlbum.map((x) => x.toMap()).toList() - }); - result.addAll({ - 'selectedAssetsInAlbumViewer': - selectedAssetsInAlbumViewer.map((x) => x.toMap()).toList() - }); - result.addAll({'isMultiselectEnable': isMultiselectEnable}); - result.addAll({'isAlbumExist': isAlbumExist}); - - return result; - } - - factory AssetSelectionState.fromMap(Map map) { - return AssetSelectionState( - selectedMonths: Set.from(map['selectedMonths']), - selectedNewAssetsForAlbum: Set.from( - map['selectedNewAssetsForAlbum']?.map((x) => ImmichAsset.fromMap(x))), - selectedAdditionalAssetsForAlbum: Set.from( - map['selectedAdditionalAssetsForAlbum'] - ?.map((x) => ImmichAsset.fromMap(x))), - selectedAssetsInAlbumViewer: Set.from( - map['selectedAssetsInAlbumViewer'] - ?.map((x) => ImmichAsset.fromMap(x))), - isMultiselectEnable: map['isMultiselectEnable'] ?? false, - isAlbumExist: map['isAlbumExist'] ?? false, - ); - } - - String toJson() => json.encode(toMap()); - - factory AssetSelectionState.fromJson(String source) => - AssetSelectionState.fromMap(json.decode(source)); - @override String toString() { return 'AssetSelectionState(selectedMonths: $selectedMonths, selectedNewAssetsForAlbum: $selectedNewAssetsForAlbum, selectedAdditionalAssetsForAlbum: $selectedAdditionalAssetsForAlbum, selectedAssetsInAlbumViewer: $selectedAssetsInAlbumViewer, isMultiselectEnable: $isMultiselectEnable, isAlbumExist: $isAlbumExist)'; @@ -99,10 +54,14 @@ class AssetSelectionState { return other is AssetSelectionState && setEquals(other.selectedMonths, selectedMonths) && setEquals(other.selectedNewAssetsForAlbum, selectedNewAssetsForAlbum) && - setEquals(other.selectedAdditionalAssetsForAlbum, - selectedAdditionalAssetsForAlbum) && setEquals( - other.selectedAssetsInAlbumViewer, selectedAssetsInAlbumViewer) && + other.selectedAdditionalAssetsForAlbum, + selectedAdditionalAssetsForAlbum, + ) && + setEquals( + other.selectedAssetsInAlbumViewer, + selectedAssetsInAlbumViewer, + ) && other.isMultiselectEnable == isMultiselectEnable && other.isAlbumExist == isAlbumExist; } diff --git a/mobile/lib/modules/sharing/models/shared_album.model.dart b/mobile/lib/modules/sharing/models/shared_album.model.dart deleted file mode 100644 index d8f1468927..0000000000 --- a/mobile/lib/modules/sharing/models/shared_album.model.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'dart:convert'; - -import 'package:collection/collection.dart'; - -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; -import 'package:immich_mobile/shared/models/user.model.dart'; - -class SharedAlbum { - final String id; - final String ownerId; - final String albumName; - final String createdAt; - final String? albumThumbnailAssetId; - final List sharedUsers; - final List? assets; - - SharedAlbum({ - required this.id, - required this.ownerId, - required this.albumName, - required this.createdAt, - required this.albumThumbnailAssetId, - required this.sharedUsers, - this.assets, - }); - - SharedAlbum copyWith({ - String? id, - String? ownerId, - String? albumName, - String? createdAt, - String? albumThumbnailAssetId, - List? sharedUsers, - List? assets, - }) { - return SharedAlbum( - id: id ?? this.id, - ownerId: ownerId ?? this.ownerId, - albumName: albumName ?? this.albumName, - createdAt: createdAt ?? this.createdAt, - albumThumbnailAssetId: - albumThumbnailAssetId ?? this.albumThumbnailAssetId, - sharedUsers: sharedUsers ?? this.sharedUsers, - assets: assets ?? this.assets, - ); - } - - Map toMap() { - final result = {}; - - result.addAll({'id': id}); - result.addAll({'ownerId': ownerId}); - result.addAll({'albumName': albumName}); - result.addAll({'createdAt': createdAt}); - if (albumThumbnailAssetId != null) { - result.addAll({'albumThumbnailAssetId': albumThumbnailAssetId}); - } - result.addAll({'sharedUsers': sharedUsers.map((x) => x.toMap()).toList()}); - if (assets != null) { - result.addAll({'assets': assets!.map((x) => x.toMap()).toList()}); - } - - return result; - } - - factory SharedAlbum.fromMap(Map map) { - return SharedAlbum( - id: map['id'] ?? '', - ownerId: map['ownerId'] ?? '', - albumName: map['albumName'] ?? '', - createdAt: map['createdAt'] ?? '', - albumThumbnailAssetId: map['albumThumbnailAssetId'], - sharedUsers: - List.from(map['sharedUsers']?.map((x) => User.fromMap(x))), - assets: map['assets'] != null - ? List.from( - map['assets']?.map((x) => ImmichAsset.fromMap(x))) - : null, - ); - } - - String toJson() => json.encode(toMap()); - - factory SharedAlbum.fromJson(String source) => - SharedAlbum.fromMap(json.decode(source)); - - @override - String toString() { - return 'SharedAlbum(id: $id, ownerId: $ownerId, albumName: $albumName, createdAt: $createdAt, albumThumbnailAssetId: $albumThumbnailAssetId, sharedUsers: $sharedUsers, assets: $assets)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - final listEquals = const DeepCollectionEquality().equals; - - return other is SharedAlbum && - other.id == id && - other.ownerId == ownerId && - other.albumName == albumName && - other.createdAt == createdAt && - other.albumThumbnailAssetId == albumThumbnailAssetId && - listEquals(other.sharedUsers, sharedUsers) && - listEquals(other.assets, assets); - } - - @override - int get hashCode { - return id.hashCode ^ - ownerId.hashCode ^ - albumName.hashCode ^ - createdAt.hashCode ^ - albumThumbnailAssetId.hashCode ^ - sharedUsers.hashCode ^ - assets.hashCode; - } -} diff --git a/mobile/lib/modules/sharing/providers/album_title.provider.dart b/mobile/lib/modules/sharing/providers/album_title.provider.dart index 5ccc82d401..126b3499a3 100644 --- a/mobile/lib/modules/sharing/providers/album_title.provider.dart +++ b/mobile/lib/modules/sharing/providers/album_title.provider.dart @@ -13,4 +13,5 @@ class AlbumTitleNotifier extends StateNotifier { } final albumTitleProvider = StateNotifierProvider( - (ref) => AlbumTitleNotifier()); + (ref) => AlbumTitleNotifier(), +); diff --git a/mobile/lib/modules/sharing/providers/album_viewer.provider.dart b/mobile/lib/modules/sharing/providers/album_viewer.provider.dart index 4e64b8eb5b..561f937102 100644 --- a/mobile/lib/modules/sharing/providers/album_viewer.provider.dart +++ b/mobile/lib/modules/sharing/providers/album_viewer.provider.dart @@ -30,7 +30,10 @@ class AlbumViewerNotifier extends StateNotifier { } Future changeAlbumTitle( - String albumId, String ownerId, String newAlbumTitle) async { + String albumId, + String ownerId, + String newAlbumTitle, + ) async { SharedAlbumService service = ref.watch(sharedAlbumServiceProvider); bool isSuccess = diff --git a/mobile/lib/modules/sharing/providers/asset_selection.provider.dart b/mobile/lib/modules/sharing/providers/asset_selection.provider.dart index c87a59aa25..809e3d18c1 100644 --- a/mobile/lib/modules/sharing/providers/asset_selection.provider.dart +++ b/mobile/lib/modules/sharing/providers/asset_selection.provider.dart @@ -1,41 +1,46 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/sharing/models/asset_selection_state.model.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class AssetSelectionNotifier extends StateNotifier { AssetSelectionNotifier() - : super(AssetSelectionState( - selectedNewAssetsForAlbum: {}, - selectedMonths: {}, - selectedAdditionalAssetsForAlbum: {}, - selectedAssetsInAlbumViewer: {}, - isAlbumExist: false, - isMultiselectEnable: false, - )); + : super( + AssetSelectionState( + selectedNewAssetsForAlbum: {}, + selectedMonths: {}, + selectedAdditionalAssetsForAlbum: {}, + selectedAssetsInAlbumViewer: {}, + isAlbumExist: false, + isMultiselectEnable: false, + ), + ); void setIsAlbumExist(bool isAlbumExist) { state = state.copyWith(isAlbumExist: isAlbumExist); } void removeAssetsInMonth( - String removedMonth, List assetsInMonth) { - Set currentAssetList = state.selectedNewAssetsForAlbum; + String removedMonth, + List assetsInMonth, + ) { + Set currentAssetList = state.selectedNewAssetsForAlbum; Set currentMonthList = state.selectedMonths; currentMonthList .removeWhere((selectedMonth) => selectedMonth == removedMonth); - for (ImmichAsset asset in assetsInMonth) { + for (AssetResponseDto asset in assetsInMonth) { currentAssetList.removeWhere((e) => e.id == asset.id); } state = state.copyWith( - selectedNewAssetsForAlbum: currentAssetList, - selectedMonths: currentMonthList); + selectedNewAssetsForAlbum: currentAssetList, + selectedMonths: currentMonthList, + ); } - void addAdditionalAssets(List assets) { + void addAdditionalAssets(List assets) { state = state.copyWith( selectedAdditionalAssetsForAlbum: { ...state.selectedAdditionalAssetsForAlbum, @@ -44,7 +49,7 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void addAllAssetsInMonth(String month, List assetsInMonth) { + void addAllAssetsInMonth(String month, List assetsInMonth) { state = state.copyWith( selectedMonths: {...state.selectedMonths, month}, selectedNewAssetsForAlbum: { @@ -54,7 +59,7 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void addNewAssets(List assets) { + void addNewAssets(List assets) { state = state.copyWith( selectedNewAssetsForAlbum: { ...state.selectedNewAssetsForAlbum, @@ -63,20 +68,20 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void removeSelectedNewAssets(List assets) { - Set currentList = state.selectedNewAssetsForAlbum; + void removeSelectedNewAssets(List assets) { + Set currentList = state.selectedNewAssetsForAlbum; - for (ImmichAsset asset in assets) { + for (AssetResponseDto asset in assets) { currentList.removeWhere((e) => e.id == asset.id); } state = state.copyWith(selectedNewAssetsForAlbum: currentList); } - void removeSelectedAdditionalAssets(List assets) { - Set currentList = state.selectedAdditionalAssetsForAlbum; + void removeSelectedAdditionalAssets(List assets) { + Set currentList = state.selectedAdditionalAssetsForAlbum; - for (ImmichAsset asset in assets) { + for (AssetResponseDto asset in assets) { currentList.removeWhere((e) => e.id == asset.id); } @@ -104,7 +109,7 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void addAssetsInAlbumViewer(List assets) { + void addAssetsInAlbumViewer(List assets) { state = state.copyWith( selectedAssetsInAlbumViewer: { ...state.selectedAssetsInAlbumViewer, @@ -113,10 +118,10 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void removeAssetsInAlbumViewer(List assets) { - Set currentList = state.selectedAssetsInAlbumViewer; + void removeAssetsInAlbumViewer(List assets) { + Set currentList = state.selectedAssetsInAlbumViewer; - for (ImmichAsset asset in assets) { + for (AssetResponseDto asset in assets) { currentList.removeWhere((e) => e.id == asset.id); } diff --git a/mobile/lib/modules/sharing/providers/shared_album.provider.dart b/mobile/lib/modules/sharing/providers/shared_album.provider.dart index efb3c090a7..792db47aa7 100644 --- a/mobile/lib/modules/sharing/providers/shared_album.provider.dart +++ b/mobile/lib/modules/sharing/providers/shared_album.provider.dart @@ -1,17 +1,19 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart'; import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart'; +import 'package:openapi/api.dart'; -class SharedAlbumNotifier extends StateNotifier> { +class SharedAlbumNotifier extends StateNotifier> { SharedAlbumNotifier(this._sharedAlbumService) : super([]); final SharedAlbumService _sharedAlbumService; getAllSharedAlbums() async { - List sharedAlbums = + List? sharedAlbums = await _sharedAlbumService.getAllSharedAlbum(); - state = sharedAlbums; + if (sharedAlbums != null) { + state = sharedAlbums; + } } Future deleteAlbum(String albumId) async { @@ -37,7 +39,9 @@ class SharedAlbumNotifier extends StateNotifier> { } Future removeAssetFromAlbum( - String albumId, List assetIds) async { + String albumId, + List assetIds, + ) async { var res = await _sharedAlbumService.removeAssetFromAlbum(albumId, assetIds); if (res) { @@ -49,12 +53,12 @@ class SharedAlbumNotifier extends StateNotifier> { } final sharedAlbumProvider = - StateNotifierProvider>((ref) { + StateNotifierProvider>((ref) { return SharedAlbumNotifier(ref.watch(sharedAlbumServiceProvider)); }); final sharedAlbumDetailProvider = FutureProvider.autoDispose - .family((ref, albumId) async { + .family((ref, albumId) async { final SharedAlbumService sharedAlbumService = ref.watch(sharedAlbumServiceProvider); diff --git a/mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart b/mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart index 8ddc122b26..0c4a2a3503 100644 --- a/mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart +++ b/mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart @@ -1,10 +1,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/shared/models/user.model.dart'; import 'package:immich_mobile/shared/services/user.service.dart'; +import 'package:openapi/api.dart'; final suggestedSharedUsersProvider = - FutureProvider.autoDispose>((ref) async { + FutureProvider.autoDispose>((ref) async { UserService userService = ref.watch(userServiceProvider); - return await userService.getAllUsersInfo(); + return await userService.getAllUsersInfo(isAll: false) ?? []; }); diff --git a/mobile/lib/modules/sharing/services/shared_album.service.dart b/mobile/lib/modules/sharing/services/shared_album.service.dart index 9183dcb8d8..92a995fb91 100644 --- a/mobile/lib/modules/sharing/services/shared_album.service.dart +++ b/mobile/lib/modules/sharing/services/shared_album.service.dart @@ -1,73 +1,69 @@ import 'dart:async'; -import 'dart:convert'; -import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; -import 'package:immich_mobile/shared/services/network.service.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; +import 'package:openapi/api.dart'; -final sharedAlbumServiceProvider = - Provider((ref) => SharedAlbumService(ref.watch(networkServiceProvider))); +final sharedAlbumServiceProvider = Provider( + (ref) => SharedAlbumService( + ref.watch(apiServiceProvider), + ), +); class SharedAlbumService { - final NetworkService _networkService; - SharedAlbumService(this._networkService); + final ApiService _apiService; + SharedAlbumService(this._apiService); - Future> getAllSharedAlbum() async { + Future?> getAllSharedAlbum() async { try { - var res = await _networkService.getRequest(url: 'album?shared=true'); - List decodedData = jsonDecode(res.toString()); - List result = - List.from(decodedData.map((e) => SharedAlbum.fromMap(e))); - - return result; + return await _apiService.albumApi.getAllAlbums(shared: true); } catch (e) { debugPrint("Error getAllSharedAlbum ${e.toString()}"); + return null; } - - return []; } - Future createSharedAlbum(String albumName, Set assets, - List sharedUserIds) async { + Future createSharedAlbum( + String albumName, + Set assets, + List sharedUserIds, + ) async { try { - var res = await _networkService.postRequest(url: 'album', data: { - "albumName": albumName, - "sharedWithUserIds": sharedUserIds, - "assetIds": assets.map((asset) => asset.id).toList(), - }); + _apiService.albumApi.createAlbum( + CreateAlbumDto( + albumName: albumName, + assetIds: assets.map((asset) => asset.id).toList(), + sharedWithUserIds: sharedUserIds, + ), + ); - return res != null; + return true; } catch (e) { debugPrint("Error createSharedAlbum ${e.toString()}"); return false; } } - Future getAlbumDetail(String albumId) async { + Future getAlbumDetail(String albumId) async { try { - var res = await _networkService.getRequest(url: 'album/$albumId'); - dynamic decodedData = jsonDecode(res.toString()); - SharedAlbum result = SharedAlbum.fromMap(decodedData); - - return result; + return await _apiService.albumApi.getAlbumInfo(albumId); } catch (e) { - throw Exception('Error getAllSharedAlbum ${e.toString()}'); + debugPrint('Error [getAlbumDetail] ${e.toString()}'); + return null; } } Future addAdditionalAssetToAlbum( - Set assets, String albumId) async { + Set assets, + String albumId, + ) async { try { - var res = - await _networkService.putRequest(url: 'album/$albumId/assets', data: { - "albumId": albumId, - "assetIds": assets.map((asset) => asset.id).toList(), - }); - - return res != null; + var result = await _apiService.albumApi.addAssetsToAlbum( + albumId, + AddAssetsDto(assetIds: assets.map((asset) => asset.id).toList()), + ); + return result != null; } catch (e) { debugPrint("Error addAdditionalAssetToAlbum ${e.toString()}"); return false; @@ -75,14 +71,16 @@ class SharedAlbumService { } Future addAdditionalUserToAlbum( - List sharedUserIds, String albumId) async { + List sharedUserIds, + String albumId, + ) async { try { - var res = - await _networkService.putRequest(url: 'album/$albumId/users', data: { - "sharedUserIds": sharedUserIds, - }); + var result = await _apiService.albumApi.addUsersToAlbum( + albumId, + AddUsersDto(sharedUserIds: sharedUserIds), + ); - return res != null; + return result != null; } catch (e) { debugPrint("Error addAdditionalUserToAlbum ${e.toString()}"); return false; @@ -91,12 +89,7 @@ class SharedAlbumService { Future deleteAlbum(String albumId) async { try { - Response res = await _networkService.deleteRequest(url: 'album/$albumId'); - - if (res.statusCode != 200) { - return false; - } - + await _apiService.albumApi.deleteAlbum(albumId); return true; } catch (e) { debugPrint("Error deleteAlbum ${e.toString()}"); @@ -106,12 +99,7 @@ class SharedAlbumService { Future leaveAlbum(String albumId) async { try { - Response res = - await _networkService.deleteRequest(url: 'album/$albumId/user/me'); - - if (res.statusCode != 200) { - return false; - } + await _apiService.albumApi.removeUserFromAlbum(albumId, "me"); return true; } catch (e) { @@ -121,16 +109,14 @@ class SharedAlbumService { } Future removeAssetFromAlbum( - String albumId, List assetIds) async { + String albumId, + List assetIds, + ) async { try { - Response res = await _networkService - .deleteRequest(url: 'album/$albumId/assets', data: { - "assetIds": assetIds, - }); - - if (res.statusCode != 200) { - return false; - } + await _apiService.albumApi.removeAssetFromAlbum( + albumId, + RemoveAssetsDto(assetIds: assetIds), + ); return true; } catch (e) { @@ -140,17 +126,18 @@ class SharedAlbumService { } Future changeTitleAlbum( - String albumId, String ownerId, String newAlbumTitle) async { + String albumId, + String ownerId, + String newAlbumTitle, + ) async { try { - Response res = - await _networkService.patchRequest(url: 'album/$albumId/', data: { - "ownerId": ownerId, - "albumName": newAlbumTitle, - }); - - if (res.statusCode != 200) { - return false; - } + await _apiService.albumApi.updateAlbumInfo( + albumId, + UpdateAlbumDto( + ownerId: ownerId, + albumName: newAlbumTitle, + ), + ); return true; } catch (e) { diff --git a/mobile/lib/modules/sharing/ui/album_action_outlined_button.dart b/mobile/lib/modules/sharing/ui/album_action_outlined_button.dart index 3a4a351cfb..fdfab59768 100644 --- a/mobile/lib/modules/sharing/ui/album_action_outlined_button.dart +++ b/mobile/lib/modules/sharing/ui/album_action_outlined_button.dart @@ -5,12 +5,12 @@ class AlbumActionOutlinedButton extends StatelessWidget { final String labelText; final IconData iconData; - const AlbumActionOutlinedButton( - {Key? key, - this.onPressed, - required this.labelText, - required this.iconData}) - : super(key: key); + const AlbumActionOutlinedButton({ + Key? key, + this.onPressed, + required this.labelText, + required this.iconData, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -31,7 +31,10 @@ class AlbumActionOutlinedButton extends StatelessWidget { label: Text( labelText, style: const TextStyle( - fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87), + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), ), onPressed: onPressed, ), diff --git a/mobile/lib/modules/sharing/ui/album_title_text_field.dart b/mobile/lib/modules/sharing/ui/album_title_text_field.dart index 1556c87fad..61dbad01a6 100644 --- a/mobile/lib/modules/sharing/ui/album_title_text_field.dart +++ b/mobile/lib/modules/sharing/ui/album_title_text_field.dart @@ -31,7 +31,10 @@ class AlbumTitleTextField extends ConsumerWidget { }, focusNode: albumTitleTextFieldFocusNode, style: TextStyle( - fontSize: 28, color: Colors.grey[700], fontWeight: FontWeight.bold), + fontSize: 28, + color: Colors.grey[700], + fontWeight: FontWeight.bold, + ), controller: albumTitleController, onTap: () { isAlbumTitleTextFieldFocus.value = true; diff --git a/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart b/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart index bcb0690a24..dd85021e5b 100644 --- a/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart +++ b/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart @@ -4,24 +4,24 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/immich_colors.dart'; -import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart'; import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart'; import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart'; import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; import 'package:immich_mobile/shared/views/immich_loading_overlay.dart'; +import 'package:openapi/api.dart'; class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { const AlbumViewerAppbar({ Key? key, - required AsyncValue albumInfo, + required AsyncValue albumInfo, required this.userId, required this.albumId, }) : _albumInfo = albumInfo, super(key: key); - final AsyncValue _albumInfo; + final AsyncValue _albumInfo; final String userId; final String albumId; @@ -105,7 +105,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { _buildBottomSheetActionButton() { if (isMultiSelectionEnable) { - if (_albumInfo.asData?.value.ownerId == userId) { + if (_albumInfo.asData?.value?.ownerId == userId) { return ListTile( leading: const Icon(Icons.delete_sweep_rounded), title: const Text( @@ -118,7 +118,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { return const SizedBox(); } } else { - if (_albumInfo.asData?.value.ownerId == userId) { + if (_albumInfo.asData?.value?.ownerId == userId) { return ListTile( leading: const Icon(Icons.delete_forever_rounded), title: const Text( diff --git a/mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart b/mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart index 52d0938680..79f87d5b27 100644 --- a/mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart +++ b/mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart @@ -2,15 +2,17 @@ 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/sharing/models/shared_album.model.dart'; import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart'; +import 'package:openapi/api.dart'; class AlbumViewerEditableTitle extends HookConsumerWidget { - final SharedAlbum albumInfo; + final AlbumResponseDto albumInfo; final FocusNode titleFocusNode; - const AlbumViewerEditableTitle( - {Key? key, required this.albumInfo, required this.titleFocusNode}) - : super(key: key); + const AlbumViewerEditableTitle({ + Key? key, + required this.albumInfo, + required this.titleFocusNode, + }) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,12 +26,15 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } } - useEffect(() { - titleFocusNode.addListener(onFocusModeChange); - return () { - titleFocusNode.removeListener(onFocusModeChange); - }; - }, []); + useEffect( + () { + titleFocusNode.addListener(onFocusModeChange); + return () { + titleFocusNode.removeListener(onFocusModeChange); + }; + }, + [], + ); return TextField( onChanged: (value) { diff --git a/mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart b/mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart index 95a2b1bf78..c5f3a6bd99 100644 --- a/mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart +++ b/mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart @@ -7,11 +7,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:openapi/api.dart'; class AlbumViewerThumbnail extends HookConsumerWidget { - final ImmichAsset asset; + final AssetResponseDto asset; const AlbumViewerThumbnail({Key? key, required this.asset}) : super(key: key); @@ -20,7 +20,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget { final cacheKey = useState(1); var box = Hive.box(userInfoBox); var thumbnailRequestUrl = - '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true'; + '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}'; var deviceId = ref.watch(authenticationProvider).deviceId; final selectedAssetsInAlbumViewer = ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer; @@ -28,7 +28,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget { ref.watch(assetSelectionProvider).isMultiselectEnable; _viewAsset() { - if (asset.type == 'IMAGE') { + if (asset.type == AssetTypeEnum.IMAGE) { AutoRouter.of(context).push( ImageViewerRoute( imageUrl: @@ -41,9 +41,10 @@ class AlbumViewerThumbnail extends HookConsumerWidget { } else { AutoRouter.of(context).push( VideoViewerRoute( - videoUrl: - '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}', - asset: asset), + videoUrl: + '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}', + asset: asset, + ), ); } } @@ -170,16 +171,13 @@ class AlbumViewerThumbnail extends HookConsumerWidget { return GestureDetector( onTap: isMultiSelectionEnable ? _handleSelectionGesture : _viewAsset, onLongPress: _enableMultiSelection, - child: Hero( - tag: asset.id, - child: Stack( - children: [ - _buildThumbnailImage(), - _buildAssetStoreLocationIcon(), - if (asset.type != 'IMAGE') _buildVideoLabel(), - if (isMultiSelectionEnable) _buildAssetSelectionIcon(), - ], - ), + child: Stack( + children: [ + _buildThumbnailImage(), + _buildAssetStoreLocationIcon(), + if (asset.type != AssetTypeEnum.IMAGE) _buildVideoLabel(), + if (isMultiSelectionEnable) _buildAssetSelectionIcon(), + ], ), ); } diff --git a/mobile/lib/modules/sharing/ui/asset_grid_by_month.dart b/mobile/lib/modules/sharing/ui/asset_grid_by_month.dart index 8d719bac57..3586487621 100644 --- a/mobile/lib/modules/sharing/ui/asset_grid_by_month.dart +++ b/mobile/lib/modules/sharing/ui/asset_grid_by_month.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/sharing/ui/selection_thumbnail_image.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class AssetGridByMonth extends HookConsumerWidget { - final List assetGroup; + final List assetGroup; const AssetGridByMonth({Key? key, required this.assetGroup}) : super(key: key); @override diff --git a/mobile/lib/modules/sharing/ui/month_group_title.dart b/mobile/lib/modules/sharing/ui/month_group_title.dart index 71e160664c..ba766721aa 100644 --- a/mobile/lib/modules/sharing/ui/month_group_title.dart +++ b/mobile/lib/modules/sharing/ui/month_group_title.dart @@ -2,15 +2,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class MonthGroupTitle extends HookConsumerWidget { final String month; - final List assetGroup; + final List assetGroup; - const MonthGroupTitle( - {Key? key, required this.month, required this.assetGroup}) - : super(key: key); + const MonthGroupTitle({ + Key? key, + required this.month, + required this.assetGroup, + }) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { @@ -75,7 +77,11 @@ class MonthGroupTitle extends HookConsumerWidget { return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only( - top: 29.0, bottom: 29.0, left: 14.0, right: 8.0), + top: 29.0, + bottom: 29.0, + left: 14.0, + right: 8.0, + ), child: Row( children: [ GestureDetector( diff --git a/mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart b/mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart index 13e6ce2035..24be821066 100644 --- a/mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart +++ b/mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart @@ -5,10 +5,10 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class SelectionThumbnailImage extends HookConsumerWidget { - final ImmichAsset asset; + final AssetResponseDto asset; const SelectionThumbnailImage({Key? key, required this.asset}) : super(key: key); @@ -18,14 +18,14 @@ class SelectionThumbnailImage extends HookConsumerWidget { final cacheKey = useState(1); var box = Hive.box(userInfoBox); var thumbnailRequestUrl = - '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true'; + '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}'; var selectedAsset = ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum; var newAssetsForAlbum = ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum; var isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist; - Widget _buildSelectionIcon(ImmichAsset asset) { + Widget _buildSelectionIcon(AssetResponseDto asset) { if (selectedAsset.contains(asset) && !isAlbumExist) { return Icon( Icons.check_circle, @@ -103,7 +103,7 @@ class SelectionThumbnailImage extends HookConsumerWidget { cacheKey: "${asset.id}-${cacheKey.value}", width: 150, height: 150, - memCacheHeight: asset.type == 'IMAGE' ? 150 : 150, + memCacheHeight: asset.type == AssetTypeEnum.IMAGE ? 150 : 150, fit: BoxFit.cover, imageUrl: thumbnailRequestUrl, httpHeaders: { @@ -131,14 +131,14 @@ class SelectionThumbnailImage extends HookConsumerWidget { child: _buildSelectionIcon(asset), ), ), - if (asset.type != 'IMAGE') + if (asset.type != AssetTypeEnum.IMAGE) Positioned( bottom: 5, right: 5, child: Row( children: [ Text( - '${asset.duration?.substring(0, 7)}', + asset.duration.substring(0, 7), style: const TextStyle( color: Colors.white, fontSize: 10, diff --git a/mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart b/mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart index b6b75d65ab..d62f1e097b 100644 --- a/mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart +++ b/mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart @@ -4,10 +4,10 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class SharedAlbumThumbnailImage extends HookConsumerWidget { - final ImmichAsset asset; + final AssetResponseDto asset; const SharedAlbumThumbnailImage({Key? key, required this.asset}) : super(key: key); @@ -18,7 +18,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { var box = Hive.box(userInfoBox); var thumbnailRequestUrl = - '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true'; + '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}'; return GestureDetector( onTap: () { @@ -30,7 +30,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { cacheKey: "${asset.id}-${cacheKey.value}", width: 500, height: 500, - memCacheHeight: asset.type == 'IMAGE' ? 500 : 500, + memCacheHeight: 500, fit: BoxFit.cover, imageUrl: thumbnailRequestUrl, httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"}, diff --git a/mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart b/mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart index 32adf2138c..e39bcf115e 100644 --- a/mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart +++ b/mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart @@ -40,7 +40,8 @@ class SharingSliverAppBar extends StatelessWidget { child: TextButton.icon( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( - Theme.of(context).primaryColor.withAlpha(20)), + Theme.of(context).primaryColor.withAlpha(20), + ), // foregroundColor: MaterialStateProperty.all(Colors.white), ), onPressed: () { @@ -65,7 +66,8 @@ class SharingSliverAppBar extends StatelessWidget { child: TextButton.icon( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( - Theme.of(context).primaryColor.withAlpha(20)), + Theme.of(context).primaryColor.withAlpha(20), + ), // foregroundColor: MaterialStateProperty.all(Colors.white), ), onPressed: null, diff --git a/mobile/lib/modules/sharing/views/album_viewer_page.dart b/mobile/lib/modules/sharing/views/album_viewer_page.dart index 1f26a643ec..c10268d677 100644 --- a/mobile/lib/modules/sharing/views/album_viewer_page.dart +++ b/mobile/lib/modules/sharing/views/album_viewer_page.dart @@ -7,7 +7,6 @@ import 'package:immich_mobile/constants/immich_colors.dart'; import 'package:immich_mobile/modules/home/ui/draggable_scrollbar.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart'; -import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart'; import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart'; import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart'; @@ -19,7 +18,7 @@ import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/shared/ui/immich_sliver_persistent_app_bar_delegate.dart'; import 'package:immich_mobile/shared/views/immich_loading_overlay.dart'; -import 'package:intl/intl.dart'; +import 'package:openapi/api.dart'; class AlbumViewerPage extends HookConsumerWidget { final String albumId; @@ -30,18 +29,18 @@ class AlbumViewerPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { FocusNode titleFocusNode = useFocusNode(); ScrollController scrollController = useScrollController(); - AsyncValue albumInfo = + AsyncValue albumInfo = ref.watch(sharedAlbumDetailProvider(albumId)); final userId = ref.watch(authenticationProvider).userId; /// Find out if the assets in album exist on the device /// If they exist, add to selected asset state to show they are already selected. - void _onAddPhotosPressed(SharedAlbum albumInfo) async { - if (albumInfo.assets?.isNotEmpty == true) { + void _onAddPhotosPressed(AlbumResponseDto albumInfo) async { + if (albumInfo.assets.isNotEmpty == true) { ref .watch(assetSelectionProvider.notifier) - .addNewAssets(albumInfo.assets!.toList()); + .addNewAssets(albumInfo.assets.toList()); } ref.watch(assetSelectionProvider.notifier).setIsAlbumExist(true); @@ -57,7 +56,9 @@ class AlbumViewerPage extends HookConsumerWidget { var isSuccess = await ref .watch(sharedAlbumServiceProvider) .addAdditionalAssetToAlbum( - returnPayload.selectedAdditionalAsset, albumId); + returnPayload.selectedAdditionalAsset, + albumId, + ); if (isSuccess) { ref.refresh(sharedAlbumDetailProvider(albumId)); @@ -72,10 +73,11 @@ class AlbumViewerPage extends HookConsumerWidget { } } - void _onAddUsersPressed(SharedAlbum albumInfo) async { - List? sharedUserIds = await AutoRouter.of(context) - .push?>( - SelectAdditionalUserForSharingRoute(albumInfo: albumInfo)); + void _onAddUsersPressed(AlbumResponseDto albumInfo) async { + List? sharedUserIds = + await AutoRouter.of(context).push?>( + SelectAdditionalUserForSharingRoute(albumInfo: albumInfo), + ); if (sharedUserIds != null) { ImmichLoadingOverlayController.appLoader.show(); @@ -92,7 +94,7 @@ class AlbumViewerPage extends HookConsumerWidget { } } - Widget _buildTitle(SharedAlbum albumInfo) { + Widget _buildTitle(AlbumResponseDto albumInfo) { return Padding( padding: const EdgeInsets.only(left: 8, right: 8, top: 16), child: userId == albumInfo.ownerId @@ -102,19 +104,24 @@ class AlbumViewerPage extends HookConsumerWidget { ) : Padding( padding: const EdgeInsets.only(left: 8.0), - child: Text(albumInfo.albumName, - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.bold)), + child: Text( + albumInfo.albumName, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), ), ); } - Widget _buildAlbumDateRange(SharedAlbum albumInfo) { + Widget _buildAlbumDateRange(AlbumResponseDto albumInfo) { String startDate = ""; DateTime parsedStartDate = - DateTime.parse(albumInfo.assets!.first.createdAt); + DateTime.parse(albumInfo.assets.first.createdAt); DateTime parsedEndDate = DateTime.parse( - albumInfo.assets?.last.createdAt ?? '11111111'); //Need default. + albumInfo.assets.last.createdAt, + ); //Need default. if (parsedStartDate.year == parsedEndDate.year) { startDate = DateFormat('LLL d').format(parsedStartDate); @@ -129,18 +136,21 @@ class AlbumViewerPage extends HookConsumerWidget { child: Text( "$startDate-$endDate", style: const TextStyle( - fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey), + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), ), ); } - Widget _buildHeader(SharedAlbum albumInfo) { + Widget _buildHeader(AlbumResponseDto albumInfo) { return SliverToBoxAdapter( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTitle(albumInfo), - if (albumInfo.assets?.isNotEmpty == true) + if (albumInfo.assets.isNotEmpty == true) _buildAlbumDateRange(albumInfo), SizedBox( height: 60, @@ -172,8 +182,8 @@ class AlbumViewerPage extends HookConsumerWidget { ); } - Widget _buildImageGrid(SharedAlbum albumInfo) { - if (albumInfo.assets?.isNotEmpty == true) { + Widget _buildImageGrid(AlbumResponseDto albumInfo) { + if (albumInfo.assets.isNotEmpty) { return SliverPadding( padding: const EdgeInsets.only(top: 10.0), sliver: SliverGrid( @@ -184,9 +194,9 @@ class AlbumViewerPage extends HookConsumerWidget { ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { - return AlbumViewerThumbnail(asset: albumInfo.assets![index]); + return AlbumViewerThumbnail(asset: albumInfo.assets[index]); }, - childCount: albumInfo.assets?.length, + childCount: albumInfo.assets.length, ), ), ); @@ -194,7 +204,7 @@ class AlbumViewerPage extends HookConsumerWidget { return const SliverToBoxAdapter(); } - Widget _buildControlButton(SharedAlbum albumInfo) { + Widget _buildControlButton(AlbumResponseDto albumInfo) { return Padding( padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 8), child: SizedBox( @@ -219,7 +229,7 @@ class AlbumViewerPage extends HookConsumerWidget { ); } - Widget _buildBody(SharedAlbum albumInfo) { + Widget _buildBody(AlbumResponseDto albumInfo) { return GestureDetector( onTap: () { titleFocusNode.unfocus(); @@ -252,9 +262,16 @@ class AlbumViewerPage extends HookConsumerWidget { return Scaffold( appBar: AlbumViewerAppbar( - albumInfo: albumInfo, userId: userId, albumId: albumId), + albumInfo: albumInfo, + userId: userId, + albumId: albumId, + ), body: albumInfo.when( - data: (albumInfo) => _buildBody(albumInfo), + data: (albumInfo) => albumInfo != null + ? _buildBody(albumInfo) + : const Center( + child: CircularProgressIndicator(), + ), error: (e, _) => Center(child: Text("Error loading album info $e")), loading: () => const Center( child: ImmichLoadingIndicator(), diff --git a/mobile/lib/modules/sharing/views/create_shared_album_page.dart b/mobile/lib/modules/sharing/views/create_shared_album_page.dart index 2da6eae0a9..6cc6b1faaf 100644 --- a/mobile/lib/modules/sharing/views/create_shared_album_page.dart +++ b/mobile/lib/modules/sharing/views/create_shared_album_page.dart @@ -56,10 +56,11 @@ class CreateSharedAlbumPage extends HookConsumerWidget { left: 10, ), child: AlbumTitleTextField( - isAlbumTitleEmpty: isAlbumTitleEmpty, - albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode, - albumTitleController: albumTitleController, - isAlbumTitleTextFieldFocus: isAlbumTitleTextFieldFocus), + isAlbumTitleEmpty: isAlbumTitleEmpty, + albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode, + albumTitleController: albumTitleController, + isAlbumTitleTextFieldFocus: isAlbumTitleTextFieldFocus, + ), ); } @@ -67,8 +68,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget { if (selectedAssets.isEmpty) { return SliverToBoxAdapter( child: Padding( - padding: EdgeInsets.only(top: 200, left: 18), - child: Text( + padding: const EdgeInsets.only(top: 200, left: 18), + child: const Text( 'create_shared_album_page_share_add_assets', style: TextStyle(fontSize: 12), ).tr(), @@ -86,13 +87,16 @@ class CreateSharedAlbumPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 16, left: 18, right: 18), child: OutlinedButton.icon( style: OutlinedButton.styleFrom( - alignment: Alignment.centerLeft, - padding: - const EdgeInsets.symmetric(vertical: 22, horizontal: 16), - side: const BorderSide( - color: Color.fromARGB(255, 206, 206, 206)), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5))), + alignment: Alignment.centerLeft, + padding: + const EdgeInsets.symmetric(vertical: 22, horizontal: 16), + side: const BorderSide( + color: Color.fromARGB(255, 206, 206, 206), + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + ), onPressed: _onSelectPhotosButtonPressed, icon: const Icon(Icons.add_rounded), label: Padding( @@ -100,9 +104,10 @@ class CreateSharedAlbumPage extends HookConsumerWidget { child: Text( 'create_shared_album_page_share_select_photos', style: TextStyle( - fontSize: 16, - color: Colors.grey[700], - fontWeight: FontWeight.bold), + fontSize: 16, + color: Colors.grey[700], + fontWeight: FontWeight.bold, + ), ).tr(), ), ), @@ -147,7 +152,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget { return GestureDetector( onTap: _onBackgroundTapped, child: SharedAlbumThumbnailImage( - asset: selectedAssets.toList()[index]), + asset: selectedAssets.toList()[index], + ), ); }, childCount: selectedAssets.length, @@ -160,58 +166,60 @@ class CreateSharedAlbumPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - elevation: 0, - centerTitle: false, - leading: IconButton( - onPressed: () { - ref.watch(assetSelectionProvider.notifier).removeAll(); - AutoRouter.of(context).pop(); - }, - icon: const Icon(Icons.close_rounded)), - title: const Text( - 'share_create_album', - style: TextStyle(color: Colors.black), - ).tr(), - actions: [ - TextButton( - onPressed: albumTitleController.text.isNotEmpty - ? _showSelectUserPage - : null, - child: Text( - 'create_shared_album_page_share'.tr(), - style: TextStyle( - fontWeight: FontWeight.bold, + appBar: AppBar( + elevation: 0, + centerTitle: false, + leading: IconButton( + onPressed: () { + ref.watch(assetSelectionProvider.notifier).removeAll(); + AutoRouter.of(context).pop(); + }, + icon: const Icon(Icons.close_rounded), + ), + title: const Text( + 'share_create_album', + style: TextStyle(color: Colors.black), + ).tr(), + actions: [ + TextButton( + onPressed: albumTitleController.text.isNotEmpty + ? _showSelectUserPage + : null, + child: Text( + 'create_shared_album_page_share'.tr(), + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + body: GestureDetector( + onTap: _onBackgroundTapped, + child: CustomScrollView( + slivers: [ + SliverAppBar( + elevation: 5, + automaticallyImplyLeading: false, + // leading: Container(), + pinned: true, + floating: false, + bottom: PreferredSize( + preferredSize: const Size.fromHeight(66.0), + child: Column( + children: [ + _buildTitleInputField(), + if (selectedAssets.isNotEmpty) _buildControlButton(), + ], ), ), ), + _buildTitle(), + _buildSelectPhotosButton(), + _buildSelectedImageGrid(), ], ), - body: GestureDetector( - onTap: _onBackgroundTapped, - child: CustomScrollView( - slivers: [ - SliverAppBar( - elevation: 5, - automaticallyImplyLeading: false, - // leading: Container(), - pinned: true, - floating: false, - bottom: PreferredSize( - preferredSize: const Size.fromHeight(66.0), - child: Column( - children: [ - _buildTitleInputField(), - if (selectedAssets.isNotEmpty) _buildControlButton(), - ], - ), - ), - ), - _buildTitle(), - _buildSelectPhotosButton(), - _buildSelectedImageGrid(), - ], - ), - )); + ), + ); } } diff --git a/mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart b/mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart index 59ebc7e009..21fd7acb72 100644 --- a/mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart +++ b/mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart @@ -3,29 +3,28 @@ 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/sharing/models/shared_album.model.dart'; import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart'; -import 'package:immich_mobile/shared/models/user.model.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; +import 'package:openapi/api.dart'; class SelectAdditionalUserForSharingPage extends HookConsumerWidget { - final SharedAlbum albumInfo; + final AlbumResponseDto albumInfo; const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { - AsyncValue> suggestedShareUsers = + AsyncValue> suggestedShareUsers = ref.watch(suggestedSharedUsersProvider); - final sharedUsersList = useState>({}); + final sharedUsersList = useState>({}); _addNewUsersHandler() { AutoRouter.of(context) .pop(sharedUsersList.value.map((e) => e.id).toList()); } - _buildTileIcon(User user) { + _buildTileIcon(UserResponseDto user) { if (sharedUsersList.value.contains(user)) { return CircleAvatar( backgroundColor: Theme.of(context).primaryColor, @@ -43,7 +42,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { } } - _buildUserList(List users) { + _buildUserList(List users) { List usersChip = []; for (var user in sharedUsersList.value) { @@ -55,9 +54,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { label: Text( user.email, style: const TextStyle( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.bold), + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.bold, + ), ), ), ), @@ -70,13 +70,14 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { children: [...usersChip], ), Padding( - padding: EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16.0), child: Text( 'select_additional_user_for_sharing_page_suggestions'.tr(), - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 14, + color: Colors.grey, + fontWeight: FontWeight.bold, + ), ), ), ListView.builder( @@ -87,13 +88,16 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { title: Text( users[index].email, style: const TextStyle( - fontSize: 14, fontWeight: FontWeight.bold), + fontSize: 14, + fontWeight: FontWeight.bold, + ), ), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where((selectedUser) => - selectedUser.id != users[index].id) + .where( + (selectedUser) => selectedUser.id != users[index].id, + ) .toSet(); } else { sharedUsersList.value = { @@ -139,7 +143,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { data: (users) { for (var sharedUsers in albumInfo.sharedUsers) { users.removeWhere( - (u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId); + (u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId, + ); } return _buildUserList(users); diff --git a/mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart b/mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart index 0e0efc4878..74c5c59df6 100644 --- a/mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart +++ b/mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart @@ -9,15 +9,16 @@ import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.da import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart'; import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/shared/models/user.model.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; +import 'package:openapi/api.dart'; class SelectUserForSharingPage extends HookConsumerWidget { const SelectUserForSharingPage({Key? key}) : super(key: key); + @override Widget build(BuildContext context, WidgetRef ref) { - final sharedUsersList = useState>({}); - AsyncValue> suggestedShareUsers = + final sharedUsersList = useState>({}); + AsyncValue> suggestedShareUsers = ref.watch(suggestedSharedUsersProvider); _createSharedAlbum() async { @@ -37,10 +38,14 @@ class SelectUserForSharingPage extends HookConsumerWidget { .navigate(const TabControllerRoute(children: [SharingRoute()])); } - ScaffoldMessenger(child: SnackBar(content: Text('select_user_for_sharing_page_err_album').tr())); + ScaffoldMessenger( + child: SnackBar( + content: const Text('select_user_for_sharing_page_err_album').tr(), + ), + ); } - _buildTileIcon(User user) { + _buildTileIcon(UserResponseDto user) { if (sharedUsersList.value.contains(user)) { return CircleAvatar( backgroundColor: Theme.of(context).primaryColor, @@ -58,7 +63,7 @@ class SelectUserForSharingPage extends HookConsumerWidget { } } - _buildUserList(List users) { + _buildUserList(List users) { List usersChip = []; for (var user in sharedUsersList.value) { @@ -70,9 +75,10 @@ class SelectUserForSharingPage extends HookConsumerWidget { label: Text( user.email, style: const TextStyle( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.bold), + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.bold, + ), ), ), ), @@ -85,13 +91,14 @@ class SelectUserForSharingPage extends HookConsumerWidget { children: [...usersChip], ), Padding( - padding: EdgeInsets.all(16.0), - child: Text( - 'share_suggestions', + padding: const EdgeInsets.all(16.0), + child: const Text( + 'select_user_for_sharing_page_share_suggestions', style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold), + fontSize: 14, + color: Colors.grey, + fontWeight: FontWeight.bold, + ), ).tr(), ), ListView.builder( @@ -102,13 +109,16 @@ class SelectUserForSharingPage extends HookConsumerWidget { title: Text( users[index].email, style: const TextStyle( - fontSize: 14, fontWeight: FontWeight.bold), + fontSize: 14, + fontWeight: FontWeight.bold, + ), ), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where((selectedUser) => - selectedUser.id != users[index].id) + .where( + (selectedUser) => selectedUser.id != users[index].id, + ) .toSet(); } else { sharedUsersList.value = { @@ -141,12 +151,13 @@ class SelectUserForSharingPage extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: - sharedUsersList.value.isEmpty ? null : _createSharedAlbum, - child: const Text( - "share_create_album", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr()) + onPressed: + sharedUsersList.value.isEmpty ? null : _createSharedAlbum, + child: const Text( + "share_create_album", + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ).tr(), + ) ], ), body: suggestedShareUsers.when( diff --git a/mobile/lib/modules/sharing/views/sharing_page.dart b/mobile/lib/modules/sharing/views/sharing_page.dart index 616b55b4b9..728d52673c 100644 --- a/mobile/lib/modules/sharing/views/sharing_page.dart +++ b/mobile/lib/modules/sharing/views/sharing_page.dart @@ -5,10 +5,10 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; -import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart'; import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/sharing/ui/sharing_sliver_appbar.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:openapi/api.dart'; import 'package:transparent_image/transparent_image.dart'; class SharingPage extends HookConsumerWidget { @@ -18,13 +18,16 @@ class SharingPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var box = Hive.box(userInfoBox); var thumbnailRequestUrl = '${box.get(serverEndpointKey)}/asset/thumbnail'; - final List sharedAlbums = ref.watch(sharedAlbumProvider); + final List sharedAlbums = ref.watch(sharedAlbumProvider); - useEffect(() { - ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums(); + useEffect( + () { + ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums(); - return null; - }, []); + return null; + }, + [], + ); _buildAlbumList() { return SliverList( @@ -60,9 +63,10 @@ class SharingPage extends HookConsumerWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.grey.shade800), + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.grey.shade800, + ), ), onTap: () { AutoRouter.of(context) @@ -133,9 +137,9 @@ class SharingPage extends HookConsumerWidget { slivers: [ const SharingSliverAppBar(), SliverPadding( - padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), sliver: SliverToBoxAdapter( - child: Text( + child: const Text( "sharing_page_album", style: TextStyle( fontWeight: FontWeight.bold, diff --git a/mobile/lib/routing/auth_guard.dart b/mobile/lib/routing/auth_guard.dart index 3677a4f631..692cd4e38c 100644 --- a/mobile/lib/routing/auth_guard.dart +++ b/mobile/lib/routing/auth_guard.dart @@ -1,21 +1,25 @@ -import 'dart:convert'; - import 'package:auto_route/auto_route.dart'; -import 'package:immich_mobile/shared/services/network.service.dart'; +import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; class AuthGuard extends AutoRouteGuard { - final NetworkService _networkService = NetworkService(); - + final ApiService _apiService; + AuthGuard(this._apiService); @override void onNavigation(NavigationResolver resolver, StackRouter router) async { try { - var res = await _networkService.postRequest(url: 'auth/validateToken'); - var jsonReponse = jsonDecode(res.toString()); - if (jsonReponse['authStatus']) { + var res = await _apiService.authenticationApi.validateAccessToken(); + + if (res != null && res.authStatus) { resolver.next(true); + } else { + router.replaceAll([const LoginRoute()]); } } catch (e) { - router.removeUntil((route) => route.name == "LoginRoute"); + debugPrint("Error [onNavigation] ${e.toString()}"); + router.replaceAll([const LoginRoute()]); + return; } } } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 6bce788af9..a964b46951 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/backup/views/album_preview_page.dart'; import 'package:immich_mobile/modules/backup/views/backup_album_selection_page.dart'; import 'package:immich_mobile/modules/backup/views/failed_backup_status_page.dart'; @@ -9,7 +10,6 @@ import 'package:immich_mobile/modules/home/views/home_page.dart'; import 'package:immich_mobile/modules/search/views/search_page.dart'; import 'package:immich_mobile/modules/search/views/search_result_page.dart'; import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart'; -import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart'; import 'package:immich_mobile/modules/sharing/views/album_viewer_page.dart'; import 'package:immich_mobile/modules/sharing/views/asset_selection_page.dart'; import 'package:immich_mobile/modules/sharing/views/create_shared_album_page.dart'; @@ -17,12 +17,13 @@ import 'package:immich_mobile/modules/sharing/views/select_additional_user_for_s import 'package:immich_mobile/modules/sharing/views/select_user_for_sharing_page.dart'; import 'package:immich_mobile/modules/sharing/views/sharing_page.dart'; import 'package:immich_mobile/routing/auth_guard.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; import 'package:immich_mobile/modules/backup/views/backup_controller_page.dart'; import 'package:immich_mobile/modules/asset_viewer/views/image_viewer_page.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/shared/views/splash_screen.dart'; import 'package:immich_mobile/shared/views/tab_controller_page.dart'; import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart'; +import 'package:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; part 'router.gr.dart'; @@ -74,5 +75,9 @@ part 'router.gr.dart'; ], ) class AppRouter extends _$AppRouter { - AppRouter() : super(authGuard: AuthGuard()); + final ApiService _apiService; + AppRouter(this._apiService) : super(authGuard: AuthGuard(_apiService)); } + +final appRouterProvider = + Provider((ref) => AppRouter(ref.watch(apiServiceProvider))); diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 16042b45a3..79ce762d01 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -234,7 +234,7 @@ class ImageViewerRoute extends PageRouteInfo { required String imageUrl, required String heroTag, required String thumbnailUrl, - required ImmichAsset asset}) + required AssetResponseDto asset}) : super(ImageViewerRoute.name, path: '/image-viewer-page', args: ImageViewerRouteArgs( @@ -263,7 +263,7 @@ class ImageViewerRouteArgs { final String thumbnailUrl; - final ImmichAsset asset; + final AssetResponseDto asset; @override String toString() { @@ -275,7 +275,7 @@ class ImageViewerRouteArgs { /// [VideoViewerPage] class VideoViewerRoute extends PageRouteInfo { VideoViewerRoute( - {Key? key, required String videoUrl, required ImmichAsset asset}) + {Key? key, required String videoUrl, required AssetResponseDto asset}) : super(VideoViewerRoute.name, path: '/video-viewer-page', args: VideoViewerRouteArgs( @@ -292,7 +292,7 @@ class VideoViewerRouteArgs { final String videoUrl; - final ImmichAsset asset; + final AssetResponseDto asset; @override String toString() { @@ -390,7 +390,7 @@ class AlbumViewerRouteArgs { class SelectAdditionalUserForSharingRoute extends PageRouteInfo { SelectAdditionalUserForSharingRoute( - {Key? key, required SharedAlbum albumInfo}) + {Key? key, required AlbumResponseDto albumInfo}) : super(SelectAdditionalUserForSharingRoute.name, path: '/select-additional-user-for-sharing-page', args: SelectAdditionalUserForSharingRouteArgs( @@ -405,7 +405,7 @@ class SelectAdditionalUserForSharingRouteArgs { final Key? key; - final SharedAlbum albumInfo; + final AlbumResponseDto albumInfo; @override String toString() { diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart index 5eecc7ec37..bac868cf30 100644 --- a/mobile/lib/routing/tab_navigation_observer.dart +++ b/mobile/lib/routing/tab_navigation_observer.dart @@ -23,7 +23,9 @@ class TabNavigationObserver extends AutoRouterObserver { @override Future didChangeTabRoute( - TabPageRoute route, TabPageRoute previousRoute) async { + TabPageRoute route, + TabPageRoute previousRoute, + ) async { // Perform tasks on re-visit to SearchRoute if (route.name == 'SearchRoute') { // Refresh Location State diff --git a/mobile/lib/shared/models/device_info.model.dart b/mobile/lib/shared/models/device_info.model.dart deleted file mode 100644 index 0792e9109e..0000000000 --- a/mobile/lib/shared/models/device_info.model.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'dart:convert'; - -class DeviceInfoRemote { - final int id; - final String userId; - final String deviceId; - final String deviceType; - final String notificationToken; - final String createdAt; - final bool isAutoBackup; - - DeviceInfoRemote({ - required this.id, - required this.userId, - required this.deviceId, - required this.deviceType, - required this.notificationToken, - required this.createdAt, - required this.isAutoBackup, - }); - - DeviceInfoRemote copyWith({ - int? id, - String? userId, - String? deviceId, - String? deviceType, - String? notificationToken, - String? createdAt, - bool? isAutoBackup, - }) { - return DeviceInfoRemote( - id: id ?? this.id, - userId: userId ?? this.userId, - deviceId: deviceId ?? this.deviceId, - deviceType: deviceType ?? this.deviceType, - notificationToken: notificationToken ?? this.notificationToken, - createdAt: createdAt ?? this.createdAt, - isAutoBackup: isAutoBackup ?? this.isAutoBackup, - ); - } - - Map toMap() { - return { - 'id': id, - 'userId': userId, - 'deviceId': deviceId, - 'deviceType': deviceType, - 'notificationToken': notificationToken, - 'createdAt': createdAt, - 'isAutoBackup': isAutoBackup, - }; - } - - factory DeviceInfoRemote.fromMap(Map map) { - return DeviceInfoRemote( - id: map['id']?.toInt() ?? 0, - userId: map['userId'] ?? '', - deviceId: map['deviceId'] ?? '', - deviceType: map['deviceType'] ?? '', - notificationToken: map['notificationToken'] ?? '', - createdAt: map['createdAt'] ?? '', - isAutoBackup: map['isAutoBackup'] ?? false, - ); - } - - String toJson() => json.encode(toMap()); - - factory DeviceInfoRemote.fromJson(String source) => - DeviceInfoRemote.fromMap(json.decode(source)); - - @override - String toString() { - return 'DeviceInfo(id: $id, userId: $userId, deviceId: $deviceId, deviceType: $deviceType, notificationToken: $notificationToken, createdAt: $createdAt, isAutoBackup: $isAutoBackup)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is DeviceInfoRemote && - other.id == id && - other.userId == userId && - other.deviceId == deviceId && - other.deviceType == deviceType && - other.notificationToken == notificationToken && - other.createdAt == createdAt && - other.isAutoBackup == isAutoBackup; - } - - @override - int get hashCode { - return id.hashCode ^ - userId.hashCode ^ - deviceId.hashCode ^ - deviceType.hashCode ^ - notificationToken.hashCode ^ - createdAt.hashCode ^ - isAutoBackup.hashCode; - } -} diff --git a/mobile/lib/shared/models/exif.model.dart b/mobile/lib/shared/models/exif.model.dart deleted file mode 100644 index 7d8528b363..0000000000 --- a/mobile/lib/shared/models/exif.model.dart +++ /dev/null @@ -1,212 +0,0 @@ -import 'dart:convert'; - -class ImmichExif { - final int? id; - final String? assetId; - final String? make; - final String? model; - final String? imageName; - final int? exifImageWidth; - final int? exifImageHeight; - final int? fileSizeInByte; - final String? orientation; - final String? dateTimeOriginal; - final String? modifyDate; - final String? lensModel; - final double? fNumber; - final double? focalLength; - final int? iso; - final double? exposureTime; - final double? latitude; - final double? longitude; - final String? city; - final String? state; - final String? country; - - ImmichExif({ - this.id, - this.assetId, - this.make, - this.model, - this.imageName, - this.exifImageWidth, - this.exifImageHeight, - this.fileSizeInByte, - this.orientation, - this.dateTimeOriginal, - this.modifyDate, - this.lensModel, - this.fNumber, - this.focalLength, - this.iso, - this.exposureTime, - this.latitude, - this.longitude, - this.city, - this.state, - this.country, - }); - - ImmichExif copyWith({ - int? id, - String? assetId, - String? make, - String? model, - String? imageName, - int? exifImageWidth, - int? exifImageHeight, - int? fileSizeInByte, - String? orientation, - String? dateTimeOriginal, - String? modifyDate, - String? lensModel, - double? fNumber, - double? focalLength, - int? iso, - double? exposureTime, - double? latitude, - double? longitude, - String? city, - String? state, - String? country, - }) { - return ImmichExif( - id: id ?? this.id, - assetId: assetId ?? this.assetId, - make: make ?? this.make, - model: model ?? this.model, - imageName: imageName ?? this.imageName, - exifImageWidth: exifImageWidth ?? this.exifImageWidth, - exifImageHeight: exifImageHeight ?? this.exifImageHeight, - fileSizeInByte: fileSizeInByte ?? this.fileSizeInByte, - orientation: orientation ?? this.orientation, - dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, - modifyDate: modifyDate ?? this.modifyDate, - lensModel: lensModel ?? this.lensModel, - fNumber: fNumber ?? this.fNumber, - focalLength: focalLength ?? this.focalLength, - iso: iso ?? this.iso, - exposureTime: exposureTime ?? this.exposureTime, - latitude: latitude ?? this.latitude, - longitude: longitude ?? this.longitude, - city: city ?? this.city, - state: state ?? this.state, - country: country ?? this.country, - ); - } - - Map toMap() { - return { - 'id': id, - 'assetId': assetId, - 'make': make, - 'model': model, - 'imageName': imageName, - 'exifImageWidth': exifImageWidth, - 'exifImageHeight': exifImageHeight, - 'fileSizeInByte': fileSizeInByte, - 'orientation': orientation, - 'dateTimeOriginal': dateTimeOriginal, - 'modifyDate': modifyDate, - 'lensModel': lensModel, - 'fNumber': fNumber, - 'focalLength': focalLength, - 'iso': iso, - 'exposureTime': exposureTime, - 'latitude': latitude, - 'longitude': longitude, - 'city': city, - 'state': state, - 'country': country, - }; - } - - factory ImmichExif.fromMap(Map map) { - return ImmichExif( - id: map['id']?.toInt(), - assetId: map['assetId'], - make: map['make'], - model: map['model'], - imageName: map['imageName'], - exifImageWidth: map['exifImageWidth']?.toInt(), - exifImageHeight: map['exifImageHeight']?.toInt(), - fileSizeInByte: map['fileSizeInByte']?.toInt(), - orientation: map['orientation'], - dateTimeOriginal: map['dateTimeOriginal'], - modifyDate: map['modifyDate'], - lensModel: map['lensModel'], - fNumber: map['fNumber']?.toDouble(), - focalLength: map['focalLength']?.toDouble(), - iso: map['iso']?.toInt(), - exposureTime: map['exposureTime']?.toDouble(), - latitude: map['latitude']?.toDouble(), - longitude: map['longitude']?.toDouble(), - city: map['city'], - state: map['state'], - country: map['country'], - ); - } - - String toJson() => json.encode(toMap()); - - factory ImmichExif.fromJson(String source) => - ImmichExif.fromMap(json.decode(source)); - - @override - String toString() { - return 'ImmichExif(id: $id, assetId: $assetId, make: $make, model: $model, imageName: $imageName, exifImageWidth: $exifImageWidth, exifImageHeight: $exifImageHeight, fileSizeInByte: $fileSizeInByte, orientation: $orientation, dateTimeOriginal: $dateTimeOriginal, modifyDate: $modifyDate, lensModel: $lensModel, fNumber: $fNumber, focalLength: $focalLength, iso: $iso, exposureTime: $exposureTime, latitude: $latitude, longitude: $longitude, city: $city, state: $state, country: $country)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ImmichExif && - other.id == id && - other.assetId == assetId && - other.make == make && - other.model == model && - other.imageName == imageName && - other.exifImageWidth == exifImageWidth && - other.exifImageHeight == exifImageHeight && - other.fileSizeInByte == fileSizeInByte && - other.orientation == orientation && - other.dateTimeOriginal == dateTimeOriginal && - other.modifyDate == modifyDate && - other.lensModel == lensModel && - other.fNumber == fNumber && - other.focalLength == focalLength && - other.iso == iso && - other.exposureTime == exposureTime && - other.latitude == latitude && - other.longitude == longitude && - other.city == city && - other.state == state && - other.country == country; - } - - @override - int get hashCode { - return id.hashCode ^ - assetId.hashCode ^ - make.hashCode ^ - model.hashCode ^ - imageName.hashCode ^ - exifImageWidth.hashCode ^ - exifImageHeight.hashCode ^ - fileSizeInByte.hashCode ^ - orientation.hashCode ^ - dateTimeOriginal.hashCode ^ - modifyDate.hashCode ^ - lensModel.hashCode ^ - fNumber.hashCode ^ - focalLength.hashCode ^ - iso.hashCode ^ - exposureTime.hashCode ^ - latitude.hashCode ^ - longitude.hashCode ^ - city.hashCode ^ - state.hashCode ^ - country.hashCode; - } -} diff --git a/mobile/lib/shared/models/immich_asset.model.dart b/mobile/lib/shared/models/immich_asset.model.dart deleted file mode 100644 index 558c19f2b5..0000000000 --- a/mobile/lib/shared/models/immich_asset.model.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'dart:convert'; - -import 'package:equatable/equatable.dart'; - -class ImmichAsset extends Equatable { - final String id; - final String deviceAssetId; - final String userId; - final String deviceId; - final String type; - final String createdAt; - final String modifiedAt; - final bool isFavorite; - final String? duration; - final String originalPath; - final String resizePath; - - const ImmichAsset({ - required this.id, - required this.deviceAssetId, - required this.userId, - required this.deviceId, - required this.type, - required this.createdAt, - required this.modifiedAt, - required this.isFavorite, - this.duration, - required this.originalPath, - required this.resizePath, - }); - - ImmichAsset copyWith({ - String? id, - String? deviceAssetId, - String? userId, - String? deviceId, - String? type, - String? createdAt, - String? modifiedAt, - bool? isFavorite, - String? duration, - String? originalPath, - String? resizePath, - }) { - return ImmichAsset( - id: id ?? this.id, - deviceAssetId: deviceAssetId ?? this.deviceAssetId, - userId: userId ?? this.userId, - deviceId: deviceId ?? this.deviceId, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - modifiedAt: modifiedAt ?? this.modifiedAt, - isFavorite: isFavorite ?? this.isFavorite, - duration: duration ?? this.duration, - originalPath: originalPath ?? this.originalPath, - resizePath: resizePath ?? this.resizePath, - ); - } - - Map toMap() { - final result = {}; - - result.addAll({'id': id}); - result.addAll({'deviceAssetId': deviceAssetId}); - result.addAll({'userId': userId}); - result.addAll({'deviceId': deviceId}); - result.addAll({'type': type}); - result.addAll({'createdAt': createdAt}); - result.addAll({'modifiedAt': modifiedAt}); - result.addAll({'isFavorite': isFavorite}); - if (duration != null) { - result.addAll({'duration': duration}); - } - result.addAll({'originalPath': originalPath}); - result.addAll({'resizePath': resizePath}); - - return result; - } - - factory ImmichAsset.fromMap(Map map) { - return ImmichAsset( - id: map['id'] ?? '', - deviceAssetId: map['deviceAssetId'] ?? '', - userId: map['userId'] ?? '', - deviceId: map['deviceId'] ?? '', - type: map['type'] ?? '', - createdAt: map['createdAt'] ?? '', - modifiedAt: map['modifiedAt'] ?? '', - isFavorite: map['isFavorite'] ?? false, - duration: map['duration'], - originalPath: map['originalPath'] ?? '', - resizePath: map['resizePath'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory ImmichAsset.fromJson(String source) => - ImmichAsset.fromMap(json.decode(source)); - - @override - String toString() { - return 'ImmichAsset(id: $id, deviceAssetId: $deviceAssetId, userId: $userId, deviceId: $deviceId, type: $type, createdAt: $createdAt, modifiedAt: $modifiedAt, isFavorite: $isFavorite, duration: $duration, originalPath: $originalPath, resizePath: $resizePath)'; - } - - @override - List get props { - return [id]; - } -} diff --git a/mobile/lib/shared/models/immich_asset_with_exif.model.dart b/mobile/lib/shared/models/immich_asset_with_exif.model.dart deleted file mode 100644 index 88ff974373..0000000000 --- a/mobile/lib/shared/models/immich_asset_with_exif.model.dart +++ /dev/null @@ -1,135 +0,0 @@ -import 'dart:convert'; - -import 'package:immich_mobile/shared/models/exif.model.dart'; - -class ImmichAssetWithExif { - final String id; - final String deviceAssetId; - final String userId; - final String deviceId; - final String type; - final String createdAt; - final String modifiedAt; - final String originalPath; - final bool isFavorite; - final String? duration; - final ImmichExif? exifInfo; - - ImmichAssetWithExif({ - required this.id, - required this.deviceAssetId, - required this.userId, - required this.deviceId, - required this.type, - required this.createdAt, - required this.modifiedAt, - required this.originalPath, - required this.isFavorite, - this.duration, - this.exifInfo, - }); - - ImmichAssetWithExif copyWith({ - String? id, - String? deviceAssetId, - String? userId, - String? deviceId, - String? type, - String? createdAt, - String? modifiedAt, - String? originalPath, - bool? isFavorite, - String? duration, - ImmichExif? exifInfo, - }) { - return ImmichAssetWithExif( - id: id ?? this.id, - deviceAssetId: deviceAssetId ?? this.deviceAssetId, - userId: userId ?? this.userId, - deviceId: deviceId ?? this.deviceId, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - modifiedAt: modifiedAt ?? this.modifiedAt, - originalPath: originalPath ?? this.originalPath, - isFavorite: isFavorite ?? this.isFavorite, - duration: duration ?? this.duration, - exifInfo: exifInfo ?? this.exifInfo, - ); - } - - Map toMap() { - return { - 'id': id, - 'deviceAssetId': deviceAssetId, - 'userId': userId, - 'deviceId': deviceId, - 'type': type, - 'createdAt': createdAt, - 'modifiedAt': modifiedAt, - 'originalPath': originalPath, - 'isFavorite': isFavorite, - 'duration': duration, - 'exifInfo': exifInfo?.toMap(), - }; - } - - factory ImmichAssetWithExif.fromMap(Map map) { - return ImmichAssetWithExif( - id: map['id'] ?? '', - deviceAssetId: map['deviceAssetId'] ?? '', - userId: map['userId'] ?? '', - deviceId: map['deviceId'] ?? '', - type: map['type'] ?? '', - createdAt: map['createdAt'] ?? '', - modifiedAt: map['modifiedAt'] ?? '', - originalPath: map['originalPath'] ?? '', - isFavorite: map['isFavorite'] ?? false, - duration: map['duration'], - exifInfo: - map['exifInfo'] != null ? ImmichExif.fromMap(map['exifInfo']) : null, - ); - } - - String toJson() => json.encode(toMap()); - - factory ImmichAssetWithExif.fromJson(String source) => - ImmichAssetWithExif.fromMap(json.decode(source)); - - @override - String toString() { - return 'ImmichAssetWithExif(id: $id, deviceAssetId: $deviceAssetId, userId: $userId, deviceId: $deviceId, type: $type, createdAt: $createdAt, modifiedAt: $modifiedAt, originalPath: $originalPath, isFavorite: $isFavorite, duration: $duration, exifInfo: $exifInfo)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ImmichAssetWithExif && - other.id == id && - other.deviceAssetId == deviceAssetId && - other.userId == userId && - other.deviceId == deviceId && - other.type == type && - other.createdAt == createdAt && - other.modifiedAt == modifiedAt && - other.originalPath == originalPath && - other.isFavorite == isFavorite && - other.duration == duration && - other.exifInfo == exifInfo; - } - - @override - int get hashCode { - return id.hashCode ^ - deviceAssetId.hashCode ^ - userId.hashCode ^ - deviceId.hashCode ^ - type.hashCode ^ - createdAt.hashCode ^ - modifiedAt.hashCode ^ - originalPath.hashCode ^ - isFavorite.hashCode ^ - duration.hashCode ^ - exifInfo.hashCode; - } -} diff --git a/mobile/lib/shared/models/mapbox_info.model.dart b/mobile/lib/shared/models/mapbox_info.model.dart deleted file mode 100644 index f3407bab32..0000000000 --- a/mobile/lib/shared/models/mapbox_info.model.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:convert'; - -class MapboxInfo { - final bool isEnable; - final String mapboxSecret; - MapboxInfo({ - required this.isEnable, - required this.mapboxSecret, - }); - - MapboxInfo copyWith({ - bool? isEnable, - String? mapboxSecret, - }) { - return MapboxInfo( - isEnable: isEnable ?? this.isEnable, - mapboxSecret: mapboxSecret ?? this.mapboxSecret, - ); - } - - Map toMap() { - return { - 'isEnable': isEnable, - 'mapboxSecret': mapboxSecret, - }; - } - - factory MapboxInfo.fromMap(Map map) { - return MapboxInfo( - isEnable: map['isEnable'] ?? false, - mapboxSecret: map['mapboxSecret'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory MapboxInfo.fromJson(String source) => - MapboxInfo.fromMap(json.decode(source)); - - @override - String toString() => - 'MapboxInfo(isEnable: $isEnable, mapboxSecret: $mapboxSecret)'; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is MapboxInfo && - other.isEnable == isEnable && - other.mapboxSecret == mapboxSecret; - } - - @override - int get hashCode => isEnable.hashCode ^ mapboxSecret.hashCode; -} diff --git a/mobile/lib/shared/models/server_info.model.dart b/mobile/lib/shared/models/server_info.model.dart deleted file mode 100644 index 7343fbe961..0000000000 --- a/mobile/lib/shared/models/server_info.model.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'dart:convert'; - -class ServerInfo { - final String diskSize; - final String diskUse; - final String diskAvailable; - final int diskSizeRaw; - final int diskUseRaw; - final int diskAvailableRaw; - final double diskUsagePercentage; - ServerInfo({ - required this.diskSize, - required this.diskUse, - required this.diskAvailable, - required this.diskSizeRaw, - required this.diskUseRaw, - required this.diskAvailableRaw, - required this.diskUsagePercentage, - }); - - ServerInfo copyWith({ - String? diskSize, - String? diskUse, - String? diskAvailable, - int? diskSizeRaw, - int? diskUseRaw, - int? diskAvailableRaw, - double? diskUsagePercentage, - }) { - return ServerInfo( - diskSize: diskSize ?? this.diskSize, - diskUse: diskUse ?? this.diskUse, - diskAvailable: diskAvailable ?? this.diskAvailable, - diskSizeRaw: diskSizeRaw ?? this.diskSizeRaw, - diskUseRaw: diskUseRaw ?? this.diskUseRaw, - diskAvailableRaw: diskAvailableRaw ?? this.diskAvailableRaw, - diskUsagePercentage: diskUsagePercentage ?? this.diskUsagePercentage, - ); - } - - Map toMap() { - return { - 'diskSize': diskSize, - 'diskUse': diskUse, - 'diskAvailable': diskAvailable, - 'diskSizeRaw': diskSizeRaw, - 'diskUseRaw': diskUseRaw, - 'diskAvailableRaw': diskAvailableRaw, - 'diskUsagePercentage': diskUsagePercentage, - }; - } - - factory ServerInfo.fromMap(Map map) { - return ServerInfo( - diskSize: map['diskSize'] ?? '', - diskUse: map['diskUse'] ?? '', - diskAvailable: map['diskAvailable'] ?? '', - diskSizeRaw: map['diskSizeRaw']?.toInt() ?? 0, - diskUseRaw: map['diskUseRaw']?.toInt() ?? 0, - diskAvailableRaw: map['diskAvailableRaw']?.toInt() ?? 0, - diskUsagePercentage: map['diskUsagePercentage']?.toDouble() ?? 0.0, - ); - } - - String toJson() => json.encode(toMap()); - - factory ServerInfo.fromJson(String source) => - ServerInfo.fromMap(json.decode(source)); - - @override - String toString() { - return 'ServerInfo(diskSize: $diskSize, diskUse: $diskUse, diskAvailable: $diskAvailable, diskSizeRaw: $diskSizeRaw, diskUseRaw: $diskUseRaw, diskAvailableRaw: $diskAvailableRaw, diskUsagePercentage: $diskUsagePercentage)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ServerInfo && - other.diskSize == diskSize && - other.diskUse == diskUse && - other.diskAvailable == diskAvailable && - other.diskSizeRaw == diskSizeRaw && - other.diskUseRaw == diskUseRaw && - other.diskAvailableRaw == diskAvailableRaw && - other.diskUsagePercentage == diskUsagePercentage; - } - - @override - int get hashCode { - return diskSize.hashCode ^ - diskUse.hashCode ^ - diskAvailable.hashCode ^ - diskSizeRaw.hashCode ^ - diskUseRaw.hashCode ^ - diskAvailableRaw.hashCode ^ - diskUsagePercentage.hashCode; - } -} diff --git a/mobile/lib/shared/models/server_info_state.model.dart b/mobile/lib/shared/models/server_info_state.model.dart index f82beb7949..7b1ea9c918 100644 --- a/mobile/lib/shared/models/server_info_state.model.dart +++ b/mobile/lib/shared/models/server_info_state.model.dart @@ -1,29 +1,22 @@ -import 'dart:convert'; - -import 'package:immich_mobile/shared/models/mapbox_info.model.dart'; -import 'package:immich_mobile/shared/models/server_version.model.dart'; +import 'package:openapi/api.dart'; class ServerInfoState { - final MapboxInfo mapboxInfo; - final ServerVersion serverVersion; + final ServerVersionReponseDto serverVersion; final bool isVersionMismatch; final String versionMismatchErrorMessage; ServerInfoState({ - required this.mapboxInfo, required this.serverVersion, required this.isVersionMismatch, required this.versionMismatchErrorMessage, }); ServerInfoState copyWith({ - MapboxInfo? mapboxInfo, - ServerVersion? serverVersion, + ServerVersionReponseDto? serverVersion, bool? isVersionMismatch, String? versionMismatchErrorMessage, }) { return ServerInfoState( - mapboxInfo: mapboxInfo ?? this.mapboxInfo, serverVersion: serverVersion ?? this.serverVersion, isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch, versionMismatchErrorMessage: @@ -31,32 +24,9 @@ class ServerInfoState { ); } - Map toMap() { - return { - 'mapboxInfo': mapboxInfo.toMap(), - 'serverVersion': serverVersion.toMap(), - 'isVersionMismatch': isVersionMismatch, - 'versionMismatchErrorMessage': versionMismatchErrorMessage, - }; - } - - factory ServerInfoState.fromMap(Map map) { - return ServerInfoState( - mapboxInfo: MapboxInfo.fromMap(map['mapboxInfo']), - serverVersion: ServerVersion.fromMap(map['serverVersion']), - isVersionMismatch: map['isVersionMismatch'] ?? false, - versionMismatchErrorMessage: map['versionMismatchErrorMessage'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory ServerInfoState.fromJson(String source) => - ServerInfoState.fromMap(json.decode(source)); - @override String toString() { - return 'ServerInfoState(mapboxInfo: $mapboxInfo, serverVersion: $serverVersion, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)'; + return 'ServerInfoState( serverVersion: $serverVersion, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)'; } @override @@ -64,7 +34,6 @@ class ServerInfoState { if (identical(this, other)) return true; return other is ServerInfoState && - other.mapboxInfo == mapboxInfo && other.serverVersion == serverVersion && other.isVersionMismatch == isVersionMismatch && other.versionMismatchErrorMessage == versionMismatchErrorMessage; @@ -72,8 +41,7 @@ class ServerInfoState { @override int get hashCode { - return mapboxInfo.hashCode ^ - serverVersion.hashCode ^ + return serverVersion.hashCode ^ isVersionMismatch.hashCode ^ versionMismatchErrorMessage.hashCode; } diff --git a/mobile/lib/shared/models/server_version.model.dart b/mobile/lib/shared/models/server_version.model.dart deleted file mode 100644 index d41fb1e133..0000000000 --- a/mobile/lib/shared/models/server_version.model.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'dart:convert'; - -class ServerVersion { - final int major; - final int minor; - final int patch; - final int build; - - ServerVersion({ - required this.major, - required this.minor, - required this.patch, - required this.build, - }); - - ServerVersion copyWith({ - int? major, - int? minor, - int? patch, - int? build, - }) { - return ServerVersion( - major: major ?? this.major, - minor: minor ?? this.minor, - patch: patch ?? this.patch, - build: build ?? this.build, - ); - } - - Map toMap() { - return { - 'major': major, - 'minor': minor, - 'patch': patch, - 'build': build, - }; - } - - factory ServerVersion.fromMap(Map map) { - return ServerVersion( - major: map['major']?.toInt() ?? 0, - minor: map['minor']?.toInt() ?? 0, - patch: map['patch']?.toInt() ?? 0, - build: map['build']?.toInt() ?? 0, - ); - } - - String toJson() => json.encode(toMap()); - - factory ServerVersion.fromJson(String source) => - ServerVersion.fromMap(json.decode(source)); - - @override - String toString() { - return 'ServerVersion(major: $major, minor: $minor, patch: $patch, build: $build)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ServerVersion && - other.major == major && - other.minor == minor && - other.patch == patch && - other.build == build; - } - - @override - int get hashCode { - return major.hashCode ^ minor.hashCode ^ patch.hashCode ^ build.hashCode; - } -} diff --git a/mobile/lib/shared/models/user.model.dart b/mobile/lib/shared/models/user.model.dart deleted file mode 100644 index 99b0ce6cc0..0000000000 --- a/mobile/lib/shared/models/user.model.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'dart:convert'; - -class User { - final String id; - final String email; - final String createdAt; - final String firstName; - final String lastName; - - User({ - required this.id, - required this.email, - required this.createdAt, - required this.firstName, - required this.lastName, - }); - - User copyWith({ - String? id, - String? email, - String? createdAt, - String? firstName, - String? lastName, - }) { - return User( - id: id ?? this.id, - email: email ?? this.email, - createdAt: createdAt ?? this.createdAt, - firstName: firstName ?? this.firstName, - lastName: lastName ?? this.lastName, - ); - } - - Map toMap() { - final result = {}; - - result.addAll({'id': id}); - result.addAll({'email': email}); - result.addAll({'createdAt': createdAt}); - - return result; - } - - factory User.fromMap(Map map) { - return User( - id: map['id'] ?? '', - email: map['email'] ?? '', - createdAt: map['createdAt'] ?? '', - firstName: map['firstName'] ?? '', - lastName: map['lastName'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory User.fromJson(String source) => User.fromMap(json.decode(source)); - - @override - String toString() => - 'UserInfo(id: $id, email: $email, createdAt: $createdAt)'; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is User && - other.id == id && - other.email == email && - other.createdAt == createdAt && - other.firstName == firstName && - other.lastName == lastName; - } - - @override - int get hashCode => id.hashCode ^ email.hashCode ^ createdAt.hashCode; -} diff --git a/mobile/lib/shared/providers/asset.provider.dart b/mobile/lib/shared/providers/asset.provider.dart index bb42fbd1eb..84ddc2dc98 100644 --- a/mobile/lib/shared/providers/asset.provider.dart +++ b/mobile/lib/shared/providers/asset.provider.dart @@ -1,21 +1,20 @@ import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/home/models/delete_asset_response.model.dart'; import 'package:immich_mobile/modules/home/services/asset.service.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; import 'package:immich_mobile/shared/services/device_info.service.dart'; import 'package:collection/collection.dart'; import 'package:intl/intl.dart'; +import 'package:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; -class AssetNotifier extends StateNotifier> { +class AssetNotifier extends StateNotifier> { final AssetService _assetService; final DeviceInfoService _deviceInfoService = DeviceInfoService(); AssetNotifier(this._assetService) : super([]); getAllAsset() async { - List? allAssets = await _assetService.getAllAsset(); + var allAssets = await _assetService.getAllAsset(); if (allAssets != null) { state = allAssets; @@ -26,11 +25,11 @@ class AssetNotifier extends StateNotifier> { state = []; } - onNewAssetUploaded(ImmichAsset newAsset) { + onNewAssetUploaded(AssetResponseDto newAsset) { state = [...state, newAsset]; } - deleteAssets(Set deleteAssets) async { + deleteAssets(Set deleteAssets) async { var deviceInfo = await _deviceInfoService.getDeviceInfo(); var deviceId = deviceInfo["deviceId"]; var deleteIdList = []; @@ -53,14 +52,15 @@ class AssetNotifier extends StateNotifier> { } // Delete asset on server - List? deleteAssetResult = + List? deleteAssetResult = await _assetService.deleteAssets(deleteAssets); + if (deleteAssetResult == null) { return; } for (var asset in deleteAssetResult) { - if (asset.status == 'success') { + if (asset.status == DeleteAssetStatus.SUCCESS) { state = state.where((immichAsset) => immichAsset.id != asset.id).toList(); } @@ -69,7 +69,7 @@ class AssetNotifier extends StateNotifier> { } final assetProvider = - StateNotifierProvider>((ref) { + StateNotifierProvider>((ref) { return AssetNotifier(ref.watch(assetServiceProvider)); }); @@ -77,17 +77,25 @@ final assetGroupByDateTimeProvider = StateProvider((ref) { var assets = ref.watch(assetProvider); assets.sortByCompare( - (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a)); - return assets.groupListsBy((element) => - DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt))); + (e) => DateTime.parse(e.createdAt), + (a, b) => b.compareTo(a), + ); + return assets.groupListsBy( + (element) => + DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt)), + ); }); final assetGroupByMonthYearProvider = StateProvider((ref) { var assets = ref.watch(assetProvider); assets.sortByCompare( - (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a)); + (e) => DateTime.parse(e.createdAt), + (a, b) => b.compareTo(a), + ); - return assets.groupListsBy((element) => - DateFormat('MMMM, y').format(DateTime.parse(element.createdAt))); + return assets.groupListsBy( + (element) => + DateFormat('MMMM, y').format(DateTime.parse(element.createdAt)), + ); }); diff --git a/mobile/lib/shared/providers/release_info.provider.dart b/mobile/lib/shared/providers/release_info.provider.dart index 65276dd737..c9f91ec847 100644 --- a/mobile/lib/shared/providers/release_info.provider.dart +++ b/mobile/lib/shared/providers/release_info.provider.dart @@ -56,4 +56,5 @@ class ReleaseInfoNotifier extends StateNotifier { } final releaseInfoProvider = StateNotifierProvider( - (ref) => ReleaseInfoNotifier()); + (ref) => ReleaseInfoNotifier(), +); diff --git a/mobile/lib/shared/providers/server_info.provider.dart b/mobile/lib/shared/providers/server_info.provider.dart index 2eb2eedfc9..0794e73cd3 100644 --- a/mobile/lib/shared/providers/server_info.provider.dart +++ b/mobile/lib/shared/providers/server_info.provider.dart @@ -1,18 +1,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/shared/models/mapbox_info.model.dart'; import 'package:immich_mobile/shared/models/server_info_state.model.dart'; -import 'package:immich_mobile/shared/models/server_version.model.dart'; import 'package:immich_mobile/shared/services/server_info.service.dart'; +import 'package:openapi/api.dart'; import 'package:package_info_plus/package_info_plus.dart'; class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) : super( ServerInfoState( - mapboxInfo: MapboxInfo(isEnable: false, mapboxSecret: ""), - serverVersion: - ServerVersion(major: 0, patch: 0, minor: 0, build: 0), + serverVersion: ServerVersionReponseDto( + major: 0, + patch_: 0, + minor: 0, + build: 0, + ), isVersionMismatch: false, versionMismatchErrorMessage: "", ), @@ -21,7 +23,8 @@ class ServerInfoNotifier extends StateNotifier { final ServerInfoService _serverInfoService; getServerVersion() async { - ServerVersion? serverVersion = await _serverInfoService.getServerVersion(); + ServerVersionReponseDto? serverVersion = + await _serverInfoService.getServerVersion(); if (serverVersion == null) { state = state.copyWith( @@ -59,7 +62,9 @@ class ServerInfoNotifier extends StateNotifier { } state = state.copyWith( - isVersionMismatch: false, versionMismatchErrorMessage: ""); + isVersionMismatch: false, + versionMismatchErrorMessage: "", + ); } Map _getDetailVersion(String version) { diff --git a/mobile/lib/shared/providers/websocket.provider.dart b/mobile/lib/shared/providers/websocket.provider.dart index 60d13f76d1..bc48762768 100644 --- a/mobile/lib/shared/providers/websocket.provider.dart +++ b/mobile/lib/shared/providers/websocket.provider.dart @@ -5,8 +5,8 @@ import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; import 'package:immich_mobile/shared/providers/asset.provider.dart'; +import 'package:openapi/api.dart'; import 'package:socket_io_client/socket_io_client.dart'; class WebscoketState { @@ -92,8 +92,11 @@ class WebsocketNotifier extends StateNotifier { socket.on('on_upload_success', (data) { var jsonString = jsonDecode(data.toString()); - ImmichAsset newAsset = ImmichAsset.fromMap(jsonString); - ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset); + AssetResponseDto? newAsset = AssetResponseDto.fromJson(jsonString); + + if (newAsset != null) { + ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset); + } }); } catch (e) { debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); @@ -119,8 +122,11 @@ class WebsocketNotifier extends StateNotifier { debugPrint("[Websocket] Start listening to event on_upload_success"); state.socket?.on('on_upload_success', (data) { var jsonString = jsonDecode(data.toString()); - ImmichAsset newAsset = ImmichAsset.fromMap(jsonString); - ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset); + AssetResponseDto? newAsset = AssetResponseDto.fromJson(jsonString); + + if (newAsset != null) { + ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset); + } }); } } diff --git a/mobile/lib/shared/services/api.service.dart b/mobile/lib/shared/services/api.service.dart new file mode 100644 index 0000000000..37630beb9c --- /dev/null +++ b/mobile/lib/shared/services/api.service.dart @@ -0,0 +1,30 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:openapi/api.dart'; + +final apiServiceProvider = Provider((ref) => ApiService()); + +class ApiService { + late ApiClient _apiClient; + + late UserApi userApi; + late AuthenticationApi authenticationApi; + late AlbumApi albumApi; + late AssetApi assetApi; + late ServerInfoApi serverInfoApi; + late DeviceInfoApi deviceInfoApi; + + setEndpoint(String endpoint) { + _apiClient = ApiClient(basePath: endpoint); + + userApi = UserApi(_apiClient); + authenticationApi = AuthenticationApi(_apiClient); + albumApi = AlbumApi(_apiClient); + assetApi = AssetApi(_apiClient); + serverInfoApi = ServerInfoApi(_apiClient); + deviceInfoApi = DeviceInfoApi(_apiClient); + } + + setAccessToken(String accessToken) { + _apiClient.addDefaultHeader('Authorization', 'bearer $accessToken'); + } +} diff --git a/mobile/lib/shared/services/device_info.service.dart b/mobile/lib/shared/services/device_info.service.dart index e1cd8a91fe..429d927fc8 100644 --- a/mobile/lib/shared/services/device_info.service.dart +++ b/mobile/lib/shared/services/device_info.service.dart @@ -2,6 +2,7 @@ import 'package:flutter_udid/flutter_udid.dart'; import 'dart:io' show Platform; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:openapi/api.dart'; final deviceInfoServiceProvider = Provider((_) => DeviceInfoService()); @@ -9,12 +10,12 @@ class DeviceInfoService { Future> getDeviceInfo() async { // Get device info var deviceId = await FlutterUdid.consistentUdid; - var deviceType = ""; + var deviceType = DeviceTypeEnum.ANDROID; if (Platform.isAndroid) { - deviceType = "ANDROID"; + deviceType = DeviceTypeEnum.ANDROID; } else if (Platform.isIOS) { - deviceType = "IOS"; + deviceType = DeviceTypeEnum.IOS; } return {"deviceId": deviceId, "deviceType": deviceType}; diff --git a/mobile/lib/shared/services/local_storage.service.dart b/mobile/lib/shared/services/local_storage.service.dart deleted file mode 100644 index f409b2d7c0..0000000000 --- a/mobile/lib/shared/services/local_storage.service.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:hive/hive.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/constants/hive_box.dart'; - -final localStorageServiceProvider = Provider((_) => LocalStorageService()); - -class LocalStorageService { - late Box _box; - - LocalStorageService() { - _box = Hive.box(userInfoBox); - } - - T get(String key) { - return _box.get(key); - } - - put(String key, T value) { - return _box.put(key, value); - } -} diff --git a/mobile/lib/shared/services/network.service.dart b/mobile/lib/shared/services/network.service.dart index 24ad7649de..f8a502d0a1 100644 --- a/mobile/lib/shared/services/network.service.dart +++ b/mobile/lib/shared/services/network.service.dart @@ -33,10 +33,11 @@ class NetworkService { } } - Future getRequest( - {required String url, - bool isByteResponse = false, - bool isStreamReponse = false}) async { + Future getRequest({ + required String url, + bool isByteResponse = false, + bool isStreamReponse = false, + }) async { try { var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); diff --git a/mobile/lib/shared/services/server_info.service.dart b/mobile/lib/shared/services/server_info.service.dart index df25fe256e..92ea9d89d4 100644 --- a/mobile/lib/shared/services/server_info.service.dart +++ b/mobile/lib/shared/services/server_info.service.dart @@ -1,33 +1,33 @@ -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/shared/models/server_info.model.dart'; -import 'package:immich_mobile/shared/models/server_version.model.dart'; -import 'package:immich_mobile/shared/services/network.service.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; +import 'package:openapi/api.dart'; -final serverInfoServiceProvider = - Provider((ref) => ServerInfoService(ref.watch(networkServiceProvider))); +final serverInfoServiceProvider = Provider( + (ref) => ServerInfoService( + ref.watch(apiServiceProvider), + ), +); class ServerInfoService { - final NetworkService _networkService; - ServerInfoService(this._networkService); + final ApiService _apiService; + ServerInfoService(this._apiService); - Future getServerInfo() async { - Response response = await _networkService.getRequest(url: 'server-info'); - - return ServerInfo.fromJson(response.toString()); + Future getServerInfo() async { + try { + return await _apiService.serverInfoApi.getServerInfo(); + } catch (e) { + debugPrint("Error [getServerInfo] ${e.toString()}"); + return null; + } } - Future getServerVersion() async { + Future getServerVersion() async { try { - Response response = - await _networkService.getRequest(url: 'server-info/version'); - - return ServerVersion.fromJson(response.toString()); + return await _apiService.serverInfoApi.getServerVersion(); } catch (e) { debugPrint("Error getting server info"); + return null; } - - return null; } } diff --git a/mobile/lib/shared/services/user.service.dart b/mobile/lib/shared/services/user.service.dart index 737cd97fc4..482f08635f 100644 --- a/mobile/lib/shared/services/user.service.dart +++ b/mobile/lib/shared/services/user.service.dart @@ -1,70 +1,49 @@ -import 'dart:convert'; - -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:http/http.dart'; import 'package:http_parser/http_parser.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:immich_mobile/constants/hive_box.dart'; -import 'package:immich_mobile/shared/models/upload_profile_image_repsonse.model.dart'; -import 'package:immich_mobile/shared/models/user.model.dart'; -import 'package:immich_mobile/shared/services/network.service.dart'; -import 'package:immich_mobile/utils/dio_http_interceptor.dart'; +import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/utils/files_helper.dart'; +import 'package:openapi/api.dart'; -final userServiceProvider = - Provider((ref) => UserService(ref.watch(networkServiceProvider))); +final userServiceProvider = Provider( + (ref) => UserService( + ref.watch(apiServiceProvider), + ), +); class UserService { - final NetworkService _networkService; - UserService(this._networkService); + final ApiService _apiService; - Future> getAllUsersInfo() async { + UserService(this._apiService); + + Future?> getAllUsersInfo({required bool isAll}) async { try { - var res = await _networkService.getRequest(url: 'user'); - List decodedData = jsonDecode(res.toString()); - List result = List.from(decodedData.map((e) => User.fromMap(e))); - - return result; + return await _apiService.userApi.getAllUsers(isAll); } catch (e) { - debugPrint("Error getAllUsersInfo ${e.toString()}"); + debugPrint("Error [getAllUsersInfo] ${e.toString()}"); + return null; } - - return []; } - Future uploadProfileImage(XFile image) async { - var dio = Dio(); - dio.interceptors.add(AuthenticatedRequestInterceptor()); - String savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); - var mimeType = FileHelper.getMimeType(image.path); - - final imageData = MultipartFile.fromBytes( - await image.readAsBytes(), - filename: image.name, - contentType: MediaType( - mimeType["type"], - mimeType["subType"], - ), - ); - - final formData = FormData.fromMap({'file': imageData}); - + Future uploadProfileImage(XFile image) async { try { - Response res = await dio.post( - '$savedEndpoint/user/profile-image', - data: formData, + var mimeType = FileHelper.getMimeType(image.path); + + return await _apiService.userApi.createProfileImage( + MultipartFile.fromBytes( + 'file', + await image.readAsBytes(), + filename: image.name, + contentType: MediaType( + mimeType["type"], + mimeType["subType"], + ), + ), ); - - var payload = UploadProfileImageResponse.fromJson(res.toString()); - - return payload; - } on DioError catch (e) { - debugPrint("Error uploading file: ${e.response}"); - return null; } catch (e) { - debugPrint("Error uploading file: $e"); + debugPrint("Error [uploadProfileImage] ${e.toString()}"); return null; } } diff --git a/mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart b/mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart index 85a67daa2c..d9c43cd353 100644 --- a/mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart +++ b/mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart @@ -22,7 +22,10 @@ class ImmichSliverPersistentAppBarDelegate @override Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { + BuildContext context, + double shrinkOffset, + bool overlapsContent, + ) { return SizedBox.expand(child: child); } diff --git a/mobile/lib/shared/views/splash_screen.dart b/mobile/lib/shared/views/splash_screen.dart index fd3828ee4a..23a5456023 100644 --- a/mobile/lib/shared/views/splash_screen.dart +++ b/mobile/lib/shared/views/splash_screen.dart @@ -19,10 +19,13 @@ class SplashScreenPage extends HookConsumerWidget { Hive.box(hiveLoginInfoBox).get(savedLoginInfoKey); void performLoggingIn() async { - var isAuthenticated = await ref - .read(authenticationProvider.notifier) - .login( - loginInfo!.email, loginInfo.password, loginInfo.serverUrl, true); + var isAuthenticated = + await ref.read(authenticationProvider.notifier).login( + loginInfo!.email, + loginInfo.password, + loginInfo.serverUrl, + true, + ); if (isAuthenticated) { // Resume backup (if enable) then navigate @@ -33,14 +36,17 @@ class SplashScreenPage extends HookConsumerWidget { } } - useEffect(() { - if (loginInfo?.isSaveLogin == true) { - performLoggingIn(); - } else { - AutoRouter.of(context).push(const LoginRoute()); - } - return null; - }, []); + useEffect( + () { + if (loginInfo?.isSaveLogin == true) { + performLoggingIn(); + } else { + AutoRouter.of(context).push(const LoginRoute()); + } + return null; + }, + [], + ); return Scaffold( backgroundColor: immichBackgroundColor, diff --git a/mobile/lib/shared/views/tab_controller_page.dart b/mobile/lib/shared/views/tab_controller_page.dart index b32baf33bf..9da202f089 100644 --- a/mobile/lib/shared/views/tab_controller_page.dart +++ b/mobile/lib/shared/views/tab_controller_page.dart @@ -35,23 +35,30 @@ class TabControllerPage extends ConsumerWidget { ? null : BottomNavigationBar( selectedLabelStyle: const TextStyle( - fontSize: 15, fontWeight: FontWeight.w600), + fontSize: 15, + fontWeight: FontWeight.w600, + ), unselectedLabelStyle: const TextStyle( - fontSize: 15, fontWeight: FontWeight.w600), + fontSize: 15, + fontWeight: FontWeight.w600, + ), currentIndex: tabsRouter.activeIndex, onTap: (index) { tabsRouter.setActiveIndex(index); }, items: [ BottomNavigationBarItem( - label: 'tab_controller_nav_photos'.tr(), - icon: const Icon(Icons.photo)), + label: 'tab_controller_nav_photos'.tr(), + icon: const Icon(Icons.photo), + ), BottomNavigationBarItem( - label: 'tab_controller_nav_search'.tr(), - icon: const Icon(Icons.search)), + label: 'tab_controller_nav_search'.tr(), + icon: const Icon(Icons.search), + ), BottomNavigationBarItem( - label: 'tab_controller_nav_sharing'.tr(), - icon: const Icon(Icons.group_outlined)), + label: 'tab_controller_nav_sharing'.tr(), + icon: const Icon(Icons.group_outlined), + ), ], ), ), diff --git a/mobile/lib/shared/views/version_announcement_overlay.dart b/mobile/lib/shared/views/version_announcement_overlay.dart index 90095ef27f..aa56bd8fbd 100644 --- a/mobile/lib/shared/views/version_announcement_overlay.dart +++ b/mobile/lib/shared/views/version_announcement_overlay.dart @@ -54,13 +54,16 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { child: RichText( text: TextSpan( style: const TextStyle( - fontSize: 14, - fontFamily: 'WorkSans', - color: Colors.black87, - height: 1.2), + fontSize: 14, + fontFamily: 'WorkSans', + color: Colors.black87, + height: 1.2, + ), children: [ TextSpan( - text: 'version_announcement_overlay_text_1'.tr(), + text: + 'version_announcement_overlay_text_1' + .tr(), ), const TextSpan( text: ' Immich ', @@ -71,11 +74,14 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { ), ), TextSpan( - text: "version_announcement_overlay_text_2".tr(), + text: + "version_announcement_overlay_text_2" + .tr(), ), TextSpan( - text: "version_announcement_overlay_release_notes" - .tr(), + text: + "version_announcement_overlay_release_notes" + .tr(), style: const TextStyle( decoration: TextDecoration.underline, ), @@ -83,7 +89,9 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { ..onTap = goToReleaseNote, ), TextSpan( - text: "version_announcement_overlay_text_3".tr(), + text: + "version_announcement_overlay_text_3" + .tr(), ) ], ), @@ -92,22 +100,25 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(top: 16.0), child: ElevatedButton( - style: ElevatedButton.styleFrom( - shape: const StadiumBorder(), - visualDensity: VisualDensity.standard, - primary: Colors.indigo, - onPrimary: Colors.grey[50], - elevation: 2, - padding: const EdgeInsets.symmetric( - vertical: 10, horizontal: 25), + style: ElevatedButton.styleFrom( + shape: const StadiumBorder(), + visualDensity: VisualDensity.standard, + primary: Colors.indigo, + onPrimary: Colors.grey[50], + elevation: 2, + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 25, ), - onPressed: onAcknowledgeTapped, - child: const Text( - "version_announcement_overlay_ack", - style: TextStyle( - fontSize: 14, - ), - ).tr()), + ), + onPressed: onAcknowledgeTapped, + child: const Text( + "version_announcement_overlay_ack", + style: TextStyle( + fontSize: 14, + ), + ).tr(), + ), ) ], ), diff --git a/mobile/openapi/.gitignore b/mobile/openapi/.gitignore new file mode 100644 index 0000000000..1be28ced09 --- /dev/null +++ b/mobile/openapi/.gitignore @@ -0,0 +1,17 @@ +# See https://dart.dev/guides/libraries/private-files + +.dart_tool/ +.packages +build/ +pubspec.lock # Except for application packages + +doc/api/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ + +# Mac +.DS_Store diff --git a/mobile/openapi/.openapi-generator-ignore b/mobile/openapi/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/mobile/openapi/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES new file mode 100644 index 0000000000..9818927be1 --- /dev/null +++ b/mobile/openapi/.openapi-generator/FILES @@ -0,0 +1,100 @@ +.gitignore +.travis.yml +README.md +analysis_options.yaml +doc/AddAssetsDto.md +doc/AddUsersDto.md +doc/AdminSignupResponseDto.md +doc/AlbumApi.md +doc/AlbumResponseDto.md +doc/AssetApi.md +doc/AssetFileUploadResponseDto.md +doc/AssetResponseDto.md +doc/AssetTypeEnum.md +doc/AuthenticationApi.md +doc/CheckDuplicateAssetDto.md +doc/CheckDuplicateAssetResponseDto.md +doc/CreateAlbumDto.md +doc/CreateDeviceInfoDto.md +doc/CreateProfileImageResponseDto.md +doc/CreateUserDto.md +doc/CuratedLocationsResponseDto.md +doc/CuratedObjectsResponseDto.md +doc/DeleteAssetDto.md +doc/DeleteAssetResponseDto.md +doc/DeleteAssetStatus.md +doc/DeviceInfoApi.md +doc/DeviceInfoResponseDto.md +doc/DeviceTypeEnum.md +doc/ExifResponseDto.md +doc/LoginCredentialDto.md +doc/LoginResponseDto.md +doc/RemoveAssetsDto.md +doc/SearchAssetDto.md +doc/ServerInfoApi.md +doc/ServerInfoResponseDto.md +doc/ServerPingResponse.md +doc/ServerVersionReponseDto.md +doc/SignUpDto.md +doc/SmartInfoResponseDto.md +doc/UpdateAlbumDto.md +doc/UpdateDeviceInfoDto.md +doc/UpdateUserDto.md +doc/UserApi.md +doc/UserCountResponseDto.md +doc/UserResponseDto.md +doc/ValidateAccessTokenResponseDto.md +git_push.sh +lib/api.dart +lib/api/album_api.dart +lib/api/asset_api.dart +lib/api/authentication_api.dart +lib/api/device_info_api.dart +lib/api/server_info_api.dart +lib/api/user_api.dart +lib/api_client.dart +lib/api_exception.dart +lib/api_helper.dart +lib/auth/api_key_auth.dart +lib/auth/authentication.dart +lib/auth/http_basic_auth.dart +lib/auth/http_bearer_auth.dart +lib/auth/oauth.dart +lib/model/add_assets_dto.dart +lib/model/add_users_dto.dart +lib/model/admin_signup_response_dto.dart +lib/model/album_response_dto.dart +lib/model/asset_file_upload_response_dto.dart +lib/model/asset_response_dto.dart +lib/model/asset_type_enum.dart +lib/model/check_duplicate_asset_dto.dart +lib/model/check_duplicate_asset_response_dto.dart +lib/model/create_album_dto.dart +lib/model/create_device_info_dto.dart +lib/model/create_profile_image_response_dto.dart +lib/model/create_user_dto.dart +lib/model/curated_locations_response_dto.dart +lib/model/curated_objects_response_dto.dart +lib/model/delete_asset_dto.dart +lib/model/delete_asset_response_dto.dart +lib/model/delete_asset_status.dart +lib/model/device_info_response_dto.dart +lib/model/device_type_enum.dart +lib/model/exif_response_dto.dart +lib/model/login_credential_dto.dart +lib/model/login_response_dto.dart +lib/model/remove_assets_dto.dart +lib/model/search_asset_dto.dart +lib/model/server_info_response_dto.dart +lib/model/server_ping_response.dart +lib/model/server_version_reponse_dto.dart +lib/model/sign_up_dto.dart +lib/model/smart_info_response_dto.dart +lib/model/update_album_dto.dart +lib/model/update_device_info_dto.dart +lib/model/update_user_dto.dart +lib/model/user_count_response_dto.dart +lib/model/user_response_dto.dart +lib/model/validate_access_token_response_dto.dart +pubspec.yaml +test/validate_access_token_response_dto_test.dart diff --git a/mobile/openapi/.openapi-generator/VERSION b/mobile/openapi/.openapi-generator/VERSION new file mode 100644 index 0000000000..6d54bbd775 --- /dev/null +++ b/mobile/openapi/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.0.1 \ No newline at end of file diff --git a/mobile/openapi/.travis.yml b/mobile/openapi/.travis.yml new file mode 100644 index 0000000000..2774ccbba0 --- /dev/null +++ b/mobile/openapi/.travis.yml @@ -0,0 +1,14 @@ +# +# AUTO-GENERATED FILE, DO NOT MODIFY! +# +# https://docs.travis-ci.com/user/languages/dart/ +# +language: dart +dart: +# Install a specific stable release +- "2.12" +install: +- pub get + +script: +- pub run test diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1e9edfc0a0d5265a22bce80429e4d2af7d9460c4 GIT binary patch literal 7409 zcmcIp+m72t5`E`aG%&%AMp%)tUlx$F0c1%t7Gm!RBPl@OHO3U#k{FY0hL^FGiT^!S z)lGJjY|4Jv^@B&OI(6#O)i?5riHxUy7CEaViGmZGtyhk-Jw*ju_<6}PKltPyIfG}v zDn;U#QQ*h%nH_n`bHC&v+n-r^;_UrB5P>zjlNynK7@f*8E2f^Od`y&)$la0$ryG{# z;&&dDQ^%R0WA?=JA`MUr%l#3gud65y>nNs7076t; zvAd`|ReNW3fK)A|$*O%E6(=Z0DHQgf%n#ua3Ud%~EIvo+k%b&e6!J8HiV1cWRnEY& z$avtM{Umlie*EY(n1~lshtNZ}n_?S$rB0^_wh%#u3!+@_4zLvB8oW%yOh6bg6=!kG zb`L99`k}*fjtk=xEFFT4aW;${XCC`S0mXR7q2ojNLDL|IwHD?wJT7<<qfWc06VtNxWzJ zvkJd@5(x;KFPN}7OfDiHas{hu{#~93Xmb?g9JZoiCsI!lt5ohcIu{#b$YXwqQR)rG z#q#ZPyR_8D#*RxAO|A4GtW^O3aYq1<5#KF!z`FQ@#Dh5%?KVS7FH~w9H_eZj`psxzG=thWB;jhoD&>AjMB4B@x!ak+!QZ$Cihp?t?@Ij9#MvjsV99@FbmgmWE0*EUJ2gm5d zl7}a?$Z{#A?Ws!ksUO7`^MWIg4( ztz#zjqxFAj2|XZ zx`~eIF4LvxV&$0xfCT`ntWy&}dqguq^`y!!I*i3JN-w)2h{XxXFOom`aYXxuzEoUx zRU7zZKvxKh;~GV`2?{{9DeO$Tl=76LHH~L2Qp=}A$`&ubpRmykxQ7>KO zfT;tT;mT8bDx%G023HR$A9jWq$vNl-A>@PBF@3n8L-9HnI6GGf+~XTB3P?u_Lct3F zL`8^d_aClzo>EUpKj;cshg~31pV~r(TWB03nO^eoNslt@9LUz*F;QY?dv`{2Y$;oG z;4V3%aj$Do=p}MhM21#FeygrP+b^o>5h8BYw0#uqIYYr*RH;6_DTq1?Az^^}R*#_6 zBeA6}a#|vdtte891Zv3S)ei;8Ww`|3!X@|}yf%#C_RA*h{j9#d!)t;1hG~c(@S7Tk zLEzg14XY&vO6?L=_P~n_4j$Yzuqm}I-n1-M9kIsa9(xP3VMkk^#c~kP<`}Rv#BKcc zobhE^C3PLpO)YDX^Zu1VSHiBmWNQSY+*&vckmk}?Zo_{|*um<}qHEC7bgayIX2ekk z+gsxdOos`Zs_R+FG_rHNHP~v8!-y`6I&=-Y9|+3Gk9EcR zindMP1z8y42LGU6ff#F5N7|q_bgiQUzK*6XG_duF$MCDlvuRF4hOT3|hV8%Bw#-aL z$3~rT9(ZRdmHv8KZA}MtBc=LTBNBMik9%z$k^#1;8oQl--=Zzr!Vb2zYN*7eK}*xI zYOewk-(3apnYP=04mOqc0Csj0L)xH!?cWs)m`dm}h`0teWBk<7d`po#w)&m|9|y-{ VoO0vwwDRaek_LV|e*>+Z)V2Ts literal 0 HcmV?d00001 diff --git a/mobile/openapi/analysis_options.yaml b/mobile/openapi/analysis_options.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mobile/openapi/doc/AddAssetsDto.md b/mobile/openapi/doc/AddAssetsDto.md new file mode 100644 index 0000000000000000000000000000000000000000..b74211d6b7c6177b40e61b3d79abf20142747601 GIT binary patch literal 437 zcma)2!Ab)$5WVLs2KG=JNP1ThRP2%2^X9!b zGp_;)=uL2XB8#K0t*ui+P7RX~RRvoz7Wfrm?n5xcn>nHA``((!!cBo9!{UP9Uffp2 zu@Dw_g*rPOVs1uun8e;p2p{nB9?I8X8lnsA$meQLoEhrsW$>y}>aSWzY03>7o21NS zDYq{1{WC}BPY*Mc(bnS8oJR&G7|17vw(Epw+sAxj0e2-37_+qNZnR!Kt?OpD)|0I) p4d)3xo3gSxQdDy|X$jxz`mp(@Hb@hkDr|t*6p(SV)XRh zlk^H8k4}4Aj%08sD`Q?GhFCKRQ53KtZGg`RQyaYQ-}DJZ*L6k*7IyR$7-m=e{%W?! zhM6#1X6od$52+d0ViY?)B7DHhyU$;Zu@Ba>C6}rlabl>Om%y7!slRF=WjyPE(;;gU zrA!JbH#YLafrE9Y#|cVkWALai0|OKERrPjN tj;1iwUq*DS^WN%6K}`w8Iee?D{raEUl$SzhJXyy-EWQT*8a@|7d;$>Xi8lZM literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/AdminSignupResponseDto.md b/mobile/openapi/doc/AdminSignupResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..ff9890de5daf8339ef6116ce95f06c4481cbfede GIT binary patch literal 541 zcma)3!Ab)$5WVLs0(+z|Z?YDwV%52XH}B1x z@TQU!z?*=zBMW2QSciUxgFl5Wq(DAF%Opfq$wx9)@-xUuDTC=>%mAY6I%^^ebb%tv z*|~pv_FWY3BADG3>EyJDnT(8(#M(HJZ}RCil&6K%M+}VQGg=d8g}$iVyg*w2g@saR zmC7=ub{x?U6UaC-;0%t}$RozY<#X2rqxqfhBNztT@XrQfzrW7`N+!7~!OIa79}YKl zz1S|BcD1aXtt|B?2cAs{L?0>YDf%>*pVQ6e;h(el5E{GJxHPwEoJ$;bIhOx^Gq%&% zXR^XH`%Nk2bR@nkVEGAgik5yo(@RtjlX)g z)VjXD_PvB#!7OG8Cr#D9qoj0D3Nkczk=3%gVMxx0gOqp)B?%2Z9^6sF8F7%+v8*G? zr_3im$iZlI3LJlBH0AKPPt$P{#5`b;)z&{i(;wUAXY0aoJm2r8Ddp*ii6uGCrJb+M zo%s!*dxFlHhf8jO5XU(^A2|-`h?dTS_!{_+9mxqCO7eM1lNEw9B(Eb^Fesqr#S-6E+i)%7P@1u+jv0ghVND=}JwTN&#rrC&oaF ztvv)XcOLLRIFVXT7W=uGtF$Z0VgZGMPrIYu!4hjhoPysjdb_npgsO%#jaih^eF(TR zmo3H__#X-iu(d4sG$8u>-@REJJ}clm9WpxEKOsF9O@iAD0$#-QCknBj5HEv3PlCSz z0a&y{ZbYjEeR^I>$b$h-3+1b_TH z$vt_n2z;c7gr;CY98a5?yQVYl3PCaW1h=IiSjZy*(}Qtr3Yyj!n*Hx%DmDzT9*ux2 z4AGfHB!Cjyd*xAg$WQc-2;JaO?pEmdr(WXC08EqAvX0>wtZH;0n?7!N7Wl8A6vfra~* zP=*Ezb1>D~P=o{l45@j{LVp=sm$vJ6hW+mTQ6D!`*{FILyZDrPzR)9S!_oj2I0SAR zwdpuJ;1F@BqPX*C3fsKk!jO%5CEdEX_ylPPW)GajmzQ>msK-XSzLXXUDqHUe5#9h%`bRhu`eIIkg=bc}#nYFbrA zeAA=%Q+MGyLT~Z;TN)0p$mX8yICB6t+6#}*#4A*F3R>3@3)q!D0cb0KJbhrVK-ksp z_HtY`w7zd+(4h~4ia7UF3dZKtur?QxD`T)9zoa_W3B`p%xbMFE z?rZK6juwoxI%`lto#pkRZMNVo8V@r<#*&0DQ7TSn;2nosS-r~>u>F3oq$8#qi;no{ zia%Txr%^Q$Mvsx&DQ#RxI?~|TS~kGH`0mrjYX-h@+LA^SqBT-rq`ecsUX-TyVkU%E zLIj!+ujI5jbwP+j20skA#b8>-H;DFz>MIA5K;<+<=oEJ6wmBH7>)d;w;dcHk9sHO% zAl5ynm)x5N=R33zVyQhnMWi>SXCcb*4VE=(5`H&)g@U7OYvP2w3Uei1lx27Z8GZfL z?X2Dk--7p)Sy8-%6^ZRuajAG6ESKdt%ck3TK3mO$K%%f}8!#l}$kQXbv|BDe;lI`S ZX7SJ3uxcq7Wl?d-7Q$nlsMa@IaSVq?es z;@olT^XJc=6AQ~9jiXqwT3WuBT4|9LQmDNpRm*Az0lVxSCCrIAi+SJ(|0`!AVm7JT zmUY6#F!Io(TNe;fWjjW8p`k)zE(+)BkLg|F*7d+Z}TEOJ_0;eAf|N zzLjlX*R$7aj=UYI;$gHGA9URXOekKngM$0gXxiH zGC;Dy)c58MsF9(1t$W$Xk@rwCdcmF89rk%VGIow^CsIX_ilGT5B?BeP{o!OZ2pvCI z)Mt??7_81@rj8#@V)%-89KmJabf%8GN+rlt3{7C*GYfgoQ`2XIzquX05dFWBP3Y+>~J0`dOR7!0eA;6WvEs+ltFbD z4O!9}o5705`y*O%NSDGJb3zb@^bLb?T{n})3Bz7TU>Q_Jd!b8YBe(OvE z`V<@nJ`Y8c@CJ79NRW#4@$RD#W7Pr)_UkoexVGEG>TbI(PZDXZUi)-**}G{U9(FG- zZu)0`bWdx$Z)ffQrrLi$30*SFz72hM$nJOuuNCyAK!=*7o%V1VO=4;HgHnm&tZIFi z-Lq!1$sX4H0*jl(dsw%A=mZQOvl~B5gcG`){m$gF!RXbYp%lzc16AoV#cvh72^ASl z(Y$w(SCUtYc*^cAh9A{H%oEHQ4C-BJ8k$|?zi*|nG5kQ(l`(YwfnmBaA$5F{RBKA1 z*8T2s`E|yO@eo=;Kn|3>cw^tt@*ewz{}s~gKPYxU{(kPn&IsStII*l#JmKZ~(=q+0 zQ2>TNPb0yT)wlp^6}WGXZSc?=M{qjufZW7M1WL7}krC+@YO4_`HBZBWcQ3Fwd;pHo(RZDk+G(S| z&ot$__ME|eh1Xk)*TvZetZFWj<8!f4!oYI4teG!6)|zoxYw1Pcuht(8cZ&l_z*sDK z0%spv#3f_e`mWG*35Z@1T?;#0z^mG}ux$HEJAu06dvBOWzx+gQ=;#4tSW@T;Q@0jV zG4I4reeZ^=O^)QaDX|BIQbZPx0WJJ~R4eryXo`5krJ+F-1q8v$jPxjzcuD5GoX4&k zthMFyBG4L>C8@D(t7)=r#de8EEf%Gdlh&kpN$Xuja?jud*NnKhoTJCJlM;>Wl zqG{gSOU^mBDhQOyg%v#ML5le(z-kne<~TpO06dk4TLM$2QFrL+h{7nACGm+R2vi}y zECscM`5__^BN>yZkZCKCNfE--5r}~*msr3rSHQ73C))s=Wo%i55KU|2DPa34 zo@U#Urlo#w+v*Fy(ZiHiv`Zui(K0gfa9;*OGhu;-RUos4AhS`DHSb3o!lhY5PodJw zq+3;BEMgm_R`Xe68MP`)G}0^iR#{&0t*y=YS)OmrJH`5Ot=z^_$o5lw3pp>6aFrX? zR|D+s5GyU$XNV$b#ab0wRcJNm5*x#+)Fq!2sSK+?!>M^` zg;HxqDP$oV(%Cbt(gIf|MdVPa1o%A6k7Gy~tz^+Hb8u`+avVWAIH5#>w2vfB97lSN za0W<&e#4GBEDD2Zde&1VVQ*y;cIG9NUy?e^Pagt2?vCq+NK<=H0p+u7R58)rFuce;CeLjy7Os%nteG!6 z)|%l^Yw1Nmpw=HPx4Afw#43v=L?s`&L-OMCOkUG%5$*b7e!4L$tTLHj>21FMRVMQi z?z(ndPA~nw2vn|en6;~ZujF)Ezcvowmh6}Ao?Oc7nQVq7Uc&wC{(zuw?6&@+cvGk literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/AssetFileUploadResponseDto.md b/mobile/openapi/doc/AssetFileUploadResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..1b4f599e17eebf28f02d6d86b6589db21d9d930d GIT binary patch literal 418 zcma)2!Ab)$5WUY=4D6vcknLShD_fypOR0J)g$Cc@tbcvZbMJQX+Y91HFWSjoncS>=U)Z09A#LnL~LcoYWXhe>NwAuIsFc0&WZ( z1!ouj_Ts%L{zWjmE7Hkn8*?*?qXZi>5`N(Gdnl!))JGS@vCnA3k`?;uW%3GX{TBwM zoK-5zlzNir{AmIi1MK(rIROgex}?X!B{>{!>UyzTHtl*@kG8Vhzm4R@l#BF{qn@&- dIet$!+toj3^G|5HhF$OEEu`_g~5z;tmOMiSva!NKP>vj<= z^XAPPX+9>JA_N<;_2i&$XE_7kLyi+X;?{*JryWKjQSfP)U zSYN#2*RP6KMYRwvJ{4+JS|xENM#$>UdeFc1*PpTc=8(IDk&%K}cf`VI2A_Zft7-ma zD`QxcjS=G7nEI@eY7q|Jn9b!op!IGlJUJ4Et_B(NzzM^?8b^KFe+RSVLnTiTXh?JbEYv z9n0Vj$(C-8`Cs5Mo_hj>P6pC_LkD!0_XBs!u%ntsI=v8kFu$eOC(v8B*Hj12_M6i9 z>}6JmQV)ND6TOe{k44pb{aKbpGwIv1Ub@H3o6Tk0UfnNd^V@}lO&oOx4}ojBnjNL4 gu34Vyx9Hhw`Jb_}bPWU-2?M{AI3_$vK2=Kn0cXWM4*&oF literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/AssetTypeEnum.md b/mobile/openapi/doc/AssetTypeEnum.md new file mode 100644 index 0000000000000000000000000000000000000000..8d514b090eb3253bef67ccd3555b1c554333ecdf GIT binary patch literal 379 zcma)1v2Fq}4Bh<|mTmxsgdL`^fYA(}=7XT`dux(_^O2L_^o$U2Qt;ZDV=3cnV^wuX^XC#)Nr>-`DN#^`EyTucBZhyC6R#?uNh2X8>>m Di63=7kgUCo9~HPHV2#LO!X_OM5i={aV9&o{NlIOwtsMS*myV zw7v$*J=QvGkM;)TbvY~abmU7X0PF;kCLpDrqtQwlJf%ly!2;*&O4II$bkO?`VETb? zb$-yBvs)o~4)nCwLK@3uitr8W#XlGw*T|h!f-RaGkQm0}hqd?17I*Lm1Nu}p3N&SI z=~&xX-0#t`Kc@>JZBU(~3w#91_jCgRj%vjfU^xAdqE9^djmm;hc`oF7-;3y^W$g&Q zz@OyM>8&!~Meu?be2W$f!`M0fMUE`*wEJqMH_-B2Su}^`Ii2d%0i$Q%MU(RQ9w=pZ zA$4iHTTuFs?w#4{o#$xFLV~56F_-84$>I~gH;tUSN|SqNRfyj`kC4lhVeK7X-lD*) zLU$^DgFN$bf`}ZMC~1k2A{VVNJfb(&c}-8_Ukp)#N4YZ6wpY$BFJBR5Ii|5nUtI-* z5j<=;M$3h(Ug`7}8_n>tR&R9bGyuD^eDtBQz{D?*uRMX6)jD$Z1f`3se79kz42 zGWp273nKYm(+pMZTSqwUdiA2f|DI479cT0WpDx*b^HhZXs+~L4MZ7QYdH1qv_NMy$ z{agj@4gM=i+25oS(59yMBkD?XfA^Ux_Xk?ZwNujC`@gcEp@+jNyJ^;)_D^;G!~R&% zdIab>7W|baO|5k~JDmZM?5$XrG?;lNeuXrn6tzJ z>L6K$x15@BqQxW^#7glRZMW%v{n}bxS#W}rU|1<0j$%XDhj}O%{y1CBuan7Sx>#H< zXWviHT~lrBe^uH4wv?$G4XHy5YT;{?SkPQWTu)R9i&#~olBMdYx6f%Vdq_Q9pGvM-${jVEAS6vU|Iy*(PkP6x0RR91 literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/CheckDuplicateAssetDto.md b/mobile/openapi/doc/CheckDuplicateAssetDto.md new file mode 100644 index 0000000000000000000000000000000000000000..259cdfc81cfaaaaf86e5bf0b998b120b44b11560 GIT binary patch literal 457 zcma)2!Ab)$5WVLs2KG=J$o8(M)$O5ROHps7upybYp}U!oOa;M@H`x}gf@m(`&Aj(! z=2buey$-G($YNhV9MBxwQ|O)1j7>_&Et3#c1&^dH@Bv|DLeS=@_b9rqvpTYHL!iho zxzukjzO&+82$Q=)9i7%OHzPYtVx%mr*%xOT+ lx-74D6xA40n!=a5Sug&n&7mpuHjwlD&Eh=p$MC5T;sdRL!{B-&v`mR)ODV8V)CdDq6+W@CC`ZCcfx+}A^GeWloi$Ow zje(=!>{7qI_%BLEA4t242JLi6@nbP29n;lG02u43T-L-V22Q>7psj~Vi{XG4EEz^ry1*Z*?)$8)6RuQ#~QuZ${yhz9^Y)dO7xA>mNl86w#0$R&Q1uPsVw_Vi!_z- z3$aR?20Be2*lBYdMbH88t#qiKHMdV5-AefhTx-wQC;H|SuW0N|b~RKA4|3=jn4lr! z8LGOD^}I*^r;Dhn+c4c|y@}g^F7#5@7kR$8Ul!$NnfEGD)C@fumT_9yfm}B3#n0g@ ayV$M%m>af~p;4B!;cphFf42AFh6@nbv7?R$%Q@8X`SWDS<3xRsE+L~$WIx}$7%*%4|xLQ`5RT!LUYR4WetC&G?AYY`kha7&@%ia1) VZOFbrs~s837Z%gNKf`k×qqgMa` literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/CreateProfileImageResponseDto.md b/mobile/openapi/doc/CreateProfileImageResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..3323e7df43a7ae7af9f643fe8cfdd1b7bcb814ea GIT binary patch literal 465 zcma)2O-lnY5WVMD4D6vcknLSh)$L)ymZf?tg$>QL4c%lyGCc_Xc$2l@DuU(`-sF8` zUIi4;>tJg~28a47qGs$O9qoXv(RbEJ5TI(k9k{Qf^{hi2Ag=lzpLxTcC~EQtJ;~$ q(BbOPvo60^M~Z6fDb3+az3ke5YIAD}y$NJJf3x@u{1HAELVN&x4v;zk literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/CreateUserDto.md b/mobile/openapi/doc/CreateUserDto.md new file mode 100644 index 0000000000000000000000000000000000000000..c7b8b4ca2834c308de892f50a785cc96ca7453c1 GIT binary patch literal 505 zcma)2O-lnY5WVMD4D6vcko2yn>h>VmQv5iT!iLSX1~-|IjELZmH(3i-q1w3&Z{C|X z$!mZj1{0n3WO2|p2@T^@#?&$i(KK*J#sWVhi~@+Je=&O$UDsKYSU4XkF-#8g%Y)sl zT2{g2s!B(vbt+_HhgqzQNBDrp_gGttxlb;#qfpR_xG2<7mEjRm>OU-`L~q<6Wl>7` zz{!RE2nsG_G&#RfvZwf+OPT8vPYDL|7cVxO>yi>M!Lo)5gUh_#UT8hLU(DO(LVMd- q>R&wuHucwPM@fzO<|%w{&)2tS?v}I_1{=u*{$cTT@YnIF5aJU=42AFh6@nbP2AtlvQ?~R_XiMmJD}@+aYt0(V*y^BTKYn&vST_ipFXHKa z)&r>k^5~SeRZj+w)zc{`MzbWX*piDIj6M}?n1m<_Sducp2ZR9!uk@SRqv*QMsKCPZ zo&v+<+P}XJpXGoIOdc{iOlm_i16zz@t$Kt{cscuA8jLzvpW~BS6DLC6ywto=QvRhy zSz1$;2`kHI4%Y1lk#usj<@1;qFxn4n6(7b3gYQ;j9V7B$G~xn{`C_|$OiKhNSmg`A z(MI0w?y73Gnb*x~UiGFhq)$CM9&RFEDac{{aSGqR>vr+azv&nfo$+KH{}^!z{1rYG GLVN*h+o#6> literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/CuratedObjectsResponseDto.md b/mobile/openapi/doc/CuratedObjectsResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..559f25148fc7565f64dad1688f0304c7657dd0a5 GIT binary patch literal 550 zcma)(K}!QM5QXpg6$5*y4P<-Q)9Ut6uyv{4N}+Kxw$W}TBvV20$D3>mR$-~Rgg5WY z8!}S>d34&_vLl20^6n5cqj`9K#g^j|qbC=!W)h+(;GVPr-XZibc-_9~EsD)%V{~9) zJ5PaObgEyUq9-{%1EZUa_DM}hW?+j^EOdwP1y3J7Up2-ySkIPRQVZflsI!xbXG*C{ zT1aV)lnImak%M*HUL>6y>c2VU28{L-8_l~R!r;4Yv5FCSHrV6{4f$fVx=mvQCYa|9 zz}`k)udm8-vYb}+d|Gyp^g7tuk-@2MaYDu>CFGV#h^m4uX#@OX#MG7rZU?ZJnHkrzyu@t#L)G<5M8&-k5-*T7%HmqLh7HEM== literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/DeleteAssetResponseDto.md b/mobile/openapi/doc/DeleteAssetResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..4cd44e030eae0a1313236c4e70af7bea20eff83a GIT binary patch literal 479 zcma)2!D_=W488j+1Ua-ZIK6MDjPx*AOKEotftXtD%o5w!>S3^tpPU7jZHLVW%#+@G zLazWH(CJ{S(Hk791_vapQ$jjo3f?7b$O};v@Z_}tdO?_&5cK&~Us3dZZ*(MK$KWHu zYVP07zMJfw39I`|otzGlhmkBMvDYKQJ3POI{56=)(FU^KG1_}#hFUysE|5}xVIgHA zO}8W^Q(Gw)=bh$3xBk_vMpH7Miw&%q#8RFpT6dj5o`|-6;0S;`>~eH4*hF2ouBy#( xTQ|EcyA;NsZzDQh#V4tYk7^1pmGGsm50C%Ua+C_43En#TX7Rc3$MRAL@d3DRm1qC} literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/DeleteAssetStatus.md b/mobile/openapi/doc/DeleteAssetStatus.md new file mode 100644 index 0000000000000000000000000000000000000000..4616d0ae06ff1d7d6672cade52a607c24791d78b GIT binary patch literal 383 zcma)1v1$V`4Bhn=f*aBdcJ_3-av2Iu2u-#SICEB8)3ar4WeDWsV_ySJyR=%wlb+tw zYmg%a6P@;KY1AE!B-G|ivJ)ex4A3Y zsIrwXd#uz+X`9M2iK7e~;|VXgKgZfS%0qHd9EXB7EQO)&stoUtQh#B9!{Mp?iNd(9 z(QtHGj>iYB7q6?fTd%aYjpgCwDU_teSG{pkQ^GvQ@9TE^{LkBxS5dH$U63CVUxvTT H=Kyd8@AY=P literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/DeviceInfoApi.md b/mobile/openapi/doc/DeviceInfoApi.md new file mode 100644 index 0000000000000000000000000000000000000000..d1c032e65af69bab4fc3eea749cd851f0552f66b GIT binary patch literal 3247 zcmeHJU2oeq6n*EfxPS*(79bhzW3mNsZEp$M#DU{H3|Ww{(zVT2CRK`Rn>G3GJCvMS zR#dD7I-nRTz_59FcrNePk$Yrv)-qSI|4jJ3O87)C%_vtw^m_El$dufgX>@)5 z6^q;3+f-U7)FwC9QGaVc*isEWl?U_>JyeKMmeJ+>q#$XTEN9ZG_e{=E=%Jwyr|i}y zrF%MFEY5*=S90O7oN$p?l{;m$h?)=fn#-uY7FS`ISeA}=A%)>}uv;O(c$4;OP+G;U z5cdE~VR$~fScD-BYkTB3VE3eDp8h*&4SGU4T8xi7SdnMAe0p1Pg!mupG3O#Ty5I@2 z)`kXc13&|Tr-uSgKgvy>eN)YbLt4xxGa4JcRI3s-tIG0%Q9etuM6Im)8$~qwfL>Qt zfiq)SFg=Uu5?>e2D!rnGdCMAmDKBN2IplntDb_Atu^WL0KUGo@{d(+NUVHExM?t?P z?hoPu*d*bisMv!3pR>#P&1gKHUR>NPW-q5_{lVd9|Icgxzn41k_oEMMm8@ySnl0*v zp2LM=kKW$!&Xl&|{iB8$`*<`M&?k!Hm>#}!0AF^AIK)zEnGwd~M(M&yoiP2@=mVrM z0IWuJ)E(e)tMAbbI6}TcPj`G<3uo`>lOQ}bujK;O0#)-GaobyG+Z|RkIjZl1ng+Lh zAQTy82pBnS0Q%F930KpNIS%Y57H+8*IXM{UOk^b7x`ebpaoeer#MF0I_KV_OJu zhF=J5ahLnAhKLZBXU1{yZEAv3duk&(<`r4SY~K?!q9e^-dpgWepuxSrR6VP6cBR$i zaBtzoW|xP?`0vYDW5bixJbr>ck0_YxQMwM|csQSqCZ|)ce6?|VVo!J1EYm8F6d_Qk zfWw?dKAT~9fS$;+?a&V26cFYLIGgLUhkdBudjpNfOaPTUddG9VBfo5 zYLapTzm+;fOJgH6W5hbm4SK6+Qk-jYfrEi8M6uEx$anep=F?Y0Y=iY=(XqEeVn%WH7(DZe;xAt) zWsOoyW5dy4vBR(##!O`gaI4ffctYl`@04n$mq^)}oO8$YK0;W0DnC%L?r-KL->*!CSb m(rKN=9z!u4F6Qz(yIL>)nagJ^z!{Ic#|w$E;E&?D5aI(!7`MFu literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/DeviceTypeEnum.md b/mobile/openapi/doc/DeviceTypeEnum.md new file mode 100644 index 0000000000000000000000000000000000000000..ab8f99acbec1b6c99143fcccd6c4390156db2bfa GIT binary patch literal 380 zcma)1v1$V`4Bhn=f*Xht(U?aOAKO}C3zswf^ F@C5+Ub!-3t literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/ExifResponseDto.md b/mobile/openapi/doc/ExifResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..0e96bdcbe96d90a76768efcc6af33a66eca444f6 GIT binary patch literal 1263 zcmb7^O>5&Y5Qgvi6@nbv2<+Z>Pb~>F(59s=^bi73t+6xI(imwB&9XnflIraCgRQ;3 z*dsslj`Yr;nWPS!?y$6Epe>g#Sl>bD$OTw12{D_=E7C^t9%M#jr>ke(fY|MJMtc@$ zJMt_~hV>tV;8_w)z{$^qW~Eyne|km;Vy!L6-}2$mrPl&N<*{QV7o#oIHYKo!p+5;=y#(ZV#AL`zvC+E9s6-bvDsY2n?W+k+#A_q;O+LbEYI$i x=ZouQoQ0X8s<+2kD{AsZzEi?n{>?tWT}{l5H%tM}bcBvSNW2yNRD3Rkcm*s@i+un9 literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/LoginCredentialDto.md b/mobile/openapi/doc/LoginCredentialDto.md new file mode 100644 index 0000000000000000000000000000000000000000..bf8eb3d21e11c0cb73f863ee1efba9425bb8cc0d GIT binary patch literal 445 zcma)2!Ab)$5WVLs2KG=J$o8(M>h>VCrKq=3*wBpY;3gB25fS`&leJ(KLFY2Od2ik% zuK|u2Y^2t+!?9iSK;b5#!yshGPFSg?fi+tP@`Ny{All=z9Z>as?`#r9K5`P6o#vM( z&qejEg4tD-PENa2$RvbWZLLRmhljUVTZj3WC<<{XXvotmR$8OCG-_Rx zTHi~eFifD}LPneOD<}6mulD=v@<|YeO$`P{%5pedwC&<@x_;*Y<;wO iR>z@bWtskBT?L~7`#(O@t+oXuId_y z)_4S$=aJ9zR}RJ84J=j+DMt#7&X<=&P zFDw-|J=_hAwKca!ydhq$|Emqu;HlLwjdIDF=pDnYDSsu2&E`2ygk+TSG()mA#M|vd zS&mkdaW$WmR%aUPlLf~r{cy3zP;|3IL;00GUQT=F#>pzcX^+P7H;YTbAH|14h)+Yt B=|}(o literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/RemoveAssetsDto.md b/mobile/openapi/doc/RemoveAssetsDto.md new file mode 100644 index 0000000000000000000000000000000000000000..d2ab8473247b414a3c4469c626ba01556cdeaaf2 GIT binary patch literal 440 zcma)2!Ab)$6ujpv9_*nuknLSXsB8~~wv_5^S=g|7t-(!RNL~fOk2hHhRxg@Mn90n% zys3Z!dL3--$lzEX(9s(%Q$kJ+lMqz}8`1{&1!0mv(8H@fq3HYG=*YslK#^f~;cqX2 zixOG{v%4allJ+q_Mz)y5PCJATczzG%YA_Aa2Daog+7V}kx;mM>LQ4IGg_OGNur^7` zES7R(6W>2^wEpxkQJHNG9`$)-V1j{sVraWgh_-#qD;9EFI)O1;yY8m07l+ld*{*75 rDnrBBp=Vvbtd11bR8E@1w|lu?|I?eRQs_+}>-k5-*T7%H=R$~29}S5I literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/SearchAssetDto.md b/mobile/openapi/doc/SearchAssetDto.md new file mode 100644 index 0000000000000000000000000000000000000000..1eadd2a0c18e5c0059c8a99e2b22ae204a874725 GIT binary patch literal 414 zcma)&F>3=c425_73c(F&48H8`bmcM>nh=_7A#mobB&M-tY;`E~$Jf3Fns#Zum`{Fs z@>7ExDVXS5&z3IjmP{JYUCt!EFhJAbGaHL?CY%%)&G>3g1j8^`lLVZPoCIgr`u){! zQ9Y|*_E4ph(VI_r literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/ServerInfoApi.md b/mobile/openapi/doc/ServerInfoApi.md new file mode 100644 index 0000000000000000000000000000000000000000..7d2840f8ca31214077dd68a4d76008ab6c25a68f GIT binary patch literal 2790 zcmeHIQEwA541UkAuvDbBCtdr#iAjZ4Fo{i7DZGq{Mt9yWaF>K69UF}Qj@`TU(v~77 zv1t!GsSmL~e|BttX$Oo&Ev*{x?>)LJbjSL_jI0u((}8zJ<`4>m(exOs%)ZJ?6pO_o zmo5lZS>pn98~Z^+HDoFVbPpLS#HcLc!|d3DbO;Vh8PpX*Fpw}b5#j_xVRE>IxAXaw z5Z{*Q10BccGpB4&MvFB5$QK{e?Xx&bl1mKjeUjw7*PiHhgDUS3!mn^#^RdTX@DF|~gK*XvU>jl2VsYgyUyUSk#)O3M;xnxB>SLFx?QWt5c~-s>L<5!?;@ z6hL1c;LtD2K+rgHuvORLD!@9WL$3}Xs$Y=FkQLCwe#2M;x|8b+R~rN03)F-vOKwiv z0jecE+DLH#FYuG@GHboC+=Z!hvcf=v6!SuPK%HAdB|vV_gEj#)Vh&Q@kS-eNpl;v{ z!_sL8Hx>o&PMbUG{^qD72W=*ef=d`JNfu81AiaNp>{<$qWrn+AXk1TG59`t%R1!^=$5F-3AX;7QxSo}%z$9izU4$ zJ)swIG@yl1X@?Tpv;unpyV3_N!5P#Ja*vEf5r04-xqk-U6S)!1NgRRIbuESUOmzmW z=d+=HJB(d~(Sb0#3)Fs6VF|Qefnyud0e|O@uO@ssxW+2u6>7>h=!ubBJt|&hN%B|D z;#ewos?-~l;w6rWAID1jKB8p6w{8;7E<&~9shuQk9UY?p+ko~I4O$Y<)Hjj~>kN41 ztN-4+N3wt d&G~nCR;>QnOBN@fr9nFFj}f1OBk?(7>;p)*z_9=T literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/ServerPingResponse.md b/mobile/openapi/doc/ServerPingResponse.md new file mode 100644 index 0000000000000000000000000000000000000000..97a79869fcc15fbeb1c1fd2767d759465bfd0c36 GIT binary patch literal 422 zcma)&K}!QM5QXpaD+cyZ8_4#qr%HPeY*|`w%fg1uScAKnFo{CJA8)c2tX?#i@J-&l z$-D|VkT=2A6I&YVBgs2S^g7tuk-@&c#rTTx9%HiP*BHnrY?y?oDwvZtzjFiF z(XoGdG@camA{bp2>EN`AnT%{PiKTW3?{N1PN@*~4(FV@TGg=a7g*th;d4iPs3kxZo zex@j8R!V6-55EiwWBo5o(A@1G&JH~uY;VpmVzargJ!bIar0>1j=W9v-n*2WBFJJ@d2NYm1Y0{ literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/SignUpDto.md b/mobile/openapi/doc/SignUpDto.md new file mode 100644 index 0000000000000000000000000000000000000000..6ca6759a9397629b953895dd0e96a68bfcad0958 GIT binary patch literal 501 zcma)2!Ab)$5WVLs2KG=J$o8(M%Jv|%rL=k~g$vh1|k+DO)!`?rIhDnI3f+cAKe6lc-5Onvfdn?+uH9E3D7br4J&hy)|)vVYR z!Q`$;N2g89WMs5SthKZ74v%l4w8o|`Vqhen(V93b)J5gt1ybrSETptf;~-^LO1a~R zz8^uxnG8CmSBmBuf5e!$Jn@=f(7*9wzrW8Z0TZlBpfF(K!{Mf`XWPZRSuJX3Dns3i ovz~SNwK`H%W4>t$U)%G|!#{U(+RAzp2t9wZ_&oUI_*4k-0UULg&j0`b literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/SmartInfoResponseDto.md b/mobile/openapi/doc/SmartInfoResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..84a273768a127c0dd0eaf78f5b403aa4eae3a0d6 GIT binary patch literal 558 zcmbu6L2JV>42AFh6@nbP29n;lG3aOyg|?J-w-BfotDU#RGPZgc?8i?|1M9|Mh52Hh z{OLU-Wk4RC_O|TEU|a4w9eCjmw8iMjMVv7ym1VFbZGc}8dJep9-t-aGe!n-;p={?V zFpP%!?ch2|&IuUZCA4?i1bGZ>F{-uh5I*4P-KT4Yu?g0*B`0c4EJSf}qolX{K%wZj5y$Y=J>4v>?3LXV S=!_@p_?yK!@JIMqDfJ1n(67e; literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/UpdateAlbumDto.md b/mobile/openapi/doc/UpdateAlbumDto.md new file mode 100644 index 0000000000000000000000000000000000000000..e31c8b41e530fea742aa43bdeecb81b4f1b29321 GIT binary patch literal 444 zcma)2O-lnY5WUZ@7}!H?Altj1R=Y6F@g{4*DuU(`-prf# zW?qFHDVXSL&z6SzAzCBU439_O3IkLX-mtMK&x8{Lqv>DFfuQR;Ym$KTk(1!;RKGmw zEsAC#%&rP`Qrf26OyX#Sjq!xfm5*V5Q07sYYNXv2~j`t0T68PfVM3`&`DLUBwG5)%g?T~j zzljFpGNNyRv8D+e5W+SD4ZR+zZP$6ed8mqVTlv9>#BLgtArUgjj_9(O_L#!2y4crO WYJK*ZGSZ?pbYU?M{4+ddjC}#2R-B|PIKJN= z?MB%$1B1Jac1}%5#lQ$rER+TLDPKN(z9ta20X-wRq!z@9s7_9LJW*A3NsF>XU6u(e z%V!Sg_MJ#NRlJJvox=J0in(ytCx*Eke))w8-^p(i`#OqtemJUWg zw94(F>8Jj`$p_k@ftgK1Y81B@ql?w*KFw4z$$4G@X)yA7eN)$?r|EbypVn3vnzjcE nj#ZvfwWCmV+YpEHw|m^ouIx>_QGwH*&~eY=`@=t<4}}n4PF>S` literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/UserApi.md b/mobile/openapi/doc/UserApi.md new file mode 100644 index 0000000000000000000000000000000000000000..9a6aea5da8b3d0c6144c3464af10510607772c41 GIT binary patch literal 8726 zcmeHMZExa65dQ98u@aRqmN05RCf7)Wa2M1|hzNW*r4e!LCHT&GU3+~!n#+IhGrI=c zn2^wNX_SH##onEnot>9wcE>d)A|5)CZ`0o>;c+|i4Wm|LABE#FIpwT<(PNS0esS)& zadUIyIk7bSStMe~>e=`~CZt6|h@gH*q=wNB0yY}-66VC5#XNAN|CKW-m_?+PVVrR} z6(0M}PKUz_Q2dedMB?>?CvNOV(ifr8EdDT8d~L428&{U)#@vzoA{G-r;Jq0Xx2$V3 z+nhb~YMLO@1U?ZoAjh&U`jAq%?SoLLlX75J?d8}?87tRupSf0rWgRcs$~oHUTg%ue9*P}&qqkb_6xRf67~N*U=DXyVAM zYr*w~oq*PLd6Fn;X8lutG#Ix#o$lr3c-a4~dtPrGF4zA>um3&`UDA*JIrZHsyW=5` z;Ta?@6tamr({L`*SjqjMNkqJ0H5=@n*|yCdcD#Ydm&87di61%v1GbF)Fp*B^a`r2k zlwi#Sfu1K9WBj;x-X44!pR|YV@o9I^J!II~87%Ebz_9m3gtRwgJZJX?!%uU^JOK^( zi_6%LMbe?-jNnjdYEG!}z~DDn)&%fq(VC#%{pNCodN4f?K@|iP3E2*sN7Yph*iZcT zkev9SF_*&j!ik+3mjD#QIL8zAF`P&AuK=tJujfMYWIKcfR|$a)kVzPk}3k$9Uu&%Aj`wn=L7}IxVkz< z!K6ATMRa{_)*37yqIw$miNttGLr=Kr3`?toZeq12;;YF+FA|6yREAu{nece7fdjG-~Qdq(|WB9xvZCUY2|f;M0kj`5v%JxLF!gIlm2V zt_i6h0i3kVs{qWBMHO-`0WmXc4uIJJ0p12QC8|QIE>2K|p%+5T3LL&+xGbINftMeF z>+e=PafI{J^5k#&2`&M9xO~%aseBBWlyS-xHr@&mYypYYc-bu$7UaJQFjV%+j*n!j zji<)7DfZu?9S+b8_cK4_-djEwB4mpFhc!pT5{0=(xHpP$&%6zg~-{EbB{UU9Iegls%)>nrLI)-J<_Oq?T@>GB67-IN?=X@_A_@Soui6V zW{M+kkGcTbbN`@M_so8gUI-OkKa9EV3aZ+kb;6$CD0*ZUJ5Q0Ksh&!b7ikh{<7IVT zn{8vI*IuWD(@&4yU(Q9jt*X8w_%SN7>JM$v?fL&y^AU0<>(*z*T)h>piUHLLk0oTiv`Jx3Et<91N71kB0_N~p= H^(E+U6}%oo literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/UserCountResponseDto.md b/mobile/openapi/doc/UserCountResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..65dab568662a91ad9adec91d523bfeddade14a1d GIT binary patch literal 416 zcma)2u}%Xq4Bh<|mToAGll~EHkD=)M;SK86Tai)Tdbj@JR}#zaVTiRQW*N;mEr}``Y#MhjbC<@ zDr}`(kSQwwV86dDE>IZP)jOVCmc!wyZ5O-MvRkiOZyU?Q=qZS)SLq`sJ^9ab{GMKJ Y@BcYlenV5Rk;@%FBt94ZT0RGW569?$qyPW_ literal 0 HcmV?d00001 diff --git a/mobile/openapi/doc/UserResponseDto.md b/mobile/openapi/doc/UserResponseDto.md new file mode 100644 index 0000000000000000000000000000000000000000..d56b444ee0f8ace3e4811a364bd068db7802ee8b GIT binary patch literal 645 zcma)3!Ab)$5WVLs0(+=k*zH|UtJ_1tmQwXp3XPj-cW{#l$rKU%c$00#R@iDT;mvz_ zlbM%Ea$uFi(vXI(dlkyVc6GK;y`>9G&X7 zC&NiHO@h%~lJ-tZA4$&$K`fL3`6-`2T)JuqjYr2wHlhV_R1{|?7SB*o{DnoHqt5dv z%kwApX!kvcI5J=qc0=S2ec<%DAwj3E^Nk0^per6SaGrM9!OX-ZE)};!y3o?0(+{n( zNf*L9`R|cliH-vt2f|QkgZ5-}L5lVIKISEv&1 | grep -v 'To https' diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart new file mode 100644 index 0000000000000000000000000000000000000000..9d632532f1fa541c9e9fb3925ee43c4db03b45d1 GIT binary patch literal 2673 zcma);ZEw>s5Xax=Qy2*;9qJm`E7Wa>iFRnxx=P!4frPMJ=h~P$wy~YE3i$4vlk{mz z8Yaz+?cbfxfBP;;k|;^Y)tATV>F4p?`2Ol~JR;XOx8pt;P08K#kxZteo9nNy5DVnT zlv#gyHh6ayg|1jAZGq0UrE?)+ND5UrNGV9jLHYisFLI-;Z?s_Y7h5}O3QsNkEQCdK z#d2&7v-s=5I&^MbvpLtwd8RxSDK04H0&HpS8W)p7fumBZ1!6v9ioSnKdB&`ZA}MB; z*)`Dy6f8b`LIVSLFJFwDF+NoWSa;hxO~ z5X#FWG;IJX!jjNzfCzQ+B*t(lmjXrDOzg%pkOu6_ad_fjywK{|RN=FPVG|VPtPQ{g zm$0S*Y_QiY<6-l3#+=}7mK6ca;u-Bb*=;moa#rLtoKh|0cCQI@4t&eh!Iqkt6jp)* z6>6@V0S6Y3`v`yufN}3atHaK93Nm47O3MfxO}##hen$isM}S-?>M->RgDK+-W&pH$ zD~0N$O3gwB)51v2(r&+{#sB&BYXcNikvDCZXTX;<#bv`ygG!vX-892pQE&^T=TeFm z)oW(|^pBIq>QXO}V_J90Rl6^*Mw{QlVw5Qsrz+!Ix6|4}&wc>+-8|kutft2mK47|p zJ%F(a`@%IH@xG|tF3a>gdS&2A%sa)AdVyJjx|gp7Cflx5#dU>cA=j%~y4J*WSh9F_ zT;Kib(Y0vJ8le{3?KbIKb6AxemU2XoR)kPEFw5KYa5=tBs_NDiUt!YDysDY~E6{TP zYw}o%lyh7c__pti4MpjMR!oX&3!H;<)axY#{a%*TC@Q}*2*oQeauL2s$R+t5bGM3z zB<{z361(addnESN^EV$lMi_7b`xE~?-#S9+#zymO!t4?(dW-Sgc!r+@bD@~TrI5JR zHfJ7}!}-y+-;3AycRHDzrfGb#HL$R_70x4i4~y}W>Dl;u7`W&S-kf}ReMmmQA&C7( nZo6Kot?ZJ literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/api/album_api.dart b/mobile/openapi/lib/api/album_api.dart new file mode 100644 index 0000000000000000000000000000000000000000..b0d7f5b8caa1375c3f3fce3ee8bb9b870cbf8aa7 GIT binary patch literal 14449 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 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart new file mode 100644 index 0000000000000000000000000000000000000000..5fda658eae875d19e799b240b13aa153489df7d8 GIT binary patch literal 5132 zcmeHKQFGcx5Ps)ZZ1NDGgCX_wp+jO*V&lX!si8J*ro%AG87<~gai`p!OlZdcy?b|( zkb>-_Ee~yn2V?29``zB|xBE$*POZ~{{`Gix@Op4Dxa^Myr*L-ucF=;;AzTc{@bmEW z{Oq?UWM(aY3YjuThwbkVYxb7k3aK!dOBKv{gk4BPqA?6G!X*mR%I^6xmdaFmuxQ2B zI*1b!DEydkMec$v={IJI{u@-9z?H6cpGlz&6DHsxZOG>wm2=mXn?+op4k9UT$;>R6 z2!4DX%oa@PT8*|dAm<=s6fEX#`g>8U)n*aX8v2P@So|3?T#A0o;d2ePR~r+0bP{ng z?EwM!%V`t3n>#ovT=#kq;+!Rs$sK7^fH@Z|f_!b`;huIV^&?c2_N4|U;LUhEg8FE9 zHHHqONINWCa&e%kJO@c!w?|Sga54@-p-Gg`KqrF9CF+<`h|@Oh>TvU{cl}Db{vAxN z2=6*RHF63T+Fe2;t6J0ApCyEiFEi9*aFKY&cib%A7!#k1xoqTE8>m(jm|lySORWti zrB@eXg>x#Ht91|}mBb%M(~eoLVrCYUT2-&gfXiAHq{}lAV#Xp5HoWxNU*|fBgBrXlyI`sHp>X zc>ww~#Ix}_Mi)T}IH|KZj#8p_K4>ZG6p!T;q?O;0sm;Zm{2lwF^M-p4V6)gtuN|T4 z*7dd+vfaI6+&oD9oJ}ovBmh3QJ>9-fG&I@lhQN)TEw20e#!Vf= zmuOv9iW_PMTI0F}@2$3Ui2$^nc~2VBJApk7VXx|^3!{m>5;mm6Q!G#uMyz*2X(l~RZ8g=a53-zGecm<7hBlJn*AZp7w}L@{Ir z$Mi<}7er_4izCD7&t|B#Ln}4JF26Fey?o4nUl9Hqv;Y6~9gz~}*OYbzx)c~sUjtc< t3kHochaQ;46^>;h`o1Z>eeU1?_wWDZk28Azf1IM}P=4I#>m&NU_9vLot2h7v literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/api/device_info_api.dart b/mobile/openapi/lib/api/device_info_api.dart new file mode 100644 index 0000000000000000000000000000000000000000..c365ac60d2fcfa5b28e686ebc1b3610933650da9 GIT binary patch literal 3841 zcmeHJ-;dfx5Ps*cnB*ZqFNyj6+Otjlu-QnXgl+Osxe=Hf*cI zG&7;X4;fcvF4&sBQ>N(Gpwi@A>3Z`>3T>D$A&)5_UvgB=T-OFmxIi5yQrwf8Suqj5 zd=W+~rgWo0;S9(n$P@)jd51o)8jVJjFs)&P4?Mz4v6O?9!{-K+C9OG$hY6r*tB<0%y214D+YxoQ$mATA4q}T|_#on7rj?bzw|u>)EOaQZb;wK^vIfipUk;W}btu2dTm(wbLOX zjFB4V@2)A8qM|7?D{6phmg?k+TIb8Pfp8MB#Cw_Cy~l`pn@Vlo$aqV>YU+^t^G*Da zq1sMuaW!eb{&jaen=f7+q_#p9quMLp7{$eX0RH#v^W&kiZT+9BI>)XQK;MP{Ioqb_ zGRPgyn=DNe9yxq;-)kwFl#c};D;mSnno{b)6Uri zG%J+c*g-V@7-4EhNu8r0&(G41{90zb*&5V4-FBYHqk{~Q_U#I3C=t-s(jaJ552$tn zN=Ue~x$#$|G5YjY;K!8sjWHn2OS0yI7Shwg^*OPQnO!AdV=rOU*7=u3 zF60RvR<&cWPps4ddpSb%l{6mI&z{lMtKE50>W+{@Pvm`b{6NoZzgst*7^$9|CHxZ( z?5J#c@O-&B0W@pz+;qeCV8tC^-A^M|8Q;`A8B5PH1*?l{FdJS#X#)RKo9Z@=tD7pT z3jbf5R+|Bv`OtLm}w2Sg|Wg8%>k literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/api/server_info_api.dart b/mobile/openapi/lib/api/server_info_api.dart new file mode 100644 index 0000000000000000000000000000000000000000..8af914b1ce638f8117742ce67ec9837ca8280a44 GIT binary patch literal 4598 zcmeHJ-)|E~5PtVxG4OzG>9A?U0}3RrNgRmO#%^MYswg@(zHz+mdH2|z!x)tRy|cUb z%fY3o3J*wruzkBbGdnZiH{ZG6Z}s~yd@~(CelmheWfZ~j7|zC1_;q}I zdh*8uHglFgC&JLdUhk*9mRrgjr47b&ZQ{91aUXJ(TTEh1afyneaxIscHni14q${zu zah6kT@O>@~yDPEe?@So}B{Y_U8@t_orj;e3D3%FdDCZK5ceh(FOQk{^r&`^x87+j0 zpFN3Z3t?=l#n%z&Ip_?P$YhT{&s(k5EEU$m1)2|NPSsowGYOwtu({K@*!ZMpNip zpWtK38Ma*sMv&BsjS?_OxltBqfpB?_HshAzRgZ5w$@v6dlzWG8gH%`gR?^}%QRb@d zbV1m)n)%WJD(LqiGdSmFdK}^exn1FE%kj&4GC>P2-?TT<^d)VJ<(t4L%|sfajIaO1 z8Fx0*mR{;)U3i9Oz0c9CCypzY?|AWBwLHAMdfrH4fg(Xu8(xr+>L!Bld-(abY3y43 zRZ}0>7Y+PP4E%JRq0gWQ=x%qALIFUHWlyRP`Y#+#PCNb_z%?i;o*ka;a?vQR+$x(; zuA6n0dv*HdPBY1#w4-H4YZ!pa)09cNaq5RH_;p=RZe0}xRcdyWq&X%%xI>&fWC<{t zf_QEWRzlL(xE7F*bLf;_Jsxt(?U7Cp4iCW5^1|=*u0?Xrx`CFu1v!D4ZuH7c(dgq0 zGdBp^0!2k4j&GG_wt;{v zj+t|&DUj#UBQ8CNNfVt18|Wx~6sDsFC&*FJ`${W&335NBNaZISI;H6Zmi#j~5wy3m zu^lqcePQRrHXZ#lX9}4W;!%O6QYp+jqC%iwZ@J^U0 m<^Kr&yIb?-erj9WZ}M!Xvpb!=zs@#ExFNC4gMU}o+t%Mz$oVw@ literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/api/user_api.dart b/mobile/openapi/lib/api/user_api.dart new file mode 100644 index 0000000000000000000000000000000000000000..69bd819de89b7a1f62a6085d774cea9ae1aa0e5f GIT binary patch literal 11424 zcmeHNZEq7f5dNNDF~+w3m3mykpK?>A$6 zUz{dspgE~U`Or4&*yHicGtbO;+vzkq9oXxhpFDYW`0ns*@BHupj*j0Pw&36d-kqGo z+mnOiqrZQ_X*BcCh)KQuto_@w20e&80%&Qq5mJNE4-5qWdDk4aHD!X_l+Go5dpG zsvoDx6;9J5mh}I4+8>OVRE-91ry(6en(>5XqK!Y#8;!;wW=cUrRZp;8`@Fjg5g)QV*5yLn0AMH*7DG8!tuWtq(9EY?qKU@}SOTxl z&rcyZJ$Zi)9kjLVWHKe&KM`Yg#RF1bGA+^sCr2Q;&Sj!Nk2qYMah0LDc(0AS8o8x~ zx9o@;{}nFYqMCwCa)h#bsAJ3_EF9FN=|?$=<}V7NJ1~%(X?|+eIVNpB2t9hOb#|N# z)6kP>1h$&M)HE3wtCB_zPg{^lK12^0=k_CxKCov}xNl*wjOh{DR|fOUU`#hpq%k%k zV8CK4?BwzjAD{=bROy##G<7SjQ$FWhPEW~73b%UkSNXVe(R*IgWyD#;Wi|1=mLj=o zf&IJl?YcghT)A|r5p2#5_*)~u&ZikS8aRO$!MN~Ghr~UJm}UX0WR_m0X$&K#js%Y* zS~kSP^12G+3`a|coMGH?r9*?Npaoqi+UR=4L$YzRX;+FNga!t`Y(tXAanrdCysfm% z-A(}Gtc~%ADqP5bRt7!N1wYtfE7nbmxoWd0GCQ~pugzg-+wv^co`t(m;yAOCuI=e9 z_d-EVE74#%0E!mhC! z@`LxS;WoR@9NZjZUD3IJBPNS@J<#dyyqLW`PHkvNM<(b1Heo{F7XIY-Rt-wU`Zm(H z&eeVzaoE`gqKo%tq#E~$OF2||nfVBFB%aS~k6fRK&OpwlSl2w>e#j~fsw_t+vM=XvZNi`BY zZ3sdW#Tx$E(}sMt7;{=OkwPOi;FwS?_(5sSpim^vTrhn8;_}2`{iATVZ z%W<_xFi$=zL?2<~GhJE&CUgibr$&BS=z@~CfuAy@WEG>RDh)@6Ly*2pwZ-Y<$9VJ` zMg@%fns<_P-*QvsoFeiJ9LkK?jyT#=uvq-d_lWL8i`LJ$1^E1!@$swJLm(+ ztSrl7F)$@m=aWhklU5Co3Vau}-p`m>JXo!SRPyebqlfjIt}9zx=Ac@>2SV*(WpGRXb;UK#qQfXJQ(r5UPpzf}0 zLKim`77^f@I&e+Mk}joafk-~Gz+4aqbb|%-RntT}^?dCCn*;Z-HbJMfA}pBXd;60z z$#llwao>02x(*}g?UbUDZs)uhTIaPKs^|#wX0|bYeE4^9KNZ(In9Q)XCF=?#X;7bl9k2(K8i z@Y-DQY_51#yW+99bcw<~tm8tbGf4A9ubvL(mD^op%6$^!0o*U8Zt|zOXjt(jxK(mf zaL<3$doQWDR&X0^_*dyzLYJsnx#%ue_&D3t+?$$v)tY-2inYRf_NzOv>uGo0*X7*| z{}YB&0GJo8dHwOWqPv>x#f>Kts!6!PI;-zHT$5nK?WyJX-xgc7TV$MfpZDrOkkJK?z>T^1xpW8@O*$$R>rXnnG$=t!0ne=!yQkFv literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart new file mode 100644 index 0000000000000000000000000000000000000000..ae695c9698d41409aaa7bf9689375e8c168ba581 GIT binary patch literal 12268 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 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart new file mode 100644 index 0000000000000000000000000000000000000000..325446498a5de516a62bceafd1a5ada48359ad3f GIT binary patch literal 3446 zcmcguYj4{&6#cGWaSIHU47hUJ4?~c|NgsBGY)#U&PKUuT1V*A`wlb-blnW!sf8QY~ z$*!GtSPLvb63e>0_ndQI91NntfL>ltuAclhz8t@MIT?@W?EKBRPopckyqeI()#&`} z??*6mmM;pX?eKZ}?0MuK#jRADXLF^qxhVM|)v`9c$au+@T-tv7ES8nh_Q4aZ{KD2I zt7@BR{<#(!$0b|hZ^bnJTdob9YxD5&TuEb@w3#RnP|O9_{AlrL-jXvp+WpcSd)pUk#kB?#m^3G>3Llal4NoZk!TjYcnG}meg*qnZ6D@H(Igpz4=4&~WR$&WYWPbmA` zDPfWZVy?=P=N5;~lwLAqeT!|MiZzgloP6|t&kN{k&KR!UAwyP?t|bXc1;43pX$f+q zWVueE1LTLDXO{|dsqA=J*>#^{pAJ_Zlrt%nr5jF65_hl3c)injI|sg=3=L_U(RAjO z7jsHHKoW-D2x9|IBKKmKAPt8UFYD5ZxHTiv+}2tKmg$mJNzxJbsoSXNx%A1yarh@~ zidVf}AAkuzNRaynoko1l5DGQds!V7%$n22Dxmm?drH6q+Ft64I<;F|M71Z!Pj$EkF z&4(Lg8N|nzu9eL;tWeVhFU3;01Pm$b9?Wz%#*9u+Dej}}ypNEElods?r-@I2Q}4JD z?gFT50#%LjW;6{wF-xPSIrc+#_2P$z?~_egqU5&8G-zUo>WU?#zboJo zvwR{j4!n~Ndr;S^Ynvy%)T(o%4t{<5EC5P^h`X*_o7%~`;$vAacl>?z&e=>flg3pi z*9Z`%RHG!qeK*bsE5mZ{@x5iXHaoHQ-v4yGmB{%wh>iI6%l{tft68v3+|S(sz4NmI zgAZj>a5u{fx>3a%!;Y>eXHO1X6>n;4!C8T+j8ZB@j?}s_7qhA=-4iD0;GDtx(AMCw9xz=&Z(I zKh)J5^@Z!#%y85k?gHAHzl2>R`wI2onCNr^z-jJDw1W~a)={lwR^zHf0-ZPwCga!MG6 zAKXB=nmfA3lap?*;7zw`aXd6kGn%(|E+mJkTNf^p_vpicryD6-a(4q{0<-YY+U!(r zOAa27|LD_)lOG`0o%N8AK-+Ni4*w6DK@U$f%@IFP!By*DSWnvlJNp6J?OD&defSpJ ze!DnM(U2CqR?dDV&zpU+vmCLdfJc<2FyR8Su|(bAZ99&su|iRK-GcSr?!}=nFgE9$ z$A*KuKxG9c*|<{q;)3ZQrUpWRQEvngC%-^^l|E?~Y`!tG*uef8}XwN;&$88dEl zp1eH|sx4njWiZW^Npmi60EH|pW+@8XqIB_o&$n6`cM`$GZ&un=7cMpUSa3sd$+q;? z%+S}Nu|#g{Y4}P>>zH&Y&q$Ex9F2$DlVXBPw5d>XO>J(&Wcuz+y4o;fgMj25s2r3= z$uv*s83w^hFl%9=`6nzVh1&o{sl!o~_Lb<0Ij$u9382#$xed38Q1xL9e(NL6DOHcC z0dg*xfCp!|T*JO~`0v31kWc1MC2WMQ`IsqYi&0(w)b5&sEX>pb@mhFVU`)`o;0Tv`L{e<}hl^RR+Tk zH6-rTB7COXq~EDN>%nMDV?Hhp;q2gXR4@IFgqr&WsX|vYg4VQ5xuS|{G7_F*T{qUrSput1Jg{n7QI7Vlb{;oa`@KQuPa((N-0^ul zvxWtYD4qL)y3pi~IG-(6X+x|kYj$2HB^q^iqp}hm2Z1jqU<)9 zn(D1bam(T`6An%{$SP!pSu17tj|@dy1786^)D5_4ssrHvB$J3XI|i{(66WLpO%D5( z+yRcw^T6{TR_B17L2946k?`e8vrL{l-SJ`CGkr3IH8zu1ao#$F{+#~$_lm1=@E!aB DTKBOp literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/auth/http_basic_auth.dart b/mobile/openapi/lib/auth/http_basic_auth.dart new file mode 100644 index 0000000000000000000000000000000000000000..81abd7185f2d425792f43334b0cabe513d5704ab GIT binary patch literal 740 zcmZuu?`zvI5dH4I;s&8kz;m`zM(Hn_5qB%J$+~49#u!Gk<>bJT)Vp&_7W}_Ya_TOF z7$ob?@7>dT9*?8(7$)Br^S7U}S@w0Z$fj^}dzU3Jox^OtfcyFM_U6YM4-3faQjuI9 zr|*xWP~y^9(s^Ymudvo<(3pmGnd@4wwUMN+aqTS0L58ZI)rND|NKX2(LGpB?)_!-2 z{981!A5MIjURcATjO18)L99?yN9TiLH5$!%ZOzidWTi}g`Ytb4ia3hAoPe#sI&GB0 z)UWd>Dr&_HpGDk9#aK)lS%G)6^T!bCpj#+GYhW9}|JdPHR>-Ljg@P%#gg8#1>p4I8 z*;3i{+L6}+_$GvA2|fJ$U#mxVuxtCIiM(U~{mEhp%DH;8uwO!AJiJ4ei-$(jrfXcm zRbR|1;hR#De}xpwsx|Cqa)}qv)Arf02LZlL*f5p z2u9T$3=aB9XM>&`2Y2Dw7ylWDL@nW;F3|W_Aa}{bVEbFK{&13NQ`k}u8>vqF^Pbvh N)V@q^{rom+qrXgY{0IO5 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/auth/http_bearer_auth.dart b/mobile/openapi/lib/auth/http_bearer_auth.dart new file mode 100644 index 0000000000000000000000000000000000000000..213f3483b8fb85dab831e269dc86a3964e8a90b5 GIT binary patch literal 1217 zcmah|QE$^Q5Pr|ExJhW~q|(Bk5UkaTn!=>59Z((+LXk(L5Q$;j1|hJGUtGDA(3jhIT@^w3QbyQwPZKD5-NT3D$Q1cOcZfA3wi;% zL?ueuRT8doc?P{A}xQs0=qFVE0~Iuhq5QAQ5+q_Zuy95M^0ZbA9i}%XDs~u~(lQQdR#L z!o%Oa>B5~TubK{bcXsbC7gxc*Z8p zF8ZD?Kyl$=>|G4N!eHIf(8xXgZ{z+=edY(VuApImst+fNJDR1QG*fMpb!n@84WW|X eV}C0&$=~&*h<|WZadYF~MUYY4O6c3Wjs5`EGLt9( literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/auth/oauth.dart b/mobile/openapi/lib/auth/oauth.dart new file mode 100644 index 0000000000000000000000000000000000000000..e9b87cffb052a80d95da7c41de72ce5509fdc9d8 GIT binary patch literal 593 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 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/album_response_dto.dart b/mobile/openapi/lib/model/album_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..80ee699e49747b35572f1431a0a56f397fc4c2fc GIT binary patch literal 5357 zcmds5ZExE+4F2945+qv3Z0R##G)uNaPIQ^CuQtga=ez!kr&{2Dk%nJPs~Nd z67Py1Qfnj~2E=C|ZM3k3hT$-pEWXJE$z)ejAH#5ju@X5Gkwt_~9Cm*GbWflcqIT+N4p#PnQ9b_D}n^_xKcK z9KH4h1FtX^W>_m1Dz|VWEv$rD{Z^zB(PtRTVnsD7mx(k+=pEG<%*;v}WmqgyX+NkQ z!!K^lBir66@oaCED7LpA1Tx=d8JxmP7<3c*ux-l$_s}{c*xq!|RqHJf{SCE+dx)6B zy>X)mPE|TQ!g3XuH$3t{#TC(pMAJx_t*^Bt#T@BQvBW&bqRLP`=UTe(A=6`E=c8`de6V= z*&y;1+~{IJVLSUFvaK|*)WzD{n(`91`6UYUXbzYn#bzRA;jRV262njB8q2WG8jvGXk?ni#>JQD;Gi!a9H>x92vC z^b>(Cv3V&X*Q*Igbo@LwTP<-w5f=YQSc?sHeJgOh;e~RB`7U>cW*x71Sy&0L1}x*z zql(+JkgR0#U8aDuBl)gUWp}WUy8)YMOgZ<}j{=$~%40;G=4x zYSwkRUeS>IsCFsYtLv8)^HKd$a!;SFHml;JwmP+X#}y>5%C+p!Kv}`)Yh^v0T6G)% z6~)?^+Rt#reUC)r@<6%37FAQ|&FYq1@f^UID(7QXoN(QMXVg8@!gD56eJ;4ZfbrNp zZd{PjDeUYaFlXMZu z4;)Q_-uud6c{*QjZ0*)lIf}gL;+9GZiH0+Y#%|NyvSF(-_vwm%K$Qp`uidw*BDZ=n z8FgXwMVh+0UDI^fQvV{sJ*2&sEhwxgKsA8y#8&EQz*bOJ`W9YvolD--a}lCYSdT%gRITi29KGylnVFV@>bau zF46s=A1#_$c;UoQ%TFyh>HKXG|Gt9u&%2c$ZIYaHarOX5$MhiH{qR~F463|LE5)PFIsPKL7Tq}usD8Y H!@Kl1f4jA? literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/asset_file_upload_response_dto.dart b/mobile/openapi/lib/model/asset_file_upload_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..40583b6ce5ef6422597482ad5746544b3d0be480 GIT binary patch literal 3506 zcmds3QE%He5PtWsxD-XK0E)cksYqtG!Qu=}+ZagG0)t@)v_#u%Wl|%l8b<2>zB^Ks zmBx05ybPFu#1?hOyZi3@j_mb1y&hbCz8{_bIlLX-UEU9`;QHN%VGLIzxE%WfBj4a>gOxylh^6IQZk76#B=IKo7bS4UZ4wbA7&r@FTC6_i1&th3BZFhRGV#U@b zEi0R9{&yubx=Xgi|0UD-ZMinMxi-7qGbN2>(xxKEfMO=NcJ5|putG>~(n85On%M=D z>FeLpY{9hYbTFI+H3L<0$x4yn-$kdB70ej8G=|%2tnhPLD3;%GQ(|0xWfeSh0D1_; z9k-R11{g@bg7JQj2?&>R#uIeiZx_+d009)R3t z>h8yLEb{(Kkx#k~t&gny-8;sRuaR&f8-P_eMoHYsLq8Le6_9?#HR!_=^M}oaN5Pwn zbywuw30hJ^eCvo3{w=L+sAn* z<7-1|s~vBZ!36Q$QJn_Ek)mzXkd0Cc`M#%gF@+(^7Ey%BZ;>Li+Lho+ zLcQqX3}fit)ngRU5ndb+#7i`oyr5*4!Wb%!*aSD9Mz={9k)~Mw(~3AICphXmRy`vc z3YumlQL^TOd>WAkT!^jRU5OLg>ypJ#UQ)jVpk98c%yb*IPi4Ov~x<7%z8z|7?8o_-e4j=Qu4ePm9D>f}~M8b>} zhR2)GbFU4}O;uPLLkHrFCmtaZ8f{LkrQG2780a|>2B|+>mbS$xqIy&ZQ7fV=nTF^D zgMaC})jy#Bgy?gF&Jh;V>fl!QHEr6P6YHt8gyp%P4lPfQJWWo@<53@KFRQ4)D8P3P z;g{RRP29RkIB=SIasWdj$j(lMq&*rmYg%W*fephk(QXRnaGI{zQ0(5ezqqQ`p~DG132s7N&+D|?GI88!_3alONASkp(G8Qnp9DeO zq0!;{QQOhAQ6B<;k2FpkUdrEbR>CfDi4;fIx&56v^$zG?pQd>Jqa)pLXYz+}NXQrMvZCba0;i1C_{bm;e9( literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..cd1e83c5f2dcbc5a712ab7507dcadc4ed115884e GIT binary patch literal 8545 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 DFn_BP literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/asset_type_enum.dart b/mobile/openapi/lib/model/asset_type_enum.dart new file mode 100644 index 0000000000000000000000000000000000000000..bf1d3ba193ab0df65bc603ba85ad8644a7fbe5da GIT binary patch literal 2851 zcmai0ZExE)5dQ98aRG)#0X%u@r^21<8nDuOXyc)E1`LKF&=M7Ml}L@GVi-mK`|e0d zwiKle5SudZ{hoVHlgVf@fyMo1{p*M5F8Z?AL`%3`eTv4gT*KXZ1E1H+)$Ko5C`OX+ zQ>N|gb@==15j~0rsWgu>rQ=NGdIurP+(&8g&(tP1!(0+?X>EALw{%YlL9N8ogQ6!rl2 zz@gOY5#vf>`^fT&??Tjb0e9_%abOR^gih!NoqFpVv{1>E>-;3-1GrlXkYv51f){(P z$!aDf%i*ilt)w&aUOjTHF$5H;FRk*2;09*zyiq6GUjJ#7>|bJutw`$FSDzOjBA7kP zL0`L@Ys>(kqw)7@8LbC3+|7W-;(oar)^Ili8tcu+=*yrFcS9O=T|Wuq8qM+?N@a}L z=iC>QDt|jRS#{!$+Q9{k@ND??Vnn{DhSa2QOgegx{%7Y73)gGt-y@=1BIF6aMg^hA6)fwn_9Pgn|1E=WWc5+(srM=jd{o`vI< zgww?La0ph_(>!)YnM8*1-QGb1layHodzO47>7Idbh}C)ka=p!1VwFDq1ZjlDxQ%x{ zpAC^*&pdLTpU43|d`YyXC|{(YgMOfN9i!2~Gif{UOE(AxDSF#f$H8E?CE*sW zO2u~hPgr3GkSx^k4l%PM{90Wh<73&0<%f-8Kc%v;=iob&f~k^(8?L?UOv<_{eT4Xeado z!Z=TWt28~qk+MOHi@#QYTNSq~79Fc}u^avTVX`=8xEOKFH7{^WrAORj#dFU+>S^0Y^v;!Y~fHF?et*ThHXup z7dF=XuR>^amu!tcbEfgP<=S9yZMxkPC5>g$#v;XpVkx+G?xr(YCL}j;rsN%(*%g!V zZ@CW5&R{6;Hli6nQ2RX8E}>++J7(4?TbZ!f?ZF zp``%^l5b$vZ9NC!N=Bb~=Yj4#(*BSvt$AwZEL7ocD{Y{e}Om% z*NYdfcyHG}7G`P}3th&ObZ^&DyO0*{xP=unt9L5pSglJ~6P5TWdBTlhdQ1F5%B;8% z#+d~zw4y4?G5pfpo}~MoC+L;1Tjl=<9AN$KUe8+ThR<7{VTr~htu`j|0)DbI^7$s+=Qlf-kI54VjTqO?mvJ1Y#ss&h7;Oe7+ zz_O}!0NIBr&Dq zwXz&uo;jNmLn40PLPDy>Q$YEq+C?ht78ZP~5cvfs{g6tY5SWj{ol-bFS3*;UzYWSw zo?|E>QM@wF3>JucM`anPp_p_s?Lip&M<`j zO?hVkI>NI9llB}p=g+9nwJ?SbMl6D>Pt)tDk0?_f|F4m4Mow@vbZq*`OsE}n84wLi z4#=n3NUE#6+T9>vb-}B}U2wc7K)K(2zMu}%8rn1B$Y+|G>GF1)^O9hrfUpeV@z5V1 z`MZTm@^L2V4~9S?*n5|!+PR|2x(SI|49cQ1W- zTVyEmuwO?dMFeMiFJ=#1C7Z7`BXB`6c9 z4bh!UtIPzQ-}GG{9?*`6=y!#V0Un~kg1u&xv}*>YiJ>D!EKR+#G(0`pq_$K;3;WW` zDmq+H!FC4KUoMJM>9R2>R<#x#z>b*J*`2Cjj{<2_YILoq3=fZ=Bts4><)3jIVOO|B(?xIH?v9*zWArU=U3^}k6FvPY zCQ__}Da5Wrqc$QpGU((?2tPK_7^BIi?`1~?hKMfi4cjBH;?oC!ZzbD^XK1L5t+8(`B{gqWN;x$Cy J__#$m`x~Lp%KZQU literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart b/mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..5c5453fd8e36851102c3357bbba3c6e78ba2c2b0 GIT binary patch literal 3642 zcmds4ZExE)5dQ98aVd&g0TgA;ry+^m28}Z`ZDSzK1`LKF&@vsfl}U}H>Kdv4`|kK6 zD~oFv-G>1)5L+Ve{hoWs93OX%kKy9?yW!DqgPXza#ogc%uHL>Mq;NTeo8cXN7+$`; z`ttzANb>cRY1=!=UY>O5RoqLZc|KD*pNW#6LM>~!7RTphTB_iD)h}St%8RR zz%iENmfKoO0}Lcz!f3bF7=#PG;2Em#mQBb5Kr9g0xiFb9gRjEa6t~ug7%IR-sS?7n zTX?O$aILX4aTAl%%qk7NUNW3~;svH`=Ug8`uMfV21uGM>pT?bU-~1C=-V-Lgc;Vvo z@-aa4_1&O^v~bTY%$b?LQB#g#I13H72vU+4+!&@;1PSc&oEu@BSQ3)WqQ0*~JME8F z3waM#`G>@1+TC65Q83$k9v;SBSI!TtI`?K$n640|qUfXLYojFE^*x#i$x6sS;TrVd ziTK0%%$LZEjC41+=#Ei5JbMxC(B$Q4Wlb&q7cc`Rr{E{ifUpL>u(IX`W?C(R<&j%L zWX@2aOF$lB&f^qhU6#J9OJN*fTOF!2pW!^YQPg#T@G?08FUQHWqR4TG=W0qF^9y@W z^vXr#iW-XSBaIct;1U-n0TRQD>j1v?q4Z89E10oQnvi&y&`4n#18Hi_ai*BXFAfV< z0p?;E&Zk>w#_ls;85-80c%v+bm#0o9)ROSowNMaW@F^fMQtVZ&jfwA#wam&I|^Kdj}y5WGAj|(!h^Gi}Ajra501dE9Oap z!EY)>VnLP4mBoBf#R;a+y$x&(s0h#Y49s&B8b70e7s41ykWdBJABQ(t7pqPF{HNu0 zL`rbbbLjd>k2v^5w*=3C3Hj73(PQ)O9Z4U@Vz8)e3vDz8 zNpPf=1Z5J95l}h&rte^H4?koCt!p%pU{bBlZg?R;s_lTLf>O-bbn4lorRlps3`?;* z>PgVDijquX8BZZTIV+BtL&K1e8ot?6OTx_7uEoH8RfvyTC&?b8@qlSJ2xHpWAJ@p9 z@89N-4_DP)tw_+-VA7cWv(MH=3IsV^bjyl>@vxU2~ng0zFr2hdJkKqn4 zEEFZ-E}r{zV$;n9?=y=UwN#|_Thy{%DxN4gE&^dB50HkVZQ&&F8? G3GZ)4uBGAt literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/create_album_dto.dart b/mobile/openapi/lib/model/create_album_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..202a474be7775b772bad7592f1c0a055224377c1 GIT binary patch literal 4056 zcmds4ZExE)5dQ98aVdgY#uRzYr@@__28}x;ZDSx!Hw=a$&=MW9l|_}LY8a{i`|e0l zmMwS9_GQ2V#Fogr<8yD%(fxj>--pYO_mi{V#<%0U%lq*ayt#fi4&iD7x08E#Ke@Vo z^XCbQk>snCDKk8eUYvL6RXpTU@pvIsybu{5K$(}Cr!mj?isvTu?_yO*WsYjFY|S=0 zE=m(C{-qQO)pNGO&w?rZHe6{muJm#BMCRJC+{7Zq1I0pcW!3f3!!nU`9cMCsKrypq zdHl=Iak6AecRF~S0l5HK@SGJQ!vB|@PLeUL;kDw-@XKsouC9!P#|}Ur-M{0eRJjHQ zIbXwcUup)zLQ8mr;`@66Spe`3gINk4F*1LPE{As0u4%-^(?5h+e$-rDry7P}U54qb zA%+;aY42`(=mEe7qeT7ywSBwzPH6M1+ZjRYn6`*N!tT71U%65ka;HZN-8*?vc09gP+`1 z3twBrL)`7O09N@m(mez==-vH_o7O0Xr}bml4$$zlg((D&0adFY&AJxFdsb966hgYe zwiU?;jJ(k@M-;Be7b0gF#Gh~ti?=2IxP9qz?L?-kTf^(lP@#T16l>wM;;q#~HSd8l zUgzAkBek=`L26rSo`2Bi!15Fv)oBn$!&jD-T*E@iRb^>vmyjA3D9|P5m|_fPA>?I- zT|_Oj7TPjKqr3{m7f4vP@l}~%pXr!7Fh=S#B8ibDmolYta|;J-b72wKq*O_A$b5zn zOkiSA=szWZyOG9@KA8QSW3;t9w> zG_R=Ks+{NTQ)qDb^OPNOU{uNd;x_2*5?<}^`xDgab^i{i4t}SOm@Mu$1^2@zpm>GQ z?EBt{n{5Q4)zAYV1>}j=4z@JLj|N+PI$5{?d&GA%FdU@OYZmq>=UpGw z;}?F2q9OS24zH54wHT}LQ$lObs#w5|`2D<$E+9j1-(YRsU=#z-2QCMXc9uUVxn}bW z=Nv3prg^yaW$x%eab0GHxctEObmj_#_=F7lY~ZA}Ig5nG4j-=y(`X#(=%6wz*VL7y z>QAIdyG>WD!vkuIs7tIT2)a?UJ4&m%qiK*Dnp}ujnmXcZXu7Bp`P12ZF|M?Xq`(F~ zOat&w)`}e(RRWMp>v?w&D{6+LS@1xe*F>F-p?P4$&(3DR@yx%Cw@u8@<>Px?P9*NG z_5-MI;W5@NF4Dc;U2HHjyWHWb+W726^d`9WzC1mP)=Xq`-N?xgvWY>OIz`KeLq}|$ ztb7vv_g+d-weA{p8efSWZH%P5#;)=1jZ4%u^y^?h17}VMb*f>Hmmd_SA4y~jOXx@? z9Ba!FiPOf>nh4=(oryM@)pGQp|4q$MM(Sua>L#rFao3sQe^5+)xW~H>p`^O;R|p#Z xbP2+%(W=C)IS0JmX~a*>!`vv|)s6eD=at`*ZcPWHM@vzS`!Y>UyiOwp{RMfl421vy literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/create_device_info_dto.dart b/mobile/openapi/lib/model/create_device_info_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..1c2cf46e16be0bc5a46cca7fb3a4caf7975befa1 GIT binary patch literal 4439 zcmds4TW{Mo6n^)wI0=ec!CYtU)8NiYyCxmnwaL(C0|p~7Xo-&5%A!Y735+!Vec$25 zQetIWp9ai8Vv9VNkLU6o>hQ34cu22*yEuRL^Xb{?$JZC9C-ipm{&YYm=X7>{K_AXf zCU5`PgBVM`Dui+47sKZ-diUD4=FLr&Yb8#;o7{b)W#2>iAl5$yk1((SHLCmw1@_MB3J?NwEL0^*ntjF^ zjcG^x_U>g!d=QzMeuJui3618lTX0ge#F!#Z&24Ye17u{)9_ znCA8Y&bFY}PFev=W#BMz*TaAN#loXBclwU2tKCg|4nW_Aq?9 zWBLQU6lP9C;7fEpFr{?@*K$?FqVOWaTv)eL9$6R^oKrO6X=Q7})Fqs!x0P6fL@OsS z#Qef`Ons3X%fuEH&%kr^eB@=kH$(}&+jo>NW&TALgnI&GSB1g=nahnv*yoZ7HVu!5 z=3cNloXfK~zAeVpdJR+HUV~c(Pp1_~WG}+Nu*P)CwvEBi65d}JE+CwWe3@kc-$aVV zqAG`Ln9d6oFF2t7$9Op*DAImc*FV9|W%Lc-f5b#@d z)9}jYC z2hXjKPSRA*!P8sXi}IvLQvYhL(m zSJ30${$F*(S=z>}RZD|y%QnqB_zw!~5U;0lTT5x=^RVVi3$8N$6>u*3N@jtdyGn$C i*_4sLr`@|RG^swi^(@xDIft1NS1_pjOZUqjO8zfRew9)H literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/create_profile_image_response_dto.dart b/mobile/openapi/lib/model/create_profile_image_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..fc7321cbb8a3cbcdb1019ca9ba810f0287fd6dc5 GIT binary patch literal 3954 zcmds4ZExE)5dQ98aVdgY!4!GTry-f$7L79`ZQ~-%1`LKF&=PI4l|_}LY8a{i`|e0e zR%}lx@@2pR#Fof=zvmv(y-VX2H-VZO~>iWYlgv$}!j_%>(=<@pN z?;{i=$u}uerhgW_I_uD@nCDXQcqUam6B$2;GA}hxW1jIP&rMjpi)A5|IjF(16(udU(nQOyx6N?lbikaZbs_TQ!GLdr~XEL9om|3tq z{_WQ|SumwL9du_v&OjDCXN8FH|Dw}LGNv`WQ=A$8NlD7`ddcSej_U$l^GhS)p##vv zYTR*Cs$2tuoUdTqsx$#%15bE_+O47i*$1eq17;y~gk@IOX?S3#XhivmnFYygPB0aK z&qj%S4ofZ_|Q zogY8k6Nsm(`0}Mq#)>dr64gz?>UcFuiyuQ;uO9TGm*0l;xD)ceb^I!*j%`-IFYgHx+uC6ly;B1 z0=vOj(cML-r0l>r)3mZ7kUoEsfaNJTs?i{fhVLvZxrUjNOK*5=mvH!IDA1)*9b=Uy zA>?I-04FrBgtjx;Kt-YW3<<+>jVcoy0UgtD#7I3xBr&qoLZ*Zgx6nS%uWdlK3%)BZ z(5fXETVTVanZf9)bpY2a-zzH;B}|*Wt_5z~i8U~bVXjNXkv5pY4-QLK0H$IXGLT)U zndK{AYoa5scq8`mm zGKYC3G=KQ5k&=o6O^F;CF5^sNfVg+$!$9`s$^IOfI8@z@Lg|4u7kH*g> z@?a0Sxsoe3&G2x58Ot;eHx;Vf^eL{(%n(<$yBbei18ADbK7ofK#DfgzJrWw{Yq%^- z1GY3zf%hX9cAkj{qc^A@U`h>)Z284wvuU5EixP}jnmQJ0Xu1{< zPg5w58sfE#q#T2)i04o}Su2*`eYcRRfAf2P0~2 zj_qRKHSgbOm-yCQiqE0Gzs5)kxJWl*cay+2ZX1oOYF)KDqBp^HmD;mgZmmWZCyfR^ z(RwW5*eP1->O0b@h@|Rx_5G;-s9Mh>)&O6LxopX#yDF}PS>O^4AYHRt`*Z4a(1bt4 z@!EpA^y7m}Bm54ah>-WhP!33&buSyV}?hLQTe?~X6B zWP3}#4OoFD;_>eI+}lHDFz5{i@aD_i`23I2&FJ>c-RKIg-+dg#a5aXT@f~~`U%k8j z>jc$E^Yxr5lU^h*E_$>S_oY-kTS%2HM8PkiE^E!_887&XmnIHtv8tppM=e;fW*ePV zwaFC!UJHfhC0pUoiYfeUxYFoc>Eq_PEVW^!$;2EF6br$XHP=TED@4h4R><-m)y$HW z*>AsQ`I0H!>)~+*HOIC>l|G(_@@`7m%Zxv^Tf6-iB83_+PfB{B-%T29H4Gc=Y zhDk?h3c>=*d4k%Vt%&RaLbPC(LMMF1L=hj^sPO!?RP$qdHZ3=pq+^>1+=RXD8HNJz z*(g!ogFUM)2xqiqjqkQF__VUu@&{K6^NyZpQWlgs#f(&trqOuzmFJjGv*hXw(jmC| z}UD;bDIsf?ec?;9eC(&2K=<&e0Nova7bsj=qiQqNGE(Bgepn{ z_uRme>E&BF=NOy?+KdBDLCJHjHB%evVrUVcu4D$_+ zv&e^Fme1W89`B2q4j-k$IKr^IABk~NHbSdZ5gHF)pq*oSzt&gvl z0~U&fSRxCH<`mHcIbdf{8GRM4N|Gb<9Mm&%6hzF2s7Ix)6-Vl027ho^u?i5nVaS?R z(6Hk-zR^UX-ta~m4lgdPj!zv8Sl>XtsaydMZILKv}p8AS+wmnl;7 zrj)KE#ET|YF^2xFzX1Ua;aPV`Pf<2~M)+1jYl??@2Ht-j-z0rZF1dIAT8B)?2u@Pl zJ5M_RnTc*R>NQ{S^6(J4P{MJ3B8;ke5N@NhUAg^!Cz`0SwzGUm4e%#T!gz7FskrZ+ zVMK|FAA_yY_e!qWtiWpt7Oc=b-i8izld8C`3qy?Cbv>E7;?bm& zKWa2WfLE=Z?nG#uz0s;Nt(K-)i@aaCrglF zRinvOt7qb75O*UP{|AjUfIEDR5K8KqaH-REOwSU0tgLF>nsLDAmPY(EH_Tm+yQZ;k mu*UcU*)~Kle6ZZ)AG7W{>o-@FU+?Z5FKBKNTI0hQiRy22%>mK? literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/curated_locations_response_dto.dart b/mobile/openapi/lib/model/curated_locations_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..5117aa99082e5129dfe184a4aa9f5b4ff4e8bbb9 GIT binary patch literal 4538 zcmds4TW{Mo6n^)wI4Oc!!CYm{(~wSSgT@`4rty$w0|p}y7>SPA%A!hA3A{A_edmy( zF0rZ%c^R+(u|)FRFW;f|_ecBtaQw&R{MB#M^XbL$<@5wT%+970IGMxw{1QIRPi7zf z+Ceg+e3vq1qJ!}D!HAaPCYOrGOR3_e$oL_Yd8v6C^Ng=~ZYF*$)`e82mxE<@Y@_3% zG_m5}N}-TFXKVZ|n8K&wN~3V4``HtjYr}FAixd@#rQpi4>t1D<$hnR)ncpCpS+P9+ z<>xqAF{MW%RA)dgK^8n`g$VKg&1jTlOlx>wDrR_kCKF}^YP{gOzz07u67EL;`xuK0 zZc3GFV36}WSPZaSfv`p=JVf3B!hmE0__%;s2_1@b(zBZgv+3JcT#LW?@64?FPE&p> z5`L^TH?v-`j=-Hg>|s0rJ{u+S8xU!GMe5t@Ms0hyqi*|P=0v`ew_GXACn~#^GNX)O z_@#m<3g*{ec!GH{E3Wn+Izl@r?+MESiq6EJeE;s=Kn!Q^ld4 z6WDA-yJYjE75Hekv3Jr9bF|wr*PqT%o)7cY*c$kW6~8)rkZ6N&B$6XA@>I(l4Rl?( z6gkTv{(^fD!4~=b!y8vwj%1<6A{}2LB{dFpHPi>>f<2;_$A^tKmZVueChYiTX*)ap z8;Z3 z%=EcN%K~d8!3o;t_W@iLR7GG(!h~tFRStmzPf9!bFwJ$TIN~`o_`_k%3P48;!@*!T zYRLb}H=3qI#dt0ahu4RerV1oA_SitO;>lN@0?uNxU8Gn`aLqRo!+*^Qv2&TfqKG~g zcR=FMuY^zppBe$aC{UEhv9B`DG#ZF!JCzwY5Vp|E19%Aj%}TLzr^*w8 zZjGr!QKS~>O_sOX#e)F1HMEB$`a#~#3tBUn7Ms7wrR_)KfaZYvemaEuQzu5a!cF>k za`!Y$V3T6pRU4n=kXC|g@7ObwZDoeGK3b`JBIy{ug=4gk8aX)h5a)C4pPkGjYu$qw z0bCNM+JH%Sx4#o+gx{Bk$FWswle*QLrl0%=1-K8Fc>f|Rsn+~8hAtp<5yR`*y2PVv z4tU+vXg_ryb3?x=FM9{AnLiS54K7Fb7X4^8yU9GZ6SsOtq1ETPKkbggimV=Rw8jq$ HM8SPA%A!Y74ZJk}edq9^ zODr{8Uk0o|V~OW}`wn$Fn(v zd&owduX3hL@;rX_d_=F}rj&}OOR3VODEKi{WuaBraHUbX(*5F@EVW^!NkxtZ#Zqu(#dWW-LX=#mg)DE7&8%3N z{`6y-t(elI5t=g~mmoL1WE&CV{}-cCRxqvMO{JLO`TY75&y2p{dV^;9sgZCu0yx4< zTyRsVQUimM-@;;0;0lCwIO8!&4zfk07a(K=%u483+tcWN>lR)|U)jfguR_&fXwdr)Mm|e7h3ZZ-Kq9c-j z>XMsKiKX?{W$VOFoZW*y=9%>;6v5`Jof}aaxZws?Ot0R^oMSd@H1$-{FqS;yS~In! zVZuzUup_i(i$op=(F6Fwow*2i&zrDzX`MiZ$g`HQok)CB8R;G(TiEg7Xe>mY?Bgi- zdL;YK>q#s5$$sPUq?_twzvFPA&QNDa_0`zA_l#BY!Kjh0RuvZxml5mfJO;stj>Lr-UFWZUqE}6c@-!=G61I^=T4!u|eYu^J zq1NbEWL+dsLC?ZDfa?O^1ePVvm^M4@5IGQ~vSS3(Qdf#2vNMB!IIP(QFbuPO!R<*q?mmBF;1AfqZC4)3FbrVTuOFv_UbB=TeGkl$%+=F7|NhWqw9 zi71nZeuOOtdv4Q{eFr0tN_pUEOE%c@sC2{HvE4zON0oHAd-Tj6k39faOWK#=-lote zno3tAnFoaT0!MYQ0w*t_K!&&2TODX{Nx!o!;{7?!Nid~zQ;-XQ}fsVWRD zh+#8}E7u@|GNhXtn>@qY2+(^hG|v5Wy)mtEqB)Pe4S7qmO02;+HGw0#W=)2%FQ(Z! zL)i#JY4vY|d46Wvx@qDmu$bk!L&lb*OFQiha-&lOFJ&af7lq^KD%5H%(hnbu)eUo+Z91izF81kxx^K0Xax}VyYHrBs6TO11SveEk0y6N z!_qay#Z$En<&Ei0a2>+>t}zDu>@r%~7J Dms7ES literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/delete_asset_dto.dart b/mobile/openapi/lib/model/delete_asset_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..4830dd40c314ef5f29ba158a4acf25bc32dc326a GIT binary patch literal 3344 zcmds3U2oeq6n*!vxG9QQ0UUYFQ{l{RgT)z|wlR=q4})O{v_!{jWzi$48b<2>zH>>* zmK|@)o(3#HEQ!3I=bn4XZnxF#($#Mdqw`;fcf%?WTs6rQ@QL>S(H|0iOl6f7Oqo&tHn|qcT_{H$*yHM{F02!UOH>AjYNn*|>h`F!TouwLxh@_d=H{YE z-u{%Nb75?&1$U10jPz0#VyR;MU$$ClF07?%B;@3kwbETXO;0VNF4Di3t~7-uL4{n= zcwcHl%7>Q97~=bT5nCW~b7f9+fnfTM@GNd0w#-nL(h<^co|#Nr#Fu zN5Q$eD`h>c8fY(#oPpJzNTy6d37ark67&Z+kf4?3I%D+1!T}}U`3O8%6?h@C8aV_L z`M8)g2&Y~r3Yb=qdXYF4Tk7s=5rr}{@e%g0u%(e`0pai?X(5)}1Wur9{6akq=$l+y zZlOx?PCH3&dS0f&j=9x#l+I=PO=g5~#IdVPxiwAYS_AQ^L>p0|i*p9?(A))^LtZHp z8BdF@TP|Tr6=cx+!XiNIom639CP?F=Ev-1TI^K^QEeKi9)YkGA+_}5sRyy5^C%34l{Swpv9Mt5-==(7Fr*YGlCB|7PQ_Po#o8k2__ z)2*s`@!SiKsoG!F70#5hUfe`yyMkBy`}ss$b#zyc7?N$rAxEBnTM2gbj-ovo1j#aI zO}Y)Dvpt>O-ehGfRn!|VR>j4cZkxGk4?lkQ10KAi91d>)`YlhDx=}c)4n6sK{Alz_ zOmoabnu*-X&Ze$^=o2GtnL8dn2e!wPfE+g(8{{%`40L+xp^23R^@oe4YcPtb9#w|* zis{O#wMR}jY=$cK4|+ak`uI!`bfcQPUax4=G{{Wn6k?HOVInj%1FAd~+4mmCm6p?- zR8+&%qx#KT@zb;_Ky#iSV8zqoXcg)}L)JV~8dLtjNImSEfWznC;_VC55B2z~A;;4# zT#S0Oy~8BvNG$TLC>(&O%&vA=HS4;1F@Gs(>s(%5MQ^W?m0gLm-QBJ(HGhw62mZOgTH=N;$Y|(1)rnBwGU1xUw12T2#0ha=fl6g~~t^O{6 wqZ)UHMTxDcBwTwe;^*cO;f&hxCS0m(<&QX+cLg3i`4QzWc>dCT*~0Mo6RsR00ssI2 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/delete_asset_response_dto.dart b/mobile/openapi/lib/model/delete_asset_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..b71336aca868043d92b2fc20331a353f1ec023a0 GIT binary patch literal 3661 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 h^eN!hbG&fsHT{k{~VjB>8%; z{D%BS($1O(1eVk4L6ntTC(sKKw+tp}-*1#lg46}vM)OOUsB|uEc~JVE!Yzl$veDC4 zZttXJvxU;4q#JLKlEKUe^B}E73=pX=y@`kZ1&!ZFqe1j|{m~iSz68i#Wp(iL#SM*L zreM!;agOkbPQ)*-r=J(|X}3y!*`fCL?6+3hgjbTWR|jNiH*!%l@;@sC{tW?Kme?;@7LTw|`O40e!tQrlfmo_JScA zQz1$x2agw_otqA_(p9DJ!T=dE3aX$!8zl5#!2Zn};^#!UYvX6zoj)`dVbfUZ$JT^j zt_O=g-8WuiYrp8JBW_R*eZ>L95TeG}m~>T^&kYjA6cUl=N7$ks(PK!V`HoLk`pW7? z%NtIr-@r6F0v7x~_8yt|hIA|v#sXMGmH?x8%7iBL8&_>jIIF-BndHH3cOZFFPvsz3 z6&h3#E54?tS90M6-HGfAOSdf~odnftqfp>NWZu}r_Zp6SGCjsTnT$ILtK|Uv6$i45 z++LHd`6fcuKb-^Oqys*6y~7x@zO;PEssTPOi!_>0ALL2c`h-3+U_6l5910mdq3S!|Wx9 zc)-)savi4d+rDet--#gl=I*=ISh#To%Y0_7u`$UF#z>z+!7HW}5aK)#c4K~^J!b=p zmw(L(YZ7Y{r~{>EyDrWllEt`T(P7@L++ykE4I(U$mjVk3E3p_9c)D`DrC`{UFt*!0 zZA|`wM~9<4ZHY;Zd%vkU7rSA#V>LlsT+1V=Fgvp5${`2$1v$7&U*K=h+d3=Si@H=gQNi zy|4JSEObQw{8oR_=!f_s5zC0liOoHt9~iZ`l^Y-5AWtS?jt}e@=imkfE~N)Gwiv6$ X258KhAGzY4>$9aXd7`Ix%*Ou%--3o4 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/exif_response_dto.dart b/mobile/openapi/lib/model/exif_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..199c955e93d5753a09069a8136a6f45a46383719 GIT binary patch literal 10335 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 I16aWS0rI_k?EnA( literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/login_credential_dto.dart b/mobile/openapi/lib/model/login_credential_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..b8a23be593df3c2a3bdc39aac520e496e99134ab GIT binary patch literal 3626 zcmds4ZExE)5dQ98aVdgY!4!GTry-f$7L7YJOJg9-1`LKF&=PI2l}VMPY8a{i`|e25 zFO|A3p9U;IY>B-0d!8db81x1Mc>Tx2T>t!V~25!|-$Tt`o`C#R>Rq)sY7+?wRxvjM{z(Ddf zOxuz(5H7@wC#cfikH`{0NFHnED|crl`1Js&8*Te97dD*N1kEww&3~S+!&@egcod2!HqCZEOHe| zg7z8wc%LEwmkqALb4LlkGKXyXo){=uY7gA$W-^8)BX&F-P5<*Ap|E8Uc8WYnDvHeWlJ!6 zBA9{696YZL2y5UAD{F3Gq1CFfHg!vg{{;&4i6T=h#4H9`mk2;Y+gcb$i-VR{nlF%@ zj@Y`+5TYg}@TSOfE3yPF4-P9<0mg3`GT$xi$o+|L42^+C@lII|FD{)-Q$GT6 z-$GXK>=VxcM=QlHa$z?x=NpAppL627RPvk(`K`GNGKYC(#2EZGI3B7BO$muZrsT|E zfVg+kgnx6#?Y@y&iauV`n&5%ZGp@F%fjvUu23yaCfFqPoQphHZO4?!-AED$J>Al&mYZA zU0RxA9rmX)Uriz*8FhyVdc+d~=sghzX)#_^_5f`}gQzjC)&yk|4PL2ae$#h$*fA3l zg4PWxMHu`+m-n-vfz+W)U^2P_fmLT`ebkk8XP>P#iB_Xqv?L1(d8Q?KX-%Fs`R!1U12@O{)? zRNb)&6USE?Ev`<=U!~T<7Pv%AqB~`~6X#wK^_r6tuLGz+*K4wekm^DPn(Ly}h*St_ zofwJX-?0{L9HHlX&i+>zI+}F{IEr^%-Lp3S55P5m2fR=O9EEe4PE@*C;C*9NBgEz$ s@Y-ZBej*g}=h_}a?k;t7sP7v+dUWHfxlQ^bsJ}Z_+|P`w9*(NN0m-O~r2qf` literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/login_response_dto.dart b/mobile/openapi/lib/model/login_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..4eec0475aa32f2857affc2ebf53bbdbd5b48ec25 GIT binary patch literal 5342 zcmds5ZExE)5dQ98aVd;i!Bl7Mry-rv7L7ZkYvUo!1`I|ZFcNLEl|_}LYZz(%`|kK6 z>#a-sX}|)+7LRx2@wwx3C!d^*PEO#}pV#vzzs)XZSFf&T=kVs@-7JRlIb6=K;r;yl z;?3Vjs79JEbEZu4Z2I)sh=yWSO2yNqROwO_{1mq3R`WdN1>f+}#9=Hpl~iWXf)zWq z*J-sisp6lvLZNxdHuzaFh2MrNjf*QiY@W$d8&;ZBxztJ{n~O(;D8%l_;;cu5dSgZY11|08S8qD{i)` z)WD$RJ6Lq}Za`S~GCoCv?rcP>0EALUNv%xuF%F*Iob zyhJ#MCh>^oTXU6?6xoXnTk$`bSr6F@{VLyxL3j09Zj1aiB2DI6@1(NM-yb8k03VDJ z z_BtfA$OR2IXe-r7lG0(&Rtm_HPW{|g=>kovJz3ktM}RcLV_RV;BBVVe??p zYe3>Ur;>4iBJL$mGVX#AhqOt?9))-?HInfV7B>@%!FyKy>W&;kzQ-ygvNJI9t(GNX z;MjU8N>)Jn5oM4-PyO!xxx2njWuYbwU?w+c(inGD9b8t+Jq{^Y@M;}9>Z;XfTD5xA zRSWo9tuW{+b%D0ZU18VAhrsIk=dQ|77_9!#gfmvQ*g9ZIa!)1XhpI5JG6&a;Xb?uj z7glV!hNY4lzt+Nvuy8L?VM3LCfoX9QL%A&qN6I^)Z9Q+WN2T}@TMFBasBMPzK&MnG zQfy9)$WrWM)-oqHae^N0zpy^oO!JxNd<$uZ?nKrzsu}1|kOST#N=ToNR%M!DLGNW| zpVrc=s?zvynvm)HA>`(!$c2)7v8q;`DdnnH5K(bj|Jp6I%k zmdw7(*pXn_hM4k^U zGi1{6#%I{QO%tC(l$A9B2SO9bIo;$sdjSQ?-{2)_QJYJnJKI37l-#k~0(&x8vO@Ft zKHONxY>Mk`VW`&jR$tt>Oeb=rCFx?m!~L)|wuCqp8tH1bsZ4_fh@K+9wA>NJkZIVH zlGshUTbJ~hdP-N`o(3In+`j`u`>WnulK6>^F=ct~c)Vfhu%&uNySI?`Rz}jXq5#z? zgeSX-t%E)cSQ={P@8LzadN3BjRd;f#1&!9Gw@m19ny}-(|BAW|PvWBTt@1>5$^D{m z3iZo>icE?^`Z>Wh;~1Yq1W~H?A%;^L39iFwek7K5p{b2|BQg)f6oFbeLrd|AW9q=Z zA(Y{}6<9Q_Zx9|EheY=lCF$NGcfzbuME8e&mgq*|iBm%>-^E~~it6jy%_)i literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/remove_assets_dto.dart b/mobile/openapi/lib/model/remove_assets_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..1457bac3885228ff60c05e95bd31c2d87f74a58a GIT binary patch literal 3429 zcmds3-*4MC5PtVxaVd&e0Tg-7Q{hZbgT)z=wlR=q4})O{v_wa2Wzr+58b<2>eRrg2 zD~h|dPXiVpmPFp~@4F)hgWg~Omw!A=PJbWYjqfiX##eBC^I;sr)dcP)5Abnvb#wja z5sH!IyMk$(o+WS2dh{tCrP4fGD4i`t#fMPK+VCRd6<=~`D7 zW}5$13ytcME%A57G(IiY290ZTSUp$LSSD>I3UnwIf@`O44mzuZd_*x@GMT;o zCCf{uO|OUUET{#j6_;!!68t~!_40}t1NVHXzVb_BxHVT+!BY=lfcf8ZTWe{6f#hqL zcEn~NTyQy0P`G-Of7< zN^8s#1I}L`?*Wqo}cUAkyR!uo$C^pD7k&n>Wtx*z9c~}-gvI?@#xQ3v-5P#a9`@(pU zsqP0K{Ta%K4=s+t>EXtiuiP2Ca_Zh-=QBIFd) zoW&sP3TZ(cSPSEL*aBju`2rikaZcAc631jjsSI1niaf*aD^)?j_=PTqFI)(&a7`h7 zXrYQ2P2>V2ZecuO9l+PODX$Yra<-%S9+$|YPo08skEN+K$DUype>g1JiW@&9z zCJ21t8$(UkDBdZ{;my#=G{qy{^)2Kj&%f{jaD-9pq7ZfibG}hn@;S$r1F7UGRk3UC zjLczPIguQn20L`MLQ_J5#sSC-28eq{O&CN9ZKEBLA`E_CDH3Z` zsa#3O7gd~K49I*xHz{j))$N1VxH@}95iW%>lo9a;Za+=#l0KG~9QePMn<**5QR?{f zqb{&xX%Z6S8r0=WH#F3O!=^K=u6bD8Mkjm7oBd8k(M~_~*$}8S&$|%wrNOzAC7%}% zy%fZtN${jiz)^JaOmX;ZE|aMyi>(uD;-t)X4c>H&8{VA+Mi)?_$7_V)mWEE~9f~c6 zCpXC-wOX@zh2s+ztTH^_21@wW(cILPrPO#`@p5Hb zEF=0yjb*ha3X^JZNeTN+k89c;1PM{f<$__i0NxLPhDvRVM1M*mVMXD`L`%~bmWCxc ze^%y3%PPt$3WXX%cym_VkZmw%5BzSmXoegFB1G!*rb*K}2f9|mdC;yodi}?eJ8V)9 z$@gX-4LyIy8p7^6lp*bLkuF&N%tUbZ@ZqZ71OOBI65NDjega5mCvkYUVEGq7u}o91 z=;lZ2=RoM~fMa+*Llsqby@OffCk+!FXut|Js#S7h(}LuQtdB-?goF}!$I^n D5JE>9 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/search_asset_dto.dart b/mobile/openapi/lib/model/search_asset_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..d90682750b877f5c228667c40070fc9be5b1884a GIT binary patch literal 3362 zcmds3ZExE)5dQ98aVd&e0Tg-7r^1=t28%N^ZDSzu1`LKF&@vsdl}U}HY8a{i`|e1+ zI$oE38ZZO4CGy_yd5#L6{WhDP|2nyw++WQm*KqUU(3A(NK7>LRhOc6t@eP+Ytlq^YSK1!cVA+=KOq>@s z*8I;xXjGSMgTFb`_;0y3Xk44)>WPxZGHGM6LWg24xOVF1sIyE+ZsJVIM-;OulkwZ1 z;v{9-^m^#df?9*hxn#MB@O#cw z%qk6|Q7~P8;R&WHlD=;+wqfmsj@W?Hs z%%s1l6-VdJPK^`-h~x=3hUpzO7<-m-Ba9OZnk=Krgs(z7-?AcB`G>%z*zeYN-dGyF zZk`u?SG~_H|Jf^tu-YNmL^1}eZjF*?&9`PPB+DTFf@?5>XW~!$OJ5-`GS~gaOMihH z&8rvG4k})nR`%4~e}N%jas{47281>6jb#Nlu-0l*TAI5hgxwki`h?0kX1WML78xRh z;II|Op|RDFT=O-O!SP5J31Y~^gsvEAWknJr15>r42Kj{pB)@YJxYCuS^pRC{#9#s! z7oiNpsn!8}h06jvktkutJ}Y(LAwYb=uqVMD#UBnEmIKCa8IsK{)SUXtcZQf( zD&8r};my#=l(i%H^(`bRPrmXM;1r|S#Y)&6Ecs4h#h0AeCzU*>A|9H%Aaj^kLRiOt zgJjKfG$kZ*JLAk?fVg*3hJmAmYa7%cqtL2&KT^0D!h|Jh5Mc04rAVw)rE*18zNq2^ zL+Ia^qZ3dOUOjZi)%-OsE?!Z98(|D3M5uw=hv{9^$Lf-O|7m%dlMk9MI7)Mrg|qBX}4j8x;iU2ydI!+N{MjC?=-ZgA1W@^_>m)Yq99 zS%!;rLGtGwcBbhauIgRYy@=ifH&re#@1ipkxn8$=@(b#_rj!t~M2W$%5 pT5`avjKTPcJj|av4Q|~1rdIw)y7f31Ke-XrzQVa1_+<}g&|erhG)w>h literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/server_info_response_dto.dart b/mobile/openapi/lib/model/server_info_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..0bc23439cab37360eb409bd995cdcd2b4d4244ce GIT binary patch literal 5260 zcmds5ZExE)5dQ98aVd&g!CYtUry;G~7K=OdEgteRU@!uKk?EMNEV?9J!%OqucSnje zEzwE)X}}6Jk?x&$$LCHtI_ex9!JEsA@zXy>r=zns7o!t+`|jhY2Pb1V9bdqw@yWZl ze;=S2Nxq&kWd_gt&z^VaRoqlk@q8v#J`*KBhE=uFe46u;FL-5o{#`7VQkkR%E7xqJ z^X1Cqiho@Rh3XYs;Ljyf_}g%$(YVrS^+HzKu*&3OiVnq0aAnnX(pf1guJck>Hz;Q2 ztjd4;H818&=}rgT8IUuOOJ1?1=;Qwvola3Qt>K)jZ(O~rX7Y^dCA#D%M#60e;0UvD z#?4Aq8W>c34U?qs6$l$>!TYF{6v@aQfX@P$xzPQo&|l8QKm3fXdwbZI`abTPZ%mZz zy1Y;3QnMTWCszfg5TEurqB!>PFo=Du5CI9?9b!@elVKn_+78W~XNMF_U>YoADpzm7C=Y0R}oF~#fdpo%xLLv)gqhOKk2U6asl!>1Ds4zId4 z0iW3Nr8_T*Z*YW&VhBdQ*RsL{IBcDXij|OmMj8yDC4PJN!d*xwGEvAufiQZG9 zE=57oy`}_1@R+|K9ureajg|^}4SGtIG92XTB_)L61{r)#iG&b?V4yg0WoMzwA5rcI z?SFnq0;{IrwgwHtX!yp;71uCRa#0(cSQ5_F847f1`cAL~u6j_dN(5{|=e59FWX0=E z4YoM6(;tQVw?rs#jm)hRD7^-a#D(E>K*VEL=hg&ByzJ!o?6qte9sR2EWY|iPfrjxpDs@XA@F_gMsa(pL2wx zgcd>S!MX#gwlms-yxaQ57_|yF?k+prmcQmFPnQKN5ilK7d;CTBWjwpsEcr{El*5d2 z*n?d}J3MeZ8-+p!99})F@8r5o^#BD3Y7ZNUrCX*Nkk+#1_P^azHFR1w3HDls+{D;5 zWWlm|I6U(YDWhD=b2#b`SqRdRow{G{Y`yD_pdJelTcg!SL0FRdS{c5A5}9wY`FC*3 z^~J5-a&MGe)29Lsx*02R?%w(9=wM56y(*2JoJkjxE7#Tpe`J?#9YutT9?*MVXauCu zVrim#P5_kE_T`#Dghah#YE8#M_o$cQ5u(er1>*WL$94&Z=s>Z;%b2mqfh4qe$~X?(mc2Za04-jcTEx0tvJ z7{R?ou+F^>KaNrVZJr})B9XrK-Cm95+7=Y4YU4}Vr#Hd1KhjT^)>`Y^ zK8OnSK#4JB6Q^h~ec(`e3%~c&8CApm!n5NmLAy-2lSS2f zi@~K7b?6T%WD?se(IwPbd+za&w1}VVmmd7sRYn_eC*9}o|JF}bt&qE@r+VQ6Z?AIi zKWMZgxWE;UqNJYkn-c9fwCmvpxL6_Un$s?;F@CyV%7>yO*|yWGGj~P!6Zwq#hagU2X80UG zcYP1h2T}eMRAQQMz$cZB$Q4(%(6A8@UnnhdN07y6We{HwV$Hix`2CN-WGO<0yN zr8F3jOMxM^>6CA zNKm9_9przGgS%%Of_`(=>uW*5k4(XQ&ys*p=Z$(yr5Zm*fI6$+UrA4 zJh&R4b(c6($!2pKo$L!=pC9HGwM^J^X2b8?8i%JQ(kzmz3k=4Pbmk|KRMwPu+V{ti>$H#G{4Ct&F!#RRjAmCaT=| z(Q;^idKa+Z7Gk4ZJdTBqkBQfbvLN+%Rk{{en8ZEmv@2Q3KaGHUGR7KQrrMV1(=@ zL`O}S0i6|Tf(_a8)M(A~4nxgoZ-Wote~Y~@(lFHKd!vkJShy0M)9&_?qUW&5_o490 zLzVV*#ID&il}vc0q;2wfT1CC7#Mi!6pMR(i|c|JZj#m{66G+0d5Pc3P&?Zxb#@W j&kZHQTWQZBe-qj%zhm9qm>50y@#U|0{^EVn!(jRoN$WT0 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/server_version_reponse_dto.dart b/mobile/openapi/lib/model/server_version_reponse_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..72caaf19409a054ec5dab2adf520d8f647860887 GIT binary patch literal 4091 zcmds4ZExE)5dQ98aVdgY!4zf9ry;G~7K=MHZDSzG3JgXd&@vq}l|_}L8W^en`|ggE zti)Ej^~-=2Xf5(y`0!qyV~>t{M@R7X&#Up_AEWcp#oMdVDZG3CaTLSp7|zF6@M(Pd z{@q`Ds79J^X2Q6^aq{B0M@w~6Xd~0PHtAgD@&u})vT~NnTrOqd;(D!?r8aJ-g~;#3 z%BE%IQX{`s%Ak26miTWe4E{Q4EIK!Kw|S-u>qOyFHNykdTuH;`cIRQaDx^(wUEH9W zTZkh4?bkG02xEIaJkEihgDzzuN|oT>m%Uz=3v1z0ns3s4Mkq=b7qY}d<*Czf-vc

BzG{(eW+|kKY>zpEIgwqBFgJ?Xxl^MpxQNHr$ z#4uZ7*Q#uYN1s_;;K9C-=c*7nq_?;S1K3i(U%&Lp^(qt7XE6P1ROYWOF`@Hf`g$y8 zv@G_xAIlj&%R`oZvr+qc1Z1$LzWABG22sqwlbr?SEPNArB`wU2UIv#F-a;yvqe7o3 zV}fD4j-jY>Y$58GJ7t+6T9s8AIY-K3wlh_RO=eSSzZA*OsVqgFT<959n&0SRm)8u1 z6BH8WA=cSKR1pKDenedLK7h|-NF%ExnXqmvp^;}53M=9t6}B=GNlZBWM`B7#z)LuR zOvoDz&wY_AOSBja&$W~A;)HcV5UHX14zh*JzQ`Hi&>`$z`GKyVSlNMwsBLhzeN zky$(#J5tBfftar~|GK=@^RS%@MDb)QIA;T&sxD3@GWM_a6P)bfVVxpx;3hrQ{UZgF zwn;1QnpK_HgjP!0`cglUVD^`AJ+#vGgG6K4Cf<;FZQyyS=9)Uc_3wjhqiNG?czawD zxpA%L+wP3HY|N wh@U!5_-VT-DSq%Z#vjPGA(7!dGhBEu`?1*Pnb;2tKgYtt&nH^olN(9yZyn?zeEgWg~OZ@%1)&wn3Xk8a-Fj;`SCyN{z3uEuaZzJ*WYt9Nhz zJV7FYu1Pi|G(_@%8F?XpYfnynkysWz6UVClizT&R<#BOHQ&G_ zDojCGKqb#mGl~$xmJH#q8y$0Z-ayD?g1;(wLw8G=P})ZSYU5`PIcrVZO!D73C0& ze6MAV!Cl`kM9nHFzM>3rI8eXey>zvADihVW@cL6!wqFmo16ZpO77vvoc&imx57okP ztX5b!C1r=)fg;I{2I^_k53HVpqdg76X!y>mHP^6Ea^;;&tOyBXfeL-1k_qN=nnJy< zaIk2KHbPq->GWEo_yWnx@}63kIC{FEp)8OFjVKFb%%z-D?>RxsL9c9ZTNhs^2PzZ^ zaU>QJ%_SlUa=?zD^7-mnm8?XjIjCdeD2JF15szxURvf908T`Rv#Tr1^h9N^*L1e>k ze5;8rz2UVq99~>n9iKKDs=k48$;)qi4tU`Zb}<)b3p2iznCuxRo~vbjPWgN2?t7|n+9Ep8d|lZx@O~h;2w}v^Ws)HHHdCbLO(|Vj zI4_!5#T5EC{+0tYglFwwp5l7?jIgbQ)^rx?6?p%7e4X_%vEjapP5uD_6#~Rxh11swfGTgr+->b@jcr0MH5B9 zGd7<)=IL0vG!o}h*uA8BDYmDe_keM?2-4`dq$ fet2*B$3IBjHPw$-k6*WUi5JA%f!6rAMKbyu_W9v| literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/smart_info_response_dto.dart b/mobile/openapi/lib/model/smart_info_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..e291cf13e3a1227a23d89a56833931c2cba3e4fc GIT binary patch literal 4535 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 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/update_user_dto.dart b/mobile/openapi/lib/model/update_user_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..d91de3aa0489b518846e2eb66d824fc333ccd19e GIT binary patch literal 7550 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 literal 0 HcmV?d00001 diff --git a/mobile/openapi/lib/model/validate_access_token_response_dto.dart b/mobile/openapi/lib/model/validate_access_token_response_dto.dart new file mode 100644 index 0000000000000000000000000000000000000000..b3f9f9735927b09a5221c0e5f8af2d3fc7d413a7 GIT binary patch literal 3678 zcmds4ZExE)5dQ98aVd&e0Tg-7ry{A{28%N^ZDSzu3JiuJ&=MW7kx7lDVi>9a`|e1+ zW$~P%`!HYzVoT(`KhGVR!Js!7!1?df$?+#L`bbJAqA3u*nxR}88WC~v<7auSG zI6yIy{E#wjhbPgSlODZ_JE=5}mrBP=k@Hh1WodXC^PI1^v|;@&R)x}brv}T{Y-8f0 zw6W%YmO`VtWGnnGn8shrwL#{d^dG?qylixeG-rQq7Bo1M;bA-RcjCGSwoW=zI! ze~FWfY18YWI}2(Fs^F3pBErA3UN6a+G4Pe;B4w7JCkZ!Zs_wbG;if>}{K6`D>;Vk0 z95>vSS{h&=`5I>XwdNpP=n0QdeZOo#9sueBfz5=8SZT9c%WP>v+}$2wv;YgGa%gwl z+6VQ(wZ;O~`PcU(taeV~C5%@wTEDy*_SnquQ?w>%sSdPO(7p<4? zgo*C@WP_Kn?T)HfLFp8N|0CuSGSYvsE^eqfBw_) zIwK`G7&>YAG>M6x4g#vvGp{r@YyVYOSzYpaaT^@9gsArS=NmNU+NM1v;-Ff| zC(G%k;O}Tkj{+)h2v3^*=zwN9eX6*&?zQqvxisxwHOyy$EZ!f|=sn~pb%|wv(mLM% zqdPM0v|6)8jzbrgEH^yd){O9-qq!+_OT+60U$eQd9kn7kAmAxRI6MS;kA%VI7_SQ3 z;T$zXP&ra-0*XZI2&f!>^MPuJLF|tqB7$9^fdG^0;M-1zidbC-)Eksy#M0EykdCHD zf#xj5@&uDg%PLATsLOZ?^^>#W=5qxYj-_Vm?9XFFwAqPT9k{OwO{LDM@`BO&u;~JX zG3~|20Xg*e{nOZ_dFk(ir_fy8V(e*Lq|2Z`r4h*%Yh2Zv8tsVQ1UGeVKaaXI8#%6Y zFnj@hEaJ>7y3sTAGozjZHR$#46AGYe)0J2Re5JwTYNq_{YAtMrOVovQ#oUk3u~$MH z)j5o}2~?(=H`zuC?qCwJ@1@psd?LY}oC@K`CIN=fW!n!q{@-$r5N)d2;a%ADe>(}n z|A34GnBujCq9oqcr$3$BbbrBH&8oz$1qZyS8H}Hrk@-`y<(a#^HOikzw;4C1M>i6? N7gqP*$XO3(-d`_Nwax$l literal 0 HcmV?d00001 diff --git a/mobile/openapi/pubspec.lock b/mobile/openapi/pubspec.lock new file mode 100644 index 0000000000..e4c9e2004b --- /dev/null +++ b/mobile/openapi/pubspec.lock @@ -0,0 +1,355 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "31.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + meta: + dependency: "direct main" + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.17.12" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "7.5.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.16.0 <3.0.0" diff --git a/mobile/openapi/pubspec.yaml b/mobile/openapi/pubspec.yaml new file mode 100644 index 0000000000..00043ba660 --- /dev/null +++ b/mobile/openapi/pubspec.yaml @@ -0,0 +1,16 @@ +# +# AUTO-GENERATED FILE, DO NOT MODIFY! +# + +name: 'openapi' +version: '1.0.0' +description: 'OpenAPI API client' +homepage: 'homepage' +environment: + sdk: '>=2.12.0 <3.0.0' +dependencies: + http: '>=0.13.0 <0.14.0' + intl: '^0.17.0' + meta: '^1.1.8' +dev_dependencies: + test: '>=1.16.0 <1.18.0' diff --git a/mobile/openapi/test/add_assets_dto_test.dart b/mobile/openapi/test/add_assets_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..ec660c40005672f54086748b9e8bfc869501936f GIT binary patch literal 592 zcmZuuOG^VW5WerP7*B1XcC{z*g@tXUpe|@Vh)6o@&TfO-WKCu(Mf~qhQhN}4NHRmd z*Cc70q$y0E7y0q+^kMoqSxhsS&F`i|$Z~kd7jU0v^V!Rhzyk87M8{X-(djq|t*(uA zq$=xFr5iej&UBtiMGdXV@NnPjb!#0TWMK1&Td&%Vm7|YNJE0p~%if}slgW7zcm6QF zu*NeQR=N~JU1@TW?hl$Z+K^X`HMPKag+`s7sA7fAC&?}&^ja+5v8Hp|lEGG|vPNO* z-o-x%ljk&Sdq6M^y$5*`n3Uzjdt#rlO*U4SutshA1Nce+1X*c=4d^&PL2&hl&>vhR z0;`?v+I}yH{<-cA;a4b1-D%I4Pwcd*uK|PUy!6m7slu*d*kIGi@5IM~*SCQPLkud) pHWt7ug4W5MfODuOj=(nbbu>(<}FY+vpBHtmVRPp>yegL}x!Z82< literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/add_users_dto_test.dart b/mobile/openapi/test/add_users_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..3dadfd8b4d3095eda84373bcb96875f9e5d2731a GIT binary patch literal 599 zcmZuuUrWO<5P$EdIG@(RTy;;fIdRZV26F}N9z-My?X?TGNlntp5Wl;Z%)!JxB)KEM zf0sm25JfOsXX)ibvPzz2SrWs1u}lUKr?5&hcueEP{N;jR4tZa2ZKtE*^(b(ysHD=! zN~KvTY8*o+JA(zo8XJ^$aO_3XDs9g)c>T%uhP9n#8b3Ori7t6Vd&@PQ7PTR9ZO+qk zC5`3MGEtDBCS969k8h&}(`A&MO?N7A0>*L~H1# z?oIwdEE*fR?FewDt~WqW0<)r68;T!W73`EKpy5J>efSCh2v`cqYY;wvjxasNhy6)F zs7kA@4SP=V^tLyEUl}j76vo~@TPN}=YrR_K8b^m rO6tlNV7CaZrm?l%!{(39=HST9Ugkc&W77y(8mC_A+vTOopWnd`Q7pvz literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/admin_signup_response_dto_test.dart b/mobile/openapi/test/admin_signup_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..d71763a3277a93f7afe58f92357e73725b76abc8 GIT binary patch literal 980 zcmbV~K}*9x5QXpg72~NDYNMV+5iFz?K^xF|@U$+;G#yNK*WHOy#Q*MOYl~o|(nB^` zc;9<(c9W(_n!@ODmLFY@r{nw4Y@EU5=5{=QEQe`6gS$MtnLHhctW#c<+Vkb<@Z>b< zwp1DGQO&JabE@$iTGIwB71r3G;lX-OjkBJ&61slZ%b;A#%HwNGUT~uuS)KOsX7oYK zeb|l{)&$mu6_pZ5b3z~Sur;hEgF)5SR3hVrHtOtH6$|Y{lB_yH&*|btS9oq68to{R zXV^{MyZ9wx44ih$TF`}d`v7tzFe)1|4{}DWyT{<9DQ0YwH%lcnnoR!?J`w<7=45mY z6h|o#F8@ILhZl(emA9?y_qw1zguMZLn@9Tx_GBucQecP$g|_hnc!A)&4C{Fb&s09I zSRIz8i1j)|D$epOkND@R#7|?CY3iNcQMHeKy#1;C3p*n=%6(#L9r#}9@Ey)zAD^f--c72X3Tc8l7Ra^Vu31qEsZU0b{1k^^)2DZr$?(L8dYyE>0Q zL2i`k@EJz8-ypW{!-Rde{{fgvBpT}ie(v5aPj73uXg$$r9H8bNwTB;eZ=e9ZfYGO& z7@-Wc%Y4PR;??*J0Q4iq6WlSSk1=k+x#kW5f+RJh-1K=anpPXPmx1yTH@0XyR~Y);3Bz=S4Ua7v zemgRj#f{xhFSW7`l`BNWhN2c^0^RO4lR}X#q*g12xizZd( zxo$~eD`I|5ebu9j9f%|AVm~|sys4jC;FdrlmtAv1w&jeJIvqV|Q9*-3jVJIY0$_A4 z6iN`Gh?3xPhcupCL=0US-L>P97vF)5rmz(ZlHZ*XY6YTV8iY8^)8PZ$nxHjYnsFPr z7u9{wMY~aDh~z08E6>s_(4Tty*TVWyk(m#xZ*j+1pzqv*SAVdC7vwQEr6F`wC2r8> z1$m53$e6FYW~orf#9DH1Xy0DeLH0|nC0O3wG`v_n>>&ATVcn-&OXk%U|Gf3{zb%X_ vsQ9Sa|uR%JeM<5RwS<#{=-HzYx9hu6|ZW0wx zRr5Ar z2bSL@?lhs>m+MFRn^}WU_OzjX}K`RC?o@8+*ECi5|?z_CojpLuRer-zw&# z?7@(x0)yFGhRTZUzF?LsS<8EivS;in<$O8J)_de$46{A_R}l^PSCuOb67+Jp?vd$q zK1RLRE89D0^VwIrvN=S`}@TsrD5w%6q^bFTIC; zj8YJ2C!uoq?{rFF12Vg+lev8YH3deU>xt8mK?fKWE_HP-eU)a+vI#TTfnf|visezQ z_r3C=^YpyP4^-WuRL_^OPyJQ`VXA#KP#4``xz%#ms>&W1Ul4wv9g4%>yZ&7n+TFh^ zpys4jRYLka;s0*l>!i(jc?le!2Y;MbtEN27J=vQ=SWaJ2);)#R7?VYIw6pMO;Gq9V J$Q`z?(H{oCGL--T literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/asset_file_upload_response_dto_test.dart b/mobile/openapi/test/asset_file_upload_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..c53ca650ca39b858d6d2519c2d0f869cf4c7807e GIT binary patch literal 590 zcmaKoL2Cjr5QXpg71PryShw|5Dnf-_i_p4Y_0UrcyR&QPZjvV17AgMsPGUV(JS3Tc z_sx5gG-YWD^X;a%ea=_;dcMgsSS(-i5o86diVeIL*>drIL$aWJs)Y5k$@qT4LMt1k zE%I7hUdt9Ap;x`bierl%DnB~+vNPKHi-c&u#ldmYdv5WwmzHoPI=YRp^m??8hFfd@hO(AuBknGmwNjzutyT@m{9Y*jaL3ENu#T})Mo5e(KSYC%+Mp6frgX+( z>ty;*;?emuTxUTr4ZQ>U5|}%OenJ1THLVua8l9oEn0d{MyMhmfQ$|o@b#DeqFuWp8M(}46ZQi_Diygwql`vmF?l`rUgDq_BaM zkOu-rzyJNtjuVGr5QZ@Rwus(-n%+(C$BXF%X7kU}5lkYuix%)Dn#^ZE-Z19Lk5U+S zdv*E#D)2+flr|Wr+Qcd4cny`REXo*jEKs>oGp3@{#+^)vd@VLME-M!s{8f?RxDo|l zOJVrip|M=t*wgVuE9-=EF-aCuN@(i1Jt@mcp^bB`GRE9esQAOXI9Uo~gJ9bu3`&vw z6d7LY5|t<^I72D}k{pA&9nR_c2e>6dX?SYJ zZQzmQ<2EboMkRGiKEiD+qDfT8pSSwM!V*`MU_H5cyW=0ak4~=@nhv5J*GIAKv4f)l z`$*iT#MV3Fp+3d6q0H~3ufi=`C5LbynL$f`u#;+!_7Nopg+n=Zty6j>`)CR+{fncE zXOEsGdeWL7ayt_%UVGQR4fg0+n%$FMP=SjsyR8BHNMvP%BduC*O#}9ktnsPrw9gu_ zj|5esCCcx7g6K}N{{v@9r@tvZI1>PQJXfi1UB?DI%l1(4$>qx*DsA&;tMirYCgpnd IdGQ>)0IWrV5dZ)H literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/asset_type_enum_test.dart b/mobile/openapi/test/asset_type_enum_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..a826ee679fc87f9894519db5a11c14321cf23c13 GIT binary patch literal 423 zcmZvYUrWO<6vf~3DXvf5psum=m)$N)^D|=D~ zd8Z8T2NkOL@7Uh11R_On zh+~i6oA=(hs*0+Ddfl}jzb&tp*LAmSV0Cf1TtL&pRolV$wz*jS{19OYdDf%#Cx-{0 z4~ujZgVL7RMq9QKl8zv#;HYOLX(Z(r`?DC0wtkX<@( zSr?mIH%;eSIgiRS(MLhC5o9ynO^QjO$T6wa5Mh3cihcRS_!g}zik(N88RWM((2+Kz z(1@GlP!*3XjL0g+-cFnh(eWf_*(!(_(mv{ zAaV{)aPpE>&X0?T9jp$foW0V1wt#PVGBg(z&Y+Qv#f)NwHA*)=&Z5@JxU&vceY3r#x^dLtS0jwKOIG_+GvogbjrHNi zp0{%)tz*(rk$IrV1)9!o&%!Dp(b7uE!pmIABz?G}yky1_(szV`W_-&EoGFcxX%YBu z(r!JNpGX{97qrJw&_dhUf`1ZtDv@u)MpuF}hf8bGg-(&35*gG?$l(Zn2!QwGLb3`( zw+csCT(AyDbK(t!QH>rBTF?dIU<`lZUHrQCjg@#a&H`v+E!p*TD|t7pei{ivR!s literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/check_duplicate_asset_response_dto_test.dart b/mobile/openapi/test/check_duplicate_asset_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..fbe09b776109b6ef7d568ea8f589db50cf522fed GIT binary patch literal 610 zcmah_Uu(iJ5P$EdxIIM?MzZxcOv!KM8y*Dya;z zQW;i?2B*-;&SJr^!4{<(oqN%0W!yyuZ@&57GTk|5@Us(!=#sZoH8=D+G?v7Ty-d%Q zw2n*1L_vn46li+7y=c}5iIz1=Rs?f3m+aw=D&@Z|M#7gr4Maj zqkGWDiQm_qo#$De!|ZxhzPX$)=bvY*c>x!TtN9d)5|-r(K9$Ad;_HFHBJx9xp3jdD zkB+lg>ef0>s&QU5+R!Nsb_i4}GSrdf>8{pY?>z5ipt;9QQ2oHl)2~5$pTB8qHw#^8m9;=_&(y8mo zVy|;qhq3F?*$W6$;5=?SKs1eY02v8<^n^?^^JC}=cG;cNHFT)$cmfX@fM6SK(SS}Z zR0QYGDC5alCcxIap&yT;@H4<@3V+0s+!yxRwt&%bDYFwUq&z})=>oha=)Lsjc>}jk z$lI+FhK;RKxX6@%tFkDQ%HuXldc4ws-)+6#Ly+lFeWQ7O9mp^00OOh(91O!9nnC`F h^oiix_sL)5ywG}mEDVA8|I+Lv{w;(wmuB>s{RZ*M5k~+3 literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/create_device_info_dto_test.dart b/mobile/openapi/test/create_device_info_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..fe250e7e7fc9061f6bad8509e5ac1af02f1dfa00 GIT binary patch literal 793 zcmb7>&r8EF6vyxVE1surFjw7490RA_WS}c(J$TAu``QKDBqr%($o_XU>Kg6T_|oZruuv!`h~i($Tam<=FKV40-wn8b_u>ltC5yvez7lhN>U6!=k; zQW<20GOQ34UPB`ri#fvzYm{zqm_@CXah(cYee$hkx^c|lM(qQIxZa(IR%PBplRi{GprI4Evu9)33F>M+0_Nh*4)@2*ms0b^X#3Mc&#)_u0=%8 z&~F`%j}V8}MgBMdKGe?^=t|(uAa@w!Mr62PzcE@5bYir z!lX;r>yHDXOQRay3w=Zv6%OFXwl#isM#vKKeSw_x(6%E3)qa3mBWOdL8n=bj0bK2U zxZTREE$M|+G)>~9HQDXk9EYS^jVH!<^j|@5O3^LZY2(x_{h~(y8T1=IEs8AL-FCq@ Dub%wY literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/create_profile_image_response_dto_test.dart b/mobile/openapi/test/create_profile_image_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..83fee5b295e865a0ad180244254226868ab2b28c GIT binary patch literal 724 zcma)(K~KU!5QXpk72^p=&?+Y*A(2=xBua_m!PBy7hqAHVU1zr%V)*aQwrY$L^w4c5 zdGCF1r*Ry`F^nIU>GAbsKDiq&CkaevHR64kvVb!-#zanc8+c0ee{A>B9%Af!%vjx&Z!PG|M>$n&up2nh)?B1WUXH w@>^K7vQ?WQ-BxE!>0smH%QQ)wh+W(1JAgC3?!S literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/create_user_dto_test.dart b/mobile/openapi/test/create_user_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..8e2e7d445a5622d46d959bdcaad9b3e5b4895c15 GIT binary patch literal 862 zcmbV}-%GXMz@l-0RX+sMTR<(i1Dc?8DS>*SiKE=_46C&vuWD;5!NL-aIy+Xy64@uorXzG57Ef5wxpidF=~SM6 zyX(%yFC>r;X}fHJHng2R$dSM`69$@j;w;$Y#p)7Ps7?0}-Vy*o7usMAI_{t#xcsT; z9$qBERcssA?XL0&7zNNklC&h($za;|GK#fn(Vehc(P2HQzX(Uz;LU zYE@EenrC@*c;7VmZtu|hSIgy}30rWVka*n_x%@vDO>P=AyPUX+O+MR4hY;1QtOO+zto$;oqR73>j z;IT*g{$`x#S)RlA=dAp4Ik}$Pj%SksE~ZzL5fmj{moxZY7SoHnJ%uIYS#4QPj}E>b zWvQjcc}6q$Z06MBF?6nrSQ~6{iB3kZJuQQ0*~qZ%Z@Y>nbYd8Pb;L?{cBwU3)?1LH zigVmdue^(5ofxXskmiKk)8j_7mK;XYdeho)Lsj#;+O2!mj&2ixGW59n;5 zT+cz8dT{a%A{Zr449?49dUo|eWxeuCZCOqZ z_CFtFeMyb;jAriH%&EmA=v)`EHrV14os6DqS_aRuk+AK(T}2Z*F^snzvEt4y^$eEv zFUV1wbKHzq-bJxa4Ap8#b3#sd+-TO4!)RLX8kNbyI&=8RR13>7%hnlT5N!3^HhAO% zIvXh0Z@-^=6w*D~mGW59=H+8~Zfs*5+&3E(VN|{@`~ji>)UAq`Mf^ct;hE aV?>#5-_|Sj?so5Xn!fXObV=!z9OO* zi=s4El&Z!Nw7PXz2&{37n)=6HZ5w0hEJM~`a_>ablCb#MD$8^&w;UU3ITKpP;?|v~ z=SDk{nuIENpehyGKzC<{)k>oiwb2#BXd|__xf1zCS{FqJkI-pk{vj(InFh6NRLpPS zx9*JpK?ohiJ{$plsP7%{Ng&}f5Kf&#O2|YzqYBtcrMo@&jsO^2DlKbJVTBxFazg3$ z#t{Q6YucvU@xmv7P9Ofng4|L$x_u_Abae-+aL_HVlx$z9;FM)e nD1bHyjpeN^?P1OA^+8>_z0QMS&tcP9nxujM&)MLQ5 literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/delete_asset_response_dto_test.dart b/mobile/openapi/test/delete_asset_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..de09a5b31ab1a8adedd13ab381095051280de0c7 GIT binary patch literal 686 zcma)&L2JT55QXpg71NUn*0i3AB2-8$LTfBD) zrHw;T8YfCs;|Lnvcq|0gxI<0-Q>}K^I6CW)^@rShVH*+--x}rEuH}wTD;?j2&NJNk z^Y+|mPg0Xm1vgZsLKp1*Y_nQv^rAMpVi|3v7I!xy-%97B=$H{YR_3p=!jZA4rByM1 zgLdl9_?LvxQ`{aWLEE&gJ@85(;WZFWy+>N2x11CcGSRzH1?;5K-5z{K0PHE1mNlp_ zN{%qOVD0wC5gRIJ8r$u(jV=f~efWk4{Er|jlGJ$Mo$(oyXb+pC qY;(j(zt?$4+}b$xI!m&EI2?0@pDS`oSD=bN;i~u_m_ebzA^HNk6yDST literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/delete_asset_status_test.dart b/mobile/openapi/test/delete_asset_status_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..0dfc82ba611004e6c3dc743cf9a3df41e4984419 GIT binary patch literal 431 zcmZvYK}!QM5QXpg72|1Js9o(zS_BKbm4bFb+k>Yt>`u4A-6Upa6_NgT6Q!s2kO>dI zH*aS0T;(||ciZavvD}m|%WYY}YW-ABpr~L|ZQ;2p)~mOxh$Zr&!65gu>FrDAeejZ#tU8UDf!!B=v-X4NKp%q%(QdJi)nkaiP~g}+@MU}L zEDKuErip=OZz%lP`C?dSEV1sKZ6hWJwEFHw*9Q!&)c=f-d92^DrMdHD(VINJY1%ql zoFPIic`7F$g(l4qPXa|egV2(h)0nduNpt%A8ct@{6uDk+mTTKWnC}oYTslk}CT*sI+aa z^l5o2xHgmbWQ?=xP!V`^X_vh^7+hmwDKah5jT E4*{3|bpQYW literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/device_info_response_dto_test.dart b/mobile/openapi/test/device_info_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..748f95d09d139ed0d5a08766658a86dc6b9160ac GIT binary patch literal 1204 zcmbW0!D`$<42JK13iY%b*v4&6B_Yra-jszlhSWLaG@014SH#v#XT~W@$-7r$XG85wEA7JN z+3Cl#B!<+maj0tJR81|OL(jd(N@0r~a#$T=>a1~L(xKa*dheC(gL3$zCnt8+ot#!X zxd(J!k~=?bFAe*kIVh?GNHw7w?0yo~5~EkG;YMU(r&)dapvs+gK1s$JVPSRoq#Hap z7Fk*nw#LE--l>msP zCe|&`&`F7KIqh7YTqL4zoayay5i!%c#R^_i56QXjfGRPr zj{&iXD_ZJ6h70qAJj;9II0jof)B&q?n8p23{uk!a7+GJx?t{6Kzo)meoWI5YhLV)S I{=Fo>0YklX4*&oF literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/device_type_enum_test.dart b/mobile/openapi/test/device_type_enum_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..27b29a53e03bcc5b5e99e7bf33d4b7ab5084752e GIT binary patch literal 425 zcmZvYT}#6-6o&8fD~?y)pf0*A*$^DGlYy=v^}?$V+S4xBp2U1ihU~waD7(>Kau|C?y1b7s9;~!@LUzU?b}tv68X?d>+hF~+a*g;^`tF| zPFvBb0ap+xIBW$DI3oG{lGSLm^^=AiKIO>?6TGna5tNNO$uUkNZQMOt7Z0~?T5mKt zFUbql#zNI8v_HC=EE^P|6N9EcV*VgW+}()gAgyEU_l}Sm*}TghSK1&+qj+44^y+N= z5AoN+}A$%$kej<5jH+1I3zf<4hM6_6 zTG6_cY1sp251U%9Tak7R*%E4M6-RMb{=Bj823PzMoY58<6)rO<-RYJZ6OR@Bu1h*o zPQ?r9r4dcVfPV9Z^?b+7tev8__;}k_z+!EN>o&Z%&kp$={(*(~+#2^bzQw(4{W+Ej zX{@;(TIxo12hJ(jg}qHBO^s6lj|u7qiB@2 zoYvaXT1b8io$4H~D3`qD%8$>pXpOeMSHa|-9UL{Cr-_B+Hl2;NbsA4 ztp~GTi095H;Wz^!G|Ue0NnouTq3&^^(2U8{Yx1E*1uYY5ID$_CfP5_!lOW<6C5L&B zY&e<`gd3|nGaQ6~9%wLzZ_{Y|?5$7@a14{Eh1d(lI$prnQ#XG7lL*%>`1-I(a3G{_i^}O({#j zX&x+F@O|I!PCBP)lBO{KSr#8}@@0NEFY^qp7q|HgvI3Sx3Ezrraee=RWkLC+md;B~fI$z0wZIO0)PAy$ zFc!76D&^-ibUnG;k$5DZhGi=V(6A@qBY|7fD1C=yxkk*qN&XmBLobz{p1|J(fYmFd zWd|xYQ6XIIn5HL}2}>Ji20NXE@Eyry1}}jz@`rayHz2DDiAwW`n*9*#;s6srz_$q2 zagWZg;o*(;VUs3lt*cmM@N%JLkrgrLc~j6^6yLd9)T5M*>{vj~2iG0Mh{|IyammKmj?g< literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/remove_assets_dto_test.dart b/mobile/openapi/test/remove_assets_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..4e5b48bcbcf46851efbc41e947f1eb9b7c46557c GIT binary patch literal 601 zcmZuuOH0E*5WeSEjHk9x8}%es#X_nS)PUB5h-`;sk}f8@ad%QF;(vE`wHNJS=K@h4SCgAQwxmOXw>PED%R+HlKjjFgAR)~tm({lWU$kzyh+%4 zF#QK%@|=dS0|e91dytdB1GRQTGw+Fg#x~hlT|$f63`g*p00_9!1{=_Ehl1eZFK0NK zCW5S;?YrS1NdH9)#_%f_1#h+I^G9~t)R%z4dQo~9mQ-QiFl?~t<(uNw;N^8B+7Kre qaT^QZH9_ZOd&gT??Icz|KIpfmh{Fe>EU?V8Jc@jUNu`SUEBOY_AI4w+ literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/search_asset_dto_test.dart b/mobile/openapi/test/search_asset_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..60021265cd696081fb8c49c1e8ee7ad46269e849 GIT binary patch literal 570 zcmZvY(M!WH5XRs8SDa7XV6M6+aSR-^lYy?_>Vr>NXs>OsP2we0hU|Yg$sEGmLy|lA zecyeTB#Dv)maj#A{g`dCmt~Qquv$N56G(H|3a2csB zgsfJ?YSH0640147G3>BMX(y*%^jeXy%xzhb269Z zrIN;SX_=_pKv4^%AU79>bwZ+Hosx~iY|ACPyJ2O^$wbjHBgC4Q@4Uf#rBQM%5_hJ4 z>v;YTv1n}KuM^;h`rd&164)ZAvRxX3Hnl4HRHA~O3ppCYcLd+ieoyIU9rVF<(5B))tYO-Hj~GKye4c^tJNieVZj@DxXr@%sf~p1hxPu%sn zkxONeWy-Kj6xf4OmKNs>3tXaf?OiRFS{Zjz!HX}xwoI3f8T>4TA-UvBIyE=+c4#a$ zH}c+7U)h4IoFizabU3YnhS*RGDjw zL#XavaaCr|Qtl*<;!3xvBOY15(Qgw<&hUi!_U>2zE%)CD&qC(A=?9>xs%JmvyC?qW I=x5&qKeH|lumAu6 literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/server_info_response_dto_test.dart b/mobile/openapi/test/server_info_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..4deaa09a9e3cc8c6ee8c838f29043875d2426afe GIT binary patch literal 1237 zcmbW0PjA~W48`yM6x=B(kk)HYLyH0n;B*Mk)kFRqc2ZfJc7)oJM#(9%q2GP9vjj!w zgPVh833%`QNJ5I^D2`#eUZv;XW)HK)bTv!hZhk+TK$5~kx`OX%GQa!zNo1|^YmOXl zuP!dHqV}juW0_Q8SrxjXYiLa4DOXfcO(skZXI(qXq1S=c3+}vfO;Aj~8qH!iSj*~= zgtPCrXV&qZm!7u|&~18Y-4uKn{;|0YR*u11Nl-3G#j zz_IkpVF!-~;p09B{mx`vp{-GhU8PCdg}mxEXBbzKyBql${RK77 BfvNxi literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/server_ping_response_test.dart b/mobile/openapi/test/server_ping_response_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..cb726801765a7ad595854138519660a3161bf1a4 GIT binary patch literal 568 zcmZvYL2tq^42AFg72XMu7~MEcAfzF+Oh_A@Sa;YdM6D^&+9Y#QCN%Ni=ag|;4@qq4 zeSXi0<0y_{`JQLDPw6JzF7q^j)%rOdLz2NJ%i$$U)~k;jmIdWwDakLUlly5DT2*O7 zDE5ZLUe!2*R<{mIfi*U${rK9e#v1aygsi{i$qCzfLHN}wVqD9HZ!3vkkK}kbxqiGb z+IgwHP$e6xy+Z16*K1ZQjZV}?S1j`fsl~&cC=QZb6kRgHz{=uNRyZ>jwX`bcGYMM{ z=KmxfosYwH6$I1JJK!&YH>4xdtJ2jLo#mhy9gQlXkxCCo@Dl;Bey_BwL3L{s2#X$U zIGRT+uE?}@I0yzkqQMychIV7$Jh3qlIwqk@#i;2D@CO7-ydn7$>{zqA(9)fB(FF?( Rhs(1h>-cksz*YC2qhE(vyH)@I literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/server_version_reponse_dto_test.dart b/mobile/openapi/test/server_version_reponse_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..2c71afa9947a29ec2627c1b4099b163887b66a5a GIT binary patch literal 862 zcmb7?K}*9x5QXpg72~NbRHHpfOTj{_2x_2K!PB}X({?r4U1v8+k^XmQH;M-BzW+U|+8Xj(9kP0tYbR{u1>tL>i0xX|d|FBT_DGK5 zEQ*yRmn3aR=vZ02$R!SqMJ=sL z`8f((cSgS?9-U9a@hu3UVRpb*0=GzSNcX&eGJ1xVt77I&@@7;CwN$#>gO3D&O$()E z1uE`RAdLU=c6+0Q9ZNEe?RG-cU&>A&zVImqgxF77$cvpffCd()p9Rh2v8QT0DIv*PV+ Fl233c4<-Nr literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/sign_up_dto_test.dart b/mobile/openapi/test/sign_up_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..4c4d376b82cc76abe67582f097f6f59935f216e6 GIT binary patch literal 850 zcmbV}QES356oudOEAF1E;OyqpP=pDsL)a`#eb`eBZCb)t_;mZ$%jzO+VYI@l2=^1UR#SwE8|WoSh;08OLgt2;UBdyD3`3lRWpNs$BjjE zV^7OdC9Px9QITPw$OSiEZcm1lLUKz>B@4vdhDrKxN7D^6mXJe_5NMXZvw{zm=8|a< z;@59(9gO}Vj$0QtOA9oiO>F^30t+0^N=HtSPbD&_n2?<=d=UV|av@m>!grr?7$50Q zcSMjWjH-1fXzY$>K@WZ`yvf2DAq(JWU7`ehIBJyNfZK4;1}|dV4%VJp9~_w7$<(*f zsIZ-75+&YYe@OppubH)5WwNsgEqG0cf8sel_}P?S@|u{ES$9!N3tkq2P5Z&^$v1_? B2~hw5 literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/smart_info_response_dto_test.dart b/mobile/openapi/test/smart_info_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..a5e901b7b385d2cea5e18c9fc34877fa2dab9f4b GIT binary patch literal 828 zcmbV~PfO%L5XJBL6y<4VU^Kg)7XK_OWRwBdfQ|I4Rv?`gBb$W3Onr4@cgZ&-dWG1gzld8CMY+s^7wZkFSyaYoKAbW zGx{LSeHh0pYXWP-ifS>WhR`QGjGA?1FsRO&R%E=^M*aAvsTRgB1 zjdqmFwHK$J9KMq<2F~O0CWxl74j?Omt6s3BY3wBiCpocTn>|^oq1R-lGx(PQ2+@$y z9Z(viLO31;r?bOMADTk$9`JC=>p&jgVfRQ{ba*_n*MJ4KS^-(L=}Zq`N_Wh4f|M_HOo}b;Q4J XZvQ-BB^&>`|7(ZOMUZLI9$vEll*0(3 literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/update_album_dto_test.dart b/mobile/openapi/test/update_album_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..a03c12656420a49fee7ae90561904fea28f462a5 GIT binary patch literal 667 zcma)&&r8EF6vyxVE1surFjw7490LdKWS}c(ckq_6^7)JH*{)l=2GB@_T zJXg{>E*%pE1&UIj>E!loSS=)4Rx4Q%=GI)Yn=6*Dxv@d8?+Bsh`5UkBR%w)6i-^9V z-#Q%sLL6Eb`Qr%qP(NFsCxN9dxWj4v+BUIM!G{tBG+fADA3g&BVoM=;4We5iN0^*Q zdi`-gU}aRRd!aWz0m1&r8EF6vyxVE1suqFjw74HUuZ_WS}c(ckq1UJ&NVyPO+$J04w+13!vV zDub+0h83d12{f{?m@}-fM(KvfS=3q?*QwyuC*N778^;WOG{TTv@|sS~4ZR&2OXkLQ z%QGddkB z{MNnc3F6SY&>u&@hx*w9T?wpo&K*X$6&WsMp(3Y(tr9uZT*&?az5)Oe6hiU}M7u|Z zaNDKp52gXprBRLU_k2Vb)f>W(ZEO7EjF2VdhXOh2p>0P7s{H`BLC}UaHEsv%Be*{J zaJ!RPTha@uXqrSxYqCGIISom78qbXJ=)Z#Al%iX*v&N}=`bCZYGw3&bUKCljyX}K- D^z8iu literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/update_user_dto_test.dart b/mobile/openapi/test/update_user_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..3010100d8ecf15d44573b8ffbca6eb53e1d45232 GIT binary patch literal 1193 zcmbVL-%r~x5Pr{JaeHc!I%xGjt6^^Xq>i^wa9E?l0S zem=|MQPtWw5=-O6Qnhprz3x3pK`nKp!)SX}oi#4(WMKP*D=%yxgri@*a!l9Q@oLfW zJCO4%?)+|gWwZ~dgHVzU)l!j5bidQARhqnLjjkCM8r0&;Cs8%%e3q?ign`BCC)RXs zENQeV=jSx09$fqf3FJc_mn|Tg#CA8zGVzgE;7c|&h&OTh_Y|L4@R(&N@DlHDP4n-%z;z|lT6w`Xb7z1L@uo0LCJ?T z7yU|CshCLNg63sWCi?T*`?at|@1Km@FTVx8$0Rj+#%F&_p>>?dyy_qr|rg-6}9p98Y49(t{{}K)}W!W@| G&)Hv=KX5nz literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/user_api_test.dart b/mobile/openapi/test/user_api_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..0d93508eeba41ae5a47f30155849a8c934ff7317 GIT binary patch literal 1375 zcmbVLO>dh(5WVv&rYB>`4Q)d-n{o_A&P=1g7I>Zy#F$}o!pHVlNhG6o5={`1a6ZB%#(OFy?;kAMt;tPahInj zA5Q}=WvR5mw9qCkWQAwomA9CuSm6ql8*OX3sA z^mJ$}H8-}Oo@r&BP%f1@3CcpEY3R0Btda_CT4_}h%smQ~e)^DRkHXj>Xg$K97TFI` z;+d{diCRWG`5wffbrIjT03J#Nykdcd0!xc#T+85>mN~41RN)Z*1ONmoq!Ja# z#(joxxy6RV^MDYg(Y_7`d#wf|*!18oQ}4XNi)(|z;a8*i5N0cKe}}fF$zbgC6=Zwc z;oMhF(tzER804kGAqcxx8A(GEpw;f-whkI_lZ6SCXOYB7Lw;%lbnNjIdJOS$*jndT zvbs^*RBt+B%<)ccq;(fe?b^A565Y6}c-@8%TGO#ust@RQz{eYStU`Wko0v< cKM8jXm%CT~=$<#IIxvQ3%s)Eezu+nO3t05EbN~PV literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/user_count_response_dto_test.dart b/mobile/openapi/test/user_count_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..1f99d9511e08b5da866bdf0b22b87fae0582b965 GIT binary patch literal 583 zcmZvZ&2GXl5QOh}itQ;NQPOg%KuASRsE}GBp>XKQSji?>O>Eb8TA_+}@7k1JnuC8< zns0X2PLe1|V6`vu+vjYX?N&vW!g}+PO(4x-n-}n!r= zWsud%uv)Y@hhFv;D~2t0DBa{*i%u)!1_|DN@snk`cg)~NFAU+5ceHD6=yYf-nHxKd zmr7d4rDLL^hN2c|0&WMJwL+q0t&$DN+>uN6aL39cH#Ukc86noZeCG|$l}5?6NN7#{ z)bZk<#G!SG->w4R)Ylg1Nnmf$JgHu~9a>E}F?A~XRHA~83ppCYcLYF0EhKM2gh5J# zH2 WWk~5sN?PP;9$d~o4c)?dj(z}!qQQv( literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/user_response_dto_test.dart b/mobile/openapi/test/user_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..861010fac67d6c2fab445acdff0b4f5275f3faed GIT binary patch literal 1296 zcmbW0(NEhj5XRs0SKOXjq>eD2CJ++UQc$I>nV>xI6ij?gtU0!Kc802n|9xkdBGgq1 z^dU|xec$)Hi_dwU=yi(AzgOIWQeRV$_O40_Xhloe|1(1gi$uR3Qb>~#qJB-XxieW(b3_lnqVM8~TW z#P5LQIh_1{du@#m!h}jm2C9`pN_M{&rjq%9&f_#>3YJ`y!^)J!j znRRG{Q#n7!vFp+Kf0BSc#SEjFk=9-i=2wdRH-;z)M^TybYvG14^tNG;E4O8u!aXE zA2wd$*QQPdMGF_TEQ>PPpEvZk!N~RN!R(|uBt9MVf{%R=@v1xoqlFLeqHKkSKx#q} zP)@^OFR#i&Fnpr%?#(+M+Mgn{gS1`4hoFzvYVfV?wfx5SCnCLnvNSwz`%E84D?gQ; RGJ{RN=-vn5v^46^**|PXjj;d# literal 0 HcmV?d00001 diff --git a/mobile/openapi/test/validate_access_token_response_dto_test.dart b/mobile/openapi/test/validate_access_token_response_dto_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..fff824700c7b8f3fe65d11b4dce65573cbe133a9 GIT binary patch literal 616 zcmah_%}T>S5WeRr##36Tjd~JAu#i>?+JHvE({@az?b2j--JK{!e0OJ4#e)S8J39m4 z-%OSzSq78GvN*n(E~fXBa+4q+#GhLusQ9~^m9{tR^b=LDi1~%`w3(9q@JiT_>3*BHV)uET4$p;bl;V`|j zCNLUSx)wvd(&T%3IB3>rLqRpxtOdp!H0u0BRU7mnN%k2bby#h2O&8XYL8mi$65KZG8Ku!Wr*ytLWCRIftly*zzo`RFal(S9VtgfL&Z3aX5NC1Sp(gqvQ z{Yn+V)jtx0;bkJkwYQxcq|x{vLOOzPv)=B-+6J)W%>yHML8PFMFIu*5fHwrr%PBnX fVEGGO?v*g?Ox1^vR9T=b@}igdjM1d)|Ig$LDp<}y literal 0 HcmV?d00001 diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 911cc10b19..ec0701fafe 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -637,6 +637,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + openapi: + dependency: "direct main" + description: + path: openapi + relative: true + source: path + version: "1.0.0" package_config: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 6633f1146e..5e284e3e62 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -48,6 +48,9 @@ dependencies: collection: ^1.16.0 http_parser: ^4.0.1 + openapi: + path: openapi + dev_dependencies: flutter_test: sdk: flutter @@ -76,3 +79,7 @@ flutter_icons: image_path_ios: "assets/immich-logo-no-outline.png" android: true # can specify file name here e.g. "ic_launcher" ios: true # can specify file name here e.g. "My-Launcher-Icon + +analyzer: + exclude: + - openapi/** diff --git a/server/apps/immich/src/api-v1/asset/asset.controller.ts b/server/apps/immich/src/api-v1/asset/asset.controller.ts index 45b5541496..2a7b30ab53 100644 --- a/server/apps/immich/src/api-v1/asset/asset.controller.ts +++ b/server/apps/immich/src/api-v1/asset/asset.controller.ts @@ -19,11 +19,10 @@ import { } from '@nestjs/common'; import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { AssetService } from './asset.service'; -import { FileFieldsInterceptor, FileInterceptor } from '@nestjs/platform-express'; +import { FileInterceptor } from '@nestjs/platform-express'; import { assetUploadOption } from '../../config/asset-upload.config'; import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { ServeFileDto } from './dto/serve-file.dto'; -import { AssetEntity } from '@app/database/entities/asset.entity'; import { Response as Res } from 'express'; import { BackgroundTaskService } from '../../modules/background-task/background-task.service'; import { DeleteAssetDto } from './dto/delete-asset.dto'; @@ -43,6 +42,7 @@ import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-a import { AssetFileUploadDto } from './dto/asset-file-upload.dto'; import { CreateAssetDto } from './dto/create-asset.dto'; import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto'; +import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto'; @UseGuards(JwtAuthGuard) @ApiBearerAuth() @@ -124,7 +124,7 @@ export class AssetController { } @Get('/searchTerm') - async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise { + async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise { return this.assetService.getAssetSearchTerm(authUser); } @@ -164,7 +164,10 @@ export class AssetController { } @Delete('/') - async deleteAsset(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) assetIds: DeleteAssetDto) { + async deleteAsset( + @GetAuthUser() authUser: AuthUserDto, + @Body(ValidationPipe) assetIds: DeleteAssetDto, + ): Promise { const deleteAssetList: AssetResponseDto[] = []; for (const id of assetIds.ids) { @@ -178,7 +181,7 @@ export class AssetController { const result = await this.assetService.deleteAssetById(authUser, assetIds); result.forEach((res) => { - deleteAssetList.filter((a) => a.id == res.id && res.status == 'success'); + deleteAssetList.filter((a) => a.id == res.id && res.status == DeleteAssetStatusEnum.SUCCESS); }); await this.backgroundTaskService.deleteFileOnDisk(deleteAssetList); diff --git a/server/apps/immich/src/api-v1/asset/asset.service.ts b/server/apps/immich/src/api-v1/asset/asset.service.ts index 647b45a315..e92e3c855b 100644 --- a/server/apps/immich/src/api-v1/asset/asset.service.ts +++ b/server/apps/immich/src/api-v1/asset/asset.service.ts @@ -22,6 +22,7 @@ import { CuratedObjectsResponseDto } from './response-dto/curated-objects-respon import { AssetResponseDto, mapAsset } from './response-dto/asset-response.dto'; import { AssetFileUploadDto } from './dto/asset-file-upload.dto'; import { CreateAssetDto } from './dto/create-asset.dto'; +import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto'; const fileInfo = promisify(stat); @@ -280,7 +281,7 @@ export class AssetService { return new StreamableFile(fileReadStream); } catch (e) { - Logger.error(`Cannot create read stream for asset ${asset.id}`, 'serveFile[IMAGE]'); + Logger.error(`Cannot create read stream for asset ${asset.id} ${JSON.stringify(e)}`, 'serveFile[IMAGE]'); throw new InternalServerErrorException( e, `Cannot read thumbnail file for asset ${asset.id} - contact your administrator`, @@ -354,8 +355,8 @@ export class AssetService { } } - public async deleteAssetById(authUser: AuthUserDto, assetIds: DeleteAssetDto) { - const result = []; + public async deleteAssetById(authUser: AuthUserDto, assetIds: DeleteAssetDto): Promise { + const result: DeleteAssetResponseDto[] = []; const target = assetIds.ids; for (const assetId of target) { @@ -367,12 +368,12 @@ export class AssetService { if (res.affected) { result.push({ id: assetId, - status: 'success', + status: DeleteAssetStatusEnum.SUCCESS, }); } else { result.push({ id: assetId, - status: 'failed', + status: DeleteAssetStatusEnum.FAILED, }); } } diff --git a/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts b/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts index ba57ee4df4..25ca5a835f 100644 --- a/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts @@ -1,7 +1,5 @@ -import { AssetType } from '@app/database/entities/asset.entity'; import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNotEmpty, IsOptional } from 'class-validator'; -import { CreateAssetDto } from './create-asset.dto'; +import { IsNotEmpty } from 'class-validator'; export class AssetFileUploadDto { @IsNotEmpty() diff --git a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts index f1194ec4de..7a5bcd3c09 100644 --- a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts @@ -1,5 +1,6 @@ import { IsNotEmpty, IsOptional } from 'class-validator'; import { AssetType } from '@app/database/entities/asset.entity'; +import { ApiProperty } from '@nestjs/swagger'; export class CreateAssetDto { @IsNotEmpty() @@ -9,6 +10,7 @@ export class CreateAssetDto { deviceId!: string; @IsNotEmpty() + @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) assetType!: AssetType; @IsNotEmpty() diff --git a/server/apps/immich/src/api-v1/asset/dto/get-all-asset-query.dto.ts b/server/apps/immich/src/api-v1/asset/dto/get-all-asset-query.dto.ts deleted file mode 100644 index 67f5e319fa..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/get-all-asset-query.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IsOptional } from 'class-validator'; - -export class GetAllAssetQueryDto { - @IsOptional() - nextPageKey?: string; -} diff --git a/server/apps/immich/src/api-v1/asset/dto/get-all-asset-response.dto.ts b/server/apps/immich/src/api-v1/asset/dto/get-all-asset-response.dto.ts deleted file mode 100644 index 5af3e205aa..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/get-all-asset-response.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { AssetEntity } from '@app/database/entities/asset.entity'; - -// TODO: this doesn't seem to be used -export class GetAllAssetReponseDto { - data!: Array<{ date: string; assets: Array }>; - count!: number; - nextPageKey!: string; -} diff --git a/server/apps/immich/src/api-v1/asset/dto/get-new-asset-query.dto.ts b/server/apps/immich/src/api-v1/asset/dto/get-new-asset-query.dto.ts deleted file mode 100644 index 2d98f0fb59..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/get-new-asset-query.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IsNotEmpty } from 'class-validator'; - -export class GetNewAssetQueryDto { - @IsNotEmpty() - latestDate!: string; -} diff --git a/server/apps/immich/src/api-v1/asset/dto/update-asset.dto.ts b/server/apps/immich/src/api-v1/asset/dto/update-asset.dto.ts deleted file mode 100644 index 891e14436b..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/update-asset.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateAssetDto } from './create-asset.dto'; - -export class UpdateAssetDto extends PartialType(CreateAssetDto) {} diff --git a/server/apps/immich/src/api-v1/asset/dto/update-exif.dto.ts b/server/apps/immich/src/api-v1/asset/dto/update-exif.dto.ts deleted file mode 100644 index 1bf5066649..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/update-exif.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateExifDto } from './create-exif.dto'; - -export class UpdateExifDto extends PartialType(CreateExifDto) {} diff --git a/server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts b/server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts index ef74145abf..5e4887fcd9 100644 --- a/server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts +++ b/server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts @@ -1,4 +1,5 @@ import { AssetEntity, AssetType } from '@app/database/entities/asset.entity'; +import { ApiProperty } from '@nestjs/swagger'; import { ExifResponseDto, mapExif } from './exif-response.dto'; import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto'; @@ -7,6 +8,8 @@ export class AssetResponseDto { deviceAssetId!: string; ownerId!: string; deviceId!: string; + + @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) type!: AssetType; originalPath!: string; resizePath!: string | null; diff --git a/server/apps/immich/src/api-v1/asset/response-dto/delete-asset-response.dto.ts b/server/apps/immich/src/api-v1/asset/response-dto/delete-asset-response.dto.ts new file mode 100644 index 0000000000..86f6afdf18 --- /dev/null +++ b/server/apps/immich/src/api-v1/asset/response-dto/delete-asset-response.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export enum DeleteAssetStatusEnum { + SUCCESS = 'SUCCESS', + FAILED = 'FAILED', +} + +export class DeleteAssetResponseDto { + id!: string; + + @ApiProperty({ type: 'string', enum: DeleteAssetStatusEnum, enumName: 'DeleteAssetStatus' }) + status!: DeleteAssetStatusEnum; +} diff --git a/server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts b/server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts index 57b0c8e9c0..c43c55b4eb 100644 --- a/server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts +++ b/server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts @@ -1,26 +1,26 @@ import { ExifEntity } from '@app/database/entities/exif.entity'; export class ExifResponseDto { - id!: string; - make: string | null = null; - model: string | null = null; - imageName: string | null = null; - exifImageWidth: number | null = null; - exifImageHeight: number | null = null; - fileSizeInByte: number | null = null; - orientation: string | null = null; - dateTimeOriginal: Date | null = null; - modifyDate: Date | null = null; - lensModel: string | null = null; - fNumber: number | null = null; - focalLength: number | null = null; - iso: number | null = null; - exposureTime: number | null = null; - latitude: number | null = null; - longitude: number | null = null; - city: string | null = null; - state: string | null = null; - country: string | null = null; + id?: string | null = null; + make?: string | null = null; + model?: string | null = null; + imageName?: string | null = null; + exifImageWidth?: number | null = null; + exifImageHeight?: number | null = null; + fileSizeInByte?: number | null = null; + orientation?: string | null = null; + dateTimeOriginal?: Date | null = null; + modifyDate?: Date | null = null; + lensModel?: string | null = null; + fNumber?: number | null = null; + focalLength?: number | null = null; + iso?: number | null = null; + exposureTime?: number | null = null; + latitude?: number | null = null; + longitude?: number | null = null; + city?: string | null = null; + state?: string | null = null; + country?: string | null = null; } export function mapExif(entity: ExifEntity): ExifResponseDto { diff --git a/server/apps/immich/src/api-v1/auth/response-dto/validate-asset-token-response.dto,.ts b/server/apps/immich/src/api-v1/auth/response-dto/validate-asset-token-response.dto,.ts index 151d59e6f1..9d4d770de2 100644 --- a/server/apps/immich/src/api-v1/auth/response-dto/validate-asset-token-response.dto,.ts +++ b/server/apps/immich/src/api-v1/auth/response-dto/validate-asset-token-response.dto,.ts @@ -1,7 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; + export class ValidateAccessTokenResponseDto { constructor(authStatus: boolean) { this.authStatus = authStatus; } - authStatus: boolean; + @ApiProperty({ type: 'boolean' }) + authStatus!: boolean; } diff --git a/server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts b/server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts index a9db6ea435..29dddea818 100644 --- a/server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts +++ b/server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts @@ -1,11 +1,13 @@ import { IsNotEmpty, IsOptional } from 'class-validator'; import { DeviceType } from '@app/database/entities/device-info.entity'; +import { ApiProperty } from '@nestjs/swagger'; export class CreateDeviceInfoDto { @IsNotEmpty() deviceId!: string; @IsNotEmpty() + @ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType }) deviceType!: DeviceType; @IsOptional() diff --git a/server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts b/server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts index cd2be701a7..a047e852b5 100644 --- a/server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts +++ b/server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts @@ -1,4 +1,17 @@ +import { DeviceType } from '@app/database/entities/device-info.entity'; import { PartialType } from '@nestjs/mapped-types'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsOptional } from 'class-validator'; import { CreateDeviceInfoDto } from './create-device-info.dto'; -export class UpdateDeviceInfoDto extends PartialType(CreateDeviceInfoDto) {} +export class UpdateDeviceInfoDto { + @IsNotEmpty() + deviceId!: string; + + @IsNotEmpty() + @ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType }) + deviceType!: DeviceType; + + @IsOptional() + isAutoBackup?: boolean; +} diff --git a/server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts b/server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts index e36389d332..5c1bcfadf9 100644 --- a/server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts +++ b/server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts @@ -1,11 +1,15 @@ import { DeviceInfoEntity, DeviceType } from '@app/database/entities/device-info.entity'; +import { ApiProperty } from '@nestjs/swagger'; export class DeviceInfoResponseDto { + @ApiProperty({ type: 'integer' }) id!: number; userId!: string; deviceId!: string; + + @ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType }) deviceType!: DeviceType; - notificationToken!: string | null; + createdAt!: string; isAutoBackup!: boolean; } @@ -16,7 +20,6 @@ export function mapDeviceInfoResponse(entity: DeviceInfoEntity): DeviceInfoRespo userId: entity.userId, deviceId: entity.deviceId, deviceType: entity.deviceType, - notificationToken: entity.notificationToken, createdAt: entity.createdAt, isAutoBackup: entity.isAutoBackup, }; diff --git a/server/apps/immich/src/api-v1/server-info/response-dto/server-info-response.dto.ts b/server/apps/immich/src/api-v1/server-info/response-dto/server-info-response.dto.ts index e0a5f5c2da..444292091b 100644 --- a/server/apps/immich/src/api-v1/server-info/response-dto/server-info-response.dto.ts +++ b/server/apps/immich/src/api-v1/server-info/response-dto/server-info-response.dto.ts @@ -1,9 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; + export class ServerInfoResponseDto { diskSize!: string; diskUse!: string; diskAvailable!: string; + + @ApiProperty({ type: 'integer' }) diskSizeRaw!: number; + + @ApiProperty({ type: 'integer' }) diskUseRaw!: number; + + @ApiProperty({ type: 'integer' }) diskAvailableRaw!: number; + + @ApiProperty({ type: 'number', format: 'float' }) diskUsagePercentage!: number; } diff --git a/server/apps/immich/src/api-v1/server-info/response-dto/server-version-response.dto.ts b/server/apps/immich/src/api-v1/server-info/response-dto/server-version-response.dto.ts index d054d9b3ce..812f3b0714 100644 --- a/server/apps/immich/src/api-v1/server-info/response-dto/server-version-response.dto.ts +++ b/server/apps/immich/src/api-v1/server-info/response-dto/server-version-response.dto.ts @@ -1,8 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; import { IServerVersion } from 'apps/immich/src/constants/server_version.constant'; export class ServerVersionReponseDto implements IServerVersion { + @ApiProperty({ type: 'integer' }) major!: number; + @ApiProperty({ type: 'integer' }) minor!: number; + @ApiProperty({ type: 'integer' }) patch!: number; + @ApiProperty({ type: 'integer' }) build!: number; } diff --git a/server/apps/immich/src/api-v1/server-info/server-info.service.ts b/server/apps/immich/src/api-v1/server-info/server-info.service.ts index 606161aa77..23de4123ea 100644 --- a/server/apps/immich/src/api-v1/server-info/server-info.service.ts +++ b/server/apps/immich/src/api-v1/server-info/server-info.service.ts @@ -5,7 +5,7 @@ import { APP_UPLOAD_LOCATION } from '../../constants/upload_location.constant'; @Injectable() export class ServerInfoService { - async getServerInfo() { + async getServerInfo(): Promise { const diskInfo = await diskusage.check(APP_UPLOAD_LOCATION); const usagePercentage = (((diskInfo.total - diskInfo.free) / diskInfo.total) * 100).toFixed(2); diff --git a/server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts b/server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts index a01f10f466..7b58ba5aa8 100644 --- a/server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts +++ b/server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts @@ -1,6 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Express } from 'express'; export class CreateProfileImageDto { @ApiProperty({ type: 'string', format: 'binary' }) - file: any; + file!: Express.Multer.File; } diff --git a/server/apps/immich/src/api-v1/user/response-dto/user-count-response.dto.ts b/server/apps/immich/src/api-v1/user/response-dto/user-count-response.dto.ts index 63df372350..bcdc3a5b78 100644 --- a/server/apps/immich/src/api-v1/user/response-dto/user-count-response.dto.ts +++ b/server/apps/immich/src/api-v1/user/response-dto/user-count-response.dto.ts @@ -1,5 +1,7 @@ +import { ApiProperty } from '@nestjs/swagger'; export class UserCountResponseDto { + @ApiProperty({ type: 'integer' }) userCount!: number; } @@ -7,4 +9,4 @@ export function mapUserCountResponse(count: number): UserCountResponseDto { return { userCount: count, }; -} \ No newline at end of file +} diff --git a/server/apps/immich/src/api-v1/user/user.controller.ts b/server/apps/immich/src/api-v1/user/user.controller.ts index 6ec543b1fa..3700050a42 100644 --- a/server/apps/immich/src/api-v1/user/user.controller.ts +++ b/server/apps/immich/src/api-v1/user/user.controller.ts @@ -11,6 +11,7 @@ import { UseInterceptors, UploadedFile, Response, + Request, StreamableFile, ParseBoolPipe, } from '@nestjs/common'; @@ -22,7 +23,7 @@ import { AdminRolesGuard } from '../../middlewares/admin-role-guard.middleware'; import { UpdateUserDto } from './dto/update-user.dto'; import { FileInterceptor } from '@nestjs/platform-express'; import { profileImageUploadOption } from '../../config/profile-image-upload.config'; -import { Response as Res } from 'express'; +import { Response as Res, Request as Req } from 'express'; import { ApiBearerAuth, ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger'; import { UserResponseDto } from './response-dto/user-response.dto'; import { UserCountResponseDto } from './response-dto/user-count-response.dto'; @@ -76,13 +77,16 @@ export class UserController { @ApiBearerAuth() @ApiConsumes('multipart/form-data') @ApiBody({ + description: 'A new avatar for the user', type: CreateProfileImageDto, }) @Post('/profile-image') async createProfileImage( @GetAuthUser() authUser: AuthUserDto, @UploadedFile() fileInfo: Express.Multer.File, + @Request() req: Req, ): Promise { + console.log(req.body, req.file); return await this.userService.createProfileImage(authUser, fileInfo); } diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index fadc2b713b..a70db8f2c1 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -1 +1 @@ -{"openapi":"3.0.0","paths":{"/user":{"get":{"operationId":"getAllUsers","parameters":[{"name":"isAll","required":true,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserResponseDto"}}}}}},"tags":["User"],"security":[{"bearer":[]}]},"post":{"operationId":"createUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]},"put":{"operationId":"updateUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/me":{"get":{"operationId":"getMyUserInfo","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/count":{"get":{"operationId":"getUserCount","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCountResponseDto"}}}}},"tags":["User"]}},"/user/profile-image":{"post":{"operationId":"createProfileImage","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/CreateProfileImageDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProfileImageResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/profile-image/{userId}":{"get":{"operationId":"getProfileImage","parameters":[{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["User"]}},"/asset/upload":{"post":{"operationId":"uploadFile","parameters":[],"requestBody":{"required":true,"description":"Asset Upload Information","content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AssetFileUploadDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFileUploadResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/download":{"get":{"operationId":"downloadFile","parameters":[{"name":"aid","required":true,"in":"query","schema":{"title":"Device Asset ID","type":"string"}},{"name":"did","required":true,"in":"query","schema":{"title":"Device ID","type":"string"}},{"name":"isThumb","required":false,"in":"query","schema":{"title":"Is serve thumbnail (resize) file","type":"boolean"}},{"name":"isWeb","required":false,"in":"query","schema":{"title":"Is request made from web","type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/file":{"get":{"operationId":"serveFile","parameters":[{"name":"aid","required":true,"in":"query","schema":{"title":"Device Asset ID","type":"string"}},{"name":"did","required":true,"in":"query","schema":{"title":"Device ID","type":"string"}},{"name":"isThumb","required":false,"in":"query","schema":{"title":"Is serve thumbnail (resize) file","type":"boolean"}},{"name":"isWeb","required":false,"in":"query","schema":{"title":"Is request made from web","type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/thumbnail/{assetId}":{"get":{"operationId":"getAssetThumbnail","parameters":[{"name":"assetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/allObjects":{"get":{"operationId":"getCuratedObjects","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CuratedObjectsResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/allLocation":{"get":{"operationId":"getCuratedLocations","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CuratedLocationsResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/searchTerm":{"get":{"operationId":"getAssetSearchTerms","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/search":{"post":{"operationId":"searchAsset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchAssetDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset":{"get":{"operationId":"getAllAssets","summary":"","description":"Get all AssetEntity belong to the user","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]},"delete":{"operationId":"deleteAsset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAssetDto"}}}},"responses":{"200":{"description":""}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/{deviceId}":{"get":{"operationId":"getUserAssetsByDeviceId","summary":"","description":"Get all asset of a device that are in the database, ID only.","parameters":[{"name":"deviceId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/assetById/{assetId}":{"get":{"operationId":"getAssetById","summary":"","description":"Get a single asset's information","parameters":[{"name":"assetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/check":{"post":{"operationId":"checkDuplicateAsset","summary":"","description":"Check duplicated asset before uploading - for Web upload used","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateAssetDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateAssetResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/auth/login":{"post":{"operationId":"login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginCredentialDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponseDto"}}}}},"tags":["Authentication"]}},"/auth/admin-sign-up":{"post":{"operationId":"adminSignUp","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignUpDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSignupResponseDto"}}}},"400":{"description":"The server already has an admin"}},"tags":["Authentication"]}},"/auth/validateToken":{"post":{"operationId":"validateAccessToken","parameters":[],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateAccessTokenResponseDto"}}}}},"tags":["Authentication"],"security":[{"bearer":[]}]}},"/device-info":{"post":{"operationId":"createDeviceInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeviceInfoDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfoResponseDto"}}}}},"tags":["Device Info"],"security":[{"bearer":[]}]},"patch":{"operationId":"updateDeviceInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDeviceInfoDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfoResponseDto"}}}}},"tags":["Device Info"],"security":[{"bearer":[]}]}},"/server-info":{"get":{"operationId":"getServerInfo","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerInfoResponseDto"}}}}},"tags":["Server Info"]}},"/server-info/ping":{"get":{"operationId":"pingServer","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerPingResponse"}}}}},"tags":["Server Info"]}},"/server-info/version":{"get":{"operationId":"getServerVersion","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerVersionReponseDto"}}}}},"tags":["Server Info"]}},"/album":{"post":{"operationId":"createAlbum","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAlbumDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"get":{"operationId":"getAllAlbums","parameters":[{"name":"shared","required":false,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/users":{"put":{"operationId":"addUsersToAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUsersDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/assets":{"put":{"operationId":"addAssetsToAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAssetsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"delete":{"operationId":"removeAssetFromAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveAssetsDto"}}}},"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}":{"get":{"operationId":"getAlbumInfo","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"delete":{"operationId":"deleteAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]},"patch":{"operationId":"updateAlbumInfo","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAlbumDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/user/{userId}":{"delete":{"operationId":"removeUserFromAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]}}},"info":{"title":"Immich","description":"Immich API","version":"1.17.0","contact":{}},"tags":[],"servers":[{"url":"/api"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http","name":"JWT","description":"Enter JWT token","in":"header"}},"schemas":{"UserResponseDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"createdAt":{"type":"string"},"profileImagePath":{"type":"string"},"shouldChangePassword":{"type":"boolean"},"isAdmin":{"type":"boolean"}},"required":["id","email","firstName","lastName","createdAt","profileImagePath","shouldChangePassword","isAdmin"]},"CreateUserDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"Doe"}},"required":["email","password","firstName","lastName"]},"UserCountResponseDto":{"type":"object","properties":{"userCount":{"type":"number"}},"required":["userCount"]},"UpdateUserDto":{"type":"object","properties":{"id":{"type":"string"},"password":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"isAdmin":{"type":"boolean"},"shouldChangePassword":{"type":"boolean"},"profileImagePath":{"type":"string"}},"required":["id"]},"CreateProfileImageDto":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]},"CreateProfileImageResponseDto":{"type":"object","properties":{"userId":{"type":"string"},"profileImagePath":{"type":"string"}},"required":["userId","profileImagePath"]},"AssetFileUploadDto":{"type":"object","properties":{"assetData":{"type":"string","format":"binary"}},"required":["assetData"]},"AssetFileUploadResponseDto":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"CuratedObjectsResponseDto":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string"},"resizePath":{"type":"string"},"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["id","object","resizePath","deviceAssetId","deviceId"]},"CuratedLocationsResponseDto":{"type":"object","properties":{"id":{"type":"string"},"city":{"type":"string"},"resizePath":{"type":"string"},"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["id","city","resizePath","deviceAssetId","deviceId"]},"SearchAssetDto":{"type":"object","properties":{"searchTerm":{"type":"string"}},"required":["searchTerm"]},"ExifResponseDto":{"type":"object","properties":{"id":{"type":"string"},"make":{"type":"string","nullable":true,"default":null},"model":{"type":"string","nullable":true,"default":null},"imageName":{"type":"string","nullable":true,"default":null},"exifImageWidth":{"type":"number","nullable":true,"default":null},"exifImageHeight":{"type":"number","nullable":true,"default":null},"fileSizeInByte":{"type":"number","nullable":true,"default":null},"orientation":{"type":"string","nullable":true,"default":null},"dateTimeOriginal":{"format":"date-time","type":"string","nullable":true,"default":null},"modifyDate":{"format":"date-time","type":"string","nullable":true,"default":null},"lensModel":{"type":"string","nullable":true,"default":null},"fNumber":{"type":"number","nullable":true,"default":null},"focalLength":{"type":"number","nullable":true,"default":null},"iso":{"type":"number","nullable":true,"default":null},"exposureTime":{"type":"number","nullable":true,"default":null},"latitude":{"type":"number","nullable":true,"default":null},"longitude":{"type":"number","nullable":true,"default":null},"city":{"type":"string","nullable":true,"default":null},"state":{"type":"string","nullable":true,"default":null},"country":{"type":"string","nullable":true,"default":null}},"required":["id","make","model","imageName","exifImageWidth","exifImageHeight","fileSizeInByte","orientation","dateTimeOriginal","modifyDate","lensModel","fNumber","focalLength","iso","exposureTime","latitude","longitude","city","state","country"]},"SmartInfoResponseDto":{"type":"object","properties":{"id":{"type":"string"},"tags":{"nullable":true,"type":"array","items":{"type":"string"}},"objects":{"nullable":true,"type":"array","items":{"type":"string"}}}},"AssetResponseDto":{"type":"object","properties":{"id":{"type":"string"},"deviceAssetId":{"type":"string"},"ownerId":{"type":"string"},"deviceId":{"type":"string"},"type":{"enum":["IMAGE","VIDEO","AUDIO","OTHER"],"type":"string"},"originalPath":{"type":"string"},"resizePath":{"type":"string","nullable":true},"createdAt":{"type":"string"},"modifiedAt":{"type":"string"},"isFavorite":{"type":"boolean"},"mimeType":{"type":"string","nullable":true},"duration":{"type":"string"},"webpPath":{"type":"string","nullable":true},"encodedVideoPath":{"type":"string","nullable":true},"exifInfo":{"$ref":"#/components/schemas/ExifResponseDto"},"smartInfo":{"$ref":"#/components/schemas/SmartInfoResponseDto"}},"required":["id","deviceAssetId","ownerId","deviceId","type","originalPath","resizePath","createdAt","modifiedAt","isFavorite","mimeType","duration","webpPath","encodedVideoPath"]},"DeleteAssetDto":{"type":"object","properties":{"ids":{"title":"Array of asset IDs to delete","example":["bf973405-3f2a-48d2-a687-2ed4167164be","dd41870b-5d00-46d2-924e-1d8489a0aa0f","fad77c3f-deef-4e7e-9608-14c1aa4e559a"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"CheckDuplicateAssetDto":{"type":"object","properties":{"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["deviceAssetId","deviceId"]},"CheckDuplicateAssetResponseDto":{"type":"object","properties":{"isExist":{"type":"boolean"}},"required":["isExist"]},"LoginCredentialDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"}},"required":["email","password"]},"LoginResponseDto":{"type":"object","properties":{"accessToken":{"type":"string","readOnly":true},"userId":{"type":"string","readOnly":true},"userEmail":{"type":"string","readOnly":true},"firstName":{"type":"string","readOnly":true},"lastName":{"type":"string","readOnly":true},"profileImagePath":{"type":"string","readOnly":true},"isAdmin":{"type":"boolean","readOnly":true},"shouldChangePassword":{"type":"boolean","readOnly":true}},"required":["accessToken","userId","userEmail","firstName","lastName","profileImagePath","isAdmin","shouldChangePassword"]},"SignUpDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"},"firstName":{"type":"string","example":"Admin"},"lastName":{"type":"string","example":"Doe"}},"required":["email","password","firstName","lastName"]},"AdminSignupResponseDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","email","firstName","lastName","createdAt"]},"ValidateAccessTokenResponseDto":{"type":"object","properties":{}},"CreateDeviceInfoDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["IOS","ANDROID","WEB"]},"isAutoBackup":{"type":"boolean"}},"required":["deviceId","deviceType"]},"DeviceInfoResponseDto":{"type":"object","properties":{"id":{"type":"number"},"userId":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"enum":["IOS","ANDROID","WEB"],"type":"string"},"notificationToken":{"type":"string","nullable":true},"createdAt":{"type":"string"},"isAutoBackup":{"type":"boolean"}},"required":["id","userId","deviceId","deviceType","notificationToken","createdAt","isAutoBackup"]},"UpdateDeviceInfoDto":{"type":"object","properties":{}},"ServerInfoResponseDto":{"type":"object","properties":{"diskSize":{"type":"string"},"diskUse":{"type":"string"},"diskAvailable":{"type":"string"},"diskSizeRaw":{"type":"number"},"diskUseRaw":{"type":"number"},"diskAvailableRaw":{"type":"number"},"diskUsagePercentage":{"type":"number"}},"required":["diskSize","diskUse","diskAvailable","diskSizeRaw","diskUseRaw","diskAvailableRaw","diskUsagePercentage"]},"ServerPingResponse":{"type":"object","properties":{"res":{"type":"string","readOnly":true,"example":"pong"}},"required":["res"]},"ServerVersionReponseDto":{"type":"object","properties":{"major":{"type":"number"},"minor":{"type":"number"},"patch":{"type":"number"},"build":{"type":"number"}},"required":["major","minor","patch","build"]},"CreateAlbumDto":{"type":"object","properties":{"albumName":{"type":"string"},"sharedWithUserIds":{"type":"array","items":{"type":"string"}},"assetIds":{"type":"array","items":{"type":"string"}}},"required":["albumName"]},"AlbumResponseDto":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"albumName":{"type":"string"},"createdAt":{"type":"string"},"albumThumbnailAssetId":{"type":"string","nullable":true},"shared":{"type":"boolean"},"sharedUsers":{"type":"array","items":{"$ref":"#/components/schemas/UserResponseDto"}},"assets":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}},"required":["id","ownerId","albumName","createdAt","albumThumbnailAssetId","shared","sharedUsers","assets"]},"AddUsersDto":{"type":"object","properties":{"sharedUserIds":{"type":"array","items":{"type":"string"}}},"required":["sharedUserIds"]},"AddAssetsDto":{"type":"object","properties":{"assetIds":{"type":"array","items":{"type":"string"}}},"required":["assetIds"]},"RemoveAssetsDto":{"type":"object","properties":{"assetIds":{"type":"array","items":{"type":"string"}}},"required":["assetIds"]},"UpdateAlbumDto":{"type":"object","properties":{"albumName":{"type":"string"},"ownerId":{"type":"string"}},"required":["albumName","ownerId"]}}}} \ No newline at end of file +{"openapi":"3.0.0","paths":{"/user":{"get":{"operationId":"getAllUsers","parameters":[{"name":"isAll","required":true,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserResponseDto"}}}}}},"tags":["User"],"security":[{"bearer":[]}]},"post":{"operationId":"createUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]},"put":{"operationId":"updateUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/me":{"get":{"operationId":"getMyUserInfo","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/count":{"get":{"operationId":"getUserCount","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCountResponseDto"}}}}},"tags":["User"]}},"/user/profile-image":{"post":{"operationId":"createProfileImage","parameters":[],"requestBody":{"required":true,"description":"A new avatar for the user","content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/CreateProfileImageDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProfileImageResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/profile-image/{userId}":{"get":{"operationId":"getProfileImage","parameters":[{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["User"]}},"/asset/upload":{"post":{"operationId":"uploadFile","parameters":[],"requestBody":{"required":true,"description":"Asset Upload Information","content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AssetFileUploadDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFileUploadResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/download":{"get":{"operationId":"downloadFile","parameters":[{"name":"aid","required":true,"in":"query","schema":{"title":"Device Asset ID","type":"string"}},{"name":"did","required":true,"in":"query","schema":{"title":"Device ID","type":"string"}},{"name":"isThumb","required":false,"in":"query","schema":{"title":"Is serve thumbnail (resize) file","type":"boolean"}},{"name":"isWeb","required":false,"in":"query","schema":{"title":"Is request made from web","type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/file":{"get":{"operationId":"serveFile","parameters":[{"name":"aid","required":true,"in":"query","schema":{"title":"Device Asset ID","type":"string"}},{"name":"did","required":true,"in":"query","schema":{"title":"Device ID","type":"string"}},{"name":"isThumb","required":false,"in":"query","schema":{"title":"Is serve thumbnail (resize) file","type":"boolean"}},{"name":"isWeb","required":false,"in":"query","schema":{"title":"Is request made from web","type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/thumbnail/{assetId}":{"get":{"operationId":"getAssetThumbnail","parameters":[{"name":"assetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/allObjects":{"get":{"operationId":"getCuratedObjects","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CuratedObjectsResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/allLocation":{"get":{"operationId":"getCuratedLocations","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CuratedLocationsResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/searchTerm":{"get":{"operationId":"getAssetSearchTerms","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/search":{"post":{"operationId":"searchAsset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchAssetDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset":{"get":{"operationId":"getAllAssets","summary":"","description":"Get all AssetEntity belong to the user","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]},"delete":{"operationId":"deleteAsset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAssetDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DeleteAssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/{deviceId}":{"get":{"operationId":"getUserAssetsByDeviceId","summary":"","description":"Get all asset of a device that are in the database, ID only.","parameters":[{"name":"deviceId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/assetById/{assetId}":{"get":{"operationId":"getAssetById","summary":"","description":"Get a single asset's information","parameters":[{"name":"assetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/check":{"post":{"operationId":"checkDuplicateAsset","summary":"","description":"Check duplicated asset before uploading - for Web upload used","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateAssetDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateAssetResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/auth/login":{"post":{"operationId":"login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginCredentialDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponseDto"}}}}},"tags":["Authentication"]}},"/auth/admin-sign-up":{"post":{"operationId":"adminSignUp","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignUpDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSignupResponseDto"}}}},"400":{"description":"The server already has an admin"}},"tags":["Authentication"]}},"/auth/validateToken":{"post":{"operationId":"validateAccessToken","parameters":[],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateAccessTokenResponseDto"}}}}},"tags":["Authentication"],"security":[{"bearer":[]}]}},"/device-info":{"post":{"operationId":"createDeviceInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeviceInfoDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfoResponseDto"}}}}},"tags":["Device Info"],"security":[{"bearer":[]}]},"patch":{"operationId":"updateDeviceInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDeviceInfoDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfoResponseDto"}}}}},"tags":["Device Info"],"security":[{"bearer":[]}]}},"/server-info":{"get":{"operationId":"getServerInfo","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerInfoResponseDto"}}}}},"tags":["Server Info"]}},"/server-info/ping":{"get":{"operationId":"pingServer","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerPingResponse"}}}}},"tags":["Server Info"]}},"/server-info/version":{"get":{"operationId":"getServerVersion","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerVersionReponseDto"}}}}},"tags":["Server Info"]}},"/album":{"post":{"operationId":"createAlbum","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAlbumDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"get":{"operationId":"getAllAlbums","parameters":[{"name":"shared","required":false,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/users":{"put":{"operationId":"addUsersToAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUsersDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/assets":{"put":{"operationId":"addAssetsToAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAssetsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"delete":{"operationId":"removeAssetFromAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveAssetsDto"}}}},"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}":{"get":{"operationId":"getAlbumInfo","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"delete":{"operationId":"deleteAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]},"patch":{"operationId":"updateAlbumInfo","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAlbumDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/user/{userId}":{"delete":{"operationId":"removeUserFromAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]}}},"info":{"title":"Immich","description":"Immich API","version":"1.17.0","contact":{}},"tags":[],"servers":[{"url":"/api"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http","name":"JWT","description":"Enter JWT token","in":"header"}},"schemas":{"UserResponseDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"createdAt":{"type":"string"},"profileImagePath":{"type":"string"},"shouldChangePassword":{"type":"boolean"},"isAdmin":{"type":"boolean"}},"required":["id","email","firstName","lastName","createdAt","profileImagePath","shouldChangePassword","isAdmin"]},"CreateUserDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"Doe"}},"required":["email","password","firstName","lastName"]},"UserCountResponseDto":{"type":"object","properties":{"userCount":{"type":"integer"}},"required":["userCount"]},"UpdateUserDto":{"type":"object","properties":{"id":{"type":"string"},"password":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"isAdmin":{"type":"boolean"},"shouldChangePassword":{"type":"boolean"},"profileImagePath":{"type":"string"}},"required":["id"]},"CreateProfileImageDto":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]},"CreateProfileImageResponseDto":{"type":"object","properties":{"userId":{"type":"string"},"profileImagePath":{"type":"string"}},"required":["userId","profileImagePath"]},"AssetFileUploadDto":{"type":"object","properties":{"assetData":{"type":"string","format":"binary"}},"required":["assetData"]},"AssetFileUploadResponseDto":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"CuratedObjectsResponseDto":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string"},"resizePath":{"type":"string"},"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["id","object","resizePath","deviceAssetId","deviceId"]},"CuratedLocationsResponseDto":{"type":"object","properties":{"id":{"type":"string"},"city":{"type":"string"},"resizePath":{"type":"string"},"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["id","city","resizePath","deviceAssetId","deviceId"]},"SearchAssetDto":{"type":"object","properties":{"searchTerm":{"type":"string"}},"required":["searchTerm"]},"AssetTypeEnum":{"type":"string","enum":["IMAGE","VIDEO","AUDIO","OTHER"]},"ExifResponseDto":{"type":"object","properties":{"id":{"type":"string","nullable":true,"default":null},"make":{"type":"string","nullable":true,"default":null},"model":{"type":"string","nullable":true,"default":null},"imageName":{"type":"string","nullable":true,"default":null},"exifImageWidth":{"type":"number","nullable":true,"default":null},"exifImageHeight":{"type":"number","nullable":true,"default":null},"fileSizeInByte":{"type":"number","nullable":true,"default":null},"orientation":{"type":"string","nullable":true,"default":null},"dateTimeOriginal":{"format":"date-time","type":"string","nullable":true,"default":null},"modifyDate":{"format":"date-time","type":"string","nullable":true,"default":null},"lensModel":{"type":"string","nullable":true,"default":null},"fNumber":{"type":"number","nullable":true,"default":null},"focalLength":{"type":"number","nullable":true,"default":null},"iso":{"type":"number","nullable":true,"default":null},"exposureTime":{"type":"number","nullable":true,"default":null},"latitude":{"type":"number","nullable":true,"default":null},"longitude":{"type":"number","nullable":true,"default":null},"city":{"type":"string","nullable":true,"default":null},"state":{"type":"string","nullable":true,"default":null},"country":{"type":"string","nullable":true,"default":null}}},"SmartInfoResponseDto":{"type":"object","properties":{"id":{"type":"string"},"tags":{"nullable":true,"type":"array","items":{"type":"string"}},"objects":{"nullable":true,"type":"array","items":{"type":"string"}}}},"AssetResponseDto":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/AssetTypeEnum"},"id":{"type":"string"},"deviceAssetId":{"type":"string"},"ownerId":{"type":"string"},"deviceId":{"type":"string"},"originalPath":{"type":"string"},"resizePath":{"type":"string","nullable":true},"createdAt":{"type":"string"},"modifiedAt":{"type":"string"},"isFavorite":{"type":"boolean"},"mimeType":{"type":"string","nullable":true},"duration":{"type":"string"},"webpPath":{"type":"string","nullable":true},"encodedVideoPath":{"type":"string","nullable":true},"exifInfo":{"$ref":"#/components/schemas/ExifResponseDto"},"smartInfo":{"$ref":"#/components/schemas/SmartInfoResponseDto"}},"required":["type","id","deviceAssetId","ownerId","deviceId","originalPath","resizePath","createdAt","modifiedAt","isFavorite","mimeType","duration","webpPath","encodedVideoPath"]},"DeleteAssetDto":{"type":"object","properties":{"ids":{"title":"Array of asset IDs to delete","example":["bf973405-3f2a-48d2-a687-2ed4167164be","dd41870b-5d00-46d2-924e-1d8489a0aa0f","fad77c3f-deef-4e7e-9608-14c1aa4e559a"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"DeleteAssetStatus":{"type":"string","enum":["SUCCESS","FAILED"]},"DeleteAssetResponseDto":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/DeleteAssetStatus"},"id":{"type":"string"}},"required":["status","id"]},"CheckDuplicateAssetDto":{"type":"object","properties":{"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["deviceAssetId","deviceId"]},"CheckDuplicateAssetResponseDto":{"type":"object","properties":{"isExist":{"type":"boolean"}},"required":["isExist"]},"LoginCredentialDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"}},"required":["email","password"]},"LoginResponseDto":{"type":"object","properties":{"accessToken":{"type":"string","readOnly":true},"userId":{"type":"string","readOnly":true},"userEmail":{"type":"string","readOnly":true},"firstName":{"type":"string","readOnly":true},"lastName":{"type":"string","readOnly":true},"profileImagePath":{"type":"string","readOnly":true},"isAdmin":{"type":"boolean","readOnly":true},"shouldChangePassword":{"type":"boolean","readOnly":true}},"required":["accessToken","userId","userEmail","firstName","lastName","profileImagePath","isAdmin","shouldChangePassword"]},"SignUpDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"},"firstName":{"type":"string","example":"Admin"},"lastName":{"type":"string","example":"Doe"}},"required":["email","password","firstName","lastName"]},"AdminSignupResponseDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","email","firstName","lastName","createdAt"]},"ValidateAccessTokenResponseDto":{"type":"object","properties":{"authStatus":{"type":"boolean"}},"required":["authStatus"]},"DeviceTypeEnum":{"type":"string","enum":["IOS","ANDROID","WEB"]},"CreateDeviceInfoDto":{"type":"object","properties":{"deviceType":{"$ref":"#/components/schemas/DeviceTypeEnum"},"deviceId":{"type":"string"},"isAutoBackup":{"type":"boolean"}},"required":["deviceType","deviceId"]},"DeviceInfoResponseDto":{"type":"object","properties":{"id":{"type":"integer"},"deviceType":{"$ref":"#/components/schemas/DeviceTypeEnum"},"userId":{"type":"string"},"deviceId":{"type":"string"},"createdAt":{"type":"string"},"isAutoBackup":{"type":"boolean"}},"required":["id","deviceType","userId","deviceId","createdAt","isAutoBackup"]},"UpdateDeviceInfoDto":{"type":"object","properties":{"deviceType":{"$ref":"#/components/schemas/DeviceTypeEnum"},"deviceId":{"type":"string"},"isAutoBackup":{"type":"boolean"}},"required":["deviceType","deviceId"]},"ServerInfoResponseDto":{"type":"object","properties":{"diskSizeRaw":{"type":"integer"},"diskUseRaw":{"type":"integer"},"diskAvailableRaw":{"type":"integer"},"diskUsagePercentage":{"type":"number","format":"float"},"diskSize":{"type":"string"},"diskUse":{"type":"string"},"diskAvailable":{"type":"string"}},"required":["diskSizeRaw","diskUseRaw","diskAvailableRaw","diskUsagePercentage","diskSize","diskUse","diskAvailable"]},"ServerPingResponse":{"type":"object","properties":{"res":{"type":"string","readOnly":true,"example":"pong"}},"required":["res"]},"ServerVersionReponseDto":{"type":"object","properties":{"major":{"type":"integer"},"minor":{"type":"integer"},"patch":{"type":"integer"},"build":{"type":"integer"}},"required":["major","minor","patch","build"]},"CreateAlbumDto":{"type":"object","properties":{"albumName":{"type":"string"},"sharedWithUserIds":{"type":"array","items":{"type":"string"}},"assetIds":{"type":"array","items":{"type":"string"}}},"required":["albumName"]},"AlbumResponseDto":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"albumName":{"type":"string"},"createdAt":{"type":"string"},"albumThumbnailAssetId":{"type":"string","nullable":true},"shared":{"type":"boolean"},"sharedUsers":{"type":"array","items":{"$ref":"#/components/schemas/UserResponseDto"}},"assets":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}},"required":["id","ownerId","albumName","createdAt","albumThumbnailAssetId","shared","sharedUsers","assets"]},"AddUsersDto":{"type":"object","properties":{"sharedUserIds":{"type":"array","items":{"type":"string"}}},"required":["sharedUserIds"]},"AddAssetsDto":{"type":"object","properties":{"assetIds":{"type":"array","items":{"type":"string"}}},"required":["assetIds"]},"RemoveAssetsDto":{"type":"object","properties":{"assetIds":{"type":"array","items":{"type":"string"}}},"required":["assetIds"]},"UpdateAlbumDto":{"type":"object","properties":{"albumName":{"type":"string"},"ownerId":{"type":"string"}},"required":["albumName","ownerId"]}}}} \ No newline at end of file diff --git a/server/package.json b/server/package.json index ab0910ac9c..7eccabdd05 100644 --- a/server/package.json +++ b/server/package.json @@ -23,7 +23,9 @@ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./apps/immich/test/jest-e2e.json", "typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js", - "api:generate-typescript": "rm -rf ../web/src/api/open-api && npx openapi-generator-cli generate -g typescript-axios -i ./immich-openapi-specs.json -o ../web/src/api/open-api" + "api:typescript": "rm -rf ../web/src/api/open-api && npx openapi-generator-cli generate -g typescript-axios -i ./immich-openapi-specs.json -o ../web/src/api/open-api", + "api:dart": "npx openapi-generator-cli generate -g dart -i ./immich-openapi-specs.json -o ../mobile/openapi", + "api:generate": "npm run api:typescript && npm run api:dart" }, "dependencies": { "@mapbox/mapbox-sdk": "^0.13.3", diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index c94ef1437c..b61c24d1bc 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -158,6 +158,12 @@ export interface AssetFileUploadResponseDto { * @interface AssetResponseDto */ export interface AssetResponseDto { + /** + * + * @type {AssetTypeEnum} + * @memberof AssetResponseDto + */ + 'type': AssetTypeEnum; /** * * @type {string} @@ -182,12 +188,6 @@ export interface AssetResponseDto { * @memberof AssetResponseDto */ 'deviceId': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'type': AssetResponseDtoTypeEnum; /** * * @type {string} @@ -255,15 +255,21 @@ export interface AssetResponseDto { */ 'smartInfo'?: SmartInfoResponseDto; } +/** + * + * @export + * @enum {string} + */ -export const AssetResponseDtoTypeEnum = { +export const AssetTypeEnum = { Image: 'IMAGE', Video: 'VIDEO', Audio: 'AUDIO', Other: 'OTHER' } as const; -export type AssetResponseDtoTypeEnum = typeof AssetResponseDtoTypeEnum[keyof typeof AssetResponseDtoTypeEnum]; +export type AssetTypeEnum = typeof AssetTypeEnum[keyof typeof AssetTypeEnum]; + /** * @@ -330,16 +336,16 @@ export interface CreateAlbumDto { export interface CreateDeviceInfoDto { /** * - * @type {string} + * @type {DeviceTypeEnum} * @memberof CreateDeviceInfoDto */ - 'deviceId': string; + 'deviceType': DeviceTypeEnum; /** * * @type {string} * @memberof CreateDeviceInfoDto */ - 'deviceType': CreateDeviceInfoDtoDeviceTypeEnum; + 'deviceId': string; /** * * @type {boolean} @@ -347,15 +353,6 @@ export interface CreateDeviceInfoDto { */ 'isAutoBackup'?: boolean; } - -export const CreateDeviceInfoDtoDeviceTypeEnum = { - Ios: 'IOS', - Android: 'ANDROID', - Web: 'WEB' -} as const; - -export type CreateDeviceInfoDtoDeviceTypeEnum = typeof CreateDeviceInfoDtoDeviceTypeEnum[keyof typeof CreateDeviceInfoDtoDeviceTypeEnum]; - /** * * @export @@ -493,6 +490,39 @@ export interface DeleteAssetDto { */ 'ids': Array; } +/** + * + * @export + * @interface DeleteAssetResponseDto + */ +export interface DeleteAssetResponseDto { + /** + * + * @type {DeleteAssetStatus} + * @memberof DeleteAssetResponseDto + */ + 'status': DeleteAssetStatus; + /** + * + * @type {string} + * @memberof DeleteAssetResponseDto + */ + 'id': string; +} +/** + * + * @export + * @enum {string} + */ + +export const DeleteAssetStatus = { + Success: 'SUCCESS', + Failed: 'FAILED' +} as const; + +export type DeleteAssetStatus = typeof DeleteAssetStatus[keyof typeof DeleteAssetStatus]; + + /** * * @export @@ -505,6 +535,12 @@ export interface DeviceInfoResponseDto { * @memberof DeviceInfoResponseDto */ 'id': number; + /** + * + * @type {DeviceTypeEnum} + * @memberof DeviceInfoResponseDto + */ + 'deviceType': DeviceTypeEnum; /** * * @type {string} @@ -517,18 +553,6 @@ export interface DeviceInfoResponseDto { * @memberof DeviceInfoResponseDto */ 'deviceId': string; - /** - * - * @type {string} - * @memberof DeviceInfoResponseDto - */ - 'deviceType': DeviceInfoResponseDtoDeviceTypeEnum; - /** - * - * @type {string} - * @memberof DeviceInfoResponseDto - */ - 'notificationToken': string | null; /** * * @type {string} @@ -542,14 +566,20 @@ export interface DeviceInfoResponseDto { */ 'isAutoBackup': boolean; } +/** + * + * @export + * @enum {string} + */ -export const DeviceInfoResponseDtoDeviceTypeEnum = { +export const DeviceTypeEnum = { Ios: 'IOS', Android: 'ANDROID', Web: 'WEB' } as const; -export type DeviceInfoResponseDtoDeviceTypeEnum = typeof DeviceInfoResponseDtoDeviceTypeEnum[keyof typeof DeviceInfoResponseDtoDeviceTypeEnum]; +export type DeviceTypeEnum = typeof DeviceTypeEnum[keyof typeof DeviceTypeEnum]; + /** * @@ -562,121 +592,121 @@ export interface ExifResponseDto { * @type {string} * @memberof ExifResponseDto */ - 'id': string; + 'id'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'make': string | null; + 'make'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'model': string | null; + 'model'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'imageName': string | null; + 'imageName'?: string | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'exifImageWidth': number | null; + 'exifImageWidth'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'exifImageHeight': number | null; + 'exifImageHeight'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'fileSizeInByte': number | null; + 'fileSizeInByte'?: number | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'orientation': string | null; + 'orientation'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'dateTimeOriginal': string | null; + 'dateTimeOriginal'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'modifyDate': string | null; + 'modifyDate'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'lensModel': string | null; + 'lensModel'?: string | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'fNumber': number | null; + 'fNumber'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'focalLength': number | null; + 'focalLength'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'iso': number | null; + 'iso'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'exposureTime': number | null; + 'exposureTime'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'latitude': number | null; + 'latitude'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'longitude': number | null; + 'longitude'?: number | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'city': string | null; + 'city'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'state': string | null; + 'state'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'country': string | null; + 'country'?: string | null; } /** * @@ -784,24 +814,6 @@ export interface SearchAssetDto { * @interface ServerInfoResponseDto */ export interface ServerInfoResponseDto { - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskSize': string; - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskUse': string; - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskAvailable': string; /** * * @type {number} @@ -826,6 +838,24 @@ export interface ServerInfoResponseDto { * @memberof ServerInfoResponseDto */ 'diskUsagePercentage': number; + /** + * + * @type {string} + * @memberof ServerInfoResponseDto + */ + 'diskSize': string; + /** + * + * @type {string} + * @memberof ServerInfoResponseDto + */ + 'diskUse': string; + /** + * + * @type {string} + * @memberof ServerInfoResponseDto + */ + 'diskAvailable': string; } /** * @@ -946,6 +976,31 @@ export interface UpdateAlbumDto { */ 'ownerId': string; } +/** + * + * @export + * @interface UpdateDeviceInfoDto + */ +export interface UpdateDeviceInfoDto { + /** + * + * @type {DeviceTypeEnum} + * @memberof UpdateDeviceInfoDto + */ + 'deviceType': DeviceTypeEnum; + /** + * + * @type {string} + * @memberof UpdateDeviceInfoDto + */ + 'deviceId': string; + /** + * + * @type {boolean} + * @memberof UpdateDeviceInfoDto + */ + 'isAutoBackup'?: boolean; +} /** * * @export @@ -1063,6 +1118,19 @@ export interface UserResponseDto { */ 'isAdmin': boolean; } +/** + * + * @export + * @interface ValidateAccessTokenResponseDto + */ +export interface ValidateAccessTokenResponseDto { + /** + * + * @type {boolean} + * @memberof ValidateAccessTokenResponseDto + */ + 'authStatus': boolean; +} /** * AlbumApi - axios parameter creator @@ -2306,7 +2374,7 @@ export const AssetApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async deleteAsset(deleteAssetDto: DeleteAssetDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async deleteAsset(deleteAssetDto: DeleteAssetDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAsset(deleteAssetDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -2349,7 +2417,7 @@ export const AssetApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async getAssetSearchTerms(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + async getAssetSearchTerms(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetSearchTerms(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -2451,7 +2519,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath * @param {*} [options] Override http request option. * @throws {RequiredError} */ - deleteAsset(deleteAssetDto: DeleteAssetDto, options?: any): AxiosPromise { + deleteAsset(deleteAssetDto: DeleteAssetDto, options?: any): AxiosPromise> { return localVarFp.deleteAsset(deleteAssetDto, options).then((request) => request(axios, basePath)); }, /** @@ -2490,7 +2558,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getAssetSearchTerms(options?: any): AxiosPromise> { + getAssetSearchTerms(options?: any): AxiosPromise> { return localVarFp.getAssetSearchTerms(options).then((request) => request(axios, basePath)); }, /** @@ -2863,7 +2931,7 @@ export const AuthenticationApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async validateAccessToken(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async validateAccessToken(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.validateAccessToken(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -2900,7 +2968,7 @@ export const AuthenticationApiFactory = function (configuration?: Configuration, * @param {*} [options] Override http request option. * @throws {RequiredError} */ - validateAccessToken(options?: any): AxiosPromise { + validateAccessToken(options?: any): AxiosPromise { return localVarFp.validateAccessToken(options).then((request) => request(axios, basePath)); }, }; @@ -2994,13 +3062,13 @@ export const DeviceInfoApiAxiosParamCreator = function (configuration?: Configur }, /** * - * @param {object} body + * @param {UpdateDeviceInfoDto} updateDeviceInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - updateDeviceInfo: async (body: object, options: AxiosRequestConfig = {}): Promise => { - // verify required parameter 'body' is not null or undefined - assertParamExists('updateDeviceInfo', 'body', body) + updateDeviceInfo: async (updateDeviceInfoDto: UpdateDeviceInfoDto, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'updateDeviceInfoDto' is not null or undefined + assertParamExists('updateDeviceInfo', 'updateDeviceInfoDto', updateDeviceInfoDto) const localVarPath = `/device-info`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -3024,7 +3092,7 @@ export const DeviceInfoApiAxiosParamCreator = function (configuration?: Configur setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(body, localVarRequestOptions, configuration) + localVarRequestOptions.data = serializeDataIfNeeded(updateDeviceInfoDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), @@ -3053,12 +3121,12 @@ export const DeviceInfoApiFp = function(configuration?: Configuration) { }, /** * - * @param {object} body + * @param {UpdateDeviceInfoDto} updateDeviceInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async updateDeviceInfo(body: object, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateDeviceInfo(body, options); + async updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.updateDeviceInfo(updateDeviceInfoDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } @@ -3082,12 +3150,12 @@ export const DeviceInfoApiFactory = function (configuration?: Configuration, bas }, /** * - * @param {object} body + * @param {UpdateDeviceInfoDto} updateDeviceInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - updateDeviceInfo(body: object, options?: any): AxiosPromise { - return localVarFp.updateDeviceInfo(body, options).then((request) => request(axios, basePath)); + updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: any): AxiosPromise { + return localVarFp.updateDeviceInfo(updateDeviceInfoDto, options).then((request) => request(axios, basePath)); }, }; }; @@ -3112,13 +3180,13 @@ export class DeviceInfoApi extends BaseAPI { /** * - * @param {object} body + * @param {UpdateDeviceInfoDto} updateDeviceInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DeviceInfoApi */ - public updateDeviceInfo(body: object, options?: AxiosRequestConfig) { - return DeviceInfoApiFp(this.configuration).updateDeviceInfo(body, options).then((request) => request(this.axios, this.basePath)); + public updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: AxiosRequestConfig) { + return DeviceInfoApiFp(this.configuration).updateDeviceInfo(updateDeviceInfoDto, options).then((request) => request(this.axios, this.basePath)); } } diff --git a/web/src/lib/components/forms/login-form.svelte b/web/src/lib/components/forms/login-form.svelte index 7b20c4402f..b0934fb5dc 100644 --- a/web/src/lib/components/forms/login-form.svelte +++ b/web/src/lib/components/forms/login-form.svelte @@ -45,10 +45,6 @@ {#if loginPageMessage} - -

{@html loginPageMessage}