diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 5017247..411c718 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -682,6 +682,13 @@ + + + + + + @@ -1512,6 +1519,7 @@ + diff --git a/app/pubspec.lock b/app/pubspec.lock index ad28987..aad4ffc 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -745,6 +745,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + logger: + dependency: "direct main" + description: + name: logger + sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 + url: "https://pub.dev" + source: hosted + version: "2.5.0" logging: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index d8a3583..b8b5148 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -65,6 +65,9 @@ dependencies: freezed_annotation: ^2.4.4 json_serializable: ^6.8.0 + # logging + logger: ^2.5.0 + dev_dependencies: flutter_test: sdk: flutter diff --git a/data/.flutter-plugins-dependencies b/data/.flutter-plugins-dependencies index d7885b4..7132871 100644 --- a/data/.flutter-plugins-dependencies +++ b/data/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"google_sign_in_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.8/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.1/","native_build":true,"dependencies":[]}],"android":[{"name":"google_sign_in_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_android-6.1.33/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_android-2.2.12/","native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.3/","native_build":true,"dependencies":[]},{"name":"url_launcher_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.14/","native_build":true,"dependencies":[]}],"macos":[{"name":"google_sign_in_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.8/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_macos","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"]},{"name":"url_launcher_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","native_build":true,"dependencies":[]}],"windows":[{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"]},{"name":"url_launcher_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.3/","native_build":true,"dependencies":[]}],"web":[{"name":"google_sign_in_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.4+3/","dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","dependencies":[]},{"name":"shared_preferences_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/","dependencies":[]},{"name":"url_launcher_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_web-2.3.3/","dependencies":[]}]},"dependencyGraph":[{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"photo_manager","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2024-11-19 10:29:04.046577","version":"3.24.4","swift_package_manager_enabled":false} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"google_sign_in_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.8/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.1/","native_build":true,"dependencies":[]}],"android":[{"name":"google_sign_in_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_android-6.1.33/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_android-2.2.12/","native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.3/","native_build":true,"dependencies":[]},{"name":"url_launcher_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.14/","native_build":true,"dependencies":[]}],"macos":[{"name":"google_sign_in_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.8/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_macos","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"]},{"name":"url_launcher_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","native_build":true,"dependencies":[]}],"windows":[{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"]},{"name":"url_launcher_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.3/","native_build":true,"dependencies":[]}],"web":[{"name":"google_sign_in_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.4+3/","dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","dependencies":[]},{"name":"shared_preferences_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/","dependencies":[]},{"name":"url_launcher_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_web-2.3.3/","dependencies":[]}]},"dependencyGraph":[{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"photo_manager","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2024-11-20 12:21:34.473093","version":"3.24.4","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/data/lib/apis/dropbox/dropbox_content_endpoints.dart b/data/lib/apis/dropbox/dropbox_content_endpoints.dart index e69de29..a5f0421 100644 --- a/data/lib/apis/dropbox/dropbox_content_endpoints.dart +++ b/data/lib/apis/dropbox/dropbox_content_endpoints.dart @@ -0,0 +1,60 @@ +import '../network/endpoint.dart'; +import '../network/urls.dart'; + +class DropboxCreateFolderEndpoint extends Endpoint { + final String name; + + const DropboxCreateFolderEndpoint({required this.name}); + + @override + String get baseUrl => BaseURL.dropboxV2; + + @override + HttpMethod get method => HttpMethod.post; + + @override + String get path => '/files/create_folder_v2'; + + @override + Object? get data => {"autorename": false, "path": "/$name"}; +} + +class DropboxListFolderEndpoint extends Endpoint { + final bool includeDeleted; + final bool includeHasExplicitSharedMembers; + final bool includeMediaInfo; + final bool includeMountedFolders; + final bool includeNonDownloadableFiles; + final String folderPath; + final bool recursive; + + const DropboxListFolderEndpoint({ + this.includeDeleted = false, + this.includeHasExplicitSharedMembers = false, + this.includeMediaInfo = false, + this.includeMountedFolders = false, + this.includeNonDownloadableFiles = false, + this.recursive = false, + required this.folderPath, + }); + + @override + String get baseUrl => BaseURL.dropboxV2; + + @override + HttpMethod get method => HttpMethod.post; + + @override + String get path => '/files/list_folder'; + + @override + Object? get data => { + "include_deleted": includeDeleted, + "include_has_explicit_shared_members": includeHasExplicitSharedMembers, + "include_media_info": includeMediaInfo, + "include_mounted_folders": includeMountedFolders, + "include_non_downloadable_files": includeNonDownloadableFiles, + "path": folderPath, + "recursive": recursive, + }; +} diff --git a/data/lib/apis/network/client.dart b/data/lib/apis/network/client.dart index b7f2419..5870e33 100644 --- a/data/lib/apis/network/client.dart +++ b/data/lib/apis/network/client.dart @@ -1,4 +1,5 @@ import '../../errors/app_error.dart'; +import '../../log/logger.dart'; import '../../services/auth_service.dart'; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -6,17 +7,17 @@ import '../../storage/app_preferences.dart'; import 'endpoint.dart'; import 'interceptors/dropbox_auth_interceptor.dart'; import 'interceptors/google_drive_auth_interceptor.dart'; +import 'interceptors/logger_interceptor.dart'; final googleAuthenticatedDioProvider = Provider((ref) { return Dio() ..options.connectTimeout = const Duration(seconds: 60) ..options.sendTimeout = const Duration(seconds: 60) ..options.receiveTimeout = const Duration(seconds: 60) - ..interceptors.add( - GoogleDriveAuthInterceptor( - googleSignIn: ref.read(googleSignInProvider), - ), - ); + ..interceptors.addAll([ + GoogleDriveAuthInterceptor(googleSignIn: ref.read(googleSignInProvider)), + LoggerInterceptor(logger: ref.read(loggerProvider)), + ]); }); final dropboxAuthenticatedDioProvider = Provider((ref) { @@ -24,19 +25,23 @@ final dropboxAuthenticatedDioProvider = Provider((ref) { ..options.connectTimeout = const Duration(seconds: 60) ..options.sendTimeout = const Duration(seconds: 60) ..options.receiveTimeout = const Duration(seconds: 60) - ..interceptors.add( + ..interceptors.addAll([ DropboxAuthInterceptor( authService: ref.read(authServiceProvider), dropboxTokenController: ref.read(AppPreferences.dropboxToken.notifier), ), - ); + LoggerInterceptor(logger: ref.read(loggerProvider)), + ]); }); final rawDioProvider = Provider((ref) { return Dio() ..options.connectTimeout = const Duration(seconds: 60) ..options.sendTimeout = const Duration(seconds: 60) - ..options.receiveTimeout = const Duration(seconds: 60); + ..options.receiveTimeout = const Duration(seconds: 60) + ..interceptors.addAll([ + LoggerInterceptor(logger: ref.read(loggerProvider)), + ]); }); extension DioExtensions on Dio { diff --git a/data/lib/apis/network/interceptors/logger_interceptor.dart b/data/lib/apis/network/interceptors/logger_interceptor.dart index e69de29..9f13d1e 100644 --- a/data/lib/apis/network/interceptors/logger_interceptor.dart +++ b/data/lib/apis/network/interceptors/logger_interceptor.dart @@ -0,0 +1,76 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:logger/logger.dart'; + +class LoggerInterceptor extends Interceptor { + final Logger logger; + + LoggerInterceptor({required this.logger}); + + String logMap(Map headers) { + final sb = StringBuffer(); + sb.write('{\n'); + headers.forEach((key, value) { + sb.write(' $key: ${value.toString()}\n'); + }); + sb.write(' }'); + return sb.toString(); + } + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + String message = '⚡️ Request Started: [${options.method}] ${options.uri}'; + if (kDebugMode) { + if (options.headers.isNotEmpty) { + message += '\n⚡️ Headers: ${logMap(options.headers)}'; + } + if (options.data != null) { + message += + '\n⚡️ Body: ${JsonEncoder.withIndent(" ").convert(options.data)}'; + } + if (options.queryParameters.isNotEmpty) { + message += '\n⚡️ Query Parameters: ${logMap(options.queryParameters)}'; + } + } + logger.d(message); + handler.next(options); + } + + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + String message = + '⚡️ Response: ${response.statusCode} -> [${response.requestOptions.method}] ${response.requestOptions.uri}'; + + if (kDebugMode) { + if (response.headers.map.isNotEmpty) { + message += '\n⚡️ Headers: ${logMap(response.headers.map)}'; + } + message += + '\n⚡️ Response body: ${JsonEncoder.withIndent(" ").convert(response.data)}'; + } + logger.d(message); + handler.next(response); + } + + @override + void onError(DioException err, ErrorInterceptorHandler handler) { + String message = + '⚡️ ERROR at [${err.requestOptions.method}] ${err.requestOptions.uri}: (${err.response?.statusCode}) ${err.message?.trim()}'; + if (err.response != null) { + if (err.response!.headers.map.isNotEmpty) { + message += '\n⚡️ Headers: ${logMap(err.response!.headers.map)}'; + } + message += + '\n⚡️ Body: ${JsonEncoder.withIndent(" ").convert(err.response?.data)}'; + } + logger.e( + message, + error: err.error, + stackTrace: err.stackTrace, + time: DateTime.now(), + ); + handler.next(err); + } +} diff --git a/data/lib/domain/config.dart b/data/lib/domain/config.dart index d2725f3..1954de2 100644 --- a/data/lib/domain/config.dart +++ b/data/lib/domain/config.dart @@ -3,3 +3,7 @@ import 'package:flutter/foundation.dart'; class FeatureFlags { static const dropboxEnabled = kDebugMode; } + +class FolderPath { + static const String backupFolderName = 'Cloud Gallery Backup'; +} diff --git a/data/lib/log/logger.dart b/data/lib/log/logger.dart index e69de29..f6a90dd 100644 --- a/data/lib/log/logger.dart +++ b/data/lib/log/logger.dart @@ -0,0 +1,21 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:logger/logger.dart'; + +final loggerProvider = Provider( + (ref) => Logger( + filter: ProductionFilter(), + printer: PrettyPrinter( + methodCount: 0, + errorMethodCount: 0, + printEmojis: false, + colors: false, + ), + ), +); + +class UnitTestLoggerFilter extends LogFilter { + @override + bool shouldLog(LogEvent event) { + return false; + } +} diff --git a/data/lib/models/dropbox/dropbox_entity/dropbox_entity.dart b/data/lib/models/dropbox/dropbox_entity/dropbox_entity.dart index f636a4a..b3306bf 100644 --- a/data/lib/models/dropbox/dropbox_entity/dropbox_entity.dart +++ b/data/lib/models/dropbox/dropbox_entity/dropbox_entity.dart @@ -1,11 +1,40 @@ +// ignore_for_file: non_constant_identifier_names + import 'package:freezed_annotation/freezed_annotation.dart'; +part 'dropbox_entity.freezed.dart'; + +part 'dropbox_entity.g.dart'; + @freezed -class ApiDropboxEntitie with _$ApiDropboxEntitie { - const factory ApiDropboxEntities({ +class ApiDropboxEntity with _$ApiDropboxEntity { + const factory ApiDropboxEntity({ required String id, required String name, required String path_lower, - required String path_desplay, - }) = _ApiDropboxEntities; + required String path_display, + @JsonKey( + name: '.tag', + unknownEnumValue: ApiDropboxEntityType.unknown, + ) + required ApiDropboxEntityType type, + }) = _ApiDropboxEntity; + + factory ApiDropboxEntity.fromJson(Map json) => + _$ApiDropboxEntityFromJson(json); + + factory ApiDropboxEntity.fromFolderJson(Map json) { + json.addAll({".tag": "folder"}); + return _$ApiDropboxEntityFromJson(json); + } +} + +@JsonEnum(valueField: "value") +enum ApiDropboxEntityType { + folder('folder'), + unknown('unknown'); + + final String value; + + const ApiDropboxEntityType(this.value); } diff --git a/data/lib/models/dropbox/dropbox_entity/dropbox_entity.freezed.dart b/data/lib/models/dropbox/dropbox_entity/dropbox_entity.freezed.dart index 9b28934..6f6e433 100644 --- a/data/lib/models/dropbox/dropbox_entity/dropbox_entity.freezed.dart +++ b/data/lib/models/dropbox/dropbox_entity/dropbox_entity.freezed.dart @@ -14,15 +14,22 @@ T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); +ApiDropboxEntity _$ApiDropboxEntityFromJson(Map json) { + return _ApiDropboxEntity.fromJson(json); +} + /// @nodoc mixin _$ApiDropboxEntity { String get id => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError; String get path_lower => throw _privateConstructorUsedError; - String get path_desplay => throw _privateConstructorUsedError; - @JsonKey(name: '.tag') + String get path_display => throw _privateConstructorUsedError; + @JsonKey(name: '.tag', unknownEnumValue: ApiDropboxEntityType.unknown) ApiDropboxEntityType get type => throw _privateConstructorUsedError; + /// Serializes this ApiDropboxEntity to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + /// Create a copy of ApiDropboxEntity /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -40,8 +47,9 @@ abstract class $ApiDropboxEntityCopyWith<$Res> { {String id, String name, String path_lower, - String path_desplay, - @JsonKey(name: '.tag') ApiDropboxEntityType type}); + String path_display, + @JsonKey(name: '.tag', unknownEnumValue: ApiDropboxEntityType.unknown) + ApiDropboxEntityType type}); } /// @nodoc @@ -62,7 +70,7 @@ class _$ApiDropboxEntityCopyWithImpl<$Res, $Val extends ApiDropboxEntity> Object? id = null, Object? name = null, Object? path_lower = null, - Object? path_desplay = null, + Object? path_display = null, Object? type = null, }) { return _then(_value.copyWith( @@ -78,9 +86,9 @@ class _$ApiDropboxEntityCopyWithImpl<$Res, $Val extends ApiDropboxEntity> ? _value.path_lower : path_lower // ignore: cast_nullable_to_non_nullable as String, - path_desplay: null == path_desplay - ? _value.path_desplay - : path_desplay // ignore: cast_nullable_to_non_nullable + path_display: null == path_display + ? _value.path_display + : path_display // ignore: cast_nullable_to_non_nullable as String, type: null == type ? _value.type @@ -102,8 +110,9 @@ abstract class _$$ApiDropboxEntityImplCopyWith<$Res> {String id, String name, String path_lower, - String path_desplay, - @JsonKey(name: '.tag') ApiDropboxEntityType type}); + String path_display, + @JsonKey(name: '.tag', unknownEnumValue: ApiDropboxEntityType.unknown) + ApiDropboxEntityType type}); } /// @nodoc @@ -122,7 +131,7 @@ class __$$ApiDropboxEntityImplCopyWithImpl<$Res> Object? id = null, Object? name = null, Object? path_lower = null, - Object? path_desplay = null, + Object? path_display = null, Object? type = null, }) { return _then(_$ApiDropboxEntityImpl( @@ -138,9 +147,9 @@ class __$$ApiDropboxEntityImplCopyWithImpl<$Res> ? _value.path_lower : path_lower // ignore: cast_nullable_to_non_nullable as String, - path_desplay: null == path_desplay - ? _value.path_desplay - : path_desplay // ignore: cast_nullable_to_non_nullable + path_display: null == path_display + ? _value.path_display + : path_display // ignore: cast_nullable_to_non_nullable as String, type: null == type ? _value.type @@ -151,14 +160,18 @@ class __$$ApiDropboxEntityImplCopyWithImpl<$Res> } /// @nodoc - +@JsonSerializable() class _$ApiDropboxEntityImpl implements _ApiDropboxEntity { const _$ApiDropboxEntityImpl( {required this.id, required this.name, required this.path_lower, - required this.path_desplay, - @JsonKey(name: '.tag') required this.type}); + required this.path_display, + @JsonKey(name: '.tag', unknownEnumValue: ApiDropboxEntityType.unknown) + required this.type}); + + factory _$ApiDropboxEntityImpl.fromJson(Map json) => + _$$ApiDropboxEntityImplFromJson(json); @override final String id; @@ -167,14 +180,14 @@ class _$ApiDropboxEntityImpl implements _ApiDropboxEntity { @override final String path_lower; @override - final String path_desplay; + final String path_display; @override - @JsonKey(name: '.tag') + @JsonKey(name: '.tag', unknownEnumValue: ApiDropboxEntityType.unknown) final ApiDropboxEntityType type; @override String toString() { - return 'ApiDropboxEntity(id: $id, name: $name, path_lower: $path_lower, path_desplay: $path_desplay, type: $type)'; + return 'ApiDropboxEntity(id: $id, name: $name, path_lower: $path_lower, path_display: $path_display, type: $type)'; } @override @@ -186,14 +199,15 @@ class _$ApiDropboxEntityImpl implements _ApiDropboxEntity { (identical(other.name, name) || other.name == name) && (identical(other.path_lower, path_lower) || other.path_lower == path_lower) && - (identical(other.path_desplay, path_desplay) || - other.path_desplay == path_desplay) && + (identical(other.path_display, path_display) || + other.path_display == path_display) && (identical(other.type, type) || other.type == type)); } + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => - Object.hash(runtimeType, id, name, path_lower, path_desplay, type); + Object.hash(runtimeType, id, name, path_lower, path_display, type); /// Create a copy of ApiDropboxEntity /// with the given fields replaced by the non-null parameter values. @@ -203,16 +217,26 @@ class _$ApiDropboxEntityImpl implements _ApiDropboxEntity { _$$ApiDropboxEntityImplCopyWith<_$ApiDropboxEntityImpl> get copyWith => __$$ApiDropboxEntityImplCopyWithImpl<_$ApiDropboxEntityImpl>( this, _$identity); + + @override + Map toJson() { + return _$$ApiDropboxEntityImplToJson( + this, + ); + } } abstract class _ApiDropboxEntity implements ApiDropboxEntity { const factory _ApiDropboxEntity( - {required final String id, - required final String name, - required final String path_lower, - required final String path_desplay, - @JsonKey(name: '.tag') required final ApiDropboxEntityType type}) = - _$ApiDropboxEntityImpl; + {required final String id, + required final String name, + required final String path_lower, + required final String path_display, + @JsonKey(name: '.tag', unknownEnumValue: ApiDropboxEntityType.unknown) + required final ApiDropboxEntityType type}) = _$ApiDropboxEntityImpl; + + factory _ApiDropboxEntity.fromJson(Map json) = + _$ApiDropboxEntityImpl.fromJson; @override String get id; @@ -221,9 +245,9 @@ abstract class _ApiDropboxEntity implements ApiDropboxEntity { @override String get path_lower; @override - String get path_desplay; + String get path_display; @override - @JsonKey(name: '.tag') + @JsonKey(name: '.tag', unknownEnumValue: ApiDropboxEntityType.unknown) ApiDropboxEntityType get type; /// Create a copy of ApiDropboxEntity diff --git a/data/lib/models/dropbox/dropbox_entity/dropbox_entity.g.dart b/data/lib/models/dropbox/dropbox_entity/dropbox_entity.g.dart index b105ce4..ca10bd1 100644 --- a/data/lib/models/dropbox/dropbox_entity/dropbox_entity.g.dart +++ b/data/lib/models/dropbox/dropbox_entity/dropbox_entity.g.dart @@ -13,7 +13,8 @@ _$ApiDropboxEntityImpl _$$ApiDropboxEntityImplFromJson( name: json['name'] as String, path_lower: json['path_lower'] as String, path_display: json['path_display'] as String, - type: $enumDecode(_$ApiDropboxEntityTypeEnumMap, json['.tag']), + type: $enumDecode(_$ApiDropboxEntityTypeEnumMap, json['.tag'], + unknownValue: ApiDropboxEntityType.unknown), ); Map _$$ApiDropboxEntityImplToJson( @@ -28,4 +29,5 @@ Map _$$ApiDropboxEntityImplToJson( const _$ApiDropboxEntityTypeEnumMap = { ApiDropboxEntityType.folder: 'folder', + ApiDropboxEntityType.unknown: 'unknown', }; diff --git a/data/lib/services/cloud_provider_service.dart b/data/lib/services/cloud_provider_service.dart index e69de29..fa36ca6 100644 --- a/data/lib/services/cloud_provider_service.dart +++ b/data/lib/services/cloud_provider_service.dart @@ -0,0 +1,7 @@ +abstract class CloudProviderService { + const CloudProviderService(); + + Future createFolder(String folderName); + + Future createBackupFolder(); +} diff --git a/data/lib/services/dropbox_services.dart b/data/lib/services/dropbox_services.dart index 2861ce0..30114ef 100644 --- a/data/lib/services/dropbox_services.dart +++ b/data/lib/services/dropbox_services.dart @@ -1,4 +1,7 @@ +import 'package:collection/collection.dart'; +import '../apis/dropbox/dropbox_content_endpoints.dart'; import '../apis/network/client.dart'; +import '../domain/config.dart'; import '../errors/app_error.dart'; import '../models/dropbox_account/dropbox_account.dart'; import '../storage/app_preferences.dart'; @@ -6,6 +9,7 @@ import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../apis/dropbox/dropbox_auth_endpoints.dart'; import '../storage/provider/preferences_provider.dart'; +import 'cloud_provider_service.dart'; final dropboxServiceProvider = Provider((ref) { return DropboxService( @@ -14,7 +18,7 @@ final dropboxServiceProvider = Provider((ref) { ); }); -class DropboxService { +class DropboxService extends CloudProviderService { final Dio _dropboxAuthenticatedDio; final PreferenceNotifier _dropboxAccountController; @@ -32,4 +36,36 @@ class DropboxService { AppError.fromError(e); } } + + @override + Future createBackupFolder() async { + final response = await _dropboxAuthenticatedDio.req( + DropboxListFolderEndpoint(folderPath: ''), + ); + if (response.statusCode == 200) { + final folderExists = (response.data['entries'] as List).firstWhereOrNull( + (element) => + element['.tag'] == 'folder' && + element['name'] == FolderPath.backupFolderName, + ); + if (folderExists == null) { + return createFolder(FolderPath.backupFolderName); + } + return folderExists['id']; + } + throw AppError.fromError(response.statusMessage ?? ''); + } + + @override + Future createFolder(String folderName) async { + final response = await _dropboxAuthenticatedDio.req( + DropboxCreateFolderEndpoint(name: folderName), + ); + + if (response.statusCode == 200) { + return response.data['metadata']['id']; + } + + throw AppError.fromError(response.statusMessage ?? ''); + } } diff --git a/data/lib/services/google_drive_service.dart b/data/lib/services/google_drive_service.dart index c17b62c..b443f99 100644 --- a/data/lib/services/google_drive_service.dart +++ b/data/lib/services/google_drive_service.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import '../apis/google_drive/google_drive_endpoint.dart'; import '../apis/network/client.dart'; +import '../domain/config.dart'; import '../models/media/media.dart'; import '../models/media_content/media_content.dart'; import 'package:dio/dio.dart'; @@ -20,7 +21,6 @@ final googleDriveServiceProvider = Provider( ); class GoogleDriveService { - final String _backUpFolderName = "Cloud Gallery Backup"; final Dio _client; final GoogleSignIn _googleSignIn; @@ -41,14 +41,14 @@ class GoogleDriveService { final driveApi = await _getGoogleDriveAPI(); final response = await driveApi.files.list( - q: "name='$_backUpFolderName' and trashed=false and mimeType='application/vnd.google-apps.folder'", + q: "name='${FolderPath.backupFolderName}' and trashed=false and mimeType='application/vnd.google-apps.folder'", ); if (response.files?.isNotEmpty ?? false) { return response.files?.first.id; } else { final folder = drive.File( - name: _backUpFolderName, + name: FolderPath.backupFolderName, mimeType: 'application/vnd.google-apps.folder', ); final googleDriveFolder = await driveApi.files.create(folder); diff --git a/data/pubspec.yaml b/data/pubspec.yaml index 817ae6d..a47b004 100644 --- a/data/pubspec.yaml +++ b/data/pubspec.yaml @@ -37,6 +37,9 @@ dependencies: collection: ^1.18.0 + # logging + logger: ^2.5.0 + dev_dependencies: flutter_test: sdk: flutter