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