Merge branch 'master' into rewrite_device_sync
This commit is contained in:
commit
8734c94a4e
19 changed files with 642 additions and 302 deletions
|
@ -1,13 +1,6 @@
|
|||
PODS:
|
||||
- background_fetch (1.1.0):
|
||||
- Flutter
|
||||
- better_player (0.0.1):
|
||||
- Cache (~> 6.0.0)
|
||||
- Flutter
|
||||
- GCDWebServer
|
||||
- HLSCachingReverseProxyServer
|
||||
- PINCache
|
||||
- Cache (6.0.0)
|
||||
- connectivity (0.0.1):
|
||||
- Flutter
|
||||
- Reachability
|
||||
|
@ -83,9 +76,6 @@ PODS:
|
|||
- FMDB (2.7.5):
|
||||
- FMDB/standard (= 2.7.5)
|
||||
- FMDB/standard (2.7.5)
|
||||
- GCDWebServer (3.5.4):
|
||||
- GCDWebServer/Core (= 3.5.4)
|
||||
- GCDWebServer/Core (3.5.4)
|
||||
- GoogleDataTransport (9.1.4):
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
|
@ -107,9 +97,6 @@ PODS:
|
|||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/UserDefaults (7.7.0):
|
||||
- GoogleUtilities/Logger
|
||||
- HLSCachingReverseProxyServer (0.1.0):
|
||||
- GCDWebServer (~> 3.5)
|
||||
- PINCache (>= 3.0.1-beta.3)
|
||||
- image_editor (0.0.1):
|
||||
- Flutter
|
||||
- in_app_purchase (0.0.1):
|
||||
|
@ -137,8 +124,6 @@ PODS:
|
|||
- nanopb/encode (= 2.30908.0)
|
||||
- nanopb/decode (2.30908.0)
|
||||
- nanopb/encode (2.30908.0)
|
||||
- open_file (0.0.1):
|
||||
- Flutter
|
||||
- open_mail_app (0.0.1):
|
||||
- Flutter
|
||||
- OrderedSet (5.0.0)
|
||||
|
@ -146,19 +131,9 @@ PODS:
|
|||
- Flutter
|
||||
- path_provider_ios (0.0.1):
|
||||
- Flutter
|
||||
- "permission_handler (5.1.0+2)":
|
||||
- Flutter
|
||||
- photo_manager (2.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- PINCache (3.0.3):
|
||||
- PINCache/Arc-exception-safe (= 3.0.3)
|
||||
- PINCache/Core (= 3.0.3)
|
||||
- PINCache/Arc-exception-safe (3.0.3):
|
||||
- PINCache/Core
|
||||
- PINCache/Core (3.0.3):
|
||||
- PINOperation (~> 1.2.1)
|
||||
- PINOperation (1.2.1)
|
||||
- PromisesObjC (2.1.0)
|
||||
- Reachability (3.2)
|
||||
- receive_sharing_intent (0.0.1):
|
||||
|
@ -169,8 +144,6 @@ PODS:
|
|||
- SDWebImageWebPCoder (0.8.4):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.10)
|
||||
- sensors (0.0.1):
|
||||
- Flutter
|
||||
- Sentry (7.11.0):
|
||||
- Sentry/Core (= 7.11.0)
|
||||
- Sentry/Core (7.11.0)
|
||||
|
@ -178,8 +151,6 @@ PODS:
|
|||
- Flutter
|
||||
- FlutterMacOS
|
||||
- Sentry (~> 7.11.0)
|
||||
- share (0.0.1):
|
||||
- Flutter
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_ios (0.0.1):
|
||||
|
@ -202,7 +173,6 @@ PODS:
|
|||
|
||||
DEPENDENCIES:
|
||||
- background_fetch (from `.symlinks/plugins/background_fetch/ios`)
|
||||
- better_player (from `.symlinks/plugins/better_player/ios`)
|
||||
- connectivity (from `.symlinks/plugins/connectivity/ios`)
|
||||
- device_info (from `.symlinks/plugins/device_info/ios`)
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
|
@ -224,16 +194,12 @@ DEPENDENCIES:
|
|||
- local_auth (from `.symlinks/plugins/local_auth/ios`)
|
||||
- motionphoto (from `.symlinks/plugins/motionphoto/ios`)
|
||||
- move_to_background (from `.symlinks/plugins/move_to_background/ios`)
|
||||
- open_file (from `.symlinks/plugins/open_file/ios`)
|
||||
- open_mail_app (from `.symlinks/plugins/open_mail_app/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||
- permission_handler (from `.symlinks/plugins/permission_handler/ios`)
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
|
||||
- sensors (from `.symlinks/plugins/sensors/ios`)
|
||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||
- share (from `.symlinks/plugins/share/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
|
@ -245,23 +211,18 @@ DEPENDENCIES:
|
|||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Cache
|
||||
- Firebase
|
||||
- FirebaseCore
|
||||
- FirebaseCoreDiagnostics
|
||||
- FirebaseInstallations
|
||||
- FirebaseMessaging
|
||||
- FMDB
|
||||
- GCDWebServer
|
||||
- GoogleDataTransport
|
||||
- GoogleUtilities
|
||||
- HLSCachingReverseProxyServer
|
||||
- libwebp
|
||||
- Mantle
|
||||
- nanopb
|
||||
- OrderedSet
|
||||
- PINCache
|
||||
- PINOperation
|
||||
- PromisesObjC
|
||||
- Reachability
|
||||
- SDWebImage
|
||||
|
@ -272,8 +233,6 @@ SPEC REPOS:
|
|||
EXTERNAL SOURCES:
|
||||
background_fetch:
|
||||
:path: ".symlinks/plugins/background_fetch/ios"
|
||||
better_player:
|
||||
:path: ".symlinks/plugins/better_player/ios"
|
||||
connectivity:
|
||||
:path: ".symlinks/plugins/connectivity/ios"
|
||||
device_info:
|
||||
|
@ -316,26 +275,18 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/motionphoto/ios"
|
||||
move_to_background:
|
||||
:path: ".symlinks/plugins/move_to_background/ios"
|
||||
open_file:
|
||||
:path: ".symlinks/plugins/open_file/ios"
|
||||
open_mail_app:
|
||||
:path: ".symlinks/plugins/open_mail_app/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_ios:
|
||||
:path: ".symlinks/plugins/path_provider_ios/ios"
|
||||
permission_handler:
|
||||
:path: ".symlinks/plugins/permission_handler/ios"
|
||||
photo_manager:
|
||||
:path: ".symlinks/plugins/photo_manager/ios"
|
||||
receive_sharing_intent:
|
||||
:path: ".symlinks/plugins/receive_sharing_intent/ios"
|
||||
sensors:
|
||||
:path: ".symlinks/plugins/sensors/ios"
|
||||
sentry_flutter:
|
||||
:path: ".symlinks/plugins/sentry_flutter/ios"
|
||||
share:
|
||||
:path: ".symlinks/plugins/share/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_ios:
|
||||
|
@ -355,8 +306,6 @@ EXTERNAL SOURCES:
|
|||
|
||||
SPEC CHECKSUMS:
|
||||
background_fetch: 3795af8a49054dc526477cc2f60d2ed41de60587
|
||||
better_player: 2406bfe8175203c7a46fa15f9d778d73b12e1646
|
||||
Cache: 4ca7e00363fca5455f26534e5607634c820ffc2d
|
||||
connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467
|
||||
device_info: d7d233b645a32c40dfdc212de5cf646ca482f175
|
||||
Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d
|
||||
|
@ -379,10 +328,8 @@ SPEC CHECKSUMS:
|
|||
fluttercontactpicker: d582836dea6b5d489f3d259f35d7817ae82ee5e6
|
||||
fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4
|
||||
GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b
|
||||
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
|
||||
HLSCachingReverseProxyServer: 59935e1e0244ad7f3375d75b5ef46e8eb26ab181
|
||||
image_editor: c1d038630eedea60d2dee9c14f36aa66c7f9cfab
|
||||
in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6
|
||||
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
|
||||
|
@ -391,24 +338,18 @@ SPEC CHECKSUMS:
|
|||
motionphoto: d4a432b8c8f22fb3ad966258597c0103c9c5ff16
|
||||
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
||||
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
|
||||
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
|
||||
open_mail_app: 794172f6a22cd16319d3ddaf45e945b2f74952b0
|
||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
||||
permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0
|
||||
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
|
||||
PINCache: 7a8fc1a691173d21dbddbf86cd515de6efa55086
|
||||
PINOperation: 00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20
|
||||
PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72
|
||||
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
|
||||
receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
|
||||
SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e
|
||||
SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
|
||||
sensors: 84eb7a30e47a649e4172b71d6e81be614c280336
|
||||
Sentry: 0c5cd63d714187b4a39c331c1f0eb04ba7868341
|
||||
sentry_flutter: efb3df2c203cd03aad255892a8d628a458656d14
|
||||
share: 0b2c3e82132f5888bccca3351c504d0003b3b410
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
|
|
|
@ -256,20 +256,15 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/Cache/Cache.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GCDWebServer/GCDWebServer.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/HLSCachingReverseProxyServer/HLSCachingReverseProxyServer.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/OrderedSet/OrderedSet.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/PINCache/PINCache.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/PINOperation/PINOperation.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
||||
|
@ -277,7 +272,6 @@
|
|||
"${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Toast/Toast.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/background_fetch/background_fetch.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/better_player/better_player.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/device_info/device_info.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/fk_user_agent/fk_user_agent.framework",
|
||||
|
@ -298,15 +292,12 @@
|
|||
"${BUILT_PRODUCTS_DIR}/motionphoto/motionphoto.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/move_to_background/move_to_background.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/open_file/open_file.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/open_mail_app/open_mail_app.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/receive_sharing_intent/receive_sharing_intent.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sensors/sensors.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/share/share.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/shared_preferences_ios/shared_preferences_ios.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework",
|
||||
|
@ -318,20 +309,15 @@
|
|||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cache.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GCDWebServer.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HLSCachingReverseProxyServer.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mantle.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OrderedSet.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINCache.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINOperation.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
||||
|
@ -339,7 +325,6 @@
|
|||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Toast.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/background_fetch.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/better_player.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fk_user_agent.framework",
|
||||
|
@ -360,15 +345,12 @@
|
|||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motionphoto.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/move_to_background.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/open_file.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/open_mail_app.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/receive_sharing_intent.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sensors.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_ios.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework",
|
||||
|
|
|
@ -10,7 +10,6 @@ import 'package:flutter_easyloading/flutter_easyloading.dart';
|
|||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/network.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
import 'package:photos/l10n/l10n.dart';
|
||||
import 'package:photos/services/app_lifecycle_service.dart';
|
||||
|
@ -58,7 +57,6 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
|
|||
darkTheme: dartTheme,
|
||||
home: EnteApp._homeWidget,
|
||||
debugShowCheckedModeBanner: false,
|
||||
navigatorKey: Network.instance.getAlice().getNavigatorKey(),
|
||||
builder: EasyLoading.init(),
|
||||
supportedLocales: L10n.all,
|
||||
localizationsDelegates: const [
|
||||
|
@ -77,7 +75,6 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
|
|||
darkTheme: darkThemeData,
|
||||
home: EnteApp._homeWidget,
|
||||
debugShowCheckedModeBanner: false,
|
||||
navigatorKey: Network.instance.getAlice().getNavigatorKey(),
|
||||
builder: EasyLoading.init(),
|
||||
supportedLocales: L10n.all,
|
||||
localizationsDelegates: const [
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:alice/alice.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:fk_user_agent/fk_user_agent.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
|
@ -13,10 +10,8 @@ int kConnectTimeout = 15000;
|
|||
|
||||
class Network {
|
||||
Dio _dio;
|
||||
Alice _alice;
|
||||
|
||||
Future<void> init() async {
|
||||
_alice = Alice(darkTheme: true, showNotification: kDebugMode);
|
||||
await FkUserAgent.init();
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
_dio = Dio(
|
||||
|
@ -30,7 +25,6 @@ class Network {
|
|||
),
|
||||
);
|
||||
_dio.interceptors.add(RequestIdInterceptor());
|
||||
_dio.interceptors.add(_alice.getDioInterceptor());
|
||||
}
|
||||
|
||||
Network._privateConstructor();
|
||||
|
@ -38,8 +32,6 @@ class Network {
|
|||
static Network instance = Network._privateConstructor();
|
||||
|
||||
Dio getDio() => _dio;
|
||||
|
||||
Alice getAlice() => _alice;
|
||||
}
|
||||
|
||||
class RequestIdInterceptor extends Interceptor {
|
||||
|
|
|
@ -363,6 +363,29 @@ extension CustomColorScheme on ColorScheme {
|
|||
Color get searchResultsBackgroundColor => brightness == Brightness.light
|
||||
? Colors.black.withOpacity(0.32)
|
||||
: Colors.black.withOpacity(0.64);
|
||||
|
||||
Color get fillFaint => brightness == Brightness.light
|
||||
? Colors.white.withOpacity(0.04)
|
||||
: Colors.black.withOpacity(0.12);
|
||||
Color get warning500 => const Color.fromRGBO(255, 101, 101, 1);
|
||||
|
||||
List<BoxShadow> get shadowMenu => brightness == Brightness.light
|
||||
? [
|
||||
BoxShadow(blurRadius: 6, color: Colors.white.withOpacity(0.16)),
|
||||
BoxShadow(
|
||||
blurRadius: 6,
|
||||
color: Colors.white.withOpacity(0.12),
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
]
|
||||
: [
|
||||
BoxShadow(blurRadius: 6, color: Colors.black.withOpacity(0.50)),
|
||||
BoxShadow(
|
||||
blurRadius: 6,
|
||||
color: Colors.black.withOpacity(0.25),
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
OutlinedButtonThemeData buildOutlinedButtonThemeData({
|
||||
|
|
5
lib/events/notification_event.dart
Normal file
5
lib/events/notification_event.dart
Normal file
|
@ -0,0 +1,5 @@
|
|||
import 'package:photos/events/event.dart';
|
||||
|
||||
// NotificationEvent event is used to re-fresh the UI to show latest notification
|
||||
// (if any)
|
||||
class NotificationEvent extends Event {}
|
|
@ -31,6 +31,7 @@ import 'package:photos/services/search_service.dart';
|
|||
import 'package:photos/services/sync_service.dart';
|
||||
import 'package:photos/services/trash_sync_service.dart';
|
||||
import 'package:photos/services/update_service.dart';
|
||||
import 'package:photos/services/user_remote_flag_service.dart';
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import 'package:photos/ui/tools/app_lock.dart';
|
||||
import 'package:photos/ui/tools/lock_screen.dart';
|
||||
|
@ -133,6 +134,7 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
|
|||
await Network.instance.init();
|
||||
await Configuration.instance.init();
|
||||
await UserService.instance.init();
|
||||
await UserRemoteFlagService.instance.init();
|
||||
await UpdateService.instance.init();
|
||||
await BillingService.instance.init();
|
||||
await CollectionsService.instance.init();
|
||||
|
|
|
@ -292,6 +292,9 @@ class CollectionsService {
|
|||
Uint8List _getDecryptedKey(Collection collection) {
|
||||
final encryptedKey = Sodium.base642bin(collection.encryptedKey);
|
||||
if (collection.owner.id == _config.getUserID()) {
|
||||
if(_config.getKey() == null) {
|
||||
throw Exception("key can not be null");
|
||||
}
|
||||
return CryptoUtil.decryptSync(
|
||||
encryptedKey,
|
||||
_config.getKey(),
|
||||
|
|
123
lib/services/user_remote_flag_service.dart
Normal file
123
lib/services/user_remote_flag_service.dart
Normal file
|
@ -0,0 +1,123 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/network.dart';
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class UserRemoteFlagService {
|
||||
final _dio = Network.instance.getDio();
|
||||
final _logger = Logger((UserRemoteFlagService).toString());
|
||||
final _config = Configuration.instance;
|
||||
late SharedPreferences _prefs;
|
||||
|
||||
UserRemoteFlagService._privateConstructor();
|
||||
|
||||
static final UserRemoteFlagService instance =
|
||||
UserRemoteFlagService._privateConstructor();
|
||||
|
||||
static const String recoveryVerificationFlag = "recoveryKeyVerified";
|
||||
static const String needRecoveryKeyVerification =
|
||||
"needRecoveryKeyVerification";
|
||||
|
||||
Future<void> init() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
bool shouldShowRecoveryVerification() {
|
||||
if (!_prefs.containsKey(needRecoveryKeyVerification)) {
|
||||
// fetch the status from remote
|
||||
unawaited(_refreshRecoveryVerificationFlag());
|
||||
return false;
|
||||
}
|
||||
return _prefs.getBool(needRecoveryKeyVerification)!;
|
||||
}
|
||||
|
||||
// markRecoveryVerificationAsDone is used to track if user has verified their
|
||||
// recovery key in the past or not. This helps in avoid showing the same
|
||||
// prompt to the user on re-install or signing into a different device
|
||||
Future<void> markRecoveryVerificationAsDone() async {
|
||||
await _updateKeyValue(recoveryVerificationFlag, true.toString());
|
||||
await _prefs.setBool(needRecoveryKeyVerification, false);
|
||||
}
|
||||
|
||||
Future<void> _refreshRecoveryVerificationFlag() async {
|
||||
final remoteStatusValue =
|
||||
await _getValue(recoveryVerificationFlag, "false");
|
||||
if (remoteStatusValue.toLowerCase() == "true") {
|
||||
await _prefs.setBool(needRecoveryKeyVerification, false);
|
||||
} else {
|
||||
// check the session creationTime. If any active session is older than
|
||||
// 1 day, set the need to verification as true
|
||||
final activeSessions = await UserService.instance.getActiveSessions();
|
||||
final int microSecondsInADay = const Duration(days: 1).inMicroseconds;
|
||||
final bool anyActiveSessionOlderThanADay =
|
||||
activeSessions.sessions.firstWhere(
|
||||
(e) =>
|
||||
(e.creationTime + microSecondsInADay) <
|
||||
DateTime.now().microsecondsSinceEpoch,
|
||||
orElse: () => null,
|
||||
) !=
|
||||
null;
|
||||
if (anyActiveSessionOlderThanADay) {
|
||||
await _prefs.setBool(needRecoveryKeyVerification, true);
|
||||
} else {
|
||||
// continue defaulting to no verification prompt
|
||||
_logger.finest('No active session older than 1 day');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _getValue(String key, String? defaultValue) async {
|
||||
try {
|
||||
final Map<String, dynamic> queryParams = {"key": key};
|
||||
if (defaultValue != null) {
|
||||
queryParams["defaultValue"] = defaultValue;
|
||||
}
|
||||
final response = await _dio.get(
|
||||
_config.getHttpEndpoint() + "/remote-store",
|
||||
queryParameters: queryParams,
|
||||
options: Options(
|
||||
headers: {
|
||||
"X-Auth-Token": _config.getToken(),
|
||||
},
|
||||
),
|
||||
);
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception("Unexpected status code ${response.statusCode}");
|
||||
}
|
||||
return response.data["value"];
|
||||
} catch (e) {
|
||||
_logger.info("Error while fetching bool status for $key", e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// _setBooleanFlag sets the corresponding flag on remote
|
||||
// to mark recovery as completed
|
||||
Future<void> _updateKeyValue(String key, String value) async {
|
||||
try {
|
||||
final response = await _dio.post(
|
||||
_config.getHttpEndpoint() + "/remote-store/update",
|
||||
data: {
|
||||
"key": key,
|
||||
"value": value,
|
||||
},
|
||||
options: Options(
|
||||
headers: {
|
||||
"X-Auth-Token": _config.getToken(),
|
||||
},
|
||||
),
|
||||
);
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception("Unexpected state");
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning("Failed to set flag for $key", e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,135 +56,149 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||
'recovery code should have $kMnemonicKeyWordCount words',
|
||||
);
|
||||
}
|
||||
final double topPadding = widget.showAppBar
|
||||
? 40
|
||||
: widget.showProgressBar
|
||||
? 32
|
||||
: 120;
|
||||
|
||||
return Scaffold(
|
||||
appBar: widget.showProgressBar
|
||||
? AppBar(
|
||||
elevation: 0,
|
||||
title: Hero(
|
||||
tag: "recovery_key",
|
||||
child: StepProgressIndicator(
|
||||
totalSteps: 4,
|
||||
currentStep: 3,
|
||||
selectedColor: Theme.of(context).colorScheme.greenAlternative,
|
||||
roundedEdges: const Radius.circular(10),
|
||||
unselectedColor:
|
||||
Theme.of(context).colorScheme.stepProgressUnselectedColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
: widget.showAppBar
|
||||
? AppBar(
|
||||
elevation: 0,
|
||||
title: Text(widget.title ?? "Recovery key"),
|
||||
)
|
||||
: null,
|
||||
body: Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
20,
|
||||
widget.showAppBar
|
||||
? 40
|
||||
: widget.showProgressBar
|
||||
? 32
|
||||
: 120,
|
||||
20,
|
||||
20,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
widget.showAppBar
|
||||
? const SizedBox.shrink()
|
||||
: Text(
|
||||
widget.title ?? "Recovery key",
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
appBar: widget.showProgressBar
|
||||
? AppBar(
|
||||
elevation: 0,
|
||||
title: Hero(
|
||||
tag: "recovery_key",
|
||||
child: StepProgressIndicator(
|
||||
totalSteps: 4,
|
||||
currentStep: 3,
|
||||
selectedColor:
|
||||
Theme.of(context).colorScheme.greenAlternative,
|
||||
roundedEdges: const Radius.circular(10),
|
||||
unselectedColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.stepProgressUnselectedColor,
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(widget.showAppBar ? 0 : 12)),
|
||||
Text(
|
||||
widget.text ??
|
||||
"If you forget your password, the only way you can recover your data is with this key.",
|
||||
style: Theme.of(context).textTheme.subtitle1,
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(top: 24)),
|
||||
DottedBorder(
|
||||
color: const Color.fromRGBO(17, 127, 56, 1),
|
||||
//color of dotted/dash line
|
||||
strokeWidth: 1,
|
||||
//thickness of dash/dots
|
||||
dashPattern: const [6, 6],
|
||||
radius: const Radius.circular(8),
|
||||
//dash patterns, 10 is dash width, 6 is space width
|
||||
child: SizedBox(
|
||||
//inner container
|
||||
height: 120, //height of inner container
|
||||
width:
|
||||
double.infinity, //width to 100% match to parent container.
|
||||
// ignore: prefer_const_literals_to_create_immutables
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: recoveryKey),
|
||||
);
|
||||
showToast(context, "Recovery key copied to clipboard");
|
||||
setState(() {
|
||||
_hasTriedToSave = true;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: const Color.fromRGBO(49, 155, 86, .2),
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(2),
|
||||
),
|
||||
color:
|
||||
Theme.of(context).colorScheme.recoveryKeyBoxColor,
|
||||
),
|
||||
)
|
||||
: widget.showAppBar
|
||||
? AppBar(
|
||||
elevation: 0,
|
||||
title: Text(widget.title ?? "Recovery key"),
|
||||
)
|
||||
: null,
|
||||
body: Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, topPadding, 20, 20),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: constraints.maxWidth,
|
||||
minHeight: constraints.maxHeight),
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
widget.showAppBar
|
||||
? const SizedBox.shrink()
|
||||
: Text(
|
||||
widget.title ?? "Recovery key",
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.all(widget.showAppBar ? 0 : 12)),
|
||||
Text(
|
||||
widget.text ??
|
||||
"If you forget your password, the only way you can recover your data is with this key.",
|
||||
style: Theme.of(context).textTheme.subtitle1,
|
||||
),
|
||||
height: 120,
|
||||
padding: const EdgeInsets.all(20),
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
recoveryKey,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
const Padding(padding: EdgeInsets.only(top: 24)),
|
||||
DottedBorder(
|
||||
color: const Color.fromRGBO(17, 127, 56, 1),
|
||||
//color of dotted/dash line
|
||||
strokeWidth: 1,
|
||||
//thickness of dash/dots
|
||||
dashPattern: const [6, 6],
|
||||
radius: const Radius.circular(8),
|
||||
//dash patterns, 10 is dash width, 6 is space width
|
||||
child: SizedBox(
|
||||
//inner container
|
||||
// height: 120, //height of inner container
|
||||
width: double
|
||||
.infinity, //width to 100% match to parent container.
|
||||
// ignore: prefer_const_literals_to_create_immutables
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: recoveryKey),
|
||||
);
|
||||
showToast(context,
|
||||
"Recovery key copied to clipboard");
|
||||
setState(() {
|
||||
_hasTriedToSave = true;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: const Color.fromRGBO(
|
||||
49, 155, 86, .2),
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(2),
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.recoveryKeyBoxColor,
|
||||
),
|
||||
padding: const EdgeInsets.all(20),
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
recoveryKey,
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(
|
||||
height: 80,
|
||||
width: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: Text(
|
||||
widget.subText ??
|
||||
"We don’t store this key, please save this in a safe place.",
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(10, 10, 10, 42),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _saveOptions(context, recoveryKey),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
), // columnEnds
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 80,
|
||||
width: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: Text(
|
||||
widget.subText ??
|
||||
"We don’t store this key, please save this in a safe place.",
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(10, 10, 10, 24),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _saveOptions(context, recoveryKey),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
List<Widget> _saveOptions(BuildContext context, String recoveryKey) {
|
||||
|
|
241
lib/ui/account/verify_recovery_page.dart
Normal file
241
lib/ui/account/verify_recovery_page.dart
Normal file
|
@ -0,0 +1,241 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
import 'package:photos/events/notification_event.dart';
|
||||
import 'package:photos/services/local_authentication_service.dart';
|
||||
import 'package:photos/services/user_remote_flag_service.dart';
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import 'package:photos/ui/account/recovery_key_page.dart';
|
||||
import 'package:photos/ui/common/dialogs.dart';
|
||||
import 'package:photos/ui/common/gradient_button.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
|
||||
class VerifyRecoveryPage extends StatefulWidget {
|
||||
const VerifyRecoveryPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<VerifyRecoveryPage> createState() => _VerifyRecoveryPageState();
|
||||
}
|
||||
|
||||
class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
|
||||
final _recoveryKey = TextEditingController();
|
||||
final Logger _logger = Logger((_VerifyRecoveryPageState).toString());
|
||||
|
||||
void _verifyRecoveryKey() async {
|
||||
final dialog = createProgressDialog(context, "Verifying recovery key...");
|
||||
await dialog.show();
|
||||
try {
|
||||
final String inputKey = _recoveryKey.text.trim();
|
||||
final String recoveryKey = Sodium.bin2hex(
|
||||
await UserService.instance.getOrCreateRecoveryKey(context),
|
||||
);
|
||||
final String recoveryKeyWords = bip39.entropyToMnemonic(recoveryKey);
|
||||
if (inputKey == recoveryKey || inputKey == recoveryKeyWords) {
|
||||
try {
|
||||
await UserRemoteFlagService.instance.markRecoveryVerificationAsDone();
|
||||
} catch (e) {
|
||||
await dialog.hide();
|
||||
if (e is DioError && e.type == DioErrorType.other) {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
"No internet connection",
|
||||
"Please check your internet connection and try again.",
|
||||
);
|
||||
} else {
|
||||
await showGenericErrorDialog(context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Bus.instance.fire(NotificationEvent());
|
||||
await dialog.hide();
|
||||
// todo: change this as per figma once the component is ready
|
||||
await showErrorDialog(
|
||||
context,
|
||||
"Recovery key verified",
|
||||
"Great! Your recovery key is valid. Thank you for verifying.\n"
|
||||
"\nPlease"
|
||||
" remember to keep your recovery key safely backed up.",
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
throw Exception("recovery key didn't match");
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.severe("failed to verify recovery key", e, s);
|
||||
await dialog.hide();
|
||||
const String errMessage =
|
||||
"The recovery key you entered is not valid. Please make sure it "
|
||||
"contains 24 words, and check the spelling of each.\n\nIf you "
|
||||
"entered an older recovery code, make sure it is 64 characters long, and check each of them.";
|
||||
final result = await showChoiceDialog(
|
||||
context,
|
||||
"Invalid key",
|
||||
errMessage,
|
||||
firstAction: "Try again",
|
||||
secondAction: "View recovery key",
|
||||
);
|
||||
if (result == DialogUserChoice.secondChoice) {
|
||||
await _onViewRecoveryKeyClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onViewRecoveryKeyClick() async {
|
||||
final hasAuthenticated =
|
||||
await LocalAuthenticationService.instance.requestLocalAuthentication(
|
||||
context,
|
||||
"Please authenticate to view your recovery key",
|
||||
);
|
||||
if (hasAuthenticated) {
|
||||
String recoveryKey;
|
||||
try {
|
||||
recoveryKey = Sodium.bin2hex(
|
||||
await UserService.instance.getOrCreateRecoveryKey(context),
|
||||
);
|
||||
routeToPage(
|
||||
context,
|
||||
RecoveryKeyPage(
|
||||
recoveryKey,
|
||||
"OK",
|
||||
showAppBar: true,
|
||||
onDone: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
showGenericErrorDialog(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: constraints.maxWidth,
|
||||
minHeight: constraints.maxHeight,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
'Verify recovery key',
|
||||
style: Theme.of(context).textTheme.headline5,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
"If you forget your password, your recovery key is the "
|
||||
"only way to recover your photos.\n\nPlease verify that "
|
||||
"you have safely backed up your 24 word recovery key by re-entering it.",
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
hintText: "Enter your recovery key",
|
||||
contentPadding: const EdgeInsets.all(20),
|
||||
border: UnderlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontFeatures: [FontFeature.tabularFigures()],
|
||||
),
|
||||
controller: _recoveryKey,
|
||||
autofocus: false,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.multiline,
|
||||
minLines: 4,
|
||||
maxLines: null,
|
||||
onChanged: (_) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
"If you saved the recovery key from older app versions, you might have a 64 character recovery code instead of 24 words. You can enter that too.",
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(0, 12, 0, 40),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
GradientButton(
|
||||
onTap: _verifyRecoveryKey,
|
||||
text: "Verify",
|
||||
paddingValue: 6,
|
||||
iconData: Icons.shield_outlined,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Hero(
|
||||
tag: "recovery_key",
|
||||
child: ElevatedButton(
|
||||
style: Theme.of(context)
|
||||
.colorScheme
|
||||
.optionalActionButtonStyle,
|
||||
onPressed: _onViewRecoveryKeyClick,
|
||||
child: const Text(
|
||||
"View recovery key",
|
||||
// same for both themes
|
||||
style: TextStyle(color: Colors.black),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
76
lib/ui/components/notification_warning_widget.dart
Normal file
76
lib/ui/components/notification_warning_widget.dart
Normal file
|
@ -0,0 +1,76 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
||||
class NotificationWarningWidget extends StatelessWidget {
|
||||
final IconData warningIcon;
|
||||
final IconData actionIcon;
|
||||
final String text;
|
||||
final GestureTapCallback onTap;
|
||||
|
||||
const NotificationWarningWidget({
|
||||
Key? key,
|
||||
required this.warningIcon,
|
||||
required this.actionIcon,
|
||||
required this.text,
|
||||
required this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
boxShadow: Theme.of(context).colorScheme.shadowMenu,
|
||||
color: Theme.of(context).colorScheme.warning500,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
warningIcon,
|
||||
size: 36,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(height: 1.4, color: Colors.white),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ClipOval(
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.fillFaint,
|
||||
child: InkWell(
|
||||
splashColor: Colors.red, // Splash color
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Icon(
|
||||
actionIcon,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
// import 'package:open_file/open_file.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/network.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
@ -152,7 +152,7 @@ class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
|
|||
},
|
||||
);
|
||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||
OpenFile.open(_saveUrl);
|
||||
// OpenFile.open(_saveUrl);
|
||||
} catch (e) {
|
||||
Logger("ApkDownloader").severe(e);
|
||||
final AlertDialog alert = AlertDialog(
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:photos/core/network.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
|
||||
class AppVersionWidget extends StatefulWidget {
|
||||
|
@ -38,7 +37,6 @@ class _AppVersionWidgetState extends State<AppVersionWidget> {
|
|||
const Duration(milliseconds: kDummyDelayDurationInMilliseconds),
|
||||
);
|
||||
await dialog.hide();
|
||||
Network.instance.getAlice().showInspector();
|
||||
}
|
||||
} else {
|
||||
_consecutiveTaps = 1;
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:expandable/expandable.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/network.dart';
|
||||
import 'package:photos/services/ignored_files_service.dart';
|
||||
import 'package:photos/services/local_sync_service.dart';
|
||||
import 'package:photos/services/sync_service.dart';
|
||||
|
@ -38,16 +37,6 @@ class DebugSectionWidget extends StatelessWidget {
|
|||
icon: Icons.navigate_next,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () async {
|
||||
Network.instance.getAlice().showInspector();
|
||||
},
|
||||
child: const SettingsTextItem(
|
||||
text: "Network requests",
|
||||
icon: Icons.navigate_next,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () async {
|
||||
|
|
|
@ -5,10 +5,15 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
import 'package:photos/events/notification_event.dart';
|
||||
import 'package:photos/events/sync_status_update_event.dart';
|
||||
import 'package:photos/services/sync_service.dart';
|
||||
import 'package:photos/services/user_remote_flag_service.dart';
|
||||
import 'package:photos/ui/account/verify_recovery_page.dart';
|
||||
import 'package:photos/ui/components/notification_warning_widget.dart';
|
||||
import 'package:photos/ui/header_error_widget.dart';
|
||||
import 'package:photos/ui/viewer/search/search_widget.dart';
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
|
||||
const double kContainerHeight = 36;
|
||||
|
||||
|
@ -21,6 +26,7 @@ class StatusBarWidget extends StatefulWidget {
|
|||
|
||||
class _StatusBarWidgetState extends State<StatusBarWidget> {
|
||||
StreamSubscription<SyncStatusUpdate> _subscription;
|
||||
StreamSubscription<NotificationEvent> _notificationSubscription;
|
||||
bool _showStatus = false;
|
||||
bool _showErrorBanner = false;
|
||||
|
||||
|
@ -51,12 +57,19 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
|
|||
});
|
||||
}
|
||||
});
|
||||
_notificationSubscription =
|
||||
Bus.instance.on<NotificationEvent>().listen((event) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription.cancel();
|
||||
_notificationSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -96,6 +109,20 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
|
|||
duration: const Duration(milliseconds: 1000),
|
||||
child: const Divider(),
|
||||
),
|
||||
UserRemoteFlagService.instance.shouldShowRecoveryVerification()
|
||||
? NotificationWarningWidget(
|
||||
warningIcon: Icons.gpp_maybe,
|
||||
actionIcon: Icons.arrow_forward,
|
||||
text: "Please ensure that you have your 24 word recovery key",
|
||||
onTap: () async => {
|
||||
await routeToPage(
|
||||
context,
|
||||
const VerifyRecoveryPage(),
|
||||
forceCustomPageRoute: true,
|
||||
)
|
||||
},
|
||||
)
|
||||
: const SizedBox.shrink()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -204,6 +204,7 @@ class CryptoUtil {
|
|||
Uint8List key,
|
||||
Uint8List nonce,
|
||||
) {
|
||||
assert(key != null, "key can not be null");
|
||||
final args = <String, dynamic>{};
|
||||
args["cipher"] = cipher;
|
||||
args["nonce"] = nonce;
|
||||
|
|
72
pubspec.lock
72
pubspec.lock
|
@ -8,15 +8,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
alice:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "42c97aebbea93d1944a199b453b41bbed1f3013a"
|
||||
url: "https://github.com/jhomlala/alice.git"
|
||||
source: git
|
||||
version: "0.2.5"
|
||||
animate_do:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -52,13 +43,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
better_player:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: better_player
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.81"
|
||||
bip39:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -115,13 +99,6 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "1.2.2"
|
||||
chopper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: chopper
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.5"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -562,13 +539,6 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_widget_from_html_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_widget_from_html_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.5+3"
|
||||
flutter_windowmanager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -590,13 +560,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.0.9"
|
||||
fwfh_text_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fwfh_text_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.7.3+2"
|
||||
google_nav_bar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -795,13 +758,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
open_file:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: open_file
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
open_mail_app:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -942,20 +898,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
permission_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.3.0"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.7.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1047,13 +989,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.3"
|
||||
sensors:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sensors
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
sentry:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1068,13 +1003,6 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "6.5.1"
|
||||
share:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -11,15 +11,13 @@ description: ente photos application
|
|||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 0.6.31+361
|
||||
version: 0.6.32+362
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
adaptive_theme: ^3.1.0
|
||||
alice:
|
||||
git: "https://github.com/jhomlala/alice.git"
|
||||
animate_do: ^2.0.0
|
||||
archive: ^3.1.2
|
||||
background_fetch: ^1.0.1
|
||||
|
@ -82,7 +80,7 @@ dependencies:
|
|||
git: "https://github.com/ente-io/motionphoto.git"
|
||||
move_to_background: ^1.0.2
|
||||
|
||||
open_file: ^3.2.1
|
||||
# open_file: ^3.2.1
|
||||
open_mail_app: ^0.4.5
|
||||
package_info_plus: ^1.0.1
|
||||
page_transition: ^2.0.2
|
||||
|
|
Loading…
Add table
Reference in a new issue