diff --git a/mobile/lib/modules/asset_viewer/ui/download_loading_indicator.dart b/mobile/lib/modules/asset_viewer/ui/download_loading_indicator.dart deleted file mode 100644 index f53d9692f4..0000000000 --- a/mobile/lib/modules/asset_viewer/ui/download_loading_indicator.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; - -class DownloadLoadingIndicator extends StatelessWidget { - const DownloadLoadingIndicator({ - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - height: 60, - width: 60, - decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - borderRadius: BorderRadius.circular(10), - ), - child: const SpinKitDancingSquare( - color: Colors.white, - size: 30.0, - ), - ); - } -} diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index b510cdd088..2240a0552d 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:flutter_swipe_detector/flutter_swipe_detector.dart'; import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; @@ -129,12 +128,16 @@ class GalleryViewerPage extends HookConsumerWidget { threeStageLoading: threeStageLoading.value, ); } else { - return SwipeDetector( - onSwipeDown: (_) { - AutoRouter.of(context).pop(); - }, - onSwipeUp: (_) { - showInfo(); + return GestureDetector( + onVerticalDragUpdate: (details) { + const int sensitivity = 10; + if (details.delta.dy > sensitivity) { + // swipe down + AutoRouter.of(context).pop(); + } else if (details.delta.dy < -sensitivity) { + // swipe up + showInfo(); + } }, child: Hero( tag: assetList[index].id, 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 7047536e3d..59540386cf 100644 --- a/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart +++ b/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart @@ -4,11 +4,11 @@ import 'package:flutter_hooks/flutter_hooks.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/providers/image_viewer_page_state.provider.dart'; -import 'package:immich_mobile/modules/asset_viewer/ui/download_loading_indicator.dart'; 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/home/services/asset.service.dart'; import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; // ignore: must_be_immutable class ImageViewerPage extends HookConsumerWidget { @@ -84,7 +84,7 @@ class ImageViewerPage extends HookConsumerWidget { ), if (downloadAssetStatus == DownloadAssetStatus.loading) const Center( - child: DownloadLoadingIndicator(), + child: ImmichLoadingIndicator(), ), ], ); 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 2d92033381..a38c7713f3 100644 --- a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart +++ b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart @@ -7,8 +7,8 @@ import 'package:immich_mobile/constants/hive_box.dart'; import 'package:chewie/chewie.dart'; import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart'; -import 'package:immich_mobile/modules/asset_viewer/ui/download_loading_indicator.dart'; import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:video_player/video_player.dart'; @@ -52,7 +52,7 @@ class VideoViewerPage extends HookConsumerWidget { ), if (downloadAssetStatus == DownloadAssetStatus.loading) const Center( - child: DownloadLoadingIndicator(), + child: ImmichLoadingIndicator(), ), ], ); diff --git a/mobile/lib/modules/backup/models/error_upload_asset.model.dart b/mobile/lib/modules/backup/models/error_upload_asset.model.dart index 6585b7c54e..d99872ef9c 100644 --- a/mobile/lib/modules/backup/models/error_upload_asset.model.dart +++ b/mobile/lib/modules/backup/models/error_upload_asset.model.dart @@ -1,7 +1,6 @@ -import 'package:equatable/equatable.dart'; import 'package:photo_manager/photo_manager.dart'; -class ErrorUploadAsset extends Equatable { +class ErrorUploadAsset { final String id; final DateTime createdAt; final String fileName; @@ -42,14 +41,25 @@ class ErrorUploadAsset extends Equatable { } @override - List get props { - return [ - id, - createdAt, - fileName, - fileType, - asset, - errorMessage, - ]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ErrorUploadAsset && + other.id == id && + other.createdAt == createdAt && + other.fileName == fileName && + other.fileType == fileType && + other.asset == asset && + other.errorMessage == errorMessage; + } + + @override + int get hashCode { + return id.hashCode ^ + createdAt.hashCode ^ + fileName.hashCode ^ + fileType.hashCode ^ + asset.hashCode ^ + errorMessage.hashCode; } } diff --git a/mobile/lib/modules/backup/views/backup_controller_page.dart b/mobile/lib/modules/backup/views/backup_controller_page.dart index 1cd74118cc..a734de85f0 100644 --- a/mobile/lib/modules/backup/views/backup_controller_page.dart +++ b/mobile/lib/modules/backup/views/backup_controller_page.dart @@ -13,7 +13,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:percent_indicator/linear_percent_indicator.dart'; import 'package:url_launcher/url_launcher.dart'; class BackupControllerPage extends HookConsumerWidget { @@ -63,14 +62,11 @@ class BackupControllerPage extends HookConsumerWidget { children: [ Padding( padding: const EdgeInsets.only(top: 8.0), - child: LinearPercentIndicator( - padding: - const EdgeInsets.symmetric(horizontal: 0, vertical: 0), - barRadius: const Radius.circular(2), - lineHeight: 10.0, - percent: backupState.serverInfo.diskUsagePercentage / 100.0, + child: LinearProgressIndicator( + minHeight: 10.0, + value: backupState.serverInfo.diskUsagePercentage / 100.0, backgroundColor: Colors.grey, - progressColor: Theme.of(context).primaryColor, + color: Theme.of(context).primaryColor, ), ), Padding( @@ -444,17 +440,21 @@ class BackupControllerPage extends HookConsumerWidget { children: [ Padding( padding: const EdgeInsets.only(top: 8.0), - child: LinearPercentIndicator( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0), - barRadius: const Radius.circular(2), - lineHeight: 10.0, - trailing: Text( - " ${backupState.progressInPercentage.toStringAsFixed(0)}%", - style: const TextStyle(fontSize: 12), - ), - percent: backupState.progressInPercentage / 100.0, - backgroundColor: Colors.grey, - progressColor: Theme.of(context).primaryColor, + child: Row( + children: [ + Expanded( + child: LinearProgressIndicator( + minHeight: 10.0, + value: backupState.progressInPercentage / 100.0, + backgroundColor: Colors.grey, + color: Theme.of(context).primaryColor, + ), + ), + Text( + " ${backupState.progressInPercentage.toStringAsFixed(0)}%", + style: const TextStyle(fontSize: 12), + ) + ], ), ), Padding( diff --git a/mobile/lib/modules/search/views/search_result_page.dart b/mobile/lib/modules/search/views/search_result_page.dart index 5f213cdce0..048cf46b9f 100644 --- a/mobile/lib/modules/search/views/search_result_page.dart +++ b/mobile/lib/modules/search/views/search_result_page.dart @@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; @@ -10,6 +9,7 @@ import 'package:immich_mobile/modules/search/providers/search_result_page.provid import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; class SearchResultPage extends HookConsumerWidget { const SearchResultPage({Key? key, required this.searchTerm}) @@ -122,11 +122,7 @@ class SearchResultPage extends HookConsumerWidget { } if (searchResultPageState.isLoading) { - return Center( - child: SpinKitDancingSquare( - color: Theme.of(context).primaryColor, - ), - ); + return const Center(child: ImmichLoadingIndicator()); } if (searchResultPageState.isSuccess) { diff --git a/mobile/lib/shared/providers/release_info.provider.dart b/mobile/lib/shared/providers/release_info.provider.dart index c9f91ec847..559a544ac1 100644 --- a/mobile/lib/shared/providers/release_info.provider.dart +++ b/mobile/lib/shared/providers/release_info.provider.dart @@ -1,7 +1,9 @@ -import 'package:dio/dio.dart'; +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:http/http.dart'; import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/shared/views/version_announcement_overlay.dart'; @@ -9,21 +11,20 @@ class ReleaseInfoNotifier extends StateNotifier { ReleaseInfoNotifier() : super(""); void checkGithubReleaseInfo() async { - var dio = Dio(); + final Client client = Client(); var box = Hive.box(hiveGithubReleaseInfoBox); try { String? localReleaseVersion = box.get(githubReleaseInfoKey); - - var res = await dio.get( - "https://api.github.com/repos/alextran1502/immich/releases/latest", - options: Options( - headers: {"Accept": "application/vnd.github.v3+json"}, - ), - ); + final res = await client.get( + Uri.parse( + "https://api.github.com/repos/alextran1502/immich/releases/latest", + ), + headers: {"Accept": "application/vnd.github.v3+json"}); if (res.statusCode == 200) { - String latestTagVersion = res.data["tag_name"]; + final data = jsonDecode(res.body); + String latestTagVersion = data["tag_name"]; state = latestTagVersion; debugPrint("Local release version $localReleaseVersion"); diff --git a/mobile/lib/shared/services/network.service.dart b/mobile/lib/shared/services/network.service.dart deleted file mode 100644 index f8a502d0a1..0000000000 --- a/mobile/lib/shared/services/network.service.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:dio/dio.dart'; -import 'package:flutter/cupertino.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/utils/dio_http_interceptor.dart'; - -final networkServiceProvider = Provider((_) => NetworkService()); - -class NetworkService { - late final Dio dio; - - NetworkService() { - dio = Dio(); - dio.interceptors.add(AuthenticatedRequestInterceptor()); - } - - Future deleteRequest({required String url, dynamic data}) async { - try { - var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); - Response res = await dio.delete('$savedEndpoint/$url', data: data); - - if (res.statusCode == 200) { - return res; - } - } on DioError catch (e) { - debugPrint("DioError: ${e.response}"); - } catch (e) { - debugPrint("ERROR deleteRequest: ${e.toString()}"); - } - } - - Future getRequest({ - required String url, - bool isByteResponse = false, - bool isStreamReponse = false, - }) async { - try { - var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); - - if (isByteResponse) { - Response> res = await dio.get>( - '$savedEndpoint/$url', - options: Options(responseType: ResponseType.bytes), - ); - - if (res.statusCode == 200) { - return res; - } - } else if (isStreamReponse) { - Response res = await dio.get( - '$savedEndpoint/$url', - options: Options(responseType: ResponseType.stream), - ); - - if (res.statusCode == 200) { - return res; - } - } else { - Response res = await dio.get('$savedEndpoint/$url'); - if (res.statusCode == 200) { - return res; - } - } - } on DioError catch (e) { - debugPrint("DioError: ${e.response}"); - } catch (e) { - debugPrint("ERROR getRequest: ${e.toString()}"); - } - } - - Future postRequest({required String url, dynamic data}) async { - try { - var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); - var validUrl = Uri.parse('$savedEndpoint/$url').toString(); - var res = await dio.post(validUrl, data: data); - - return res; - } on DioError catch (e) { - debugPrint("[postRequest] DioError: ${e.response}"); - return null; - } catch (e) { - debugPrint("ERROR PostRequest: $e"); - return null; - } - } - - Future putRequest({required String url, dynamic data}) async { - try { - var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); - var validUrl = Uri.parse('$savedEndpoint/$url').toString(); - var res = await dio.put(validUrl, data: data); - - return res; - } on DioError catch (e) { - debugPrint("DioError: ${e.response}"); - return null; - } catch (e) { - debugPrint("ERROR PutRequest: $e"); - return null; - } - } - - Future patchRequest({required String url, dynamic data}) async { - try { - var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); - var validUrl = Uri.parse('$savedEndpoint/$url').toString(); - var res = await dio.patch(validUrl, data: data); - - return res; - } on DioError catch (e) { - debugPrint("DioError: ${e.response}"); - } catch (e) { - debugPrint("ERROR PatchRequest: $e"); - } - } - - Future pingServer() async { - try { - var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); - var validUrl = Uri.parse('$savedEndpoint/server-info/ping').toString(); - - debugPrint("ping server at url $validUrl"); - - var res = await dio.get(validUrl); - var jsonRespsonse = jsonDecode(res.toString()); - - return jsonRespsonse["res"] == "pong"; - } on DioError catch (e) { - debugPrint("[PING SERVER] DioError: ${e.response} - $e"); - return false; - } catch (e) { - debugPrint("ERROR PingServer: $e"); - return false; - } - } -} diff --git a/mobile/lib/shared/ui/immich_loading_indicator.dart b/mobile/lib/shared/ui/immich_loading_indicator.dart index f5735811a0..84a33ba046 100644 --- a/mobile/lib/shared/ui/immich_loading_indicator.dart +++ b/mobile/lib/shared/ui/immich_loading_indicator.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; class ImmichLoadingIndicator extends StatelessWidget { const ImmichLoadingIndicator({ @@ -15,10 +14,8 @@ class ImmichLoadingIndicator extends StatelessWidget { color: Theme.of(context).primaryColor.withAlpha(200), borderRadius: BorderRadius.circular(10), ), - child: const SpinKitDancingSquare( - color: Colors.white, - size: 30.0, - ), + padding: const EdgeInsets.all(15), + child: const CircularProgressIndicator(color: Colors.white), ); } } diff --git a/mobile/lib/utils/dio_http_interceptor.dart b/mobile/lib/utils/dio_http_interceptor.dart deleted file mode 100644 index 335e7035b8..0000000000 --- a/mobile/lib/utils/dio_http_interceptor.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:hive_flutter/hive_flutter.dart'; -import 'package:immich_mobile/constants/hive_box.dart'; - -class AuthenticatedRequestInterceptor extends Interceptor { - @override - void onRequest(RequestOptions options, RequestInterceptorHandler handler) { - // debugPrint('REQUEST[${options.method}] => PATH: ${options.path}'); - - var box = Hive.box(userInfoBox); - - options.headers["Authorization"] = "Bearer ${box.get(accessTokenKey)}"; - options.responseType = ResponseType.plain; - return super.onRequest(options, handler); - } -} diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 46376b615a..b51cb47b43 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -239,13 +239,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" - dio: - dependency: "direct main" - description: - name: dio - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.6" easy_localization: dependency: "direct main" description: @@ -260,20 +253,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.2" - equatable: - dependency: "direct main" - description: - name: equatable - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.3" - exif: - dependency: "direct main" - description: - name: exif - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.1" fake_async: dependency: transitive description: @@ -336,7 +315,7 @@ packages: source: hosted version: "0.18.4" flutter_launcher_icons: - dependency: "direct main" + dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" @@ -375,20 +354,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0-dev.7" - flutter_spinkit: - dependency: "direct main" - description: - name: flutter_spinkit - url: "https://pub.dartlang.org" - source: hosted - version: "5.1.0" - flutter_swipe_detector: - dependency: "direct main" - description: - name: flutter_swipe_detector - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -756,13 +721,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.11.1" - percent_indicator: - dependency: "direct main" - description: - name: percent_indicator - url: "https://pub.dartlang.org" - source: hosted - version: "4.2.2" petitparser: dependency: transitive description: @@ -1027,13 +985,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.0" - sprintf: - dependency: transitive - description: - name: sprintf - url: "https://pub.dartlang.org" - source: hosted - version: "6.0.0" sqflite: dependency: transitive description: @@ -1112,7 +1063,7 @@ packages: source: hosted version: "1.0.0" transparent_image: - dependency: "direct main" + dependency: transitive description: name: transparent_image url: "https://pub.dartlang.org" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 461e99b02a..3df117be65 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -16,14 +16,9 @@ dependencies: hooks_riverpod: ^2.0.0-dev.0 hive: ^2.2.1 hive_flutter: ^1.1.0 - dio: ^4.0.4 cached_network_image: ^3.2.2 - percent_indicator: ^4.2.2 intl: ^0.17.0 auto_route: ^5.0.1 - exif: ^3.1.1 - transparent_image: ^2.0.0 - flutter_launcher_icons: "^0.9.2" fluttertoast: ^8.0.8 video_player: ^2.2.18 chewie: ^1.3.5 @@ -33,10 +28,6 @@ dependencies: flutter_map: ^0.14.0 flutter_udid: ^2.0.0 package_info_plus: ^1.4.0 - flutter_spinkit: ^5.1.0 - flutter_swipe_detector: ^2.0.0 - equatable: ^2.0.3 - image_picker: ^0.8.5+3 url_launcher: ^6.1.3 http: 0.13.4 cancellation_token_http: ^1.1.0 @@ -44,7 +35,6 @@ dependencies: share_plus: ^4.0.10 flutter_displaymode: ^0.4.0 scrollable_positioned_list: ^0.3.4 - path: ^1.8.1 path_provider: ^2.0.11 latlong2: ^0.8.1 @@ -54,6 +44,10 @@ dependencies: openapi: path: openapi + # easy to remove packages: + image_picker: ^0.8.5+3 # only used to select user profile image from system gallery -> we can simply select an image from within immich? + + dev_dependencies: flutter_test: sdk: flutter @@ -61,6 +55,7 @@ dev_dependencies: hive_generator: ^1.1.2 build_runner: ^2.2.1 auto_route_generator: ^5.0.2 + flutter_launcher_icons: "^0.9.2" flutter: uses-material-design: true