From 939b07f262fba845c5f8fe21753fa009fad4386c Mon Sep 17 00:00:00 2001
From: Pratik Canopas <109139581+cp-pratik-k@users.noreply.github.com>
Date: Mon, 18 Nov 2024 11:16:26 +0530
Subject: [PATCH] Dropbox sign in (#46)
* Implement dropbox sign in
* Implement dropbox sign in
* Implement dropbox-sign-in
* Fix dropbox-sign-in
* Fix dropbox-sign-in
* Fix dropbox-sign-in
* Added feature flags
* Added feature flags
---
.idea/libraries/Dart_Packages.xml | 206 +++++-
.idea/libraries/Flutter_Plugins.xml | 55 +-
app/android/app/src/main/AndroidManifest.xml | 15 +
app/assets/images/icons/dropbox.svg | 4 +
app/assets/locales/app_en.arb | 9 +-
app/ios/Podfile.lock | 34 +-
app/ios/Runner/Info.plist | 1 +
app/ios/Runner/Runner.entitlements | 4 +
app/lib/domain/assets/assets_paths.dart | 1 +
.../extensions/app_error_extensions.dart | 6 +-
.../domain/handlers/deep_links_handler.dart | 56 ++
app/lib/main.dart | 3 +
app/lib/ui/flow/accounts/accounts_screen.dart | 177 +++--
.../accounts/accounts_screen_view_model.dart | 32 +-
.../flow/accounts/components/account_tab.dart | 45 +-
app/lib/ui/flow/home/components/hints.dart | 49 +-
.../ui/flow/home/home_screen_view_model.dart | 4 +-
.../flutter/generated_plugin_registrant.cc | 4 +
app/linux/flutter/generated_plugins.cmake | 1 +
.../Flutter/GeneratedPluginRegistrant.swift | 4 +
app/pubspec.lock | 276 +++++---
app/pubspec.yaml | 1 +
.../flutter/generated_plugin_registrant.cc | 3 +
app/windows/flutter/generated_plugins.cmake | 1 +
data/.flutter-plugins | 7 +
data/.flutter-plugins-dependencies | 2 +-
.../apis/dropbox/dropbox_auth_endpoints.dart | 85 +++
.../google_drive/google_drive_endpoint.dart | 6 +-
data/lib/apis/network/base_url.dart | 4 -
data/lib/apis/network/client.dart | 21 +-
.../dropbox_auth_interceptor.dart | 54 ++
...art => google_drive_auth_interceptor.dart} | 1 +
data/lib/apis/network/oauth2.dart | 54 ++
data/lib/apis/network/secrets.dart | 4 +
data/lib/apis/network/urls.dart | 16 +
data/lib/domain/config.dart | 5 +
data/lib/errors/app_error.dart | 24 +-
data/lib/errors/l10n_error_codes.dart | 7 +-
data/lib/extensions/date_time_extension.dart | 3 +
.../dropbox_account/dropbox_account.dart | 40 ++
.../dropbox_account.freezed.dart | 642 ++++++++++++++++++
.../dropbox_account/dropbox_account.g.dart | 58 ++
data/lib/models/token/token.dart | 40 ++
data/lib/models/token/token.freezed.dart | 297 ++++++++
data/lib/models/token/token.g.dart | 30 +
data/lib/services/auth_service.dart | 126 +++-
data/lib/services/dropbox_services.dart | 35 +
data/lib/storage/app_preferences.dart | 30 +-
.../provider/preferences_provider.dart | 33 +
data/pubspec.yaml | 2 +
style/lib/buttons/buttons_list.dart | 4 +-
style/lib/theme/colors.dart | 1 +
52 files changed, 2291 insertions(+), 331 deletions(-)
create mode 100644 app/assets/images/icons/dropbox.svg
create mode 100644 app/lib/domain/handlers/deep_links_handler.dart
create mode 100644 data/lib/apis/dropbox/dropbox_auth_endpoints.dart
delete mode 100644 data/lib/apis/network/base_url.dart
create mode 100644 data/lib/apis/network/interceptors/dropbox_auth_interceptor.dart
rename data/lib/apis/network/interceptors/{auth_interceptor.dart => google_drive_auth_interceptor.dart} (96%)
create mode 100644 data/lib/apis/network/oauth2.dart
create mode 100644 data/lib/apis/network/secrets.dart
create mode 100644 data/lib/apis/network/urls.dart
create mode 100644 data/lib/domain/config.dart
create mode 100644 data/lib/extensions/date_time_extension.dart
create mode 100644 data/lib/models/dropbox_account/dropbox_account.dart
create mode 100644 data/lib/models/dropbox_account/dropbox_account.freezed.dart
create mode 100644 data/lib/models/dropbox_account/dropbox_account.g.dart
create mode 100644 data/lib/models/token/token.dart
create mode 100644 data/lib/models/token/token.freezed.dart
create mode 100644 data/lib/models/token/token.g.dart
create mode 100644 data/lib/services/dropbox_services.dart
diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
index a4b4973..5b84c32 100644
--- a/.idea/libraries/Dart_Packages.xml
+++ b/.idea/libraries/Dart_Packages.xml
@@ -5,6 +5,7 @@
+
@@ -37,9 +38,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -75,6 +105,7 @@
+
@@ -96,6 +127,7 @@
+
@@ -110,6 +142,7 @@
+
@@ -159,6 +192,7 @@
+
@@ -173,6 +207,7 @@
+
@@ -187,6 +222,7 @@
+
@@ -201,6 +237,7 @@
+
@@ -208,7 +245,7 @@
-
+
@@ -250,6 +287,7 @@
+
@@ -292,6 +330,7 @@
+
@@ -299,6 +338,7 @@
+
@@ -306,7 +346,7 @@
-
+
@@ -327,6 +367,7 @@
+
@@ -440,6 +481,7 @@
+
@@ -454,7 +496,7 @@
-
+
@@ -468,6 +510,7 @@
+
@@ -482,6 +525,7 @@
+
@@ -489,6 +533,7 @@
+
@@ -517,6 +562,7 @@
+
@@ -524,10 +570,18 @@
+
+
+
+
+
+
+
+
@@ -538,7 +592,7 @@
-
+
@@ -580,6 +634,7 @@
+
@@ -630,6 +685,7 @@
+
@@ -665,6 +721,7 @@
+
@@ -707,7 +764,7 @@
-
+
@@ -728,6 +785,7 @@
+
@@ -749,6 +807,7 @@
+
@@ -763,7 +822,7 @@
-
+
@@ -777,14 +836,14 @@
-
+
-
+
@@ -819,6 +878,7 @@
+
@@ -847,6 +907,7 @@
+
@@ -952,6 +1013,7 @@
+
@@ -1064,7 +1126,7 @@
-
+
@@ -1085,7 +1147,7 @@
-
+
@@ -1099,14 +1161,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
@@ -1148,7 +1239,7 @@
-
+
@@ -1176,28 +1267,28 @@
-
+
-
+
-
+
-
+
@@ -1239,6 +1330,7 @@
+
@@ -1253,7 +1345,7 @@
-
+
@@ -1267,13 +1359,14 @@
-
+
+
@@ -1281,6 +1374,7 @@
+
@@ -1302,20 +1396,29 @@
+
+
+
+
+
+
+
+
+
@@ -1323,30 +1426,38 @@
+
+
+
+
-
+
+
+
+
-
+
+
@@ -1360,26 +1471,34 @@
+
-
+
+
+
+
+
+
+
-
+
+
@@ -1388,37 +1507,43 @@
+
+
-
+
+
+
-
+
-
-
+
+
+
+
@@ -1434,6 +1559,7 @@
+
@@ -1449,37 +1575,45 @@
-
+
-
+
+
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
-
+
+
+
diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml
index a10e5f6..70cf51d 100644
--- a/.idea/libraries/Flutter_Plugins.xml
+++ b/.idea/libraries/Flutter_Plugins.xml
@@ -2,44 +2,61 @@
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
+
+
-
-
-
+
+
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml
index 9cea4f0..9c7b7ef 100644
--- a/app/android/app/src/main/AndroidManifest.xml
+++ b/app/android/app/src/main/AndroidManifest.xml
@@ -29,10 +29,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/assets/images/icons/dropbox.svg b/app/assets/images/icons/dropbox.svg
new file mode 100644
index 0000000..5559d5f
--- /dev/null
+++ b/app/assets/images/icons/dropbox.svg
@@ -0,0 +1,4 @@
+
diff --git a/app/assets/locales/app_en.arb b/app/assets/locales/app_en.arb
index 3697275..9adaf0c 100644
--- a/app/assets/locales/app_en.arb
+++ b/app/assets/locales/app_en.arb
@@ -8,6 +8,7 @@
"common_accounts": "Accounts",
"common_local": "Local",
"common_google_drive": "Google Drive",
+ "common_dropbox": "Dropbox",
"common_term_and_condition": "Terms and Conditions",
"common_privacy_policy": "Privacy Policy",
"common_today": "Today",
@@ -28,6 +29,7 @@
"something_went_wrong_error": "Something went wrong! Please try again later.",
"user_google_sign_in_account_not_found_error": "You haven't signed in with Google account yet. Please sign in with Google account and try again.",
"back_up_folder_not_found_error": "Back up folder not found!",
+ "auth_session_expired_error": "Your session has expired. Please log in again to continue using the app.",
"unable_to_load_media_error": "Unable to load media!",
@@ -53,9 +55,7 @@
"greetings_hey_there_text": "Hey There!",
"greetings_hey_text": "Hey",
- "hint_google_sign_in_message": "Sign in with Google and effortlessly link your Google Drive to your Cloud Gallery. Enjoy quick access to all your awesome content in one spot",
- "hint_google_auto_backup_message": "Enable Auto Back Up to Google Drive and never lose your precious memories. Your photos and videos will be automatically backed up to your Google Drive",
- "hint_action_auto_backup": "Enable Auto Back Up",
+ "hint_sign_in_message": "Sign in with Google Drive or Dropbox and enjoy quick access to all your awesome content in one spot",
"delete_media_from_device_confirmation_message": "Are you sure you want to delete this media? It will be permanently deleted from your device.",
"delete_media_from_google_drive_confirmation_message": "Are you sure you want to delete this media? It will be permanently deleted from your Google Drive.",
@@ -79,6 +79,9 @@
"download_from_google_drive_text": "Download from Google Drive",
"download_from_google_drive_alert_message": " Are you sure you want to download this media? It will be saved to your gallery.",
+ "sign_in_with_google_drive_text": "Sign in with Google Drive",
+ "sign_in_with_dropbox_text": "Sign in with Dropbox",
+
"name_text": "Name",
"size_text": "Size",
"created_at_text": "Created at",
diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock
index f6cf19a..957989c 100644
--- a/app/ios/Podfile.lock
+++ b/app/ios/Podfile.lock
@@ -1,4 +1,6 @@
PODS:
+ - app_links (0.0.2):
+ - Flutter
- AppAuth (1.7.5):
- AppAuth/Core (= 1.7.5)
- AppAuth/ExternalUserAgent (= 1.7.5)
@@ -7,14 +9,14 @@ PODS:
- AppAuth/Core
- Firebase/CoreOnly (11.4.0):
- FirebaseCore (= 11.4.0)
- - firebase_core (3.7.0):
+ - firebase_core (3.8.0):
- Firebase/CoreOnly (= 11.4.0)
- Flutter
- FirebaseCore (11.4.0):
- FirebaseCoreInternal (~> 11.0)
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/Logger (~> 8.0)
- - FirebaseCoreInternal (11.4.2):
+ - FirebaseCoreInternal (11.5.0):
- "GoogleUtilities/NSData+zlib (~> 8.0)"
- Flutter (1.0.0)
- flutter_local_notifications (0.0.1):
@@ -23,11 +25,9 @@ PODS:
- Flutter
- Toast
- google_sign_in_ios (0.0.1):
- - AppAuth (>= 1.7.4)
- Flutter
- FlutterMacOS
- - GoogleSignIn (~> 7.1)
- - GTMSessionFetcher (>= 3.4.0)
+ - GoogleSignIn (~> 7.0)
- GoogleSignIn (7.1.0):
- AppAuth (< 2.0, >= 1.7.3)
- GTMAppAuth (< 5.0, >= 4.1.1)
@@ -43,11 +43,7 @@ PODS:
- GTMAppAuth (4.1.1):
- AppAuth/Core (~> 1.7)
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
- - GTMSessionFetcher (3.5.0):
- - GTMSessionFetcher/Full (= 3.5.0)
- GTMSessionFetcher/Core (3.5.0)
- - GTMSessionFetcher/Full (3.5.0):
- - GTMSessionFetcher/Core
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
@@ -67,6 +63,8 @@ PODS:
- Flutter
- FlutterMacOS
- Toast (4.1.1)
+ - url_launcher_ios (0.0.1):
+ - Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- FlutterMacOS
@@ -75,6 +73,7 @@ PODS:
- FlutterMacOS
DEPENDENCIES:
+ - app_links (from `.symlinks/plugins/app_links/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- Flutter (from `Flutter`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
@@ -87,6 +86,7 @@ DEPENDENCIES:
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
+ - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
@@ -103,6 +103,8 @@ SPEC REPOS:
- Toast
EXTERNAL SOURCES:
+ app_links:
+ :path: ".symlinks/plugins/app_links/ios"
firebase_core:
:path: ".symlinks/plugins/firebase_core/ios"
Flutter:
@@ -127,34 +129,38 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
+ url_launcher_ios:
+ :path: ".symlinks/plugins/url_launcher_ios/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
SPEC CHECKSUMS:
+ app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
Firebase: cf1b19f21410b029b6786a54e9764a0cacad3c99
- firebase_core: f8c5b220a8f9c436fdbd075ae321cef3d96ef181
+ firebase_core: 9efc3ecf689cdbc90f13f4dc58108c83ea46b266
FirebaseCore: e0510f1523bc0eb21653cac00792e1e2bd6f1771
- FirebaseCoreInternal: 35731192cab10797b88411be84940d2beb33a238
+ FirebaseCoreInternal: f47dd28ae7782e6a4738aad3106071a8fe0af604
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
- google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38
+ google_sign_in_ios: 989eea5abe94af62050782714daf920be883d4a2
GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
- path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
+ path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
- video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
+ url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586
+ video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579
webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4
PODFILE CHECKSUM: 4c438addb11b6da45ed7ae408823d68256222460
diff --git a/app/ios/Runner/Info.plist b/app/ios/Runner/Info.plist
index e68707c..a805d32 100644
--- a/app/ios/Runner/Info.plist
+++ b/app/ios/Runner/Info.plist
@@ -26,6 +26,7 @@
CFBundleURLSchemes
com.googleusercontent.apps.64221198282-h8h8gjo3991iijeg2agdkmfh6tr014nq
+ cloudgallery
diff --git a/app/ios/Runner/Runner.entitlements b/app/ios/Runner/Runner.entitlements
index 903def2..4d2025c 100644
--- a/app/ios/Runner/Runner.entitlements
+++ b/app/ios/Runner/Runner.entitlements
@@ -4,5 +4,9 @@
aps-environment
development
+ com.apple.developer.associated-domains
+
+ applinks:cloudgallery.com
+
diff --git a/app/lib/domain/assets/assets_paths.dart b/app/lib/domain/assets/assets_paths.dart
index 332d3d7..a639cbf 100644
--- a/app/lib/domain/assets/assets_paths.dart
+++ b/app/lib/domain/assets/assets_paths.dart
@@ -10,4 +10,5 @@ class PathImages {
class PathIcons {
String get googleDrive => 'assets/images/icons/google-drive.svg';
+ String get dropbox => 'assets/images/icons/dropbox.svg';
}
diff --git a/app/lib/domain/extensions/app_error_extensions.dart b/app/lib/domain/extensions/app_error_extensions.dart
index 0a7e3ee..0e8fcc7 100644
--- a/app/lib/domain/extensions/app_error_extensions.dart
+++ b/app/lib/domain/extensions/app_error_extensions.dart
@@ -7,14 +7,16 @@ extension AppErrorExtensions on Object {
String l10nMessage(BuildContext context) {
if (this is AppError) {
switch ((this as AppError).l10nCode) {
- case AppErrorL10nCodes.noInternetConnection:
+ case AppErrorL10nCodes.noInternetConnectionError:
return context.l10n.no_internet_connection_error;
case AppErrorL10nCodes.somethingWentWrongError:
return context.l10n.something_went_wrong_error;
case AppErrorL10nCodes.googleSignInUserNotFoundError:
return context.l10n.user_google_sign_in_account_not_found_error;
- case AppErrorL10nCodes.backUpFolderNotFound:
+ case AppErrorL10nCodes.backUpFolderNotFoundError:
return context.l10n.back_up_folder_not_found_error;
+ case AppErrorL10nCodes.authSessionExpiredError:
+ return context.l10n.auth_session_expired_error;
default:
return (this as AppError).message ??
context.l10n.something_went_wrong_error;
diff --git a/app/lib/domain/handlers/deep_links_handler.dart b/app/lib/domain/handlers/deep_links_handler.dart
new file mode 100644
index 0000000..8e93b88
--- /dev/null
+++ b/app/lib/domain/handlers/deep_links_handler.dart
@@ -0,0 +1,56 @@
+import 'dart:developer';
+
+import 'package:app_links/app_links.dart';
+import 'package:data/apis/network/urls.dart';
+import 'package:data/services/auth_service.dart';
+import 'package:data/services/dropbox_services.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+final appLinksProvider = Provider((ref) {
+ return AppLinks();
+});
+
+class DeepLinkHandler {
+ static Future observeDeepLinks({
+ required ProviderContainer container,
+ }) async {
+ final appLinks = container.read(appLinksProvider);
+
+ Future handleDeepLink(Uri link) async {
+ if (link.toString().contains(RedirectURL.auth) &&
+ link.queryParameters['code'] != null) {
+ // Set the dropbox token from the code
+ final authService = container.read(authServiceProvider);
+ await authService.setDropboxTokenFromCode(
+ code: link.queryParameters['code']!,
+ );
+
+ final dropboxService = container.read(dropboxServiceProvider);
+ await dropboxService.setCurrentUserAccount();
+ }
+ }
+
+ try {
+ final initialLink = await appLinks.getInitialLink();
+ if (initialLink != null && !kDebugMode) handleDeepLink(initialLink);
+ } catch (error) {
+ log(
+ "Failed to handle initial deep link",
+ error: error,
+ name: "DeepLinkHandler",
+ );
+ }
+
+ appLinks.uriLinkStream.listen(
+ (link) => handleDeepLink(link),
+ onError: (error) {
+ log(
+ "Failed to listen to deep links",
+ error: error,
+ name: "DeepLinkHandler",
+ );
+ },
+ );
+ }
+}
diff --git a/app/lib/main.dart b/app/lib/main.dart
index 833d962..75efe01 100644
--- a/app/lib/main.dart
+++ b/app/lib/main.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:io';
+import 'domain/handlers/deep_links_handler.dart';
import 'firebase_options.dart';
import 'package:data/storage/provider/preferences_provider.dart';
import 'package:firebase_core/firebase_core.dart';
@@ -23,6 +24,8 @@ Future main() async {
final container = await _configureContainerWithAsyncDependency();
+ DeepLinkHandler.observeDeepLinks(container: container);
+
runApp(
UncontrolledProviderScope(
container: container,
diff --git a/app/lib/ui/flow/accounts/accounts_screen.dart b/app/lib/ui/flow/accounts/accounts_screen.dart
index 2ff16e0..f1d3fe2 100644
--- a/app/lib/ui/flow/accounts/accounts_screen.dart
+++ b/app/lib/ui/flow/accounts/accounts_screen.dart
@@ -1,4 +1,5 @@
import '../../../components/app_page.dart';
+import '../../../domain/assets/assets_paths.dart';
import '../../../domain/extensions/context_extensions.dart';
import '../../../domain/extensions/widget_extensions.dart';
import 'accounts_screen_view_model.dart';
@@ -6,8 +7,9 @@ import 'components/settings_action_list.dart';
import 'package:data/storage/app_preferences.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:style/animations/on_tap_scale.dart';
+import 'package:flutter_svg/flutter_svg.dart';
import 'package:style/extensions/context_extensions.dart';
import 'package:style/text/app_text_style.dart';
import 'package:style/theme/colors.dart';
@@ -15,6 +17,7 @@ import 'package:style/buttons/buttons_list.dart';
import 'package:style/buttons/switch.dart';
import '../../../components/snack_bar.dart';
import 'components/account_tab.dart';
+import 'package:data/domain/config.dart';
class AccountsScreen extends ConsumerStatefulWidget {
const AccountsScreen({super.key});
@@ -45,66 +48,138 @@ class _AccountsScreenState extends ConsumerState {
@override
Widget build(BuildContext context) {
_errorObserver();
- final googleAccount = ref.watch(
- accountsStateNotifierProvider.select((value) => value.googleAccount),
- );
return AppPage(
title: context.l10n.common_accounts,
bodyBuilder: (context) {
return ListView(
- padding: context.systemPadding + const EdgeInsets.all(16),
+ padding: context.systemPadding +
+ const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
children: [
- if (googleAccount != null)
- AccountsTab(
- name: googleAccount.displayName ?? googleAccount.email,
- serviceDescription: context.l10n.common_google_drive,
- profileImage: googleAccount.photoUrl,
- actionList: ActionList(
- buttons: [
- ActionListButton(
- title: context.l10n.common_auto_back_up,
- trailing: Consumer(
- builder: (context, ref, child) {
- final googleDriveAutoBackUp = ref.watch(
- AppPreferences.canTakeAutoBackUpInGoogleDrive,
- );
- return AppSwitch(
- value: googleDriveAutoBackUp,
- onChanged: (bool value) {
- ref
- .read(
- AppPreferences
- .canTakeAutoBackUpInGoogleDrive
- .notifier,
- )
- .state = value;
- },
- );
+ _googleAccount(context: context),
+ if (FeatureFlags.dropboxEnabled) _dropboxAccount(context: context),
+ const SettingsActionList(),
+ const SizedBox(height: 16),
+ _buildVersion(context: context),
+ ],
+ );
+ },
+ );
+ }
+
+ Widget _googleAccount({required BuildContext context}) {
+ return Consumer(
+ builder: (context, ref, child) {
+ final googleAccount = ref.watch(
+ accountsStateNotifierProvider.select((value) => value.googleAccount),
+ );
+
+ if (googleAccount != null) {
+ return AccountsTab(
+ name: googleAccount.displayName ?? googleAccount.email,
+ serviceDescription:
+ "${context.l10n.common_google_drive} - ${googleAccount.email}",
+ profileImage: googleAccount.photoUrl,
+ actionList: ActionList(
+ buttons: [
+ ActionListButton(
+ title: context.l10n.common_auto_back_up,
+ trailing: Consumer(
+ builder: (context, ref, child) {
+ final googleDriveAutoBackUp =
+ ref.watch(AppPreferences.googleDriveAutoBackUp);
+ return AppSwitch(
+ value: googleDriveAutoBackUp,
+ onChanged: (bool value) {
+ ref
+ .read(
+ AppPreferences.googleDriveAutoBackUp.notifier,
+ )
+ .state = value;
},
- ),
- ),
- ActionListButton(
- title: context.l10n.common_sign_out,
- onPressed: notifier.signOutWithGoogle,
- ),
- ],
+ );
+ },
+ ),
),
- backgroundColor: AppColors.googleDriveColor.withAlpha(50),
+ ActionListButton(
+ title: context.l10n.common_sign_out,
+ onPressed: notifier.signOutWithGoogle,
+ ),
+ ],
+ ),
+ backgroundColor: AppColors.googleDriveColor.withAlpha(50),
+ );
+ }
+ return ActionList(
+ buttons: [
+ ActionListButton(
+ leading: SvgPicture.asset(
+ Assets.images.icons.googleDrive,
+ height: 24,
+ width: 24,
),
- if (googleAccount == null)
- OnTapScale(
- onTap: () {
- notifier.signInWithGoogle();
- },
- child: AccountsTab(
- name: context.l10n.add_account_title,
- backgroundColor: context.colorScheme.containerNormal,
+ title: context.l10n.sign_in_with_google_drive_text,
+ onPressed: () {
+ notifier.signInWithGoogle();
+ },
+ ),
+ ],
+ );
+ },
+ );
+ }
+
+ Widget _dropboxAccount({required BuildContext context}) {
+ return Consumer(
+ builder: (context, ref, child) {
+ final dropboxAccount =
+ ref.watch(AppPreferences.dropboxCurrentUserAccount);
+ if (dropboxAccount != null) {
+ return AccountsTab(
+ name: dropboxAccount.name.display_name,
+ serviceDescription:
+ "${context.l10n.common_dropbox} - ${dropboxAccount.email}",
+ profileImage: dropboxAccount.profile_photo_url,
+ actionList: ActionList(
+ buttons: [
+ ActionListButton(
+ title: context.l10n.common_auto_back_up,
+ trailing: Consumer(
+ builder: (context, ref, child) {
+ final dropboxAutoBackUp =
+ ref.watch(AppPreferences.dropboxAutoBackUp);
+ return AppSwitch(
+ value: dropboxAutoBackUp,
+ onChanged: (bool value) {
+ ref
+ .read(AppPreferences.dropboxAutoBackUp.notifier)
+ .state = value;
+ },
+ );
+ },
+ ),
+ ),
+ ActionListButton(
+ title: context.l10n.common_sign_out,
+ onPressed: notifier.signOutWithDropbox,
),
+ ],
+ ),
+ backgroundColor: AppColors.dropBoxColor.withAlpha(50),
+ );
+ }
+ return ActionList(
+ buttons: [
+ ActionListButton(
+ leading: SvgPicture.asset(
+ Assets.images.icons.dropbox,
+ height: 24,
+ width: 24,
),
- const SizedBox(height: 16),
- const SettingsActionList(),
- const SizedBox(height: 16),
- _buildVersion(context: context),
+ title: context.l10n.sign_in_with_dropbox_text,
+ onPressed: () {
+ notifier.signInWithDropbox();
+ },
+ ),
],
);
},
diff --git a/app/lib/ui/flow/accounts/accounts_screen_view_model.dart b/app/lib/ui/flow/accounts/accounts_screen_view_model.dart
index 5d78832..fa22f31 100644
--- a/app/lib/ui/flow/accounts/accounts_screen_view_model.dart
+++ b/app/lib/ui/flow/accounts/accounts_screen_view_model.dart
@@ -1,8 +1,6 @@
import 'dart:async';
import 'package:data/services/auth_service.dart';
import 'package:data/services/device_service.dart';
-import 'package:data/storage/app_preferences.dart';
-import 'package:data/storage/provider/preferences_provider.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:google_sign_in/google_sign_in.dart';
@@ -14,7 +12,6 @@ final accountsStateNotifierProvider =
(ref) => AccountsStateNotifier(
ref.read(deviceServiceProvider),
ref.read(authServiceProvider),
- ref.read(AppPreferences.canTakeAutoBackUpInGoogleDrive.notifier),
),
);
@@ -22,13 +19,9 @@ class AccountsStateNotifier extends StateNotifier {
final DeviceService _deviceService;
final AuthService _authService;
StreamSubscription? _googleAccountSubscription;
- PreferenceNotifier canTakeAutoBackUpInGoogleDrive;
- AccountsStateNotifier(
- this._deviceService,
- this._authService,
- this.canTakeAutoBackUpInGoogleDrive,
- ) : super(AccountsState(googleAccount: _authService.googleAccount));
+ AccountsStateNotifier(this._deviceService, this._authService)
+ : super(AccountsState(googleAccount: _authService.googleAccount));
Future init() async {
_getAppVersion();
@@ -50,6 +43,7 @@ class AccountsStateNotifier extends StateNotifier {
Future signInWithGoogle() async {
try {
+ state = state.copyWith(error: null);
await _authService.signInWithGoogle();
} catch (e) {
state = state.copyWith(error: e);
@@ -58,8 +52,26 @@ class AccountsStateNotifier extends StateNotifier {
Future signOutWithGoogle() async {
try {
+ state = state.copyWith(error: null);
await _authService.signOutWithGoogle();
- canTakeAutoBackUpInGoogleDrive.state = false;
+ } catch (e) {
+ state = state.copyWith(error: e);
+ }
+ }
+
+ Future signInWithDropbox() async {
+ try {
+ state = state.copyWith(error: null);
+ await _authService.signInWithDropBox();
+ } catch (e) {
+ state = state.copyWith(error: e);
+ }
+ }
+
+ Future signOutWithDropbox() async {
+ try {
+ state = state.copyWith(error: null);
+ await _authService.signOutWithDropBox();
} catch (e) {
state = state.copyWith(error: e);
}
diff --git a/app/lib/ui/flow/accounts/components/account_tab.dart b/app/lib/ui/flow/accounts/components/account_tab.dart
index a855142..85c8439 100644
--- a/app/lib/ui/flow/accounts/components/account_tab.dart
+++ b/app/lib/ui/flow/accounts/components/account_tab.dart
@@ -1,3 +1,4 @@
+import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:style/extensions/context_extensions.dart';
import 'package:style/text/app_text_style.dart';
@@ -21,6 +22,7 @@ class AccountsTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
+ margin: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(12),
@@ -28,38 +30,43 @@ class AccountsTab extends StatelessWidget {
child: Column(
children: [
Padding(
- padding: const EdgeInsets.all(16),
+ padding:
+ const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
child: Row(
children: [
_buildProfileAvtar(context: context),
const SizedBox(width: 16),
- Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- name,
- style: AppTextStyles.subtitle2.copyWith(
- color: context.colorScheme.textPrimary,
- ),
- ),
- if (serviceDescription != null) ...[
- const SizedBox(height: 4),
+ Expanded(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
Text(
- serviceDescription ?? '',
- style: AppTextStyles.body2.copyWith(
- color: context.colorScheme.textSecondary,
+ name,
+ style: AppTextStyles.subtitle2.copyWith(
+ color: context.colorScheme.textPrimary,
+ overflow: TextOverflow.ellipsis,
),
),
+ if (serviceDescription != null) ...[
+ const SizedBox(height: 4),
+ Text(
+ serviceDescription ?? '',
+ style: AppTextStyles.body2.copyWith(
+ color: context.colorScheme.textSecondary,
+ overflow: TextOverflow.visible,
+ ),
+ ),
+ ],
],
- ],
+ ),
),
],
),
),
if (actionList != null)
Padding(
- padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
+ padding: const EdgeInsets.only(left: 8, right: 8),
child: actionList,
),
],
@@ -75,7 +82,7 @@ class AccountsTab extends StatelessWidget {
border: Border.all(color: context.colorScheme.outline, width: 0.8),
image: profileImage != null
? DecorationImage(
- image: NetworkImage(profileImage!),
+ image: CachedNetworkImageProvider(profileImage!),
)
: null,
shape: BoxShape.circle,
diff --git a/app/lib/ui/flow/home/components/hints.dart b/app/lib/ui/flow/home/components/hints.dart
index 0ad518f..02c2b90 100644
--- a/app/lib/ui/flow/home/components/hints.dart
+++ b/app/lib/ui/flow/home/components/hints.dart
@@ -1,5 +1,6 @@
import '../../../../domain/extensions/context_extensions.dart';
-import '../home_screen_view_model.dart';
+import '../../../navigation/app_route.dart';
+import 'package:data/services/auth_service.dart';
import 'package:data/storage/app_preferences.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -10,49 +11,21 @@ class HomeScreenHints extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
- final googleAccount =
- ref.watch(homeViewStateNotifier.select((value) => value.googleAccount));
- final canTakeAutoBackUpInGoogleDrive =
- ref.watch(AppPreferences.canTakeAutoBackUpInGoogleDrive);
- final googleDriveSignInHintShown =
- ref.watch(AppPreferences.googleDriveSignInHintShown);
- final googleDriveAutoBackUpHintShown =
- ref.watch(AppPreferences.googleDriveAutoBackUpHintShown);
- if (!googleDriveSignInHintShown && googleAccount == null) {
+ final googleAccount = ref.watch(googleUserAccountProvider);
+ final dropboxAccount = ref.watch(AppPreferences.dropboxCurrentUserAccount);
+ final signInHintShown = ref.watch(AppPreferences.signInHintShown);
+
+ if (!signInHintShown && googleAccount == null && dropboxAccount == null) {
return HintView(
title: context.l10n.greetings_hey_there_text,
- hint: context.l10n.hint_google_sign_in_message,
+ hint: context.l10n.hint_sign_in_message,
onClose: () {
- ref.read(AppPreferences.googleDriveSignInHintShown.notifier).state =
- true;
+ ref.read(AppPreferences.signInHintShown.notifier).state = true;
},
actionTitle: context.l10n.add_account_title,
onActionTap: () {
- ref.read(homeViewStateNotifier.notifier).signInWithGoogle();
- ref.read(AppPreferences.googleDriveSignInHintShown.notifier).state =
- true;
- },
- );
- } else if (googleAccount != null &&
- !googleDriveAutoBackUpHintShown &&
- !canTakeAutoBackUpInGoogleDrive) {
- return HintView(
- title:
- "${context.l10n.greetings_hey_text} ${googleAccount.displayName?.split(' ').first ?? "There"}!",
- hint: context.l10n.hint_google_auto_backup_message,
- onClose: () {
- ref
- .read(AppPreferences.googleDriveAutoBackUpHintShown.notifier)
- .state = true;
- },
- actionTitle: context.l10n.hint_action_auto_backup,
- onActionTap: () {
- ref
- .read(AppPreferences.canTakeAutoBackUpInGoogleDrive.notifier)
- .state = true;
- ref
- .read(AppPreferences.googleDriveAutoBackUpHintShown.notifier)
- .state = true;
+ AccountRoute().push(context);
+ ref.read(AppPreferences.signInHintShown.notifier).state = true;
},
);
} else {
diff --git a/app/lib/ui/flow/home/home_screen_view_model.dart b/app/lib/ui/flow/home/home_screen_view_model.dart
index 20c3fc1..9339a1a 100644
--- a/app/lib/ui/flow/home/home_screen_view_model.dart
+++ b/app/lib/ui/flow/home/home_screen_view_model.dart
@@ -26,10 +26,10 @@ final homeViewStateNotifier =
ref.read(googleDriveServiceProvider),
ref.read(authServiceProvider),
ref.read(googleDriveProcessRepoProvider),
- ref.read(AppPreferences.canTakeAutoBackUpInGoogleDrive),
+ ref.read(AppPreferences.googleDriveAutoBackUp),
);
- ref.listen(AppPreferences.canTakeAutoBackUpInGoogleDrive, (previous, next) {
+ ref.listen(AppPreferences.googleDriveAutoBackUp, (previous, next) {
homeView.updateAutoBackUpStatus(next);
});
return homeView;
diff --git a/app/linux/flutter/generated_plugin_registrant.cc b/app/linux/flutter/generated_plugin_registrant.cc
index f6f23bf..3792af4 100644
--- a/app/linux/flutter/generated_plugin_registrant.cc
+++ b/app/linux/flutter/generated_plugin_registrant.cc
@@ -6,9 +6,13 @@
#include "generated_plugin_registrant.h"
+#include
#include
void fl_register_plugins(FlPluginRegistry* registry) {
+ g_autoptr(FlPluginRegistrar) gtk_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
+ gtk_plugin_register_with_registrar(gtk_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
diff --git a/app/linux/flutter/generated_plugins.cmake b/app/linux/flutter/generated_plugins.cmake
index f16b4c3..5d07423 100644
--- a/app/linux/flutter/generated_plugins.cmake
+++ b/app/linux/flutter/generated_plugins.cmake
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ gtk
url_launcher_linux
)
diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift
index 7d5f5c0..01d269d 100644
--- a/app/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
+import app_links
import firebase_core
import flutter_local_notifications
import google_sign_in_ios
@@ -14,10 +15,12 @@ import photo_manager
import share_plus
import shared_preferences_foundation
import sqflite_darwin
+import url_launcher_macos
import video_player_avfoundation
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
@@ -27,6 +30,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
+ UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin"))
}
diff --git a/app/pubspec.lock b/app/pubspec.lock
index 7faa8bf..ad28987 100644
--- a/app/pubspec.lock
+++ b/app/pubspec.lock
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _discoveryapis_commons
- sha256: "113c4100b90a5b70a983541782431b82168b3cae166ab130649c36eb3559d498"
+ sha256: f8bb1fdbd77f3d5c1d62b5b0eca75fbf1e41bf4f6c62628f880582e2182ae45d
url: "https://pub.dev"
source: hosted
- version: "1.0.7"
+ version: "1.0.6"
_fe_analyzer_shared:
dependency: transitive
description:
@@ -38,14 +38,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.11.3"
+ app_links:
+ dependency: "direct main"
+ description:
+ name: app_links
+ sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.3.2"
+ app_links_linux:
+ dependency: transitive
+ description:
+ name: app_links_linux
+ sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.3"
+ app_links_platform_interface:
+ dependency: transitive
+ description:
+ name: app_links_platform_interface
+ sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.2"
+ app_links_web:
+ dependency: transitive
+ description:
+ name: app_links_web
+ sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
args:
dependency: transitive
description:
name: args
- sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
+ sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
- version: "2.6.0"
+ version: "2.4.2"
async:
dependency: transitive
description:
@@ -82,10 +114,10 @@ packages:
dependency: transitive
description:
name: build_daemon
- sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
+ sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
url: "https://pub.dev"
source: hosted
- version: "4.0.2"
+ version: "4.0.1"
build_resolvers:
dependency: transitive
description:
@@ -106,10 +138,10 @@ packages:
dependency: transitive
description:
name: build_runner_core
- sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
+ sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799"
url: "https://pub.dev"
source: hosted
- version: "7.3.2"
+ version: "7.3.0"
built_collection:
dependency: transitive
description:
@@ -122,10 +154,10 @@ packages:
dependency: transitive
description:
name: built_value
- sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
+ sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e
url: "https://pub.dev"
source: hosted
- version: "8.9.2"
+ version: "8.9.1"
cached_network_image:
dependency: "direct main"
description:
@@ -178,10 +210,10 @@ packages:
dependency: transitive
description:
name: cli_util
- sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
+ sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
- version: "0.4.2"
+ version: "0.4.1"
clock:
dependency: transitive
description:
@@ -194,10 +226,10 @@ packages:
dependency: transitive
description:
name: code_builder
- sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
+ sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
url: "https://pub.dev"
source: hosted
- version: "4.10.1"
+ version: "4.10.0"
collection:
dependency: "direct main"
description:
@@ -210,10 +242,10 @@ packages:
dependency: transitive
description:
name: convert
- sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
+ sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
- version: "3.1.2"
+ version: "3.1.1"
cross_file:
dependency: transitive
description:
@@ -226,18 +258,18 @@ packages:
dependency: transitive
description:
name: crypto
- sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
+ sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
- version: "3.0.6"
+ version: "3.0.3"
csslib:
dependency: transitive
description:
name: csslib
- sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
+ sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
- version: "1.0.2"
+ version: "1.0.0"
cupertino_icons:
dependency: "direct main"
description:
@@ -282,10 +314,10 @@ packages:
dependency: transitive
description:
name: dart_style
- sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
+ sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
url: "https://pub.dev"
source: hosted
- version: "2.3.7"
+ version: "2.3.6"
data:
dependency: "direct main"
description:
@@ -337,26 +369,26 @@ packages:
dependency: transitive
description:
name: ffi
- sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
+ sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev"
source: hosted
- version: "2.1.3"
+ version: "2.1.2"
file:
dependency: transitive
description:
name: file
- sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
+ sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
- version: "7.0.1"
+ version: "7.0.0"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
- sha256: e59141ff83e70a9ba571a1f8733c5598cf57e6e68037ab185581d7fc0a436738
+ sha256: "2438a75ad803e818ad3bd5df49137ee619c46b6fc7101f4dbc23da07305ce553"
url: "https://pub.dev"
source: hosted
- version: "3.7.0"
+ version: "3.8.0"
firebase_core_platform_interface:
dependency: transitive
description:
@@ -377,10 +409,10 @@ packages:
dependency: transitive
description:
name: fixnum
- sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
+ sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
- version: "1.1.1"
+ version: "1.1.0"
flutter:
dependency: "direct main"
description: flutter
@@ -493,10 +525,10 @@ packages:
dependency: transitive
description:
name: frontend_server_client
- sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
+ sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
url: "https://pub.dev"
source: hosted
- version: "4.0.0"
+ version: "3.2.0"
glob:
dependency: transitive
description:
@@ -509,10 +541,10 @@ packages:
dependency: "direct main"
description:
name: go_router
- sha256: ce89c5a993ca5eea74535f798478502c30a625ecb10a1de4d7fef5cd1bcac2a4
+ sha256: "8ae664a70174163b9f65ea68dd8673e29db8f9095de7b5cd00e167c621f4fef5"
url: "https://pub.dev"
source: hosted
- version: "14.4.1"
+ version: "14.6.0"
go_router_builder:
dependency: "direct dev"
description:
@@ -525,10 +557,10 @@ packages:
dependency: transitive
description:
name: google_identity_services_web
- sha256: "304e2a4c25d84c287df6d7ebf5b93d8bbd4ceb817d96c2686d44f6a346f227b6"
+ sha256: bf215f340648e78697fdac486d75fca67f85944502e579bdcecd029babc6f4d8
url: "https://pub.dev"
source: hosted
- version: "0.3.1+5"
+ version: "0.3.2"
google_sign_in:
dependency: "direct main"
description:
@@ -541,18 +573,18 @@ packages:
dependency: transitive
description:
name: google_sign_in_android
- sha256: "0928059d2f0840f63c7b07a30cf73b593ae872cdd0dbd46d1b9ba878d2599c01"
+ sha256: bfd42c81c30c6faba16e0f62968d5505a87504aaa672b3155ee931461abb0a49
url: "https://pub.dev"
source: hosted
- version: "6.1.33"
+ version: "6.1.21"
google_sign_in_ios:
dependency: transitive
description:
name: google_sign_in_ios
- sha256: "83f015169102df1ab2905cf8abd8934e28f87db9ace7a5fa676998842fed228a"
+ sha256: a7d653803468d30b82ceb47ea00fe86d23c56e63eb2e5c2248bb68e9df203217
url: "https://pub.dev"
source: hosted
- version: "5.7.8"
+ version: "5.7.4"
google_sign_in_platform_interface:
dependency: transitive
description:
@@ -581,18 +613,26 @@ packages:
dependency: transitive
description:
name: googleapis_auth
- sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938
+ sha256: cafc46446574fd42826aa4cd4d623c94482598fda0a5a5649bf2781bcbc09258
url: "https://pub.dev"
source: hosted
- version: "1.6.0"
+ version: "1.5.0"
graphs:
dependency: transitive
description:
name: graphs
- sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
+ sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
url: "https://pub.dev"
source: hosted
- version: "2.3.2"
+ version: "2.3.1"
+ gtk:
+ dependency: transitive
+ description:
+ name: gtk
+ sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
hotreloader:
dependency: transitive
description:
@@ -605,10 +645,10 @@ packages:
dependency: transitive
description:
name: html
- sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec"
+ sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
- version: "0.15.5"
+ version: "0.15.4"
http:
dependency: transitive
description:
@@ -653,10 +693,10 @@ packages:
dependency: transitive
description:
name: js
- sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
+ sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
- version: "0.7.1"
+ version: "0.6.7"
json_annotation:
dependency: transitive
description:
@@ -709,10 +749,10 @@ packages:
dependency: transitive
description:
name: logging
- sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
+ sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
- version: "1.3.0"
+ version: "1.2.0"
macros:
dependency: transitive
description:
@@ -749,10 +789,10 @@ packages:
dependency: transitive
description:
name: mime
- sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+ sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
url: "https://pub.dev"
source: hosted
- version: "2.0.0"
+ version: "1.0.5"
octo_image:
dependency: transitive
description:
@@ -797,10 +837,10 @@ packages:
dependency: transitive
description:
name: path_parsing
- sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
+ sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
url: "https://pub.dev"
source: hosted
- version: "1.1.0"
+ version: "1.0.1"
path_provider:
dependency: "direct main"
description:
@@ -821,10 +861,10 @@ packages:
dependency: transitive
description:
name: path_provider_foundation
- sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
+ sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
url: "https://pub.dev"
source: hosted
- version: "2.4.0"
+ version: "2.3.2"
path_provider_linux:
dependency: transitive
description:
@@ -845,10 +885,10 @@ packages:
dependency: transitive
description:
name: path_provider_windows
- sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
+ sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
- version: "2.3.0"
+ version: "2.2.1"
permission_handler:
dependency: "direct main"
description:
@@ -861,10 +901,10 @@ packages:
dependency: transitive
description:
name: permission_handler_android
- sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1"
+ sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474"
url: "https://pub.dev"
source: hosted
- version: "12.0.13"
+ version: "12.0.5"
permission_handler_apple:
dependency: transitive
description:
@@ -877,18 +917,18 @@ packages:
dependency: transitive
description:
name: permission_handler_html
- sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851
+ sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
url: "https://pub.dev"
source: hosted
- version: "0.1.3+2"
+ version: "0.1.1"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
- sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9
+ sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78"
url: "https://pub.dev"
source: hosted
- version: "4.2.3"
+ version: "4.2.0"
permission_handler_windows:
dependency: transitive
description:
@@ -925,10 +965,10 @@ packages:
dependency: transitive
description:
name: platform
- sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
+ sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
- version: "3.1.6"
+ version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
@@ -957,10 +997,10 @@ packages:
dependency: transitive
description:
name: pubspec_parse
- sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
+ sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
url: "https://pub.dev"
source: hosted
- version: "1.3.0"
+ version: "1.2.3"
riverpod:
dependency: transitive
description:
@@ -1077,10 +1117,10 @@ packages:
dependency: transitive
description:
name: shelf_web_socket
- sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
+ sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
url: "https://pub.dev"
source: hosted
- version: "2.0.0"
+ version: "1.0.4"
sky_engine:
dependency: transitive
description: flutter
@@ -1209,10 +1249,10 @@ packages:
dependency: transitive
description:
name: synchronized
- sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
+ sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
url: "https://pub.dev"
source: hosted
- version: "3.3.0+3"
+ version: "3.1.0+1"
term_glyph:
dependency: transitive
description:
@@ -1233,10 +1273,10 @@ packages:
dependency: transitive
description:
name: timezone
- sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
+ sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0"
url: "https://pub.dev"
source: hosted
- version: "0.9.4"
+ version: "0.9.2"
timing:
dependency: transitive
description:
@@ -1249,18 +1289,50 @@ packages:
dependency: transitive
description:
name: typed_data
- sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+ sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.2"
+ url_launcher:
+ dependency: transitive
+ description:
+ name: url_launcher
+ sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.2.6"
+ url_launcher_android:
+ dependency: transitive
+ description:
+ name: url_launcher_android
+ sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.3.1"
+ url_launcher_ios:
+ dependency: transitive
+ description:
+ name: url_launcher_ios
+ sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
url: "https://pub.dev"
source: hosted
- version: "1.4.0"
+ version: "6.2.5"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
- sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
+ sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
url: "https://pub.dev"
source: hosted
- version: "3.2.0"
+ version: "3.1.1"
+ url_launcher_macos:
+ dependency: transitive
+ description:
+ name: url_launcher_macos
+ sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.0"
url_launcher_platform_interface:
dependency: transitive
description:
@@ -1305,10 +1377,10 @@ packages:
dependency: transitive
description:
name: vector_graphics_codec
- sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb"
+ sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
url: "https://pub.dev"
source: hosted
- version: "1.1.12"
+ version: "1.1.11+1"
vector_graphics_compiler:
dependency: transitive
description:
@@ -1337,34 +1409,34 @@ packages:
dependency: transitive
description:
name: video_player_android
- sha256: "391e092ba4abe2f93b3e625bd6b6a6ec7d7414279462c1c0ee42b5ab8d0a0898"
+ sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2"
url: "https://pub.dev"
source: hosted
- version: "2.7.16"
+ version: "2.4.12"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
- sha256: cd5ab8a8bc0eab65ab0cea40304097edc46da574c8c1ecdee96f28cd8ef3792f
+ sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed"
url: "https://pub.dev"
source: hosted
- version: "2.6.2"
+ version: "2.5.6"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
- sha256: "229d7642ccd9f3dc4aba169609dd6b5f3f443bb4cc15b82f7785fcada5af9bbb"
+ sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6"
url: "https://pub.dev"
source: hosted
- version: "6.2.3"
+ version: "6.2.2"
video_player_web:
dependency: transitive
description:
name: video_player_web
- sha256: "881b375a934d8ebf868c7fb1423b2bfaa393a0a265fa3f733079a86536064a10"
+ sha256: "8e9cb7fe94e49490e67bbc15149691792b58a0ade31b32e3f3688d104a0e057b"
url: "https://pub.dev"
source: hosted
- version: "2.3.3"
+ version: "2.2.0"
visibility_detector:
dependency: "direct main"
description:
@@ -1397,22 +1469,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
- web_socket:
- dependency: transitive
- description:
- name: web_socket
- sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
- url: "https://pub.dev"
- source: hosted
- version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
- sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
+ sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
url: "https://pub.dev"
source: hosted
- version: "3.0.1"
+ version: "2.4.0"
webview_flutter:
dependency: "direct main"
description:
@@ -1425,10 +1489,10 @@ packages:
dependency: transitive
description:
name: webview_flutter_android
- sha256: dec83a8da0a2dcd8a25418534cc59348dbc2855fa1dd0cc929c62b6029fde392
+ sha256: "86c2d01c37c4578ee46560109cf2e18fb271f0d080a796f09188d0952352e057"
url: "https://pub.dev"
source: hosted
- version: "4.0.1"
+ version: "4.0.2"
webview_flutter_platform_interface:
dependency: transitive
description:
@@ -1441,26 +1505,26 @@ packages:
dependency: transitive
description:
name: webview_flutter_wkwebview
- sha256: f14ee08021772fed913da8daebcfdeb46be457081e521e93e9918fe6cd1ce9e8
+ sha256: "3be297aa4ca78205abdd284cf55f168c35246c75b3079990ad8ba9d257681a30"
url: "https://pub.dev"
source: hosted
- version: "3.16.1"
+ version: "3.16.2"
win32:
dependency: transitive
description:
name: win32
- sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2"
+ sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
url: "https://pub.dev"
source: hosted
- version: "5.8.0"
+ version: "5.5.4"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
- sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
+ sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted
- version: "1.1.0"
+ version: "1.0.4"
xml:
dependency: transitive
description:
diff --git a/app/pubspec.yaml b/app/pubspec.yaml
index 4eb3d1f..d8a3583 100644
--- a/app/pubspec.yaml
+++ b/app/pubspec.yaml
@@ -52,6 +52,7 @@ dependencies:
flutter_local_notifications: ^18.0.1
share_plus: ^10.1.2
webview_flutter: ^4.10.0
+ app_links: ^6.3.2
photo_manager: ^3.6.2
photo_manager_image_provider: ^2.2.0
diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc
index df715fe..d21bfa4 100644
--- a/app/windows/flutter/generated_plugin_registrant.cc
+++ b/app/windows/flutter/generated_plugin_registrant.cc
@@ -6,12 +6,15 @@
#include "generated_plugin_registrant.h"
+#include
#include
#include
#include
#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
+ AppLinksPluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake
index 2c858c6..5b2c499 100644
--- a/app/windows/flutter/generated_plugins.cmake
+++ b/app/windows/flutter/generated_plugins.cmake
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ app_links
firebase_core
permission_handler_windows
share_plus
diff --git a/data/.flutter-plugins b/data/.flutter-plugins
index 7b899f9..2d28e59 100644
--- a/data/.flutter-plugins
+++ b/data/.flutter-plugins
@@ -16,3 +16,10 @@ shared_preferences_foundation=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/sha
shared_preferences_linux=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/
shared_preferences_web=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/
shared_preferences_windows=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/
+url_launcher=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher-6.3.1/
+url_launcher_android=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.14/
+url_launcher_ios=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.1/
+url_launcher_linux=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/
+url_launcher_macos=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.1/
+url_launcher_web=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_web-2.3.3/
+url_launcher_windows=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.3/
diff --git a/data/.flutter-plugins-dependencies b/data/.flutter-plugins-dependencies
index 347d170..5232ece 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":[]}],"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":[]}],"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":[]}],"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"]}],"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"]}],"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":[]}]},"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"]}],"date_created":"2024-11-12 12:27:29.401092","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-15 10:45:14.730523","version":"3.24.4","swift_package_manager_enabled":false}
\ No newline at end of file
diff --git a/data/lib/apis/dropbox/dropbox_auth_endpoints.dart b/data/lib/apis/dropbox/dropbox_auth_endpoints.dart
new file mode 100644
index 0000000..ea813ed
--- /dev/null
+++ b/data/lib/apis/dropbox/dropbox_auth_endpoints.dart
@@ -0,0 +1,85 @@
+import '../network/urls.dart';
+import '../network/endpoint.dart';
+
+class DropboxTokenEndpoint extends Endpoint {
+ final String code;
+ final String codeVerifier;
+ final String clientId;
+ final String redirectUrl;
+ final String clientSecret;
+
+ const DropboxTokenEndpoint({
+ required this.redirectUrl,
+ required this.code,
+ required this.codeVerifier,
+ required this.clientId,
+ required this.clientSecret,
+ });
+
+ @override
+ String get baseUrl => BaseURL.dropboxOAuth2Api;
+
+ @override
+ String get path => '/token';
+
+ @override
+ HttpMethod get method => HttpMethod.post;
+
+ @override
+ String? get contentType => 'application/x-www-form-urlencoded';
+
+ @override
+ Map get data => {
+ 'code': code,
+ 'grant_type': 'authorization_code',
+ 'redirect_uri': redirectUrl,
+ 'code_verifier': codeVerifier,
+ 'client_id': clientId,
+ 'client_secret': clientSecret,
+ };
+}
+
+class DropboxRefreshTokenEndpoint extends Endpoint {
+ final String refreshToken;
+ final String clientId;
+ final String clientSecret;
+
+ const DropboxRefreshTokenEndpoint({
+ required this.refreshToken,
+ required this.clientId,
+ required this.clientSecret,
+ });
+
+ @override
+ String get baseUrl => BaseURL.dropboxOAuth2Api;
+
+ @override
+ String get path => '/token';
+
+ @override
+ HttpMethod get method => HttpMethod.post;
+
+ @override
+ String? get contentType => 'application/x-www-form-urlencoded';
+
+ @override
+ Map get data => {
+ 'refresh_token': refreshToken,
+ 'grant_type': 'refresh_token',
+ 'client_id': clientId,
+ 'client_secret': clientSecret,
+ };
+}
+
+class DropboxGetUserAccountEndpoint extends Endpoint {
+ const DropboxGetUserAccountEndpoint();
+
+ @override
+ String get baseUrl => BaseURL.dropboxV2;
+
+ @override
+ HttpMethod get method => HttpMethod.post;
+
+ @override
+ String get path => '/users/get_current_account';
+}
diff --git a/data/lib/apis/google_drive/google_drive_endpoint.dart b/data/lib/apis/google_drive/google_drive_endpoint.dart
index 0e11348..561e3e1 100644
--- a/data/lib/apis/google_drive/google_drive_endpoint.dart
+++ b/data/lib/apis/google_drive/google_drive_endpoint.dart
@@ -1,10 +1,10 @@
import 'dart:convert';
-import '../network/base_url.dart';
import '../network/endpoint.dart';
import '../../models/media_content/media_content.dart';
import 'package:dio/dio.dart';
import 'package:googleapis/drive/v3.dart' as drive;
import 'package:http_parser/http_parser.dart';
+import '../network/urls.dart';
class UploadGoogleDriveFile extends Endpoint {
final drive.File request;
@@ -20,7 +20,7 @@ class UploadGoogleDriveFile extends Endpoint {
});
@override
- String get baseUrl => BaseURL.googleDriveUpload;
+ String get baseUrl => BaseURL.googleDriveUploadV3;
@override
CancelToken? get cancelToken => cancellationToken;
@@ -77,7 +77,7 @@ class DownloadGoogleDriveFileContent extends DownloadEndpoint {
});
@override
- String get baseUrl => BaseURL.googleDrive;
+ String get baseUrl => BaseURL.googleDriveV3;
@override
String get path => '/files/$id';
diff --git a/data/lib/apis/network/base_url.dart b/data/lib/apis/network/base_url.dart
deleted file mode 100644
index 0096322..0000000
--- a/data/lib/apis/network/base_url.dart
+++ /dev/null
@@ -1,4 +0,0 @@
-class BaseURL {
- static const googleDriveUpload = 'https://www.googleapis.com/upload/drive/v3';
- static const googleDrive = 'https://www.googleapis.com/drive/v3';
-}
diff --git a/data/lib/apis/network/client.dart b/data/lib/apis/network/client.dart
index 3791b8e..b7f2419 100644
--- a/data/lib/apis/network/client.dart
+++ b/data/lib/apis/network/client.dart
@@ -1,9 +1,11 @@
-import 'interceptors/auth_interceptor.dart';
import '../../errors/app_error.dart';
import '../../services/auth_service.dart';
import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
+import '../../storage/app_preferences.dart';
import 'endpoint.dart';
+import 'interceptors/dropbox_auth_interceptor.dart';
+import 'interceptors/google_drive_auth_interceptor.dart';
final googleAuthenticatedDioProvider = Provider((ref) {
return Dio()
@@ -17,6 +19,19 @@ final googleAuthenticatedDioProvider = Provider((ref) {
);
});
+final dropboxAuthenticatedDioProvider = Provider((ref) {
+ return Dio()
+ ..options.connectTimeout = const Duration(seconds: 60)
+ ..options.sendTimeout = const Duration(seconds: 60)
+ ..options.receiveTimeout = const Duration(seconds: 60)
+ ..interceptors.add(
+ DropboxAuthInterceptor(
+ authService: ref.read(authServiceProvider),
+ dropboxTokenController: ref.read(AppPreferences.dropboxToken.notifier),
+ ),
+ );
+});
+
final rawDioProvider = Provider((ref) {
return Dio()
..options.connectTimeout = const Duration(seconds: 60)
@@ -43,8 +58,8 @@ extension DioExtensions on Dio {
onReceiveProgress: endpoint.onReceiveProgress,
onSendProgress: endpoint.onSendProgress,
);
- } catch (error) {
- throw AppError.fromError(error);
+ } catch (e) {
+ throw AppError.fromError(e);
}
}
diff --git a/data/lib/apis/network/interceptors/dropbox_auth_interceptor.dart b/data/lib/apis/network/interceptors/dropbox_auth_interceptor.dart
new file mode 100644
index 0000000..34d637c
--- /dev/null
+++ b/data/lib/apis/network/interceptors/dropbox_auth_interceptor.dart
@@ -0,0 +1,54 @@
+import 'dart:async';
+import '../../../services/auth_service.dart';
+import 'package:dio/dio.dart';
+import '../../../models/token/token.dart';
+import '../../../storage/provider/preferences_provider.dart';
+
+class DropboxAuthInterceptor extends Interceptor {
+ final AuthService authService;
+ final PreferenceNotifier dropboxTokenController;
+
+ Completer? _refreshTokenCompleter;
+
+ DropboxAuthInterceptor({
+ required this.dropboxTokenController,
+ required this.authService,
+ });
+
+ @override
+ Future onRequest(
+ RequestOptions options,
+ RequestInterceptorHandler handler,
+ ) async {
+ final dropboxToken = dropboxTokenController.state;
+ if (dropboxToken != null) {
+ await _refreshTokenIfNeeded(dropboxToken);
+ options.headers.addAll({
+ 'Authorization': 'Bearer ${dropboxToken.access_token}',
+ });
+ }
+ handler.next(options);
+ }
+
+ @override
+ void onError(DioException err, ErrorInterceptorHandler handler) async {
+ if (err.response?.statusCode == 401 &&
+ dropboxTokenController.state != null) {
+ await _refreshTokenIfNeeded(dropboxTokenController.state!);
+ }
+ handler.next(err);
+ }
+
+ Future _refreshTokenIfNeeded(DropboxToken dropboxToken) async {
+ if (dropboxToken.expires_in.isBefore(DateTime.now())) {
+ if (_refreshTokenCompleter == null) {
+ _refreshTokenCompleter = Completer();
+ await authService.refreshDropboxToken();
+ _refreshTokenCompleter?.complete();
+ _refreshTokenCompleter = null;
+ } else {
+ await _refreshTokenCompleter!.future;
+ }
+ }
+ }
+}
diff --git a/data/lib/apis/network/interceptors/auth_interceptor.dart b/data/lib/apis/network/interceptors/google_drive_auth_interceptor.dart
similarity index 96%
rename from data/lib/apis/network/interceptors/auth_interceptor.dart
rename to data/lib/apis/network/interceptors/google_drive_auth_interceptor.dart
index b4501c6..7bcc654 100644
--- a/data/lib/apis/network/interceptors/auth_interceptor.dart
+++ b/data/lib/apis/network/interceptors/google_drive_auth_interceptor.dart
@@ -1,3 +1,4 @@
+import 'dart:async';
import 'package:dio/dio.dart';
import 'package:google_sign_in/google_sign_in.dart';
diff --git a/data/lib/apis/network/oauth2.dart b/data/lib/apis/network/oauth2.dart
new file mode 100644
index 0000000..544489b
--- /dev/null
+++ b/data/lib/apis/network/oauth2.dart
@@ -0,0 +1,54 @@
+import 'dart:convert';
+import 'dart:math' show Random;
+import 'package:crypto/crypto.dart' show sha256;
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+final oauth2Provider = Provider((ref) => Oauth2());
+
+class Oauth2 {
+ String _generateCodeVerifier({
+ String charset =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~',
+ }) =>
+ List.generate(
+ 128,
+ (i) => charset[Random.secure().nextInt(charset.length)],
+ ).join();
+
+ String get generateCodeVerifier => _generateCodeVerifier();
+
+ Uri getAuthorizationUrl({
+ required String clientId,
+ required Uri authorizationEndpoint,
+ required String redirectUri,
+ required String codeVerifier,
+ String responseType = 'code',
+ List scopes = const [],
+ String delimiter = ' ',
+ Map additionalParameters = const {},
+ String? state,
+ }) {
+ final codeChallenge = base64Url
+ .encode(sha256.convert(ascii.encode(codeVerifier)).bytes)
+ .replaceAll('=', '');
+
+ final parameters = {
+ 'client_id': clientId,
+ 'redirect_uri': redirectUri,
+ 'response_type': responseType,
+ 'code_challenge': codeChallenge,
+ 'code_challenge_method': 'S256',
+ };
+
+ if (additionalParameters.isNotEmpty) {
+ parameters.addAll(additionalParameters);
+ }
+ if (state != null) parameters['state'] = state;
+ if (scopes.isNotEmpty) parameters['scope'] = scopes.join(delimiter);
+
+ return authorizationEndpoint.replace(
+ queryParameters: Map.from(authorizationEndpoint.queryParameters)
+ ..addAll(parameters),
+ );
+ }
+}
diff --git a/data/lib/apis/network/secrets.dart b/data/lib/apis/network/secrets.dart
new file mode 100644
index 0000000..470ebe4
--- /dev/null
+++ b/data/lib/apis/network/secrets.dart
@@ -0,0 +1,4 @@
+class AppSecretes {
+ static const dropBoxAppKey = '873x7j2iwh8mrea';
+ static const dropBoxAppSecret = 'mq2azqdd6y1upzr';
+}
diff --git a/data/lib/apis/network/urls.dart b/data/lib/apis/network/urls.dart
new file mode 100644
index 0000000..b9a2894
--- /dev/null
+++ b/data/lib/apis/network/urls.dart
@@ -0,0 +1,16 @@
+class BaseURL {
+ // Google Drive API Base URL
+ static const googleDriveUploadV3 =
+ 'https://www.googleapis.com/upload/drive/v3';
+ static const googleDriveV3 = 'https://www.googleapis.com/drive/v3';
+
+ // Dropbox API Base URL
+ static const dropboxContentV2 = 'https://content.dropboxapi.com/2';
+ static const dropboxOAuth2Web = 'https://www.dropbox.com/oauth2';
+ static const dropboxOAuth2Api = 'https://api.dropboxapi.com/oauth2';
+ static const dropboxV2 = 'https://api.dropboxapi.com/2';
+}
+
+class RedirectURL {
+ static const String auth = 'cloudgallery://cloudgallery.com/auth';
+}
diff --git a/data/lib/domain/config.dart b/data/lib/domain/config.dart
new file mode 100644
index 0000000..d2725f3
--- /dev/null
+++ b/data/lib/domain/config.dart
@@ -0,0 +1,5 @@
+import 'package:flutter/foundation.dart';
+
+class FeatureFlags {
+ static const dropboxEnabled = kDebugMode;
+}
diff --git a/data/lib/errors/app_error.dart b/data/lib/errors/app_error.dart
index 654d07b..1d9b6ef 100644
--- a/data/lib/errors/app_error.dart
+++ b/data/lib/errors/app_error.dart
@@ -36,7 +36,7 @@ class AppError implements Exception {
class NoConnectionError extends AppError {
const NoConnectionError()
: super(
- l10nCode: AppErrorL10nCodes.noInternetConnection,
+ l10nCode: AppErrorL10nCodes.noInternetConnectionError,
message:
"No internet connection. Please check your network and try again.",
);
@@ -61,17 +61,33 @@ class RequestCancelledByUser extends AppError {
class BackUpFolderNotFound extends AppError {
const BackUpFolderNotFound()
: super(
- l10nCode: AppErrorL10nCodes.backUpFolderNotFound,
+ l10nCode: AppErrorL10nCodes.backUpFolderNotFoundError,
message: "Back up folder not found",
+ statusCode: 404,
);
}
class UnableToSaveFileInGallery extends AppError {
const UnableToSaveFileInGallery()
- : super(message: "Unable to save file in gallery");
+ : super(
+ l10nCode: AppErrorL10nCodes.unableToSaveFileInGalleryError,
+ message: "Unable to save file in gallery",
+ );
}
class SomethingWentWrongError extends AppError {
const SomethingWentWrongError({super.message, super.statusCode})
- : super(l10nCode: AppErrorL10nCodes.somethingWentWrongError);
+ : super(
+ l10nCode: AppErrorL10nCodes.somethingWentWrongError,
+ );
+}
+
+class AuthSessionExpiredError extends AppError {
+ const AuthSessionExpiredError()
+ : super(
+ l10nCode: AppErrorL10nCodes.authSessionExpiredError,
+ message:
+ "User authentication session expired. Unable to get access token.",
+ statusCode: 401,
+ );
}
diff --git a/data/lib/errors/l10n_error_codes.dart b/data/lib/errors/l10n_error_codes.dart
index fcad6a8..434426d 100644
--- a/data/lib/errors/l10n_error_codes.dart
+++ b/data/lib/errors/l10n_error_codes.dart
@@ -1,6 +1,9 @@
class AppErrorL10nCodes {
- static const noInternetConnection = 'no-internet-connection';
+ static const noInternetConnectionError = 'no-internet-connection';
static const somethingWentWrongError = 'something-went-wrong';
+ static const authSessionExpiredError = 'auth-session-expired';
static const googleSignInUserNotFoundError = 'google-sing-in-user-not-found';
- static const backUpFolderNotFound = 'back-up-folder-not-found';
+ static const backUpFolderNotFoundError = 'back-up-folder-not-found';
+ static const unableToSaveFileInGalleryError =
+ 'unable-to-save-file-in-gallery';
}
diff --git a/data/lib/extensions/date_time_extension.dart b/data/lib/extensions/date_time_extension.dart
new file mode 100644
index 0000000..2fea8fb
--- /dev/null
+++ b/data/lib/extensions/date_time_extension.dart
@@ -0,0 +1,3 @@
+extension DateTimeExtension on DateTime {
+ int get secondsSinceEpoch => millisecondsSinceEpoch ~/ 1000;
+}
diff --git a/data/lib/models/dropbox_account/dropbox_account.dart b/data/lib/models/dropbox_account/dropbox_account.dart
new file mode 100644
index 0000000..a21afbe
--- /dev/null
+++ b/data/lib/models/dropbox_account/dropbox_account.dart
@@ -0,0 +1,40 @@
+// ignore_for_file: non_constant_identifier_names
+
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'dropbox_account.freezed.dart';
+part 'dropbox_account.g.dart';
+
+@freezed
+class DropboxAccount with _$DropboxAccount {
+ const factory DropboxAccount({
+ required String account_id,
+ required DropboxAccountName name,
+ required String email,
+ required bool email_verified,
+ required bool disabled,
+ required String country,
+ required String locale,
+ required String referral_link,
+ required bool is_paired,
+ String? profile_photo_url,
+ String? team_member_id,
+ }) = _DropboxAccount;
+
+ factory DropboxAccount.fromJson(Map json) =>
+ _$DropboxAccountFromJson(json);
+}
+
+@freezed
+class DropboxAccountName with _$DropboxAccountName {
+ const factory DropboxAccountName({
+ required String abbreviated_name,
+ required String display_name,
+ required String familiar_name,
+ required String given_name,
+ required String surname,
+ }) = _DropboxAccountName;
+
+ factory DropboxAccountName.fromJson(Map json) =>
+ _$DropboxAccountNameFromJson(json);
+}
diff --git a/data/lib/models/dropbox_account/dropbox_account.freezed.dart b/data/lib/models/dropbox_account/dropbox_account.freezed.dart
new file mode 100644
index 0000000..2a4da4f
--- /dev/null
+++ b/data/lib/models/dropbox_account/dropbox_account.freezed.dart
@@ -0,0 +1,642 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'dropbox_account.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+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');
+
+DropboxAccount _$DropboxAccountFromJson(Map json) {
+ return _DropboxAccount.fromJson(json);
+}
+
+/// @nodoc
+mixin _$DropboxAccount {
+ String get account_id => throw _privateConstructorUsedError;
+ DropboxAccountName get name => throw _privateConstructorUsedError;
+ String get email => throw _privateConstructorUsedError;
+ bool get email_verified => throw _privateConstructorUsedError;
+ bool get disabled => throw _privateConstructorUsedError;
+ String get country => throw _privateConstructorUsedError;
+ String get locale => throw _privateConstructorUsedError;
+ String get referral_link => throw _privateConstructorUsedError;
+ bool get is_paired => throw _privateConstructorUsedError;
+ String? get profile_photo_url => throw _privateConstructorUsedError;
+ String? get team_member_id => throw _privateConstructorUsedError;
+
+ /// Serializes this DropboxAccount to a JSON map.
+ Map toJson() => throw _privateConstructorUsedError;
+
+ /// Create a copy of DropboxAccount
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ $DropboxAccountCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $DropboxAccountCopyWith<$Res> {
+ factory $DropboxAccountCopyWith(
+ DropboxAccount value, $Res Function(DropboxAccount) then) =
+ _$DropboxAccountCopyWithImpl<$Res, DropboxAccount>;
+ @useResult
+ $Res call(
+ {String account_id,
+ DropboxAccountName name,
+ String email,
+ bool email_verified,
+ bool disabled,
+ String country,
+ String locale,
+ String referral_link,
+ bool is_paired,
+ String? profile_photo_url,
+ String? team_member_id});
+
+ $DropboxAccountNameCopyWith<$Res> get name;
+}
+
+/// @nodoc
+class _$DropboxAccountCopyWithImpl<$Res, $Val extends DropboxAccount>
+ implements $DropboxAccountCopyWith<$Res> {
+ _$DropboxAccountCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of DropboxAccount
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? account_id = null,
+ Object? name = null,
+ Object? email = null,
+ Object? email_verified = null,
+ Object? disabled = null,
+ Object? country = null,
+ Object? locale = null,
+ Object? referral_link = null,
+ Object? is_paired = null,
+ Object? profile_photo_url = freezed,
+ Object? team_member_id = freezed,
+ }) {
+ return _then(_value.copyWith(
+ account_id: null == account_id
+ ? _value.account_id
+ : account_id // ignore: cast_nullable_to_non_nullable
+ as String,
+ name: null == name
+ ? _value.name
+ : name // ignore: cast_nullable_to_non_nullable
+ as DropboxAccountName,
+ email: null == email
+ ? _value.email
+ : email // ignore: cast_nullable_to_non_nullable
+ as String,
+ email_verified: null == email_verified
+ ? _value.email_verified
+ : email_verified // ignore: cast_nullable_to_non_nullable
+ as bool,
+ disabled: null == disabled
+ ? _value.disabled
+ : disabled // ignore: cast_nullable_to_non_nullable
+ as bool,
+ country: null == country
+ ? _value.country
+ : country // ignore: cast_nullable_to_non_nullable
+ as String,
+ locale: null == locale
+ ? _value.locale
+ : locale // ignore: cast_nullable_to_non_nullable
+ as String,
+ referral_link: null == referral_link
+ ? _value.referral_link
+ : referral_link // ignore: cast_nullable_to_non_nullable
+ as String,
+ is_paired: null == is_paired
+ ? _value.is_paired
+ : is_paired // ignore: cast_nullable_to_non_nullable
+ as bool,
+ profile_photo_url: freezed == profile_photo_url
+ ? _value.profile_photo_url
+ : profile_photo_url // ignore: cast_nullable_to_non_nullable
+ as String?,
+ team_member_id: freezed == team_member_id
+ ? _value.team_member_id
+ : team_member_id // ignore: cast_nullable_to_non_nullable
+ as String?,
+ ) as $Val);
+ }
+
+ /// Create a copy of DropboxAccount
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @pragma('vm:prefer-inline')
+ $DropboxAccountNameCopyWith<$Res> get name {
+ return $DropboxAccountNameCopyWith<$Res>(_value.name, (value) {
+ return _then(_value.copyWith(name: value) as $Val);
+ });
+ }
+}
+
+/// @nodoc
+abstract class _$$DropboxAccountImplCopyWith<$Res>
+ implements $DropboxAccountCopyWith<$Res> {
+ factory _$$DropboxAccountImplCopyWith(_$DropboxAccountImpl value,
+ $Res Function(_$DropboxAccountImpl) then) =
+ __$$DropboxAccountImplCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call(
+ {String account_id,
+ DropboxAccountName name,
+ String email,
+ bool email_verified,
+ bool disabled,
+ String country,
+ String locale,
+ String referral_link,
+ bool is_paired,
+ String? profile_photo_url,
+ String? team_member_id});
+
+ @override
+ $DropboxAccountNameCopyWith<$Res> get name;
+}
+
+/// @nodoc
+class __$$DropboxAccountImplCopyWithImpl<$Res>
+ extends _$DropboxAccountCopyWithImpl<$Res, _$DropboxAccountImpl>
+ implements _$$DropboxAccountImplCopyWith<$Res> {
+ __$$DropboxAccountImplCopyWithImpl(
+ _$DropboxAccountImpl _value, $Res Function(_$DropboxAccountImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of DropboxAccount
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? account_id = null,
+ Object? name = null,
+ Object? email = null,
+ Object? email_verified = null,
+ Object? disabled = null,
+ Object? country = null,
+ Object? locale = null,
+ Object? referral_link = null,
+ Object? is_paired = null,
+ Object? profile_photo_url = freezed,
+ Object? team_member_id = freezed,
+ }) {
+ return _then(_$DropboxAccountImpl(
+ account_id: null == account_id
+ ? _value.account_id
+ : account_id // ignore: cast_nullable_to_non_nullable
+ as String,
+ name: null == name
+ ? _value.name
+ : name // ignore: cast_nullable_to_non_nullable
+ as DropboxAccountName,
+ email: null == email
+ ? _value.email
+ : email // ignore: cast_nullable_to_non_nullable
+ as String,
+ email_verified: null == email_verified
+ ? _value.email_verified
+ : email_verified // ignore: cast_nullable_to_non_nullable
+ as bool,
+ disabled: null == disabled
+ ? _value.disabled
+ : disabled // ignore: cast_nullable_to_non_nullable
+ as bool,
+ country: null == country
+ ? _value.country
+ : country // ignore: cast_nullable_to_non_nullable
+ as String,
+ locale: null == locale
+ ? _value.locale
+ : locale // ignore: cast_nullable_to_non_nullable
+ as String,
+ referral_link: null == referral_link
+ ? _value.referral_link
+ : referral_link // ignore: cast_nullable_to_non_nullable
+ as String,
+ is_paired: null == is_paired
+ ? _value.is_paired
+ : is_paired // ignore: cast_nullable_to_non_nullable
+ as bool,
+ profile_photo_url: freezed == profile_photo_url
+ ? _value.profile_photo_url
+ : profile_photo_url // ignore: cast_nullable_to_non_nullable
+ as String?,
+ team_member_id: freezed == team_member_id
+ ? _value.team_member_id
+ : team_member_id // ignore: cast_nullable_to_non_nullable
+ as String?,
+ ));
+ }
+}
+
+/// @nodoc
+@JsonSerializable()
+class _$DropboxAccountImpl implements _DropboxAccount {
+ const _$DropboxAccountImpl(
+ {required this.account_id,
+ required this.name,
+ required this.email,
+ required this.email_verified,
+ required this.disabled,
+ required this.country,
+ required this.locale,
+ required this.referral_link,
+ required this.is_paired,
+ this.profile_photo_url,
+ this.team_member_id});
+
+ factory _$DropboxAccountImpl.fromJson(Map json) =>
+ _$$DropboxAccountImplFromJson(json);
+
+ @override
+ final String account_id;
+ @override
+ final DropboxAccountName name;
+ @override
+ final String email;
+ @override
+ final bool email_verified;
+ @override
+ final bool disabled;
+ @override
+ final String country;
+ @override
+ final String locale;
+ @override
+ final String referral_link;
+ @override
+ final bool is_paired;
+ @override
+ final String? profile_photo_url;
+ @override
+ final String? team_member_id;
+
+ @override
+ String toString() {
+ return 'DropboxAccount(account_id: $account_id, name: $name, email: $email, email_verified: $email_verified, disabled: $disabled, country: $country, locale: $locale, referral_link: $referral_link, is_paired: $is_paired, profile_photo_url: $profile_photo_url, team_member_id: $team_member_id)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$DropboxAccountImpl &&
+ (identical(other.account_id, account_id) ||
+ other.account_id == account_id) &&
+ (identical(other.name, name) || other.name == name) &&
+ (identical(other.email, email) || other.email == email) &&
+ (identical(other.email_verified, email_verified) ||
+ other.email_verified == email_verified) &&
+ (identical(other.disabled, disabled) ||
+ other.disabled == disabled) &&
+ (identical(other.country, country) || other.country == country) &&
+ (identical(other.locale, locale) || other.locale == locale) &&
+ (identical(other.referral_link, referral_link) ||
+ other.referral_link == referral_link) &&
+ (identical(other.is_paired, is_paired) ||
+ other.is_paired == is_paired) &&
+ (identical(other.profile_photo_url, profile_photo_url) ||
+ other.profile_photo_url == profile_photo_url) &&
+ (identical(other.team_member_id, team_member_id) ||
+ other.team_member_id == team_member_id));
+ }
+
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ int get hashCode => Object.hash(
+ runtimeType,
+ account_id,
+ name,
+ email,
+ email_verified,
+ disabled,
+ country,
+ locale,
+ referral_link,
+ is_paired,
+ profile_photo_url,
+ team_member_id);
+
+ /// Create a copy of DropboxAccount
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$DropboxAccountImplCopyWith<_$DropboxAccountImpl> get copyWith =>
+ __$$DropboxAccountImplCopyWithImpl<_$DropboxAccountImpl>(
+ this, _$identity);
+
+ @override
+ Map toJson() {
+ return _$$DropboxAccountImplToJson(
+ this,
+ );
+ }
+}
+
+abstract class _DropboxAccount implements DropboxAccount {
+ const factory _DropboxAccount(
+ {required final String account_id,
+ required final DropboxAccountName name,
+ required final String email,
+ required final bool email_verified,
+ required final bool disabled,
+ required final String country,
+ required final String locale,
+ required final String referral_link,
+ required final bool is_paired,
+ final String? profile_photo_url,
+ final String? team_member_id}) = _$DropboxAccountImpl;
+
+ factory _DropboxAccount.fromJson(Map json) =
+ _$DropboxAccountImpl.fromJson;
+
+ @override
+ String get account_id;
+ @override
+ DropboxAccountName get name;
+ @override
+ String get email;
+ @override
+ bool get email_verified;
+ @override
+ bool get disabled;
+ @override
+ String get country;
+ @override
+ String get locale;
+ @override
+ String get referral_link;
+ @override
+ bool get is_paired;
+ @override
+ String? get profile_photo_url;
+ @override
+ String? get team_member_id;
+
+ /// Create a copy of DropboxAccount
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ _$$DropboxAccountImplCopyWith<_$DropboxAccountImpl> get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+DropboxAccountName _$DropboxAccountNameFromJson(Map json) {
+ return _DropboxAccountName.fromJson(json);
+}
+
+/// @nodoc
+mixin _$DropboxAccountName {
+ String get abbreviated_name => throw _privateConstructorUsedError;
+ String get display_name => throw _privateConstructorUsedError;
+ String get familiar_name => throw _privateConstructorUsedError;
+ String get given_name => throw _privateConstructorUsedError;
+ String get surname => throw _privateConstructorUsedError;
+
+ /// Serializes this DropboxAccountName to a JSON map.
+ Map toJson() => throw _privateConstructorUsedError;
+
+ /// Create a copy of DropboxAccountName
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ $DropboxAccountNameCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $DropboxAccountNameCopyWith<$Res> {
+ factory $DropboxAccountNameCopyWith(
+ DropboxAccountName value, $Res Function(DropboxAccountName) then) =
+ _$DropboxAccountNameCopyWithImpl<$Res, DropboxAccountName>;
+ @useResult
+ $Res call(
+ {String abbreviated_name,
+ String display_name,
+ String familiar_name,
+ String given_name,
+ String surname});
+}
+
+/// @nodoc
+class _$DropboxAccountNameCopyWithImpl<$Res, $Val extends DropboxAccountName>
+ implements $DropboxAccountNameCopyWith<$Res> {
+ _$DropboxAccountNameCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of DropboxAccountName
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? abbreviated_name = null,
+ Object? display_name = null,
+ Object? familiar_name = null,
+ Object? given_name = null,
+ Object? surname = null,
+ }) {
+ return _then(_value.copyWith(
+ abbreviated_name: null == abbreviated_name
+ ? _value.abbreviated_name
+ : abbreviated_name // ignore: cast_nullable_to_non_nullable
+ as String,
+ display_name: null == display_name
+ ? _value.display_name
+ : display_name // ignore: cast_nullable_to_non_nullable
+ as String,
+ familiar_name: null == familiar_name
+ ? _value.familiar_name
+ : familiar_name // ignore: cast_nullable_to_non_nullable
+ as String,
+ given_name: null == given_name
+ ? _value.given_name
+ : given_name // ignore: cast_nullable_to_non_nullable
+ as String,
+ surname: null == surname
+ ? _value.surname
+ : surname // ignore: cast_nullable_to_non_nullable
+ as String,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$DropboxAccountNameImplCopyWith<$Res>
+ implements $DropboxAccountNameCopyWith<$Res> {
+ factory _$$DropboxAccountNameImplCopyWith(_$DropboxAccountNameImpl value,
+ $Res Function(_$DropboxAccountNameImpl) then) =
+ __$$DropboxAccountNameImplCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call(
+ {String abbreviated_name,
+ String display_name,
+ String familiar_name,
+ String given_name,
+ String surname});
+}
+
+/// @nodoc
+class __$$DropboxAccountNameImplCopyWithImpl<$Res>
+ extends _$DropboxAccountNameCopyWithImpl<$Res, _$DropboxAccountNameImpl>
+ implements _$$DropboxAccountNameImplCopyWith<$Res> {
+ __$$DropboxAccountNameImplCopyWithImpl(_$DropboxAccountNameImpl _value,
+ $Res Function(_$DropboxAccountNameImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of DropboxAccountName
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? abbreviated_name = null,
+ Object? display_name = null,
+ Object? familiar_name = null,
+ Object? given_name = null,
+ Object? surname = null,
+ }) {
+ return _then(_$DropboxAccountNameImpl(
+ abbreviated_name: null == abbreviated_name
+ ? _value.abbreviated_name
+ : abbreviated_name // ignore: cast_nullable_to_non_nullable
+ as String,
+ display_name: null == display_name
+ ? _value.display_name
+ : display_name // ignore: cast_nullable_to_non_nullable
+ as String,
+ familiar_name: null == familiar_name
+ ? _value.familiar_name
+ : familiar_name // ignore: cast_nullable_to_non_nullable
+ as String,
+ given_name: null == given_name
+ ? _value.given_name
+ : given_name // ignore: cast_nullable_to_non_nullable
+ as String,
+ surname: null == surname
+ ? _value.surname
+ : surname // ignore: cast_nullable_to_non_nullable
+ as String,
+ ));
+ }
+}
+
+/// @nodoc
+@JsonSerializable()
+class _$DropboxAccountNameImpl implements _DropboxAccountName {
+ const _$DropboxAccountNameImpl(
+ {required this.abbreviated_name,
+ required this.display_name,
+ required this.familiar_name,
+ required this.given_name,
+ required this.surname});
+
+ factory _$DropboxAccountNameImpl.fromJson(Map json) =>
+ _$$DropboxAccountNameImplFromJson(json);
+
+ @override
+ final String abbreviated_name;
+ @override
+ final String display_name;
+ @override
+ final String familiar_name;
+ @override
+ final String given_name;
+ @override
+ final String surname;
+
+ @override
+ String toString() {
+ return 'DropboxAccountName(abbreviated_name: $abbreviated_name, display_name: $display_name, familiar_name: $familiar_name, given_name: $given_name, surname: $surname)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$DropboxAccountNameImpl &&
+ (identical(other.abbreviated_name, abbreviated_name) ||
+ other.abbreviated_name == abbreviated_name) &&
+ (identical(other.display_name, display_name) ||
+ other.display_name == display_name) &&
+ (identical(other.familiar_name, familiar_name) ||
+ other.familiar_name == familiar_name) &&
+ (identical(other.given_name, given_name) ||
+ other.given_name == given_name) &&
+ (identical(other.surname, surname) || other.surname == surname));
+ }
+
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ int get hashCode => Object.hash(runtimeType, abbreviated_name, display_name,
+ familiar_name, given_name, surname);
+
+ /// Create a copy of DropboxAccountName
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$DropboxAccountNameImplCopyWith<_$DropboxAccountNameImpl> get copyWith =>
+ __$$DropboxAccountNameImplCopyWithImpl<_$DropboxAccountNameImpl>(
+ this, _$identity);
+
+ @override
+ Map toJson() {
+ return _$$DropboxAccountNameImplToJson(
+ this,
+ );
+ }
+}
+
+abstract class _DropboxAccountName implements DropboxAccountName {
+ const factory _DropboxAccountName(
+ {required final String abbreviated_name,
+ required final String display_name,
+ required final String familiar_name,
+ required final String given_name,
+ required final String surname}) = _$DropboxAccountNameImpl;
+
+ factory _DropboxAccountName.fromJson(Map json) =
+ _$DropboxAccountNameImpl.fromJson;
+
+ @override
+ String get abbreviated_name;
+ @override
+ String get display_name;
+ @override
+ String get familiar_name;
+ @override
+ String get given_name;
+ @override
+ String get surname;
+
+ /// Create a copy of DropboxAccountName
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ _$$DropboxAccountNameImplCopyWith<_$DropboxAccountNameImpl> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/data/lib/models/dropbox_account/dropbox_account.g.dart b/data/lib/models/dropbox_account/dropbox_account.g.dart
new file mode 100644
index 0000000..695f536
--- /dev/null
+++ b/data/lib/models/dropbox_account/dropbox_account.g.dart
@@ -0,0 +1,58 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'dropbox_account.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+_$DropboxAccountImpl _$$DropboxAccountImplFromJson(Map json) =>
+ _$DropboxAccountImpl(
+ account_id: json['account_id'] as String,
+ name: DropboxAccountName.fromJson(json['name'] as Map),
+ email: json['email'] as String,
+ email_verified: json['email_verified'] as bool,
+ disabled: json['disabled'] as bool,
+ country: json['country'] as String,
+ locale: json['locale'] as String,
+ referral_link: json['referral_link'] as String,
+ is_paired: json['is_paired'] as bool,
+ profile_photo_url: json['profile_photo_url'] as String?,
+ team_member_id: json['team_member_id'] as String?,
+ );
+
+Map _$$DropboxAccountImplToJson(
+ _$DropboxAccountImpl instance) =>
+ {
+ 'account_id': instance.account_id,
+ 'name': instance.name,
+ 'email': instance.email,
+ 'email_verified': instance.email_verified,
+ 'disabled': instance.disabled,
+ 'country': instance.country,
+ 'locale': instance.locale,
+ 'referral_link': instance.referral_link,
+ 'is_paired': instance.is_paired,
+ 'profile_photo_url': instance.profile_photo_url,
+ 'team_member_id': instance.team_member_id,
+ };
+
+_$DropboxAccountNameImpl _$$DropboxAccountNameImplFromJson(
+ Map json) =>
+ _$DropboxAccountNameImpl(
+ abbreviated_name: json['abbreviated_name'] as String,
+ display_name: json['display_name'] as String,
+ familiar_name: json['familiar_name'] as String,
+ given_name: json['given_name'] as String,
+ surname: json['surname'] as String,
+ );
+
+Map _$$DropboxAccountNameImplToJson(
+ _$DropboxAccountNameImpl instance) =>
+ {
+ 'abbreviated_name': instance.abbreviated_name,
+ 'display_name': instance.display_name,
+ 'familiar_name': instance.familiar_name,
+ 'given_name': instance.given_name,
+ 'surname': instance.surname,
+ };
diff --git a/data/lib/models/token/token.dart b/data/lib/models/token/token.dart
new file mode 100644
index 0000000..3a48909
--- /dev/null
+++ b/data/lib/models/token/token.dart
@@ -0,0 +1,40 @@
+// ignore_for_file: non_constant_identifier_names
+
+import '../../extensions/date_time_extension.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'token.freezed.dart';
+part 'token.g.dart';
+
+class ExpiresInJsonConverter implements JsonConverter {
+ const ExpiresInJsonConverter();
+
+ @override
+ DateTime fromJson(int json) {
+ final date = DateTime.fromMillisecondsSinceEpoch(
+ DateTime.now().millisecondsSinceEpoch + (json * 1000),
+ );
+ return date;
+ }
+
+ @override
+ int toJson(DateTime dateTime) {
+ return dateTime.secondsSinceEpoch;
+ }
+}
+
+@freezed
+abstract class DropboxToken with _$DropboxToken {
+ const factory DropboxToken({
+ required String access_token,
+ required String token_type,
+ @ExpiresInJsonConverter() required DateTime expires_in,
+ required String refresh_token,
+ required String account_id,
+ required String scope,
+ required String uid,
+ }) = _DropboxToken;
+
+ factory DropboxToken.fromJson(Map json) =>
+ _$DropboxTokenFromJson(json);
+}
diff --git a/data/lib/models/token/token.freezed.dart b/data/lib/models/token/token.freezed.dart
new file mode 100644
index 0000000..39879bc
--- /dev/null
+++ b/data/lib/models/token/token.freezed.dart
@@ -0,0 +1,297 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'token.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+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');
+
+DropboxToken _$DropboxTokenFromJson(Map json) {
+ return _DropboxToken.fromJson(json);
+}
+
+/// @nodoc
+mixin _$DropboxToken {
+ String get access_token => throw _privateConstructorUsedError;
+ String get token_type => throw _privateConstructorUsedError;
+ @ExpiresInJsonConverter()
+ DateTime get expires_in => throw _privateConstructorUsedError;
+ String get refresh_token => throw _privateConstructorUsedError;
+ String get account_id => throw _privateConstructorUsedError;
+ String get scope => throw _privateConstructorUsedError;
+ String get uid => throw _privateConstructorUsedError;
+
+ /// Serializes this DropboxToken to a JSON map.
+ Map toJson() => throw _privateConstructorUsedError;
+
+ /// Create a copy of DropboxToken
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ $DropboxTokenCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $DropboxTokenCopyWith<$Res> {
+ factory $DropboxTokenCopyWith(
+ DropboxToken value, $Res Function(DropboxToken) then) =
+ _$DropboxTokenCopyWithImpl<$Res, DropboxToken>;
+ @useResult
+ $Res call(
+ {String access_token,
+ String token_type,
+ @ExpiresInJsonConverter() DateTime expires_in,
+ String refresh_token,
+ String account_id,
+ String scope,
+ String uid});
+}
+
+/// @nodoc
+class _$DropboxTokenCopyWithImpl<$Res, $Val extends DropboxToken>
+ implements $DropboxTokenCopyWith<$Res> {
+ _$DropboxTokenCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of DropboxToken
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? access_token = null,
+ Object? token_type = null,
+ Object? expires_in = null,
+ Object? refresh_token = null,
+ Object? account_id = null,
+ Object? scope = null,
+ Object? uid = null,
+ }) {
+ return _then(_value.copyWith(
+ access_token: null == access_token
+ ? _value.access_token
+ : access_token // ignore: cast_nullable_to_non_nullable
+ as String,
+ token_type: null == token_type
+ ? _value.token_type
+ : token_type // ignore: cast_nullable_to_non_nullable
+ as String,
+ expires_in: null == expires_in
+ ? _value.expires_in
+ : expires_in // ignore: cast_nullable_to_non_nullable
+ as DateTime,
+ refresh_token: null == refresh_token
+ ? _value.refresh_token
+ : refresh_token // ignore: cast_nullable_to_non_nullable
+ as String,
+ account_id: null == account_id
+ ? _value.account_id
+ : account_id // ignore: cast_nullable_to_non_nullable
+ as String,
+ scope: null == scope
+ ? _value.scope
+ : scope // ignore: cast_nullable_to_non_nullable
+ as String,
+ uid: null == uid
+ ? _value.uid
+ : uid // ignore: cast_nullable_to_non_nullable
+ as String,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$DropboxTokenImplCopyWith<$Res>
+ implements $DropboxTokenCopyWith<$Res> {
+ factory _$$DropboxTokenImplCopyWith(
+ _$DropboxTokenImpl value, $Res Function(_$DropboxTokenImpl) then) =
+ __$$DropboxTokenImplCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call(
+ {String access_token,
+ String token_type,
+ @ExpiresInJsonConverter() DateTime expires_in,
+ String refresh_token,
+ String account_id,
+ String scope,
+ String uid});
+}
+
+/// @nodoc
+class __$$DropboxTokenImplCopyWithImpl<$Res>
+ extends _$DropboxTokenCopyWithImpl<$Res, _$DropboxTokenImpl>
+ implements _$$DropboxTokenImplCopyWith<$Res> {
+ __$$DropboxTokenImplCopyWithImpl(
+ _$DropboxTokenImpl _value, $Res Function(_$DropboxTokenImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of DropboxToken
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? access_token = null,
+ Object? token_type = null,
+ Object? expires_in = null,
+ Object? refresh_token = null,
+ Object? account_id = null,
+ Object? scope = null,
+ Object? uid = null,
+ }) {
+ return _then(_$DropboxTokenImpl(
+ access_token: null == access_token
+ ? _value.access_token
+ : access_token // ignore: cast_nullable_to_non_nullable
+ as String,
+ token_type: null == token_type
+ ? _value.token_type
+ : token_type // ignore: cast_nullable_to_non_nullable
+ as String,
+ expires_in: null == expires_in
+ ? _value.expires_in
+ : expires_in // ignore: cast_nullable_to_non_nullable
+ as DateTime,
+ refresh_token: null == refresh_token
+ ? _value.refresh_token
+ : refresh_token // ignore: cast_nullable_to_non_nullable
+ as String,
+ account_id: null == account_id
+ ? _value.account_id
+ : account_id // ignore: cast_nullable_to_non_nullable
+ as String,
+ scope: null == scope
+ ? _value.scope
+ : scope // ignore: cast_nullable_to_non_nullable
+ as String,
+ uid: null == uid
+ ? _value.uid
+ : uid // ignore: cast_nullable_to_non_nullable
+ as String,
+ ));
+ }
+}
+
+/// @nodoc
+@JsonSerializable()
+class _$DropboxTokenImpl implements _DropboxToken {
+ const _$DropboxTokenImpl(
+ {required this.access_token,
+ required this.token_type,
+ @ExpiresInJsonConverter() required this.expires_in,
+ required this.refresh_token,
+ required this.account_id,
+ required this.scope,
+ required this.uid});
+
+ factory _$DropboxTokenImpl.fromJson(Map json) =>
+ _$$DropboxTokenImplFromJson(json);
+
+ @override
+ final String access_token;
+ @override
+ final String token_type;
+ @override
+ @ExpiresInJsonConverter()
+ final DateTime expires_in;
+ @override
+ final String refresh_token;
+ @override
+ final String account_id;
+ @override
+ final String scope;
+ @override
+ final String uid;
+
+ @override
+ String toString() {
+ return 'DropboxToken(access_token: $access_token, token_type: $token_type, expires_in: $expires_in, refresh_token: $refresh_token, account_id: $account_id, scope: $scope, uid: $uid)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$DropboxTokenImpl &&
+ (identical(other.access_token, access_token) ||
+ other.access_token == access_token) &&
+ (identical(other.token_type, token_type) ||
+ other.token_type == token_type) &&
+ (identical(other.expires_in, expires_in) ||
+ other.expires_in == expires_in) &&
+ (identical(other.refresh_token, refresh_token) ||
+ other.refresh_token == refresh_token) &&
+ (identical(other.account_id, account_id) ||
+ other.account_id == account_id) &&
+ (identical(other.scope, scope) || other.scope == scope) &&
+ (identical(other.uid, uid) || other.uid == uid));
+ }
+
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ int get hashCode => Object.hash(runtimeType, access_token, token_type,
+ expires_in, refresh_token, account_id, scope, uid);
+
+ /// Create a copy of DropboxToken
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$DropboxTokenImplCopyWith<_$DropboxTokenImpl> get copyWith =>
+ __$$DropboxTokenImplCopyWithImpl<_$DropboxTokenImpl>(this, _$identity);
+
+ @override
+ Map toJson() {
+ return _$$DropboxTokenImplToJson(
+ this,
+ );
+ }
+}
+
+abstract class _DropboxToken implements DropboxToken {
+ const factory _DropboxToken(
+ {required final String access_token,
+ required final String token_type,
+ @ExpiresInJsonConverter() required final DateTime expires_in,
+ required final String refresh_token,
+ required final String account_id,
+ required final String scope,
+ required final String uid}) = _$DropboxTokenImpl;
+
+ factory _DropboxToken.fromJson(Map json) =
+ _$DropboxTokenImpl.fromJson;
+
+ @override
+ String get access_token;
+ @override
+ String get token_type;
+ @override
+ @ExpiresInJsonConverter()
+ DateTime get expires_in;
+ @override
+ String get refresh_token;
+ @override
+ String get account_id;
+ @override
+ String get scope;
+ @override
+ String get uid;
+
+ /// Create a copy of DropboxToken
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ _$$DropboxTokenImplCopyWith<_$DropboxTokenImpl> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/data/lib/models/token/token.g.dart b/data/lib/models/token/token.g.dart
new file mode 100644
index 0000000..c8fa152
--- /dev/null
+++ b/data/lib/models/token/token.g.dart
@@ -0,0 +1,30 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'token.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+_$DropboxTokenImpl _$$DropboxTokenImplFromJson(Map json) =>
+ _$DropboxTokenImpl(
+ access_token: json['access_token'] as String,
+ token_type: json['token_type'] as String,
+ expires_in: const ExpiresInJsonConverter()
+ .fromJson((json['expires_in'] as num).toInt()),
+ refresh_token: json['refresh_token'] as String,
+ account_id: json['account_id'] as String,
+ scope: json['scope'] as String,
+ uid: json['uid'] as String,
+ );
+
+Map _$$DropboxTokenImplToJson(_$DropboxTokenImpl instance) =>
+ {
+ 'access_token': instance.access_token,
+ 'token_type': instance.token_type,
+ 'expires_in': const ExpiresInJsonConverter().toJson(instance.expires_in),
+ 'refresh_token': instance.refresh_token,
+ 'account_id': instance.account_id,
+ 'scope': instance.scope,
+ 'uid': instance.uid,
+ };
diff --git a/data/lib/services/auth_service.dart b/data/lib/services/auth_service.dart
index df9e983..85e5f05 100644
--- a/data/lib/services/auth_service.dart
+++ b/data/lib/services/auth_service.dart
@@ -1,6 +1,19 @@
+import 'dart:async';
+import '../apis/network/client.dart';
+import '../apis/network/oauth2.dart';
+import '../errors/app_error.dart';
+import '../models/dropbox_account/dropbox_account.dart';
+import '../storage/app_preferences.dart';
+import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:googleapis/drive/v3.dart' as google_drive;
+import 'package:url_launcher/url_launcher.dart';
+import '../apis/dropbox/dropbox_auth_endpoints.dart';
+import '../apis/network/secrets.dart';
+import '../apis/network/urls.dart';
+import '../models/token/token.dart';
+import '../storage/provider/preferences_provider.dart';
final googleUserAccountProvider = StateProvider((ref) {
final googleSignIn = ref.read(googleSignInProvider);
@@ -23,21 +36,44 @@ final googleSignInProvider = Provider(
final authServiceProvider = Provider(
(ref) => AuthService(
ref.read(googleSignInProvider),
+ ref.read(rawDioProvider),
+ ref.read(oauth2Provider),
+ ref.read(AppPreferences.dropboxToken.notifier),
+ ref.read(AppPreferences.dropboxPKCECodeVerifier.notifier),
+ ref.read(AppPreferences.dropboxCurrentUserAccount.notifier),
+ ref.read(AppPreferences.googleDriveAutoBackUp.notifier),
+ ref.read(AppPreferences.dropboxAutoBackUp.notifier),
),
);
class AuthService {
final GoogleSignIn _googleSignIn;
+ final Oauth2 _oauth2;
+ final Dio _dio;
+ final PreferenceNotifier _dropboxTokenController;
+ final PreferenceNotifier _dropboxAccountController;
+ final PreferenceNotifier _dropboxCodeVerifierPrefProvider;
+ final PreferenceNotifier _googleDriveAutoBackUpController;
+ final PreferenceNotifier _dropboxAutoBackUpController;
- AuthService(this._googleSignIn) {
+ AuthService(
+ this._googleSignIn,
+ this._dio,
+ this._oauth2,
+ this._dropboxTokenController,
+ this._dropboxCodeVerifierPrefProvider,
+ this._dropboxAccountController,
+ this._googleDriveAutoBackUpController,
+ this._dropboxAutoBackUpController,
+ ) {
signInSilently();
}
Future signInSilently() async {
try {
await _googleSignIn.signInSilently(suppressErrors: true);
- } catch (_) {
- rethrow;
+ } catch (e) {
+ throw AppError.fromError(e);
}
}
@@ -47,16 +83,92 @@ class AuthService {
if (googleSignInAccount != null) {
await googleSignInAccount.authentication;
}
- } catch (_) {
- rethrow;
+ } catch (e) {
+ throw AppError.fromError(e);
}
}
Future signOutWithGoogle() async {
try {
await _googleSignIn.signOut();
- } catch (_) {
- rethrow;
+ _googleDriveAutoBackUpController.state = false;
+ } catch (e) {
+ throw AppError.fromError(e);
+ }
+ }
+
+ /// Launches the URL in the browser for OAuth 2 authentication with Dropbox.
+ /// Retrieves the access token using the Proof of Key Code Exchange (PKCE) flow.
+ Future signInWithDropBox() async {
+ try {
+ final codeVerifier = _oauth2.generateCodeVerifier;
+ _dropboxCodeVerifierPrefProvider.state = codeVerifier;
+ final authorizationUrl = _oauth2.getAuthorizationUrl(
+ clientId: AppSecretes.dropBoxAppKey,
+ authorizationEndpoint:
+ Uri.parse('${BaseURL.dropboxOAuth2Web}/authorize'),
+ additionalParameters: {'token_access_type': 'offline'},
+ redirectUri: RedirectURL.auth,
+ codeVerifier: codeVerifier,
+ );
+ await launchUrl(authorizationUrl);
+ } catch (e) {
+ throw AppError.fromError(e);
+ }
+ }
+
+ Future setDropboxTokenFromCode({required String code}) async {
+ try {
+ if (_dropboxCodeVerifierPrefProvider.state == null) {
+ throw const SomethingWentWrongError(
+ message: "Dropbox code verifier is missing",
+ );
+ }
+ final res = await _dio.req(
+ DropboxTokenEndpoint(
+ code: code,
+ codeVerifier: _dropboxCodeVerifierPrefProvider.state!,
+ clientId: AppSecretes.dropBoxAppKey,
+ redirectUrl: RedirectURL.auth,
+ clientSecret: AppSecretes.dropBoxAppSecret,
+ ),
+ );
+ if (res.data != null) {
+ _dropboxTokenController.state = DropboxToken.fromJson(res.data);
+ _dropboxCodeVerifierPrefProvider.state = null;
+ }
+ } catch (e) {
+ throw AppError.fromError(e);
+ }
+ }
+
+ Future signOutWithDropBox() async {
+ _dropboxTokenController.state = null;
+ _dropboxAccountController.state = null;
+ _dropboxAutoBackUpController.state = false;
+ }
+
+ Future refreshDropboxToken() async {
+ try {
+ if (_dropboxTokenController.state != null) {
+ final res = await _dio.req(
+ DropboxRefreshTokenEndpoint(
+ refreshToken: _dropboxTokenController.state!.refresh_token,
+ clientId: AppSecretes.dropBoxAppKey,
+ clientSecret: AppSecretes.dropBoxAppSecret,
+ ),
+ );
+ final newToken = DropboxToken.fromJson(res.data);
+ _dropboxTokenController.state = _dropboxTokenController.state!.copyWith(
+ access_token: newToken.access_token,
+ expires_in: newToken.expires_in,
+ token_type: newToken.token_type,
+ );
+ } else {
+ throw const AuthSessionExpiredError();
+ }
+ } catch (e) {
+ throw AppError.fromError(e);
}
}
diff --git a/data/lib/services/dropbox_services.dart b/data/lib/services/dropbox_services.dart
new file mode 100644
index 0000000..2861ce0
--- /dev/null
+++ b/data/lib/services/dropbox_services.dart
@@ -0,0 +1,35 @@
+import '../apis/network/client.dart';
+import '../errors/app_error.dart';
+import '../models/dropbox_account/dropbox_account.dart';
+import '../storage/app_preferences.dart';
+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';
+
+final dropboxServiceProvider = Provider((ref) {
+ return DropboxService(
+ ref.read(dropboxAuthenticatedDioProvider),
+ ref.read(AppPreferences.dropboxCurrentUserAccount.notifier),
+ );
+});
+
+class DropboxService {
+ final Dio _dropboxAuthenticatedDio;
+ final PreferenceNotifier _dropboxAccountController;
+
+ const DropboxService(
+ this._dropboxAuthenticatedDio,
+ this._dropboxAccountController,
+ );
+
+ Future setCurrentUserAccount() async {
+ try {
+ final res = await _dropboxAuthenticatedDio
+ .req(const DropboxGetUserAccountEndpoint());
+ _dropboxAccountController.state = DropboxAccount.fromJson(res.data);
+ } catch (e) {
+ AppError.fromError(e);
+ }
+ }
+}
diff --git a/data/lib/storage/app_preferences.dart b/data/lib/storage/app_preferences.dart
index 85db6ce..c468e7b 100644
--- a/data/lib/storage/app_preferences.dart
+++ b/data/lib/storage/app_preferences.dart
@@ -1,3 +1,5 @@
+import '../models/dropbox_account/dropbox_account.dart';
+import '../models/token/token.dart';
import 'provider/preferences_provider.dart';
class AppPreferences {
@@ -16,18 +18,36 @@ class AppPreferences {
defaultValue: true,
);
- static final canTakeAutoBackUpInGoogleDrive = createPrefProvider(
+ static final googleDriveAutoBackUp = createPrefProvider(
prefKey: "google_drive_auto_backup",
defaultValue: false,
);
- static final googleDriveSignInHintShown = createPrefProvider(
- prefKey: "google_drive_sign_in_hint_shown",
+ static final dropboxAutoBackUp = createPrefProvider(
+ prefKey: "dropbox_auto_backup",
defaultValue: false,
);
- static final googleDriveAutoBackUpHintShown = createPrefProvider(
- prefKey: "google_drive_sign_in_hint_shown",
+ static final signInHintShown = createPrefProvider(
+ prefKey: "sign_in_hint_shown",
defaultValue: false,
);
+
+ static final dropboxToken = createEncodedPrefProvider(
+ prefKey: "dropbox_token",
+ toJson: (value) => value.toJson(),
+ fromJson: (json) => DropboxToken.fromJson(json),
+ );
+
+ static final dropboxCurrentUserAccount =
+ createEncodedPrefProvider(
+ prefKey: "dropbox_current_user_account",
+ toJson: (value) => value.toJson(),
+ fromJson: (json) => DropboxAccount.fromJson(json),
+ );
+
+ static final dropboxPKCECodeVerifier = createPrefProvider(
+ prefKey: "dropbox_code_verifier",
+ defaultValue: null,
+ );
}
diff --git a/data/lib/storage/provider/preferences_provider.dart b/data/lib/storage/provider/preferences_provider.dart
index 2f32645..647729f 100644
--- a/data/lib/storage/provider/preferences_provider.dart
+++ b/data/lib/storage/provider/preferences_provider.dart
@@ -1,3 +1,5 @@
+import 'dart:convert';
+
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -31,6 +33,37 @@ StateNotifierProvider, T> createPrefProvider({
);
}
+StateNotifierProvider, T?> createEncodedPrefProvider({
+ required String prefKey,
+ T? defaultValue,
+ required Map Function(T value) toJson,
+ required T Function(Map json) fromJson,
+}) {
+ T? jsonPodToObject(String? json, T? defaultValue) {
+ if (json == null) {
+ return defaultValue;
+ }
+ return fromJson(jsonDecode(json));
+ }
+
+ return StateNotifierProvider, T?>(
+ (ref) => PreferenceNotifier(
+ jsonPodToObject(
+ ref.watch(sharedPreferencesProvider).getString(prefKey),
+ defaultValue,
+ ),
+ (curr) {
+ final prefs = ref.watch(sharedPreferencesProvider);
+ if (curr == null) {
+ prefs.remove(prefKey);
+ } else {
+ prefs.setString(prefKey, jsonEncode(toJson(curr)));
+ }
+ },
+ ),
+ );
+}
+
class PreferenceNotifier extends StateNotifier {
Function(T curr)? onUpdate;
diff --git a/data/pubspec.yaml b/data/pubspec.yaml
index 7c4dc98..817ae6d 100644
--- a/data/pubspec.yaml
+++ b/data/pubspec.yaml
@@ -17,6 +17,8 @@ dependencies:
photo_manager: ^3.6.2
package_info_plus: ^8.1.1
path_provider: ^2.1.5
+ crypto: ^3.0.3
+ url_launcher: ^6.2.6
# authentication
google_sign_in: ^6.2.2
diff --git a/style/lib/buttons/buttons_list.dart b/style/lib/buttons/buttons_list.dart
index ea266ec..4f975d6 100644
--- a/style/lib/buttons/buttons_list.dart
+++ b/style/lib/buttons/buttons_list.dart
@@ -20,14 +20,14 @@ class ActionListButton {
class ActionList extends StatelessWidget {
final List buttons;
- final EdgeInsetsGeometry? margin;
+ final EdgeInsetsGeometry margin;
final BorderRadius borderRadius;
final Color? background;
const ActionList({
super.key,
required this.buttons,
- this.margin,
+ this.margin = const EdgeInsets.symmetric(vertical: 8),
this.background,
this.borderRadius = const BorderRadius.all(Radius.circular(12)),
});
diff --git a/style/lib/theme/colors.dart b/style/lib/theme/colors.dart
index 867f7f4..c41b7bc 100644
--- a/style/lib/theme/colors.dart
+++ b/style/lib/theme/colors.dart
@@ -39,4 +39,5 @@ class AppColors {
static const awarenessWarningColor = Color(0xFFFFA500);
static const googleDriveColor = Color(0xFF4285F4);
+ static const dropBoxColor = Color(0xFF0061FE);
}