Compare commits
134 Commits
ghcr/serve
...
face_thumb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a324914d88 | ||
|
|
9386587851 | ||
|
|
1f76f048cf | ||
|
|
458483b1dc | ||
|
|
e5f9264164 | ||
|
|
98266d877d | ||
|
|
fcc6354bdc | ||
|
|
2ebc67db40 | ||
|
|
5f6786ab3f | ||
|
|
356407a3c2 | ||
|
|
a90a38bdcf | ||
|
|
0d61397d51 | ||
|
|
93f464507d | ||
|
|
ae1e435d77 | ||
|
|
2894bd2386 | ||
|
|
3addc83c14 | ||
|
|
5b47f69d93 | ||
|
|
1c02064211 | ||
|
|
ba01e2d181 | ||
|
|
fdfc155add | ||
|
|
3c5a29b0ab | ||
|
|
fa65a993c0 | ||
|
|
4ff77067dc | ||
|
|
437b3d028a | ||
|
|
234daac3b8 | ||
|
|
f433040aaf | ||
|
|
b841666f5d | ||
|
|
418d20b336 | ||
|
|
8afc4bb0cb | ||
|
|
ecd3ce850f | ||
|
|
bc61727e8b | ||
|
|
32f987e551 | ||
|
|
3596d0e42d | ||
|
|
4d9b6ecbc6 | ||
|
|
d7f019c4f5 | ||
|
|
20d498722b | ||
|
|
5423bde48a | ||
|
|
794aa39441 | ||
|
|
4f1db7f001 | ||
|
|
ab96fdb379 | ||
|
|
90650995f7 | ||
|
|
f83cd57b6f | ||
|
|
f0273def2f | ||
|
|
4c4a1991ca | ||
|
|
6762d5b426 | ||
|
|
d4e2317816 | ||
|
|
2040044994 | ||
|
|
a3ee242faa | ||
|
|
78f2bb0d7d | ||
|
|
b723b7daf0 | ||
|
|
6e7ee1fa16 | ||
|
|
6ef9e0fdb0 | ||
|
|
e2e0436830 | ||
|
|
dea67250c8 | ||
|
|
dc2246aa47 | ||
|
|
adb1c96ce6 | ||
|
|
c413111768 | ||
|
|
6d6cd91b22 | ||
|
|
3708a347f5 | ||
|
|
ac802f022c | ||
|
|
99d84a1154 | ||
|
|
126a96326f | ||
|
|
b7ead2004a | ||
|
|
87fad99863 | ||
|
|
9f727bb95d | ||
|
|
b8ecf0f42f | ||
|
|
0de2afe0fe | ||
|
|
bd42a4d1f6 | ||
|
|
22e0cd2168 | ||
|
|
0662baac73 | ||
|
|
ef3c561cf1 | ||
|
|
45c231b9d0 | ||
|
|
6f07399b5a | ||
|
|
8524742c92 | ||
|
|
51496ee148 | ||
|
|
385f52de0f | ||
|
|
974482a442 | ||
|
|
8a90aee4a0 | ||
|
|
9125090a3d | ||
|
|
848fae73b4 | ||
|
|
4a552fbcb4 | ||
|
|
85ef085084 | ||
|
|
f1d128f6b0 | ||
|
|
c925ed2117 | ||
|
|
142a5f9661 | ||
|
|
e70b7eb1e8 | ||
|
|
ae0c83b1aa | ||
|
|
8f1ee2ef15 | ||
|
|
f1d978fbf7 | ||
|
|
4604280ef8 | ||
|
|
18ab4060b2 | ||
|
|
9a4d465672 | ||
|
|
7ea9483cca | ||
|
|
d9add4f827 | ||
|
|
3c19c00a70 | ||
|
|
12c19d1ed1 | ||
|
|
c757b837f1 | ||
|
|
a9f1c0dbd4 | ||
|
|
896d77a83e | ||
|
|
b1210e1d15 | ||
|
|
127df30242 | ||
|
|
c0c17af51a | ||
|
|
d92ec2276e | ||
|
|
1f99727ab9 | ||
|
|
85784920a9 | ||
|
|
d7b3af063b | ||
|
|
baa3d49d4b | ||
|
|
d2c2062256 | ||
|
|
c646909765 | ||
|
|
8b4f03b256 | ||
|
|
976bd0134c | ||
|
|
8a785aac8f | ||
|
|
452812af11 | ||
|
|
44fb8fec1a | ||
|
|
1d8fc7aba8 | ||
|
|
654db76175 | ||
|
|
364170f38d | ||
|
|
0cd7c92672 | ||
|
|
962aaa1b7a | ||
|
|
6ec0c550a3 | ||
|
|
b67fcdb9ed | ||
|
|
2f4c3c7777 | ||
|
|
cb84164466 | ||
|
|
7b6aed426d | ||
|
|
81c539979d | ||
|
|
3cffd969b4 | ||
|
|
fbb15adf11 | ||
|
|
3efd36ab7b | ||
|
|
11cb355e98 | ||
|
|
caadca9a48 | ||
|
|
0a50e33023 | ||
|
|
5eef2a5816 | ||
|
|
fe4bf5c217 | ||
|
|
912fc72600 |
2
.github/workflows/auth-internal-release.yml
vendored
2
.github/workflows/auth-internal-release.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch: # Allow manually running the action
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
FLUTTER_VERSION: "3.29.3"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
2
.github/workflows/auth-lint.yml
vendored
2
.github/workflows/auth-lint.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
- ".github/workflows/auth-lint.yml"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
FLUTTER_VERSION: "3.29.3"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
2
.github/workflows/auth-release.yml
vendored
2
.github/workflows/auth-release.yml
vendored
@@ -29,7 +29,7 @@ on:
|
||||
- "auth-v*"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
FLUTTER_VERSION: "3.29.3"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
2
.github/workflows/auth-win-sign.yml
vendored
2
.github/workflows/auth-win-sign.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
workflow_dispatch: # Allow manually running the action
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
FLUTTER_VERSION: "3.29.3"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch: # Allow manually running the action
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
FLUTTER_VERSION: "3.29.3"
|
||||
RUST_VERSION: "1.85.1"
|
||||
|
||||
permissions:
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch: # Allow manually running the action
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
FLUTTER_VERSION: "3.29.3"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
2
.github/workflows/mobile-lint.yml
vendored
2
.github/workflows/mobile-lint.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
- ".github/workflows/mobile-lint.yml"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
FLUTTER_VERSION: "3.29.3"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
2
.github/workflows/mobile-release.yml
vendored
2
.github/workflows/mobile-release.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
- "photos-v*"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
FLUTTER_VERSION: "3.29.3"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"flutter": "3.24.3"
|
||||
"flutter": "3.29.3"
|
||||
}
|
||||
3
mobile/apps/auth/.gitignore
vendored
3
mobile/apps/auth/.gitignore
vendored
@@ -44,4 +44,5 @@ android/key.properties
|
||||
dist/
|
||||
|
||||
# FVM Version Cache
|
||||
.fvm/
|
||||
.fvm/
|
||||
lib/l10n/arb/*.dart
|
||||
@@ -44,7 +44,7 @@ or managing your secrets, please use our mobile or desktop app.
|
||||
|
||||
## 🧑💻 Build from source
|
||||
|
||||
1. [Install Flutter](https://flutter.dev/docs/get-started/install)
|
||||
1. [Install Flutter v3.29.3](https://flutter.dev/docs/get-started/install).
|
||||
|
||||
2. Pull in all submodules with `git submodule update --init --recursive`
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
import lldb
|
||||
|
||||
def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
|
||||
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
|
||||
base = frame.register["x0"].GetValueAsAddress()
|
||||
page_len = frame.register["x1"].GetValueAsUnsigned()
|
||||
|
||||
# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
|
||||
# first page to see if handled it correctly. This makes diagnosing
|
||||
# misconfiguration (e.g. missing breakpoint) easier.
|
||||
data = bytearray(page_len)
|
||||
data[0:8] = b'IHELPED!'
|
||||
|
||||
error = lldb.SBError()
|
||||
frame.GetThread().GetProcess().WriteMemory(base, data, error)
|
||||
if not error.Success():
|
||||
print(f'Failed to write into {base}[+{page_len}]', error)
|
||||
return
|
||||
|
||||
def __lldb_init_module(debugger: lldb.SBDebugger, _):
|
||||
target = debugger.GetDummyTarget()
|
||||
# Caveat: must use BreakpointCreateByRegEx here and not
|
||||
# BreakpointCreateByName. For some reasons callback function does not
|
||||
# get carried over from dummy target for the later.
|
||||
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
|
||||
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
|
||||
bp.SetAutoContinue(True)
|
||||
print("-- LLDB integration loaded --")
|
||||
5
mobile/apps/auth/ios/Flutter/ephemeral/flutter_lldbinit
Normal file
5
mobile/apps/auth/ios/Flutter/ephemeral/flutter_lldbinit
Normal file
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
command script import --relative-to-command-file flutter_lldb_helper.py
|
||||
@@ -26,6 +26,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
@@ -43,11 +44,13 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
@@ -40,7 +40,7 @@ class _AppState extends State<App>
|
||||
late StreamSubscription<SignedOutEvent> _signedOutEvent;
|
||||
late StreamSubscription<SignedInEvent> _signedInEvent;
|
||||
Locale? locale;
|
||||
setLocale(Locale newLocale) {
|
||||
void setLocale(Locale newLocale) {
|
||||
setState(() {
|
||||
locale = newLocale;
|
||||
});
|
||||
@@ -82,7 +82,7 @@ class _AppState extends State<App>
|
||||
UpdateService.instance.getLatestVersionInfo(),
|
||||
);
|
||||
},
|
||||
barrierColor: Colors.black.withOpacity(0.85),
|
||||
barrierColor: Colors.black.withValues(alpha: 0.85),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,10 +27,8 @@ Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
|
||||
|
||||
await runZonedGuarded(
|
||||
() async {
|
||||
await BlocOverrides.runZoned(
|
||||
() async => runApp(await builder()),
|
||||
blocObserver: AppBlocObserver(),
|
||||
);
|
||||
Bloc.observer = AppBlocObserver();
|
||||
runApp(await builder());
|
||||
},
|
||||
(error, stackTrace) => log(error.toString(), stackTrace: stackTrace),
|
||||
);
|
||||
|
||||
@@ -39,7 +39,7 @@ final lightThemeData = ThemeData(
|
||||
bodyLarge: const TextStyle(color: Colors.orange),
|
||||
),
|
||||
cardColor: const Color.fromRGBO(250, 250, 250, 1.0),
|
||||
dialogTheme: const DialogTheme().copyWith(
|
||||
dialogTheme: const DialogThemeData().copyWith(
|
||||
backgroundColor: const Color.fromRGBO(250, 250, 250, 1.0), //
|
||||
titleTextStyle: const TextStyle(
|
||||
color: Colors.black,
|
||||
@@ -150,7 +150,7 @@ final darkThemeData = ThemeData(
|
||||
elevation: 0,
|
||||
),
|
||||
cardColor: const Color.fromRGBO(10, 15, 15, 1.0),
|
||||
dialogTheme: const DialogTheme().copyWith(
|
||||
dialogTheme: const DialogThemeData().copyWith(
|
||||
backgroundColor: const Color.fromRGBO(15, 15, 15, 1.0),
|
||||
titleTextStyle: const TextStyle(
|
||||
color: Colors.white,
|
||||
@@ -275,7 +275,7 @@ TextTheme _buildTextTheme(Color textColor) {
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
color: textColor.withOpacity(0.4),
|
||||
color: textColor.withValues(alpha: 0.4),
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
@@ -343,7 +343,7 @@ extension CustomColorScheme on ColorScheme {
|
||||
: const Color.fromRGBO(48, 48, 48, 0.5);
|
||||
|
||||
Color get iconColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.75)
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.75)
|
||||
: const Color.fromRGBO(255, 255, 255, 1);
|
||||
|
||||
Color get bgColorForQuestions => brightness == Brightness.light
|
||||
@@ -354,7 +354,7 @@ extension CustomColorScheme on ColorScheme {
|
||||
|
||||
Color get cupertinoPickerTopColor => brightness == Brightness.light
|
||||
? const Color.fromARGB(255, 238, 238, 238)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.1);
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.1);
|
||||
|
||||
Color get stepProgressUnselectedColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(196, 196, 196, 0.6)
|
||||
@@ -381,20 +381,20 @@ extension CustomColorScheme on ColorScheme {
|
||||
: const Color.fromRGBO(20, 20, 20, 1);
|
||||
|
||||
Color get galleryThumbDrawColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.8)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.5);
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.8)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.5);
|
||||
|
||||
Color get backupEnabledBgColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(230, 230, 230, 0.95)
|
||||
: const Color.fromRGBO(10, 40, 40, 0.3);
|
||||
|
||||
Color get dotsIndicatorActiveColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.5)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.5);
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.5)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.5);
|
||||
|
||||
Color get dotsIndicatorInactiveColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.12)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.12);
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.12)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.12);
|
||||
|
||||
Color get toastTextColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(255, 255, 255, 1)
|
||||
@@ -409,8 +409,8 @@ extension CustomColorScheme on ColorScheme {
|
||||
: const Color.fromRGBO(100, 100, 100, 1);
|
||||
|
||||
Color get themeSwitchInactiveIconColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.5)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.5);
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.5)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.5);
|
||||
|
||||
Color get searchResultsColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(245, 245, 245, 1.0)
|
||||
@@ -421,8 +421,8 @@ extension CustomColorScheme on ColorScheme {
|
||||
: const Color.fromRGBO(150, 150, 150, 1);
|
||||
|
||||
Color get searchResultsBackgroundColor => brightness == Brightness.light
|
||||
? Colors.black.withOpacity(0.32)
|
||||
: Colors.black.withOpacity(0.64);
|
||||
? Colors.black.withValues(alpha: 0.32)
|
||||
: Colors.black.withValues(alpha: 0.64);
|
||||
|
||||
Color get codeCardBackgroundColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(246, 246, 246, 1)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import "package:ente_auth/l10n/arb/app_localizations.dart"
|
||||
show AppLocalizations;
|
||||
import "package:flutter/widgets.dart";
|
||||
import "package:flutter_gen/gen_l10n/app_localizations.dart";
|
||||
|
||||
export "package:flutter_gen/gen_l10n/app_localizations.dart";
|
||||
export "package:ente_auth/l10n/arb/app_localizations.dart"
|
||||
show AppLocalizations;
|
||||
|
||||
extension AppLocalizationsX on BuildContext {
|
||||
AppLocalizations get l10n => AppLocalizations.of(this);
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:convert';
|
||||
|
||||
class BillingPlans {
|
||||
final List<BillingPlan> plans;
|
||||
final FreePlan freePlan;
|
||||
final FreePlan? freePlan;
|
||||
|
||||
BillingPlans({
|
||||
required this.plans,
|
||||
@@ -12,12 +12,12 @@ class BillingPlans {
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'plans': plans.map((x) => x.toMap()).toList(),
|
||||
'freePlan': freePlan.toMap(),
|
||||
'freePlan': freePlan?.toMap(),
|
||||
};
|
||||
}
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
static BillingPlans fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return BillingPlans(plans: [], freePlan: null);
|
||||
|
||||
return BillingPlans(
|
||||
plans: List<BillingPlan>.from(
|
||||
@@ -49,7 +49,7 @@ class FreePlan {
|
||||
};
|
||||
}
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
static FreePlan? fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
|
||||
return FreePlan(
|
||||
@@ -91,7 +91,7 @@ class BillingPlan {
|
||||
};
|
||||
}
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
static BillingPlan? fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
|
||||
return BillingPlan(
|
||||
|
||||
@@ -332,7 +332,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
},
|
||||
);
|
||||
},
|
||||
barrierColor: Colors.black.withOpacity(0.85),
|
||||
barrierColor: Colors.black.withValues(alpha: 0.85),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -75,7 +75,7 @@ class CodeDisplayStore {
|
||||
builder: (BuildContext context) {
|
||||
return EditTagDialog(tag: tag);
|
||||
},
|
||||
barrierColor: Colors.black.withOpacity(0.85),
|
||||
barrierColor: Colors.black.withValues(alpha: 0.85),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -147,12 +147,13 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
||||
),
|
||||
),
|
||||
child: DottedBorder(
|
||||
padding: EdgeInsets.zero,
|
||||
borderType: BorderType.RRect,
|
||||
strokeWidth: 1,
|
||||
color: const Color(0xFF6B6B6B),
|
||||
dashPattern: const [6, 6],
|
||||
radius: const Radius.circular(8),
|
||||
options: const RoundedRectDottedBorderOptions(
|
||||
padding: EdgeInsets.zero,
|
||||
strokeWidth: 1,
|
||||
color: Color(0xFF6B6B6B),
|
||||
dashPattern: [6, 6],
|
||||
radius: Radius.circular(8),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Stack(
|
||||
@@ -314,7 +315,13 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
||||
await _recoveryKeyFile.delete();
|
||||
}
|
||||
_recoveryKeyFile.writeAsStringSync(recoveryKey);
|
||||
await Share.shareXFiles([XFile(_recoveryKeyFile.path)]);
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
files: [
|
||||
XFile(_recoveryKeyFile.path),
|
||||
],
|
||||
),
|
||||
);
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
|
||||
@@ -82,7 +82,7 @@ class _SessionsPageState extends State<SessionsPage> {
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.8),
|
||||
.withValues(alpha: 0.8),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
@@ -95,7 +95,7 @@ class _SessionsPageState extends State<SessionsPage> {
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.8),
|
||||
.withValues(alpha: 0.8),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -54,12 +54,12 @@ class BannerWidget extends StatelessWidget {
|
||||
dashColor = const Color.fromRGBO(255, 191, 12, 1);
|
||||
boxShadow = [
|
||||
BoxShadow(
|
||||
color: const Color(0xFFFDB816).withOpacity(0.1),
|
||||
color: const Color(0xFFFDB816).withValues(alpha: 0.1),
|
||||
blurRadius: 50,
|
||||
spreadRadius: 80,
|
||||
),
|
||||
BoxShadow(
|
||||
color: const Color(0xFFFDB816).withOpacity(0.2),
|
||||
color: const Color(0xFFFDB816).withValues(alpha: 0.2),
|
||||
blurRadius: 25,
|
||||
),
|
||||
];
|
||||
@@ -71,12 +71,13 @@ class BannerWidget extends StatelessWidget {
|
||||
dashColor = const Color.fromRGBO(233, 233, 233, 1);
|
||||
boxShadow = [
|
||||
BoxShadow(
|
||||
color: const Color.fromRGBO(78, 78, 78, 1).withOpacity(0.2),
|
||||
color: const Color.fromRGBO(78, 78, 78, 1).withValues(alpha: 0.2),
|
||||
blurRadius: 50,
|
||||
spreadRadius: 100,
|
||||
),
|
||||
BoxShadow(
|
||||
color: const Color.fromRGBO(23, 22, 22, 0.30).withOpacity(0.1),
|
||||
color:
|
||||
const Color.fromRGBO(23, 22, 22, 0.30).withValues(alpha: 0.1),
|
||||
blurRadius: 25,
|
||||
),
|
||||
];
|
||||
@@ -87,12 +88,12 @@ class BannerWidget extends StatelessWidget {
|
||||
dashColor = const Color.fromRGBO(29, 185, 84, 1);
|
||||
boxShadow = [
|
||||
BoxShadow(
|
||||
color: const Color.fromRGBO(38, 203, 95, 1).withOpacity(0.08),
|
||||
color: const Color.fromRGBO(38, 203, 95, 1).withValues(alpha: 0.08),
|
||||
blurRadius: 50,
|
||||
spreadRadius: 100,
|
||||
),
|
||||
BoxShadow(
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.50).withOpacity(0.08),
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.50).withValues(alpha: 0.08),
|
||||
blurRadius: 25,
|
||||
),
|
||||
];
|
||||
@@ -103,12 +104,12 @@ class BannerWidget extends StatelessWidget {
|
||||
imagePath = "assets/discount.png";
|
||||
boxShadow = [
|
||||
BoxShadow(
|
||||
color: const Color.fromRGBO(38, 203, 95, 1).withOpacity(0.08),
|
||||
color: const Color.fromRGBO(38, 203, 95, 1).withValues(alpha: 0.08),
|
||||
blurRadius: 50,
|
||||
spreadRadius: 100,
|
||||
),
|
||||
BoxShadow(
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.50).withOpacity(0.08),
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.50).withValues(alpha: 0.08),
|
||||
blurRadius: 25,
|
||||
),
|
||||
];
|
||||
@@ -121,10 +122,11 @@ class BannerWidget extends StatelessWidget {
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
child: DottedBorder(
|
||||
borderType: BorderType.RRect,
|
||||
radius: const Radius.circular(50),
|
||||
dashPattern: const <double>[3, 3],
|
||||
color: dashColor,
|
||||
options: RoundedRectDottedBorderOptions(
|
||||
radius: const Radius.circular(50),
|
||||
dashPattern: <double>[3, 3],
|
||||
color: dashColor,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
if (BannerType.starUs == type)
|
||||
|
||||
@@ -28,7 +28,7 @@ class CustomIconWidget extends StatelessWidget {
|
||||
width: 1.5,
|
||||
color: getEnteColorScheme(context)
|
||||
.tagChipSelectedColor
|
||||
.withOpacity(0.5),
|
||||
.withValues(alpha: 0.5),
|
||||
),
|
||||
borderRadius: SmoothBorderRadius(
|
||||
cornerRadius: 15.5,
|
||||
@@ -102,7 +102,7 @@ class CustomIconWidget extends StatelessWidget {
|
||||
child: Icon(
|
||||
Icons.edit,
|
||||
size: 16,
|
||||
color: Colors.black.withOpacity(0.9),
|
||||
color: Colors.black.withValues(alpha: 0.9),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -52,7 +52,7 @@ class NotificationWidget extends StatelessWidget {
|
||||
subTextStyle = textTheme.miniMuted;
|
||||
strokeColorScheme = colorScheme;
|
||||
boxShadow = [
|
||||
BoxShadow(color: Colors.black.withOpacity(0.25), blurRadius: 1),
|
||||
BoxShadow(color: Colors.black.withValues(alpha: 0.25), blurRadius: 1),
|
||||
];
|
||||
break;
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ class TitleBarWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
_actionsWithPaddingInBetween() {
|
||||
List<Widget> _actionsWithPaddingInBetween() {
|
||||
if (actionIcons == null) {
|
||||
return <Widget>[const SizedBox.shrink()];
|
||||
}
|
||||
@@ -135,7 +135,7 @@ class TitleBarWidget extends StatelessWidget {
|
||||
return <Widget>[const SizedBox.shrink()];
|
||||
}
|
||||
if (length == 1) {
|
||||
return actionIcons;
|
||||
return actionIcons!;
|
||||
}
|
||||
while (index < length) {
|
||||
if (!addWhiteSpace) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/models/typedefs.dart';
|
||||
import 'package:ente_auth/theme/colors.dart';
|
||||
import 'package:ente_auth/ui/common/loading_widget.dart';
|
||||
import 'package:ente_auth/utils/debouncer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -104,7 +105,7 @@ class _ToggleSwitchWidgetState extends State<ToggleSwitchWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _stateIcon(enteColorScheme) {
|
||||
Widget _stateIcon(EnteColorScheme enteColorScheme) {
|
||||
if (executionState == ExecutionState.idle) {
|
||||
return const SizedBox(width: 24);
|
||||
} else if (executionState == ExecutionState.inProgress) {
|
||||
|
||||
@@ -23,7 +23,8 @@ class CoachMarkWidget extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).colorScheme.surface.withOpacity(0.1),
|
||||
color:
|
||||
Theme.of(context).colorScheme.surface.withValues(alpha: 0.1),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
|
||||
child: Row(
|
||||
|
||||
@@ -77,7 +77,7 @@ class AboutSectionWidget extends StatelessWidget {
|
||||
UpdateService.instance.getLatestVersionInfo(),
|
||||
);
|
||||
},
|
||||
barrierColor: Colors.black.withOpacity(0.85),
|
||||
barrierColor: Colors.black.withValues(alpha: 0.85),
|
||||
);
|
||||
} else {
|
||||
showShortToast(
|
||||
|
||||
@@ -56,7 +56,7 @@ class AccountSectionWidget extends StatelessWidget {
|
||||
builder: (BuildContext context) {
|
||||
return const ChangeEmailDialog();
|
||||
},
|
||||
barrierColor: Colors.black.withOpacity(0.85),
|
||||
barrierColor: Colors.black.withValues(alpha: 0.85),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class _DuplicateCodePageState extends State<DuplicateCodePage> {
|
||||
color: Theme.of(context)
|
||||
.iconTheme
|
||||
.color!
|
||||
.withOpacity(0.7),
|
||||
.withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(left: 4)),
|
||||
|
||||
@@ -170,9 +170,14 @@ Future<void> _exportCodes(
|
||||
}
|
||||
codeFile.writeAsStringSync(fileContent);
|
||||
final Size size = MediaQuery.of(context).size;
|
||||
await Share.shareXFiles(
|
||||
[XFile(codeFile.path)],
|
||||
sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2),
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
files: <XFile>[
|
||||
XFile(codeFile.path, mimeType: 'text/plain'),
|
||||
],
|
||||
sharePositionOrigin:
|
||||
Rect.fromLTWH(0, 0, size.width, size.height / 2),
|
||||
),
|
||||
);
|
||||
Future.delayed(const Duration(seconds: 30), () async {
|
||||
if (codeFile.existsSync()) {
|
||||
|
||||
@@ -116,10 +116,10 @@ class _LockScreenConfirmPasswordState extends State<LockScreenConfirmPassword> {
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Colors.grey.shade500.withOpacity(0.2),
|
||||
Colors.grey.shade50.withOpacity(0.1),
|
||||
Colors.grey.shade400.withOpacity(0.2),
|
||||
Colors.grey.shade300.withOpacity(0.4),
|
||||
Colors.grey.shade500.withValues(alpha: 0.2),
|
||||
Colors.grey.shade50.withValues(alpha: 0.1),
|
||||
Colors.grey.shade400.withValues(alpha: 0.2),
|
||||
Colors.grey.shade300.withValues(alpha: 0.4),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import "dart:io";
|
||||
|
||||
import "package:ente_auth/l10n/l10n.dart";
|
||||
import "package:ente_auth/theme/colors.dart";
|
||||
import "package:ente_auth/theme/ente_theme.dart";
|
||||
import "package:ente_auth/theme/text_style.dart";
|
||||
import "package:ente_auth/ui/settings/lock_screen/custom_pin_keypad.dart";
|
||||
import "package:ente_auth/utils/lock_screen_settings.dart";
|
||||
import "package:flutter/material.dart";
|
||||
@@ -89,7 +91,7 @@ class _LockScreenConfirmPinState extends State<LockScreenConfirmPin> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getBody(colorTheme, textTheme) {
|
||||
Widget _getBody(EnteColorScheme colorTheme, EnteTextTheme textTheme) {
|
||||
return Center(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@@ -107,10 +109,10 @@ class _LockScreenConfirmPinState extends State<LockScreenConfirmPin> {
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Colors.grey.shade500.withOpacity(0.2),
|
||||
Colors.grey.shade50.withOpacity(0.1),
|
||||
Colors.grey.shade400.withOpacity(0.2),
|
||||
Colors.grey.shade300.withOpacity(0.4),
|
||||
Colors.grey.shade500.withValues(alpha: 0.2),
|
||||
Colors.grey.shade50.withValues(alpha: 0.1),
|
||||
Colors.grey.shade400.withValues(alpha: 0.2),
|
||||
Colors.grey.shade300.withValues(alpha: 0.4),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
|
||||
@@ -127,10 +127,10 @@ class _LockScreenPasswordState extends State<LockScreenPassword> {
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Colors.grey.shade500.withOpacity(0.2),
|
||||
Colors.grey.shade50.withOpacity(0.1),
|
||||
Colors.grey.shade400.withOpacity(0.2),
|
||||
Colors.grey.shade300.withOpacity(0.4),
|
||||
Colors.grey.shade500.withValues(alpha: 0.2),
|
||||
Colors.grey.shade50.withValues(alpha: 0.1),
|
||||
Colors.grey.shade400.withValues(alpha: 0.2),
|
||||
Colors.grey.shade300.withValues(alpha: 0.4),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
|
||||
@@ -178,10 +178,10 @@ class _LockScreenPinState extends State<LockScreenPin> {
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Colors.grey.shade500.withOpacity(0.2),
|
||||
Colors.grey.shade50.withOpacity(0.1),
|
||||
Colors.grey.shade400.withOpacity(0.2),
|
||||
Colors.grey.shade300.withOpacity(0.4),
|
||||
Colors.grey.shade500.withValues(alpha: 0.2),
|
||||
Colors.grey.shade50.withValues(alpha: 0.1),
|
||||
Colors.grey.shade400.withValues(alpha: 0.2),
|
||||
Colors.grey.shade300.withValues(alpha: 0.4),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
|
||||
@@ -43,7 +43,7 @@ class _SupportSectionWidgetState extends State<SupportSectionWidget> {
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
PlatformUtil.openWebView(
|
||||
await PlatformUtil.openWebView(
|
||||
context,
|
||||
context.l10n.faq,
|
||||
"https://help.ente.io/auth/faq",
|
||||
|
||||
@@ -35,7 +35,7 @@ class SortCodeMenuWidget extends StatelessWidget {
|
||||
text,
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.7),
|
||||
color: Theme.of(context).iconTheme.color!.withValues(alpha: 0.7),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/locale.dart';
|
||||
import 'package:ente_auth/utils/lock_screen_settings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
/// A widget which handles app lifecycle events for showing and hiding a lock screen.
|
||||
/// This should wrap around a `MyApp` widget (or equivalent).
|
||||
|
||||
@@ -96,10 +96,10 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Colors.grey.shade500.withOpacity(0.2),
|
||||
Colors.grey.shade50.withOpacity(0.1),
|
||||
Colors.grey.shade400.withOpacity(0.2),
|
||||
Colors.grey.shade300.withOpacity(0.4),
|
||||
Colors.grey.shade500.withValues(alpha: 0.2),
|
||||
Colors.grey.shade50.withValues(alpha: 0.1),
|
||||
Colors.grey.shade400.withValues(alpha: 0.2),
|
||||
Colors.grey.shade300.withValues(alpha: 0.4),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
|
||||
@@ -98,8 +98,9 @@ class _TwoFactorRecoveryPageState extends State<TwoFactorRecoveryPage> {
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 12,
|
||||
color:
|
||||
getEnteColorScheme(context).textBase.withOpacity(0.9),
|
||||
color: getEnteColorScheme(context)
|
||||
.textBase
|
||||
.withValues(alpha: 0.9),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -180,7 +180,7 @@ Future<ButtonResult?> showGenericErrorDialog({
|
||||
context,
|
||||
context.l10n.faq,
|
||||
"https://help.ente.io/auth/troubleshooting/windows-login",
|
||||
);
|
||||
).ignore();
|
||||
},
|
||||
),
|
||||
ButtonWidget(
|
||||
|
||||
@@ -34,7 +34,7 @@ class DirectoryUtils {
|
||||
}
|
||||
|
||||
static String migratedNamingChanges = "migrated_naming_changes.b5";
|
||||
static migrateNamingChanges() async {
|
||||
static Future<void> migrateNamingChanges() async {
|
||||
try {
|
||||
final sharedPrefs = await SharedPreferences.getInstance();
|
||||
if (sharedPrefs.containsKey(migratedNamingChanges)) {
|
||||
@@ -57,7 +57,7 @@ class DirectoryUtils {
|
||||
Directory oldDataDir;
|
||||
Directory newDataDir = await getApplicationSupportDirectory();
|
||||
await newDataDir.create(recursive: true);
|
||||
|
||||
|
||||
if (Platform.isLinux) {
|
||||
oldDataDir = Directory(
|
||||
p.join(dataHome.path, "ente_auth"),
|
||||
|
||||
@@ -232,9 +232,13 @@ Future<void> exportLogs(
|
||||
MimeType.zip,
|
||||
);
|
||||
} else {
|
||||
await Share.shareXFiles(
|
||||
[XFile(zipFilePath, mimeType: 'application/zip')],
|
||||
sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2),
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
files: <XFile>[
|
||||
XFile(zipFilePath, mimeType: 'application/zip'),
|
||||
],
|
||||
sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,11 @@ class PlatformUtil {
|
||||
? cupertinoTextSelectionControls
|
||||
: desktopTextSelectionControls;
|
||||
|
||||
static openWebView(BuildContext context, String title, String url) async {
|
||||
static Future<void> openWebView(
|
||||
BuildContext context,
|
||||
String title,
|
||||
String url,
|
||||
) async {
|
||||
if (PlatformUtil.isDesktop()) {
|
||||
await launchUrlString(url);
|
||||
return;
|
||||
|
||||
@@ -91,9 +91,11 @@ Future<ShareResult> shareText(
|
||||
}) async {
|
||||
try {
|
||||
final sharePosOrigin = _sharePosOrigin(context, key);
|
||||
return Share.share(
|
||||
text,
|
||||
sharePositionOrigin: sharePosOrigin,
|
||||
return SharePlus.instance.share(
|
||||
ShareParams(
|
||||
text: text,
|
||||
sharePositionOrigin: sharePosOrigin,
|
||||
),
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logger("ShareUtil").severe("failed to share text", e, s);
|
||||
|
||||
@@ -47,6 +47,6 @@ void showToast(
|
||||
}
|
||||
}
|
||||
|
||||
void showShortToast(context, String message) {
|
||||
void showShortToast(BuildContext context, String message) {
|
||||
showToast(context, message, toastLength: Toast.LENGTH_SHORT);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class WindowsProtocolHandler {
|
||||
hKey,
|
||||
txtKey,
|
||||
txtValue,
|
||||
REG_VALUE_TYPE.REG_SZ,
|
||||
REG_SZ,
|
||||
txtData,
|
||||
txtData.length * 2 + 2,
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <flutter_local_authentication/flutter_local_authentication_plugin.h>
|
||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||
#include <gtk/gtk_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
||||
#include <sentry_flutter/sentry_flutter_plugin.h>
|
||||
#include <sodium_libs/sodium_libs_plugin.h>
|
||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||
@@ -31,9 +31,9 @@ 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) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
|
||||
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) sentry_flutter_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin");
|
||||
sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar);
|
||||
|
||||
@@ -7,7 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flutter_local_authentication
|
||||
flutter_secure_storage_linux
|
||||
gtk
|
||||
screen_retriever
|
||||
screen_retriever_linux
|
||||
sentry_flutter
|
||||
sodium_libs
|
||||
sqlite3_flutter_libs
|
||||
|
||||
@@ -8,6 +8,7 @@ import Foundation
|
||||
import app_links
|
||||
import connectivity_plus
|
||||
import device_info_plus
|
||||
import file_picker
|
||||
import file_saver
|
||||
import flutter_inappwebview_macos
|
||||
import flutter_local_authentication
|
||||
@@ -16,12 +17,12 @@ import flutter_secure_storage_macos
|
||||
import local_auth_darwin
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import screen_retriever
|
||||
import screen_retriever_macos
|
||||
import sentry_flutter
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
import sodium_libs
|
||||
import sqflite
|
||||
import sqflite_darwin
|
||||
import sqlite3_flutter_libs
|
||||
import tray_manager
|
||||
import url_launcher_macos
|
||||
@@ -31,15 +32,16 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin"))
|
||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||
FlutterLocalAuthenticationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalAuthenticationPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||
FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin"))
|
||||
LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
||||
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
|
||||
@@ -2,13 +2,14 @@ PODS:
|
||||
- app_links (1.0.0):
|
||||
- FlutterMacOS
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- cupertino_http (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- device_info_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- file_picker (0.0.1):
|
||||
- FlutterMacOS
|
||||
- file_saver (0.0.1):
|
||||
- FlutterMacOS
|
||||
- flutter_inappwebview_macos (0.0.1):
|
||||
@@ -18,7 +19,7 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- FlutterMacOS
|
||||
- flutter_secure_storage_macos (6.1.1):
|
||||
- flutter_secure_storage_macos (6.1.3):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- local_auth_darwin (0.0.1):
|
||||
@@ -32,7 +33,7 @@ PODS:
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- screen_retriever (0.0.1):
|
||||
- screen_retriever_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- Sentry/HybridSDK (8.46.0)
|
||||
- sentry_flutter (8.14.2):
|
||||
@@ -46,39 +47,44 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- sodium_libs (2.2.1):
|
||||
- FlutterMacOS
|
||||
- sqflite (0.0.3):
|
||||
- sqflite_darwin (0.0.4):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- "sqlite3 (3.46.1+1)":
|
||||
- "sqlite3/common (= 3.46.1+1)"
|
||||
- "sqlite3/common (3.46.1+1)"
|
||||
- "sqlite3/dbstatvtab (3.46.1+1)":
|
||||
- sqlite3 (3.50.1):
|
||||
- sqlite3/common (= 3.50.1)
|
||||
- sqlite3/common (3.50.1)
|
||||
- sqlite3/dbstatvtab (3.50.1):
|
||||
- sqlite3/common
|
||||
- "sqlite3/fts5 (3.46.1+1)":
|
||||
- sqlite3/fts5 (3.50.1):
|
||||
- sqlite3/common
|
||||
- "sqlite3/perf-threadsafe (3.46.1+1)":
|
||||
- sqlite3/math (3.50.1):
|
||||
- sqlite3/common
|
||||
- "sqlite3/rtree (3.46.1+1)":
|
||||
- sqlite3/perf-threadsafe (3.50.1):
|
||||
- sqlite3/common
|
||||
- sqlite3/rtree (3.50.1):
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- "sqlite3 (~> 3.46.0+1)"
|
||||
- sqlite3 (~> 3.50.1)
|
||||
- sqlite3/dbstatvtab
|
||||
- sqlite3/fts5
|
||||
- sqlite3/math
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
- tray_manager (0.0.1):
|
||||
- FlutterMacOS
|
||||
- url_launcher_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- window_manager (0.2.0):
|
||||
- window_manager (0.5.0):
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
|
||||
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/darwin`)
|
||||
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
|
||||
- cupertino_http (from `Flutter/ephemeral/.symlinks/plugins/cupertino_http/darwin`)
|
||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
|
||||
- file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`)
|
||||
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
||||
- flutter_local_authentication (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos`)
|
||||
@@ -89,13 +95,13 @@ DEPENDENCIES:
|
||||
- objective_c (from `Flutter/ephemeral/.symlinks/plugins/objective_c/macos`)
|
||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||
- screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`)
|
||||
- sentry_flutter (from `Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos`)
|
||||
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sodium_libs (from `Flutter/ephemeral/.symlinks/plugins/sodium_libs/macos`)
|
||||
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
|
||||
- sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`)
|
||||
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
|
||||
- sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/darwin`)
|
||||
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||
@@ -110,11 +116,13 @@ EXTERNAL SOURCES:
|
||||
app_links:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/app_links/macos
|
||||
connectivity_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/darwin
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
|
||||
cupertino_http:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/cupertino_http/darwin
|
||||
device_info_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
|
||||
file_picker:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
|
||||
file_saver:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/file_saver/macos
|
||||
flutter_inappwebview_macos:
|
||||
@@ -135,8 +143,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||
path_provider_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
screen_retriever:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
||||
screen_retriever_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos
|
||||
sentry_flutter:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos
|
||||
share_plus:
|
||||
@@ -145,10 +153,10 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||
sodium_libs:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sodium_libs/macos
|
||||
sqflite:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin
|
||||
sqflite_darwin:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin
|
||||
sqlite3_flutter_libs:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/darwin
|
||||
tray_manager:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos
|
||||
url_launcher_macos:
|
||||
@@ -157,33 +165,34 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: 9028728e32c83a0831d9db8cf91c526d16cc5468
|
||||
connectivity_plus: 3f6c9057f4cd64198dc826edfb0542892f825343
|
||||
app_links: afe860c55c7ef176cea7fb630a2b7d7736de591d
|
||||
connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
|
||||
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
|
||||
device_info_plus: b0fafc687fb901e2af612763340f1b0d4352f8e5
|
||||
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||
file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f
|
||||
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
||||
flutter_local_authentication: 2f9a2682f498abcc12d7e9729b5007a947170fdc
|
||||
flutter_local_notifications: 453432cd6399a07d072885bc7828fb2307868856
|
||||
flutter_secure_storage_macos: b2d62a774c23b060f0b99d0173b0b36abb4a8632
|
||||
flutter_local_notifications: 13862b132e32eb858dea558a86d45d08daeacfe7
|
||||
flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
|
||||
local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19
|
||||
objective_c: ec13431e45ba099cb734eb2829a5c1cd37986cba
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: a8a591e70e87ce97ce5d21b2594f69cea9e0312f
|
||||
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
screen_retriever: 4f97c103641aab8ce183fa5af3b87029df167936
|
||||
screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f
|
||||
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
|
||||
sentry_flutter: 27892878729f42701297c628eb90e7c6529f3684
|
||||
share_plus: 11c7b7fa7020465584eca3ff6392c5bc1e399d6e
|
||||
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
|
||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||
sodium_libs: b9459e5bfc1185349f43472e79fc5d8e526b2bda
|
||||
sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3
|
||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||
sqlite3_flutter_libs: 03311aede9d32fb2d24e32bebb8cd01c3b2e6239
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
sqlite3: 1d85290c3321153511f6e900ede7a1608718bbd5
|
||||
sqlite3_flutter_libs: e7fc8c9ea2200ff3271f08f127842131746b70e2
|
||||
tray_manager: a104b5c81b578d83f3c3d0f40a997c8b10810166
|
||||
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
|
||||
window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c
|
||||
window_manager: b729e31d38fb04905235df9ea896128991cad99e
|
||||
|
||||
PODFILE CHECKSUM: 6ff827273ace187339fc5d3684072a26ad85c298
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,11 +10,11 @@ environment:
|
||||
dependencies:
|
||||
adaptive_theme: ^3.1.0 # done
|
||||
app_links: ^6.3.3
|
||||
archive: ^3.3.7
|
||||
archive: ^4.0.7
|
||||
auto_size_text: ^3.0.0
|
||||
base32: ^2.1.3
|
||||
bip39: ^1.0.6 #done
|
||||
bloc: ^8.1.2
|
||||
bloc: ^9.0.0
|
||||
clipboard: ^0.1.3
|
||||
collection: ^1.18.0 # dart
|
||||
confetti: ^0.8.0
|
||||
@@ -22,7 +22,7 @@ dependencies:
|
||||
convert: ^3.1.1
|
||||
device_info_plus: ^9.1.1
|
||||
dio: ^5.8.0+1
|
||||
dotted_border: ^2.0.0+2
|
||||
dotted_border: ^3.1.0
|
||||
dropdown_button2: ^2.3.9
|
||||
email_validator: ^3.0.0
|
||||
ente_crypto_dart:
|
||||
@@ -32,19 +32,19 @@ dependencies:
|
||||
expandable: ^5.0.1
|
||||
expansion_tile_card: ^3.0.0
|
||||
ffi: ^2.1.0
|
||||
figma_squircle: ^0.5.3
|
||||
file_picker: ^8.1.7
|
||||
figma_squircle: ^0.6.3
|
||||
file_picker: ^10.2.0
|
||||
# https://github.com/incrediblezayed/file_saver/issues/86
|
||||
file_saver: ^0.2.11
|
||||
file_saver: ^0.3.0
|
||||
fixnum: ^1.1.0
|
||||
fk_user_agent: ^2.1.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_animate: ^4.1.0
|
||||
flutter_bloc: ^8.0.1
|
||||
flutter_bloc: ^9.1.1
|
||||
flutter_context_menu: ^0.2.0
|
||||
flutter_displaymode: ^0.6.0
|
||||
flutter_email_sender: ^6.0.2
|
||||
flutter_email_sender: ^7.0.0
|
||||
# revert to pub.dev when merged
|
||||
# https://github.com/pichillilorenzo/flutter_inappwebview/pull/2548
|
||||
flutter_inappwebview:
|
||||
@@ -57,7 +57,7 @@ dependencies:
|
||||
git:
|
||||
url: https://github.com/eaceto/flutter_local_authentication
|
||||
ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96
|
||||
flutter_local_notifications: ^17.2.2
|
||||
flutter_local_notifications: ^18.0.1
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
flutter_native_splash: ^2.2.13
|
||||
@@ -87,30 +87,27 @@ dependencies:
|
||||
pinput: ^5.0.0
|
||||
pointycastle: ^3.7.3
|
||||
privacy_screen: ^0.0.6
|
||||
protobuf: ^3.0.0
|
||||
protobuf: ^4.1.0
|
||||
qr_code_scanner: ^1.0.1
|
||||
qr_flutter: ^4.1.0
|
||||
sentry: ^8.14.2
|
||||
sentry_flutter: ^8.14.2
|
||||
share_plus: ^10.0.2
|
||||
share_plus: ^11.0.0
|
||||
shared_preferences: ^2.0.5
|
||||
sqflite:
|
||||
git:
|
||||
url: https://github.com/tekartik/sqflite
|
||||
path: sqflite
|
||||
sqflite: ^2.4.2
|
||||
sqflite_common_ffi: ^2.3.0+4
|
||||
sqlite3: ^2.4.3
|
||||
sqlite3_flutter_libs: ^0.5.24
|
||||
steam_totp: ^0.0.1
|
||||
step_progress_indicator: ^1.0.2
|
||||
styled_text: ^8.1.0
|
||||
tray_manager: ^0.2.1
|
||||
tray_manager: ^0.5.0
|
||||
tuple: ^2.0.0
|
||||
url_launcher: ^6.3.1
|
||||
url_launcher_ios: ^6.3.1
|
||||
uuid: ^4.2.2
|
||||
win32: ^5.1.1
|
||||
window_manager: ^0.4.2
|
||||
window_manager: ^0.5.0
|
||||
xdg_directories: ^1.0.4
|
||||
|
||||
dev_dependencies:
|
||||
@@ -118,7 +115,7 @@ dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
json_serializable: ^6.2.0
|
||||
lints: ^5.0.0
|
||||
lints: ^5.1.1
|
||||
mocktail: ^1.0.3
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import "package:ente_auth/l10n/l10n.dart";
|
||||
import "package:ente_auth/l10n/arb/app_localizations.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_localizations/flutter_localizations.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
@@ -8,7 +8,6 @@ extension PumpApp on WidgetTester {
|
||||
return pumpWidget(
|
||||
MaterialApp(
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <flutter_local_authentication/flutter_local_authentication_plugin_c_api.h>
|
||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||
#include <local_auth_windows/local_auth_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
|
||||
#include <sentry_flutter/sentry_flutter_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <sodium_libs/sodium_libs_plugin_c_api.h>
|
||||
@@ -37,8 +37,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||
LocalAuthPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
|
||||
SentryFlutterPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SentryFlutterPlugin"));
|
||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||
|
||||
@@ -10,7 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flutter_local_authentication
|
||||
flutter_secure_storage_windows
|
||||
local_auth_windows
|
||||
screen_retriever
|
||||
screen_retriever_windows
|
||||
sentry_flutter
|
||||
share_plus
|
||||
sodium_libs
|
||||
|
||||
3
mobile/apps/photos/.fvmrc
Normal file
3
mobile/apps/photos/.fvmrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"flutter": "3.29.3"
|
||||
}
|
||||
3
mobile/apps/photos/.gitignore
vendored
3
mobile/apps/photos/.gitignore
vendored
@@ -43,3 +43,6 @@ fastlane/report.xml
|
||||
|
||||
# Android related
|
||||
android/app/build/
|
||||
|
||||
# FVM Version Cache
|
||||
.fvm/
|
||||
@@ -46,7 +46,7 @@ You can alternatively install the build from PlayStore or F-Droid.
|
||||
|
||||
## 🧑💻 Building from source
|
||||
|
||||
1. [Install Flutter v3.24.3](https://flutter.dev/docs/get-started/install).
|
||||
1. [Install Flutter v3.29.3](https://flutter.dev/docs/get-started/install).
|
||||
|
||||
2. Pull in all submodules with `git submodule update --init --recursive`
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ android {
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
@@ -132,7 +133,7 @@ flutter {
|
||||
source = "../.."
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencies {
|
||||
implementation 'io.sentry:sentry-android:2.0.0'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.21'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0'
|
||||
@@ -142,7 +143,7 @@ dependencies {
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
||||
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||
|
||||
constraints {
|
||||
implementation("androidx.work:work-runtime:2.8.1") {
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package io.ente.photos
|
||||
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||
|
||||
class MainActivity : FlutterFragmentActivity() {
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine)
|
||||
}
|
||||
}
|
||||
class MainActivity : FlutterFragmentActivity() {}
|
||||
|
||||
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
|
||||
@@ -18,7 +18,7 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "8.1.0" apply false
|
||||
id "com.android.application" version "8.6.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.1.10" apply false
|
||||
}
|
||||
|
||||
|
||||
2
mobile/apps/photos/ios/.gitignore
vendored
2
mobile/apps/photos/ios/.gitignore
vendored
@@ -32,3 +32,5 @@ Flutter/.last_build_id
|
||||
!default.mode2v3
|
||||
!default.pbxuser
|
||||
!default.perspectivev3
|
||||
|
||||
Flutter/ephemeral
|
||||
@@ -1,7 +1,6 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
source 'https://github.com/ente-io/ffmpeg-kit-custom-repo-ios.git'
|
||||
platform :ios, '13.0'
|
||||
|
||||
source 'https://github.com/ente-io/ffmpeg-kit-custom-repo-ios.git'
|
||||
source 'https://cdn.cocoapods.org/'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
|
||||
@@ -18,37 +18,37 @@ PODS:
|
||||
- Flutter
|
||||
- file_saver (0.0.1):
|
||||
- Flutter
|
||||
- Firebase/CoreOnly (11.8.0):
|
||||
- FirebaseCore (~> 11.8.0)
|
||||
- Firebase/Messaging (11.8.0):
|
||||
- Firebase/CoreOnly (11.15.0):
|
||||
- FirebaseCore (~> 11.15.0)
|
||||
- Firebase/Messaging (11.15.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseMessaging (~> 11.8.0)
|
||||
- firebase_core (3.12.0):
|
||||
- Firebase/CoreOnly (= 11.8.0)
|
||||
- FirebaseMessaging (~> 11.15.0)
|
||||
- firebase_core (3.15.1):
|
||||
- Firebase/CoreOnly (= 11.15.0)
|
||||
- Flutter
|
||||
- firebase_messaging (15.2.3):
|
||||
- Firebase/Messaging (= 11.8.0)
|
||||
- firebase_messaging (15.2.9):
|
||||
- Firebase/Messaging (= 11.15.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- FirebaseCore (11.8.1):
|
||||
- FirebaseCoreInternal (~> 11.8.0)
|
||||
- GoogleUtilities/Environment (~> 8.0)
|
||||
- GoogleUtilities/Logger (~> 8.0)
|
||||
- FirebaseCoreInternal (11.8.0):
|
||||
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||
- FirebaseInstallations (11.8.0):
|
||||
- FirebaseCore (~> 11.8.0)
|
||||
- GoogleUtilities/Environment (~> 8.0)
|
||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||
- FirebaseCore (11.15.0):
|
||||
- FirebaseCoreInternal (~> 11.15.0)
|
||||
- GoogleUtilities/Environment (~> 8.1)
|
||||
- GoogleUtilities/Logger (~> 8.1)
|
||||
- FirebaseCoreInternal (11.15.0):
|
||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||
- FirebaseInstallations (11.15.0):
|
||||
- FirebaseCore (~> 11.15.0)
|
||||
- GoogleUtilities/Environment (~> 8.1)
|
||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||
- PromisesObjC (~> 2.4)
|
||||
- FirebaseMessaging (11.8.0):
|
||||
- FirebaseCore (~> 11.8.0)
|
||||
- FirebaseMessaging (11.15.0):
|
||||
- FirebaseCore (~> 11.15.0)
|
||||
- FirebaseInstallations (~> 11.0)
|
||||
- GoogleDataTransport (~> 10.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
||||
- GoogleUtilities/Environment (~> 8.0)
|
||||
- GoogleUtilities/Reachability (~> 8.0)
|
||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||
- GoogleUtilities/Environment (~> 8.1)
|
||||
- GoogleUtilities/Reachability (~> 8.1)
|
||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||
- nanopb (~> 3.30910.0)
|
||||
- Flutter (1.0.0)
|
||||
- flutter_email_sender (0.0.1):
|
||||
@@ -127,9 +127,6 @@ PODS:
|
||||
- libwebp/sharpyuv (1.5.0)
|
||||
- libwebp/webp (1.5.0):
|
||||
- libwebp/sharpyuv
|
||||
- local_auth_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- local_auth_ios (0.0.1):
|
||||
- Flutter
|
||||
- Mantle (2.2.0):
|
||||
@@ -176,7 +173,7 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- photo_manager (2.0.0):
|
||||
- photo_manager (3.7.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- privacy_screen (0.0.1):
|
||||
@@ -203,23 +200,26 @@ PODS:
|
||||
- sqflite_darwin (0.0.4):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqlite3 (3.49.2):
|
||||
- sqlite3/common (= 3.49.2)
|
||||
- sqlite3/common (3.49.2)
|
||||
- sqlite3/dbstatvtab (3.49.2):
|
||||
- sqlite3 (3.50.1):
|
||||
- sqlite3/common (= 3.50.1)
|
||||
- sqlite3/common (3.50.1)
|
||||
- sqlite3/dbstatvtab (3.50.1):
|
||||
- sqlite3/common
|
||||
- sqlite3/fts5 (3.49.2):
|
||||
- sqlite3/fts5 (3.50.1):
|
||||
- sqlite3/common
|
||||
- sqlite3/perf-threadsafe (3.49.2):
|
||||
- sqlite3/math (3.50.1):
|
||||
- sqlite3/common
|
||||
- sqlite3/rtree (3.49.2):
|
||||
- sqlite3/perf-threadsafe (3.50.1):
|
||||
- sqlite3/common
|
||||
- sqlite3/rtree (3.50.1):
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqlite3 (~> 3.49.0)
|
||||
- sqlite3 (~> 3.50.1)
|
||||
- sqlite3/dbstatvtab
|
||||
- sqlite3/fts5
|
||||
- sqlite3/math
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
- system_info_plus (0.0.1):
|
||||
@@ -269,7 +269,6 @@ DEPENDENCIES:
|
||||
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
|
||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||
- launcher_icon_switcher (from `.symlinks/plugins/launcher_icon_switcher/ios`)
|
||||
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
||||
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
|
||||
- maps_launcher (from `.symlinks/plugins/maps_launcher/ios`)
|
||||
- media_extension (from `.symlinks/plugins/media_extension/ios`)
|
||||
@@ -304,7 +303,7 @@ DEPENDENCIES:
|
||||
- workmanager (from `.symlinks/plugins/workmanager/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/ente-io/ffmpeg-kit-custom-repo-ios.git:
|
||||
https://github.com/ente-io/ffmpeg-kit-custom-repo-ios:
|
||||
- ffmpeg_kit_custom
|
||||
trunk:
|
||||
- Firebase
|
||||
@@ -377,8 +376,6 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/integration_test/ios"
|
||||
launcher_icon_switcher:
|
||||
:path: ".symlinks/plugins/launcher_icon_switcher/ios"
|
||||
local_auth_darwin:
|
||||
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
||||
local_auth_ios:
|
||||
:path: ".symlinks/plugins/local_auth_ios/ios"
|
||||
maps_launcher:
|
||||
@@ -445,84 +442,83 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/workmanager/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: f3e17e4ee5e357b39d8b95290a9b2c299fca71c6
|
||||
battery_info: b6c551049266af31556b93c9d9b9452cfec0219f
|
||||
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
||||
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
|
||||
dart_ui_isolate: d5bcda83ca4b04f129d70eb90110b7a567aece14
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
app_links: 76b66b60cc809390ca1ad69bfd66b998d2387ac7
|
||||
battery_info: 83f3aae7be2fccefab1d2bf06b8aa96f11c8bcdd
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
|
||||
dart_ui_isolate: 46f6714abe6891313267153ef6f9748d8ecfcab1
|
||||
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||
ffmpeg_kit_custom: 682b4f2f1ff1f8abae5a92f6c3540f2441d5be99
|
||||
ffmpeg_kit_flutter: 9dce4803991478c78c6fb9f972703301101095fe
|
||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||
Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf
|
||||
firebase_core: 6e223dfa350b2edceb729cea505eaaef59330682
|
||||
firebase_messaging: 07fde77ae28c08616a1d4d870450efc2b38cf40d
|
||||
FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d
|
||||
FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629
|
||||
FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917
|
||||
FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8
|
||||
ffmpeg_kit_flutter: 915b345acc97d4142e8a9a8549d177ff10f043f5
|
||||
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
|
||||
firebase_core: ece862f94b2bc72ee0edbeec7ab5c7cb09fe1ab5
|
||||
firebase_messaging: e1a5fae495603115be1d0183bc849da748734e2b
|
||||
FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
|
||||
FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
|
||||
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
|
||||
FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_email_sender: e03bdda7637bcd3539bfe718fddd980e9508efaa
|
||||
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
||||
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
||||
flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f
|
||||
flutter_native_splash: f71420956eb811e6d310720fee915f1d42852e7a
|
||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||
flutter_sodium: a00383520fc689c688b66fd3092984174712493e
|
||||
flutter_timezone: ac3da59ac941ff1c98a2e1f0293420e020120282
|
||||
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
|
||||
flutter_email_sender: aa1e9772696691d02cd91fea829856c11efb8e58
|
||||
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
|
||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
|
||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
|
||||
flutter_sodium: 7e4621538491834eba53bd524547854bcbbd6987
|
||||
flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
|
||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
|
||||
image_editor_common: d6f6644ae4a6de80481e89fe6d0a8c49e30b4b43
|
||||
in_app_purchase_storekit: a1ce04056e23eecc666b086040239da7619cd783
|
||||
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
||||
launcher_icon_switcher: 8e0ad2131a20c51c1dd939896ee32e70cd845b37
|
||||
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
|
||||
image_editor_common: 3de87e7c4804f4ae24c8f8a998362b98c105cac1
|
||||
in_app_purchase_storekit: d1a48cb0f8b29dbf5f85f782f5dd79b21b90a5e6
|
||||
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
||||
launcher_icon_switcher: 84c218d233505aa7d8655d8fa61a3ba802c022da
|
||||
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
||||
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
|
||||
local_auth_ios: 5046a18c018dd973247a0564496c8898dbb5adf9
|
||||
local_auth_ios: f7a1841beef3151d140a967c2e46f30637cdf451
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
maps_launcher: 2e5b6a2d664ec6c27f82ffa81b74228d770ab203
|
||||
media_extension: 6618f07abd762cdbfaadf1b0c56a287e820f0c84
|
||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
||||
motion_sensors: 03f55b7c637a7e365a0b5f9697a449f9059d5d91
|
||||
motionphoto: 8b65ce50c7d7ff3c767534fc3768b2eed9ac24e4
|
||||
move_to_background: cd3091014529ec7829e342ad2d75c0a11f4378a5
|
||||
maps_launcher: edf829809ba9e894d70e569bab11c16352dedb45
|
||||
media_extension: 671e2567880d96c95c65c9a82ccceed8f2e309fd
|
||||
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
|
||||
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
|
||||
motion_sensors: 741e702c17467b9569a92165dda8d4d88c6167f1
|
||||
motionphoto: 23e2aeb5c6380112f69468d71f970fa7438e5ed1
|
||||
move_to_background: 7e3467dd2a1d1013e98c9c1cb93fd53cd7ef9d84
|
||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||
native_video_player: 29ab24a926804ac8c4a57eb6d744c7d927c2bc3e
|
||||
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
|
||||
onnxruntime: e7c2ae44385191eaad5ae64c935a72debaddc997
|
||||
native_video_player: 6809dec117e8997161dbfb42a6f90d6df71a504d
|
||||
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
|
||||
onnxruntime: f9b296392c96c42882be020a59dbeac6310d81b2
|
||||
onnxruntime-c: a909204639a1f035f575127ac406f781ac797c9c
|
||||
onnxruntime-objc: b6fab0f1787aa6f7190c2013f03037df4718bd8b
|
||||
open_mail_app: 70273c53f768beefdafbe310c3d9086e4da3cb02
|
||||
open_mail_app: 7314a609e88eed22d53671279e189af7a0ab0f11
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
||||
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
photo_manager: 1d80ae07a89a67dfbcae95953a1e5a24af7c3e62
|
||||
privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
receive_sharing_intent: 79c848f5b045674ad60b9fea3bafea59962ad2c1
|
||||
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
|
||||
SDWebImage: f29024626962457f3470184232766516dee8dfea
|
||||
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
||||
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
|
||||
sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b
|
||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||
sqlite3: 3c950dc86011117c307eb0b28c4a7bb449dce9f1
|
||||
sqlite3_flutter_libs: 069c435986dd4b63461aecd68f4b30be4a9e9daa
|
||||
system_info_plus: 5393c8da281d899950d751713575fbf91c7709aa
|
||||
thermal: a9261044101ae8f532fa29cab4e8270b51b3f55c
|
||||
ua_client_hints: aeabd123262c087f0ce151ef96fa3ab77bfc8b38
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
|
||||
video_thumbnail: 94ba6705afbaa120b77287080424930f23ea0c40
|
||||
volume_controller: 2e3de73d6e7e81a0067310d17fb70f2f86d71ac7
|
||||
wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56
|
||||
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
||||
sentry_flutter: 27892878729f42701297c628eb90e7c6529f3684
|
||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
sqlite3: 1d85290c3321153511f6e900ede7a1608718bbd5
|
||||
sqlite3_flutter_libs: e7fc8c9ea2200ff3271f08f127842131746b70e2
|
||||
system_info_plus: 555ce7047fbbf29154726db942ae785c29211740
|
||||
thermal: d4c48be750d1ddbab36b0e2dcb2471531bc8df41
|
||||
ua_client_hints: 92fe0d139619b73ec9fcb46cc7e079a26178f586
|
||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
|
||||
video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
|
||||
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
|
||||
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
|
||||
workmanager: b89e4e4445d8b57ee2fdbf1c3925696ebe5b8990
|
||||
|
||||
PODFILE CHECKSUM: a8ef88ad74ba499756207e7592c6071a96756d18
|
||||
PODFILE CHECKSUM: cce2cd3351d3488dca65b151118552b680e23635
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -548,7 +548,6 @@
|
||||
"${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/launcher_icon_switcher/launcher_icon_switcher.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/local_auth_darwin/local_auth_darwin.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/local_auth_ios/local_auth_ios.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/maps_launcher/maps_launcher.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/media_extension/media_extension.framework",
|
||||
@@ -644,7 +643,6 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/launcher_icon_switcher.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth_darwin.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth_ios.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/maps_launcher.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_extension.framework",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
@@ -43,11 +44,13 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
arb-dir: lib/l10n
|
||||
template-arb-file: intl_en.arb
|
||||
output-localization-file: app_localizations.dart
|
||||
@@ -5,7 +5,7 @@ import 'package:adaptive_theme/adaptive_theme.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
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:home_widget/home_widget.dart' as hw;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:media_extension/media_extension_action_types.dart';
|
||||
@@ -20,6 +20,7 @@ import 'package:photos/services/app_lifecycle_service.dart';
|
||||
import "package:photos/services/home_widget_service.dart";
|
||||
import "package:photos/services/memory_home_widget_service.dart";
|
||||
import "package:photos/services/people_home_widget_service.dart";
|
||||
import "package:photos/services/smart_albums_service.dart";
|
||||
import 'package:photos/services/sync/sync_service.dart';
|
||||
import 'package:photos/ui/tabs/home_widget.dart';
|
||||
import "package:photos/ui/viewer/actions/file_viewer.dart";
|
||||
@@ -74,8 +75,10 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
|
||||
_peopleChangedSubscription = Bus.instance.on<PeopleChangedEvent>().listen(
|
||||
(event) async {
|
||||
_changeCallbackDebouncer.run(
|
||||
() async =>
|
||||
unawaited(PeopleHomeWidgetService.instance.checkPeopleChanged()),
|
||||
() async {
|
||||
unawaited(PeopleHomeWidgetService.instance.checkPeopleChanged());
|
||||
unawaited(SmartAlbumsService.instance.syncSmartAlbums());
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -143,8 +146,10 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
|
||||
supportedLocales: appSupportedLocales,
|
||||
localeListResolutionCallback: localResolutionCallBack,
|
||||
localizationsDelegates: const [
|
||||
...AppLocalizations.localizationsDelegates,
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -166,8 +171,10 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
|
||||
supportedLocales: appSupportedLocales,
|
||||
localeListResolutionCallback: localResolutionCallBack,
|
||||
localizationsDelegates: const [
|
||||
...AppLocalizations.localizationsDelegates,
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
library super_logging;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:core';
|
||||
|
||||
@@ -128,4 +128,21 @@ extension EntitiesDB on FilesDB {
|
||||
}
|
||||
return maps.values.first as String?;
|
||||
}
|
||||
|
||||
Future<Map<String, int>> getUpdatedAts(
|
||||
EntityType type,
|
||||
List<String> ids,
|
||||
) async {
|
||||
final db = await sqliteAsyncDB;
|
||||
final List<Map<String, dynamic>> maps = await db.getAll(
|
||||
'SELECT id, updatedAt FROM entities WHERE type = ? AND id IN (${List.filled(ids.length, '?').join(',')})',
|
||||
[type.name, ...ids],
|
||||
);
|
||||
return Map<String, int>.fromEntries(
|
||||
List.generate(
|
||||
maps.length,
|
||||
(i) => MapEntry(maps[i]['id'] as String, maps[i]['updatedAt'] as int),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,6 +1479,7 @@ class FilesDB with SqlDbBase {
|
||||
|
||||
final inParam = ids.map((id) => "'$id'").join(',');
|
||||
final db = await instance.sqliteAsyncDB;
|
||||
|
||||
final results = await db.getAll(
|
||||
'SELECT * FROM $filesTable WHERE $columnUploadedFileID IN ($inParam) ORDER BY $columnCreationTime $order',
|
||||
);
|
||||
|
||||
@@ -316,28 +316,48 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
|
||||
// read person from db
|
||||
final db = await instance.asyncDB;
|
||||
if (personID != null) {
|
||||
logger.info("[$recentFileID] Processing personID path: $personID");
|
||||
final List<int> fileId = [recentFileID];
|
||||
logger.info("[$recentFileID] Initial fileId list: $fileId");
|
||||
|
||||
int? avatarFileId;
|
||||
if (avatarFaceId != null) {
|
||||
logger.info(
|
||||
"[$recentFileID] Looking up avatarFileId for avatarFaceId: $avatarFaceId",
|
||||
);
|
||||
avatarFileId = tryGetFileIdFromFaceId(avatarFaceId);
|
||||
logger.info("[$recentFileID] avatarFileId result: $avatarFileId");
|
||||
if (avatarFileId != null) {
|
||||
fileId.add(avatarFileId);
|
||||
logger.info(
|
||||
"[$recentFileID] Updated fileId list with avatarFileId: $fileId",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logger.info("[$recentFileID] No avatarFaceId provided");
|
||||
}
|
||||
const String queryClusterID = '''
|
||||
SELECT $clusterIDColumn
|
||||
FROM $clusterPersonTable
|
||||
WHERE $personIdColumn = ?
|
||||
''';
|
||||
logger.info(
|
||||
"[$recentFileID] Executing cluster query for personID: $personID",
|
||||
);
|
||||
|
||||
final clusterRows = await db.getAll(
|
||||
queryClusterID,
|
||||
[personID],
|
||||
);
|
||||
logger.info(
|
||||
"[$recentFileID] Found ${clusterRows.length} cluster rows: $clusterRows",
|
||||
);
|
||||
|
||||
final clusterIDs =
|
||||
clusterRows.map((e) => e[clusterIDColumn] as String).toList();
|
||||
logger.info("[$recentFileID] Extracted clusterIDs: $clusterIDs");
|
||||
|
||||
final List<Map<String, dynamic>> faceMaps = await db.getAll(
|
||||
'''
|
||||
final faceQuery = '''
|
||||
SELECT * FROM $facesTable
|
||||
WHERE $faceIDColumn IN (
|
||||
SELECT $faceIDColumn
|
||||
@@ -346,45 +366,193 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
|
||||
)
|
||||
AND $fileIDColumn IN (${List.filled(fileId.length, '?').join(',')})
|
||||
ORDER BY $faceScore DESC
|
||||
''',
|
||||
[...clusterIDs, ...fileId],
|
||||
''';
|
||||
final queryParams = [...clusterIDs, ...fileId];
|
||||
|
||||
logger.info(
|
||||
"[$recentFileID] Executing face query with ${clusterIDs.length} clusterIDs and ${fileId.length} fileIDs",
|
||||
);
|
||||
logger.info("[$recentFileID] Face query: $faceQuery");
|
||||
logger.info("[$recentFileID] Query parameters: $queryParams");
|
||||
|
||||
final List<Map<String, dynamic>> faceMaps = await db.getAll(
|
||||
faceQuery,
|
||||
queryParams,
|
||||
);
|
||||
logger.info("[$recentFileID] Found ${faceMaps.length} face maps");
|
||||
if (faceMaps.isNotEmpty) {
|
||||
logger.info(
|
||||
"[$recentFileID] Found ${faceMaps.length} faces, processing selection",
|
||||
);
|
||||
if (avatarFileId != null) {
|
||||
logger.info(
|
||||
"[$recentFileID] Looking for face with avatarFileId: $avatarFileId",
|
||||
);
|
||||
final row = faceMaps.firstWhereOrNull(
|
||||
(element) => (element[fileIDColumn] as int) == avatarFileId,
|
||||
);
|
||||
if (row != null) {
|
||||
return mapRowToFace(row);
|
||||
logger.info(
|
||||
"[$recentFileID] Found avatar face, returning: ${row[faceIDColumn]}",
|
||||
);
|
||||
final face = mapRowToFace(row);
|
||||
logger.info(
|
||||
"[$recentFileID] getCoverFaceForPerson SUCCESS (avatar): returning face ${face.faceID}",
|
||||
);
|
||||
return face;
|
||||
} else {
|
||||
logger.info(
|
||||
"[$recentFileID] Avatar face not found in results, falling back to first face",
|
||||
);
|
||||
}
|
||||
}
|
||||
return mapRowToFace(faceMaps.first);
|
||||
logger.info(
|
||||
"[$recentFileID] Returning first face: ${faceMaps.first[faceIDColumn]}",
|
||||
);
|
||||
final face = mapRowToFace(faceMaps.first);
|
||||
logger.info(
|
||||
"[$recentFileID] getCoverFaceForPerson SUCCESS (first): returning face ${face.faceID}",
|
||||
);
|
||||
return face;
|
||||
} else {
|
||||
// Diagnostic queries to understand why faceMaps is empty
|
||||
logger.info(
|
||||
"[$recentFileID] faceMaps is empty, running diagnostic queries",
|
||||
);
|
||||
|
||||
// Test 1: Check if faces exist in faceClustersTable for these clusterIDs
|
||||
if (clusterIDs.isNotEmpty) {
|
||||
final faceIDsInClusters = await db.getAll(
|
||||
'''
|
||||
SELECT $faceIDColumn
|
||||
FROM $faceClustersTable
|
||||
WHERE $clusterIDColumn IN (${List.filled(clusterIDs.length, '?').join(',')})
|
||||
''',
|
||||
clusterIDs,
|
||||
);
|
||||
logger.info(
|
||||
"[$recentFileID] Found ${faceIDsInClusters.length} faceIDs in faceClustersTable for clusterIDs: ${faceIDsInClusters.map((e) => e[faceIDColumn]).toList()}",
|
||||
);
|
||||
|
||||
// Test 2: Check if any of those faces exist in facesTable
|
||||
if (faceIDsInClusters.isNotEmpty) {
|
||||
final faceIDsFromClusters = faceIDsInClusters
|
||||
.map((e) => e[faceIDColumn] as String)
|
||||
.toList();
|
||||
final facesInFacesTable = await db.getAll(
|
||||
'''
|
||||
SELECT $faceIDColumn, $fileIDColumn
|
||||
FROM $facesTable
|
||||
WHERE $faceIDColumn IN (${List.filled(faceIDsFromClusters.length, '?').join(',')})
|
||||
''',
|
||||
faceIDsFromClusters,
|
||||
);
|
||||
logger.info(
|
||||
"[$recentFileID] Found ${facesInFacesTable.length} faces in facesTable: ${facesInFacesTable.map((e) => '${e[faceIDColumn]}(file:${e[fileIDColumn]})').toList()}",
|
||||
);
|
||||
|
||||
// Test 3: Check if any of those faces are for our target fileIDs
|
||||
final facesForTargetFiles = facesInFacesTable
|
||||
.where(
|
||||
(face) => fileId.contains(face[fileIDColumn] as int),
|
||||
)
|
||||
.toList();
|
||||
logger.info(
|
||||
"[$recentFileID] Found ${facesForTargetFiles.length} faces for target fileIDs $fileId: ${facesForTargetFiles.map((e) => '${e[faceIDColumn]}(file:${e[fileIDColumn]})').toList()}",
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
"[$recentFileID] No faceIDs found in faceClustersTable for these clusterIDs",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logger.info("[$recentFileID] No clusterIDs to check");
|
||||
}
|
||||
|
||||
// Test 4: Check if there are any faces at all for the target fileIDs
|
||||
final allFacesForFiles = await db.getAll(
|
||||
'''
|
||||
SELECT $faceIDColumn, $fileIDColumn
|
||||
FROM $facesTable
|
||||
WHERE $fileIDColumn IN (${List.filled(fileId.length, '?').join(',')})
|
||||
''',
|
||||
fileId,
|
||||
);
|
||||
logger.info(
|
||||
"[$recentFileID] Found ${allFacesForFiles.length} total faces for fileIDs $fileId: ${allFacesForFiles.map((e) => '${e[faceIDColumn]}(file:${e[fileIDColumn]})').toList()}",
|
||||
);
|
||||
|
||||
if (clusterID == null) {
|
||||
logger.severe(
|
||||
"[$recentFileID] Didn't find any faces for personID $personID in getCoverFaceForPerson. fileID: $fileId, clusterIDs: $clusterIDs",
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
"[$recentFileID] No faces found for personID, but clusterID provided, will try clusterID path",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clusterID != null) {
|
||||
logger.info("[$recentFileID] Processing clusterID path: $clusterID");
|
||||
const String queryFaceID = '''
|
||||
SELECT $faceIDColumn
|
||||
FROM $faceClustersTable
|
||||
WHERE $clusterIDColumn = ?
|
||||
''';
|
||||
logger.info(
|
||||
"[$recentFileID] Executing faceID query for clusterID: $clusterID",
|
||||
);
|
||||
logger.info("[$recentFileID] Query: $queryFaceID");
|
||||
|
||||
final List<Map<String, dynamic>> faceMaps = await db.getAll(
|
||||
queryFaceID,
|
||||
[clusterID],
|
||||
);
|
||||
logger.info(
|
||||
"[$recentFileID] Found ${faceMaps.length} face mappings for cluster: ${faceMaps.map((e) => e[faceIDColumn]).toList()}",
|
||||
);
|
||||
|
||||
final List<Face>? faces = await getFacesForGivenFileID(recentFileID);
|
||||
logger.info(
|
||||
"[$recentFileID] getFacesForGivenFileID returned ${faces?.length ?? 0} faces: ${faces?.map((f) => f.faceID).toList() ?? []}",
|
||||
);
|
||||
|
||||
if (faces != null) {
|
||||
logger.info(
|
||||
"[$recentFileID] Searching for matching face in ${faces.length} faces",
|
||||
);
|
||||
for (final face in faces) {
|
||||
if (faceMaps.any(
|
||||
final isMatch = faceMaps.any(
|
||||
(element) => (element[faceIDColumn] as String) == face.faceID,
|
||||
)) {
|
||||
);
|
||||
logger.info(
|
||||
"[$recentFileID] Checking face ${face.faceID}: match=$isMatch",
|
||||
);
|
||||
if (isMatch) {
|
||||
logger.info(
|
||||
"[$recentFileID] getCoverFaceForPerson SUCCESS (cluster): returning face ${face.faceID}",
|
||||
);
|
||||
return face;
|
||||
}
|
||||
}
|
||||
logger.info(
|
||||
"[$recentFileID] No matching faces found in file for clusterID",
|
||||
);
|
||||
} else {
|
||||
logger.severe(
|
||||
"[$recentFileID] Didn't find any faces for clusterID $clusterID in getCoverFaceForPerson. faces: $faces",
|
||||
);
|
||||
}
|
||||
}
|
||||
if (personID == null && clusterID == null) {
|
||||
logger
|
||||
.severe("[$recentFileID] personID and clusterID cannot be null both");
|
||||
throw Exception("personID and clusterID cannot be null");
|
||||
}
|
||||
logger.severe(
|
||||
"[$recentFileID] getCoverFaceForPerson FAILED: No face found (personID: $personID, clusterID: $clusterID)",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,20 +42,22 @@ final lightThemeData = ThemeData(
|
||||
bodyLarge: const TextStyle(color: Colors.orange),
|
||||
),
|
||||
cardColor: const Color.fromRGBO(250, 250, 250, 1.0),
|
||||
dialogTheme: const DialogTheme().copyWith(
|
||||
backgroundColor: const Color.fromRGBO(250, 250, 250, 1.0), //
|
||||
titleTextStyle: const TextStyle(
|
||||
dialogTheme: const DialogThemeData(
|
||||
backgroundColor: Color.fromRGBO(250, 250, 250, 1.0), //
|
||||
titleTextStyle: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
contentTextStyle: const TextStyle(
|
||||
contentTextStyle: TextStyle(
|
||||
fontFamily: 'Inter-Medium',
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme().copyWith(
|
||||
focusedBorder: const UnderlineInputBorder(
|
||||
@@ -117,20 +119,22 @@ final darkThemeData = ThemeData(
|
||||
elevation: 0,
|
||||
),
|
||||
cardColor: const Color.fromRGBO(10, 15, 15, 1.0),
|
||||
dialogTheme: const DialogTheme().copyWith(
|
||||
backgroundColor: const Color.fromRGBO(15, 15, 15, 1.0),
|
||||
titleTextStyle: const TextStyle(
|
||||
dialogTheme: const DialogThemeData(
|
||||
backgroundColor: Color.fromRGBO(15, 15, 15, 1.0),
|
||||
titleTextStyle: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
contentTextStyle: const TextStyle(
|
||||
contentTextStyle: TextStyle(
|
||||
fontFamily: 'Inter-Medium',
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme().copyWith(
|
||||
focusedBorder: const UnderlineInputBorder(
|
||||
@@ -206,7 +210,7 @@ TextTheme _buildTextTheme(Color textColor) {
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
color: textColor.withOpacity(0.6),
|
||||
color: textColor.withValues(alpha: 0.6),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
@@ -278,7 +282,7 @@ extension CustomColorScheme on ColorScheme {
|
||||
: const Color.fromRGBO(48, 48, 48, 0.5);
|
||||
|
||||
Color get iconColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.75)
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.75)
|
||||
: const Color.fromRGBO(255, 255, 255, 1);
|
||||
|
||||
Color get bgColorForQuestions => brightness == Brightness.light
|
||||
@@ -289,7 +293,7 @@ extension CustomColorScheme on ColorScheme {
|
||||
|
||||
Color get cupertinoPickerTopColor => brightness == Brightness.light
|
||||
? const Color.fromARGB(255, 238, 238, 238)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.1);
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.1);
|
||||
|
||||
Color get stepProgressUnselectedColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(196, 196, 196, 0.6)
|
||||
@@ -300,20 +304,20 @@ extension CustomColorScheme on ColorScheme {
|
||||
: const Color.fromRGBO(20, 20, 20, 1);
|
||||
|
||||
Color get galleryThumbDrawColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.8)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.5);
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.8)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.5);
|
||||
|
||||
Color get backupEnabledBgColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(230, 230, 230, 0.95)
|
||||
: const Color.fromRGBO(10, 40, 40, 0.3);
|
||||
|
||||
Color get dotsIndicatorActiveColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.5)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.5);
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.5)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.5);
|
||||
|
||||
Color get dotsIndicatorInactiveColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.12)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.12);
|
||||
? const Color.fromRGBO(0, 0, 0, 1).withValues(alpha: 0.12)
|
||||
: const Color.fromRGBO(255, 255, 255, 1).withValues(alpha: 0.12);
|
||||
|
||||
Color get toastTextColor => brightness == Brightness.light
|
||||
? const Color.fromRGBO(255, 255, 255, 1)
|
||||
@@ -336,8 +340,8 @@ extension CustomColorScheme on ColorScheme {
|
||||
: const Color.fromRGBO(150, 150, 150, 1);
|
||||
|
||||
Color get searchResultsBackgroundColor => brightness == Brightness.light
|
||||
? Colors.black.withOpacity(0.32)
|
||||
: Colors.black.withOpacity(0.64);
|
||||
? Colors.black.withValues(alpha: 0.32)
|
||||
: Colors.black.withValues(alpha: 0.64);
|
||||
|
||||
EnteTheme get enteTheme =>
|
||||
brightness == Brightness.light ? lightTheme : darkTheme;
|
||||
|
||||
@@ -57,9 +57,10 @@ const kLastFGTaskHeartBeatTime = "fg_task_hb_time";
|
||||
const kHeartBeatFrequency = Duration(seconds: 1);
|
||||
const kFGSyncFrequency = Duration(minutes: 5);
|
||||
const kFGHomeWidgetSyncFrequency = Duration(minutes: 15);
|
||||
const kBGTaskTimeout = Duration(seconds: 28);
|
||||
const kBGTaskTimeout = Duration(seconds: 58);
|
||||
const kBGPushTimeout = Duration(seconds: 28);
|
||||
const kFGTaskDeathTimeoutInMicroseconds = 5000000;
|
||||
bool isProcessBg = true;
|
||||
|
||||
void main() async {
|
||||
debugRepaintRainbowEnabled = false;
|
||||
@@ -86,6 +87,7 @@ void main() async {
|
||||
Future<void> _runInForeground(AdaptiveThemeMode? savedThemeMode) async {
|
||||
return await runWithLogs(() async {
|
||||
_logger.info("Starting app in foreground");
|
||||
isProcessBg = false;
|
||||
await _init(false, via: 'mainMethod');
|
||||
final Locale? locale = await getLocale(noFallback: true);
|
||||
runApp(
|
||||
@@ -306,7 +308,7 @@ void logLocalSettings() {
|
||||
);
|
||||
_logger.info("Gallery grid size: ${localSettings.getPhotoGridSize()}");
|
||||
_logger.info(
|
||||
"Video streaming is enalbed: ${VideoPreviewService.instance.isVideoStreamingEnabled}",
|
||||
"Video streaming is enabled: ${VideoPreviewService.instance.isVideoStreamingEnabled}",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ enum EntityType {
|
||||
person,
|
||||
cgroup,
|
||||
unknown,
|
||||
smartConfig,
|
||||
}
|
||||
|
||||
EntityType typeFromString(String type) {
|
||||
@@ -15,6 +16,8 @@ EntityType typeFromString(String type) {
|
||||
return EntityType.location;
|
||||
case "cgroup":
|
||||
return EntityType.cgroup;
|
||||
case "sconfig":
|
||||
return EntityType.cgroup;
|
||||
}
|
||||
debugPrint("unexpected entity type $type");
|
||||
return EntityType.unknown;
|
||||
@@ -22,10 +25,13 @@ EntityType typeFromString(String type) {
|
||||
|
||||
extension EntityTypeExtn on EntityType {
|
||||
bool isZipped() {
|
||||
if (this == EntityType.location || this == EntityType.person) {
|
||||
return false;
|
||||
switch (this) {
|
||||
case EntityType.location:
|
||||
case EntityType.person:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String typeToString() {
|
||||
@@ -36,6 +42,8 @@ extension EntityTypeExtn on EntityType {
|
||||
return "person";
|
||||
case EntityType.cgroup:
|
||||
return "cgroup";
|
||||
case EntityType.smartConfig:
|
||||
return "sconfig";
|
||||
case EntityType.unknown:
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
143
mobile/apps/photos/lib/models/collection/smart_album_config.dart
Normal file
143
mobile/apps/photos/lib/models/collection/smart_album_config.dart
Normal file
@@ -0,0 +1,143 @@
|
||||
typedef PersonInfo = ({int updatedAt, Set<int> addedFiles});
|
||||
|
||||
class SmartAlbumConfig {
|
||||
// A nullable remote ID for syncing purposes
|
||||
final String? remoteId;
|
||||
|
||||
final int collectionId;
|
||||
// person ids
|
||||
final Set<String> personIDs;
|
||||
// person id mapped with updatedat, file ids
|
||||
final Map<String, PersonInfo> infoMap;
|
||||
final int updatedAt;
|
||||
|
||||
SmartAlbumConfig({
|
||||
this.remoteId,
|
||||
required this.collectionId,
|
||||
required this.personIDs,
|
||||
required this.infoMap,
|
||||
this.updatedAt = 0,
|
||||
});
|
||||
|
||||
Future<SmartAlbumConfig> getUpdatedConfig(Set<String> newPersonsIds) async {
|
||||
final toAdd = newPersonsIds.difference(personIDs);
|
||||
final toRemove = personIDs.difference(newPersonsIds);
|
||||
final newInfoMap = Map<String, PersonInfo>.from(infoMap);
|
||||
|
||||
// Remove whats not needed
|
||||
for (final personId in toRemove) {
|
||||
newInfoMap.remove(personId);
|
||||
}
|
||||
|
||||
// Add files which are needed
|
||||
for (final personId in toAdd) {
|
||||
newInfoMap[personId] = (updatedAt: 0, addedFiles: <int>{});
|
||||
}
|
||||
|
||||
return SmartAlbumConfig(
|
||||
remoteId: remoteId,
|
||||
collectionId: collectionId,
|
||||
personIDs: newPersonsIds,
|
||||
infoMap: newInfoMap,
|
||||
updatedAt: DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
}
|
||||
|
||||
Future<SmartAlbumConfig> addFiles(
|
||||
String personId,
|
||||
int updatedAt,
|
||||
Set<int> fileId,
|
||||
) async {
|
||||
if (!infoMap.containsKey(personId)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final newInfoMap = Map<String, PersonInfo>.from(infoMap);
|
||||
newInfoMap[personId] = (
|
||||
updatedAt: updatedAt,
|
||||
addedFiles: newInfoMap[personId]!.addedFiles.union(fileId),
|
||||
);
|
||||
return SmartAlbumConfig(
|
||||
remoteId: remoteId,
|
||||
collectionId: collectionId,
|
||||
personIDs: personIDs,
|
||||
infoMap: newInfoMap,
|
||||
updatedAt: DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
}
|
||||
|
||||
// toJson and fromJson methods
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"remote_id": remoteId,
|
||||
"collection_id": collectionId,
|
||||
"person_ids": personIDs.toList(),
|
||||
"updated_at": updatedAt,
|
||||
"info_map": infoMap.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
{
|
||||
"updated_at": value.updatedAt,
|
||||
"added_files": value.addedFiles.toList(),
|
||||
},
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
factory SmartAlbumConfig.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
String? remoteId,
|
||||
int? updatedAt,
|
||||
) {
|
||||
final personIDs = Set<String>.from(json["person_ids"] as List? ?? []);
|
||||
final infoMap = (json["info_map"] as Map<String, dynamic>).map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
(
|
||||
updatedAt: value["updated_at"] as int? ??
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
addedFiles: Set<int>.from(value["added_files"] as List? ?? []),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return SmartAlbumConfig(
|
||||
remoteId: remoteId,
|
||||
collectionId: json["collection_id"] as int,
|
||||
personIDs: personIDs,
|
||||
infoMap: infoMap,
|
||||
updatedAt: updatedAt ?? DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
}
|
||||
|
||||
SmartAlbumConfig merge(SmartAlbumConfig b) {
|
||||
if (remoteId == b.remoteId) {
|
||||
if (updatedAt >= b.updatedAt) {
|
||||
return this;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
return SmartAlbumConfig(
|
||||
remoteId: b.updatedAt <= updatedAt ? b.remoteId : remoteId,
|
||||
collectionId: b.collectionId,
|
||||
personIDs: personIDs.union(b.personIDs),
|
||||
infoMap: {
|
||||
...infoMap,
|
||||
...b.infoMap.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
(
|
||||
updatedAt: infoMap[key]?.updatedAt != null &&
|
||||
infoMap[key]!.updatedAt > value.updatedAt
|
||||
? infoMap[key]!.updatedAt
|
||||
: value.updatedAt,
|
||||
addedFiles: infoMap[key]?.addedFiles.union(value.addedFiles) ??
|
||||
value.addedFiles,
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ class DownloadManager {
|
||||
final existingSize = await baseFile.length();
|
||||
if (existingSize == task.totalBytes) {
|
||||
_logger.info(
|
||||
'Download already exists for ${task.filename} (${existingSize}/${task.totalBytes} bytes)',
|
||||
'Download already exists for ${task.filename} ($existingSize/${task.totalBytes} bytes)',
|
||||
);
|
||||
task = task.copyWith(
|
||||
status: DownloadStatus.completed,
|
||||
|
||||
@@ -79,16 +79,19 @@ class EntityService {
|
||||
" ${id == null ? 'Adding' : 'Updating'} entity of type: " +
|
||||
type.typeToString(),
|
||||
);
|
||||
late LocalEntityData localData;
|
||||
|
||||
final EntityData data = id == null
|
||||
? await _gateway.createEntity(type, encryptedData, header)
|
||||
: await _gateway.updateEntity(type, id, encryptedData, header);
|
||||
final LocalEntityData localData = LocalEntityData(
|
||||
localData = LocalEntityData(
|
||||
id: data.id,
|
||||
type: type,
|
||||
data: plainText,
|
||||
ownerID: data.userID,
|
||||
updatedAt: data.updatedAt,
|
||||
);
|
||||
|
||||
await _db.upsertEntities([localData]);
|
||||
syncEntities().ignore();
|
||||
return localData;
|
||||
@@ -103,6 +106,8 @@ class EntityService {
|
||||
try {
|
||||
await _remoteToLocalSync(EntityType.location);
|
||||
await _remoteToLocalSync(EntityType.cgroup);
|
||||
// TODO: Change to smart config
|
||||
await _remoteToLocalSync(EntityType.person);
|
||||
} catch (e) {
|
||||
_logger.severe("Failed to sync entities", e);
|
||||
}
|
||||
@@ -249,4 +254,11 @@ class EntityService {
|
||||
final hash = md5.convert(utf8.encode(preHash)).toString().substring(0, 10);
|
||||
return hash;
|
||||
}
|
||||
|
||||
Future<Map<String, int>> getUpdatedAts(
|
||||
EntityType type,
|
||||
List<String> personIds,
|
||||
) async {
|
||||
return await _db.getUpdatedAts(type, personIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import "package:photos/models/base/id.dart";
|
||||
import "package:photos/services/isolate_functions.dart";
|
||||
import "package:synchronized/synchronized.dart";
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
abstract class SuperIsolate {
|
||||
Logger get logger;
|
||||
|
||||
|
||||
49
mobile/apps/photos/lib/services/isolated_ffmpeg_service.dart
Normal file
49
mobile/apps/photos/lib/services/isolated_ffmpeg_service.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import "dart:async";
|
||||
import "dart:isolate";
|
||||
|
||||
import "package:ffmpeg_kit_flutter/ffmpeg_kit.dart";
|
||||
import "package:ffmpeg_kit_flutter/ffprobe_kit.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import "package:photos/utils/ffprobe_util.dart";
|
||||
|
||||
class IsolatedFfmpegService {
|
||||
static Future<Map> runFfmpeg(String command) async {
|
||||
final rootIsolateToken = RootIsolateToken.instance!;
|
||||
return await Isolate.run<Map>(() => _ffmpegRun(command, rootIsolateToken));
|
||||
}
|
||||
|
||||
static Future<Map> getVideoInfo(String file) async {
|
||||
final rootIsolateToken = RootIsolateToken.instance!;
|
||||
return await Isolate.run<Map>(() => _getVideoProps(file, rootIsolateToken));
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<Map> _getVideoProps(
|
||||
String filePath,
|
||||
RootIsolateToken rootIsolateToken,
|
||||
) async {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
|
||||
final session = await FFprobeKit.getMediaInformation(filePath);
|
||||
final mediaInfo = session.getMediaInformation();
|
||||
|
||||
if (mediaInfo == null) {
|
||||
return {};
|
||||
}
|
||||
|
||||
final metadata = await FFProbeUtil.getMetadata(mediaInfo);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<Map> _ffmpegRun(String value, RootIsolateToken rootIsolateToken) async {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
|
||||
final session = await FFmpegKit.execute(value, true);
|
||||
final returnCode = await session.getReturnCode();
|
||||
final output = await session.getOutput();
|
||||
|
||||
return {
|
||||
"returnCode": returnCode?.getValue(),
|
||||
"output": output,
|
||||
};
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data' show Uint8List;
|
||||
|
||||
import "package:computer/computer.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/models/ml/face/box.dart";
|
||||
import "package:photos/services/isolate_functions.dart";
|
||||
import "package:photos/services/isolate_service.dart";
|
||||
import "package:photos/utils/image_ml_util.dart";
|
||||
|
||||
final Computer _computer = Computer.shared();
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
class FaceThumbnailGenerator extends SuperIsolate {
|
||||
@override
|
||||
Logger get logger => _logger;
|
||||
@@ -35,22 +33,83 @@ class FaceThumbnailGenerator extends SuperIsolate {
|
||||
/// Uses [generateFaceThumbnailsUsingCanvas] inside the isolate.
|
||||
Future<List<Uint8List>> generateFaceThumbnails(
|
||||
String imagePath,
|
||||
List<FaceBox> faceBoxes,
|
||||
) async {
|
||||
final List<Map<String, dynamic>> faceBoxesJson =
|
||||
faceBoxes.map((box) => box.toJson()).toList();
|
||||
final List<Uint8List> faces = await runInIsolate(
|
||||
IsolateOperation.generateFaceThumbnails,
|
||||
{
|
||||
'imagePath': imagePath,
|
||||
'faceBoxesList': faceBoxesJson,
|
||||
},
|
||||
).then((value) => value.cast<Uint8List>());
|
||||
final compressedFaces =
|
||||
await compressFaceThumbnails({'listPngBytes': faces});
|
||||
_logger.fine(
|
||||
"Compressed face thumbnails from sizes ${faces.map((e) => e.length / 1024).toList()} to ${compressedFaces.map((e) => e.length / 1024).toList()} kilobytes",
|
||||
);
|
||||
return compressedFaces;
|
||||
List<FaceBox> faceBoxes, {
|
||||
int? fileID,
|
||||
}) async {
|
||||
try {
|
||||
_logger.info(
|
||||
"[$fileID] FaceThumbnailGenerator START: Generating face thumbnails for ${faceBoxes.length} face boxes in $imagePath",
|
||||
);
|
||||
|
||||
_logger
|
||||
.info("[$fileID] Converting ${faceBoxes.length} face boxes to JSON");
|
||||
final List<Map<String, dynamic>> faceBoxesJson =
|
||||
faceBoxes.map((box) => box.toJson()).toList();
|
||||
_logger.info(
|
||||
"[$fileID] Face boxes converted to JSON, preparing isolate operation",
|
||||
);
|
||||
|
||||
_logger.info(
|
||||
"[$fileID] Running generateFaceThumbnails in isolate with imagePath: $imagePath",
|
||||
);
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
final List<Uint8List> faces = await runInIsolate(
|
||||
IsolateOperation.generateFaceThumbnails,
|
||||
{
|
||||
'imagePath': imagePath,
|
||||
'faceBoxesList': faceBoxesJson,
|
||||
},
|
||||
).then((value) => value.cast<Uint8List>());
|
||||
|
||||
stopwatch.stop();
|
||||
_logger.info(
|
||||
"[$fileID] Isolate operation completed in ${stopwatch.elapsedMilliseconds}ms",
|
||||
);
|
||||
_logger.info(
|
||||
"[$fileID] Generated ${faces.length} face thumbnails with sizes: ${faces.map((e) => '${(e.length / 1024).toStringAsFixed(1)}KB').toList()}",
|
||||
);
|
||||
|
||||
if (faces.length != faceBoxes.length) {
|
||||
_logger.severe(
|
||||
"[$fileID] Mismatch: Expected ${faceBoxes.length} face thumbnails but got ${faces.length}",
|
||||
);
|
||||
}
|
||||
|
||||
_logger.info("[$fileID] Starting face thumbnail compression");
|
||||
final compressionStopwatch = Stopwatch()..start();
|
||||
|
||||
final compressedFaces =
|
||||
await compressFaceThumbnails({'listPngBytes': faces}, fileID: fileID);
|
||||
|
||||
compressionStopwatch.stop();
|
||||
_logger.info(
|
||||
"[$fileID] Compression completed in ${compressionStopwatch.elapsedMilliseconds}ms",
|
||||
);
|
||||
_logger.info(
|
||||
"[$fileID] Compressed face thumbnails from sizes ${faces.map((e) => '${(e.length / 1024).toStringAsFixed(1)}KB').toList()} to ${compressedFaces.map((e) => '${(e.length / 1024).toStringAsFixed(1)}KB').toList()}",
|
||||
);
|
||||
|
||||
final totalOriginalSize =
|
||||
faces.fold<int>(0, (sum, face) => sum + face.length);
|
||||
final totalCompressedSize =
|
||||
compressedFaces.fold<int>(0, (sum, face) => sum + face.length);
|
||||
final compressionRatio = totalOriginalSize > 0
|
||||
? (totalCompressedSize / totalOriginalSize * 100).toStringAsFixed(1)
|
||||
: "0.0";
|
||||
|
||||
_logger.info(
|
||||
"[$fileID] FaceThumbnailGenerator END: Total size reduced from ${(totalOriginalSize / 1024).toStringAsFixed(1)}KB to ${(totalCompressedSize / 1024).toStringAsFixed(1)}KB (${compressionRatio}% of original)",
|
||||
);
|
||||
|
||||
return compressedFaces;
|
||||
} catch (e, s) {
|
||||
_logger.severe(
|
||||
"[$fileID] Failed to generate face thumbnails for $imagePath",
|
||||
e,
|
||||
s,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import "dart:io" show File;
|
||||
|
||||
import "package:flutter/cupertino.dart";
|
||||
import "package:flutter/foundation.dart" show kDebugMode;
|
||||
import "package:flutter/material.dart" show BuildContext;
|
||||
import "package:logging/logging.dart";
|
||||
import "package:path_provider/path_provider.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
|
||||
280
mobile/apps/photos/lib/services/smart_albums_service.dart
Normal file
280
mobile/apps/photos/lib/services/smart_albums_service.dart
Normal file
@@ -0,0 +1,280 @@
|
||||
import "dart:async";
|
||||
import "dart:convert";
|
||||
|
||||
import "package:flutter/widgets.dart" show BuildContext;
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/api/entity/type.dart";
|
||||
import "package:photos/models/collection/smart_album_config.dart";
|
||||
import "package:photos/models/local_entity_data.dart";
|
||||
import "package:photos/service_locator.dart" show entityService;
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/ui/actions/collection/collection_file_actions.dart";
|
||||
import "package:photos/ui/actions/collection/collection_sharing_actions.dart";
|
||||
import "package:photos/ui/components/action_sheet_widget.dart"
|
||||
show showActionSheet;
|
||||
import "package:photos/ui/components/buttons/button_widget.dart";
|
||||
import "package:photos/ui/components/models/button_type.dart";
|
||||
|
||||
class SmartAlbumsService {
|
||||
SmartAlbumsService._();
|
||||
|
||||
static final SmartAlbumsService instance = SmartAlbumsService._();
|
||||
|
||||
final _logger = Logger((SmartAlbumsService).toString());
|
||||
|
||||
int _lastCacheRefreshTime = 0;
|
||||
|
||||
Future<Map<int, SmartAlbumConfig>>? _cachedConfigsFuture;
|
||||
|
||||
static const type = EntityType.person;
|
||||
|
||||
void clearCache() {
|
||||
_cachedConfigsFuture = null;
|
||||
_lastCacheRefreshTime = 0;
|
||||
}
|
||||
|
||||
Future<void> refreshSmartConfigCache() async {
|
||||
_lastCacheRefreshTime = 0;
|
||||
// wait to ensure cache is refreshed
|
||||
final _ = await getSmartConfigs();
|
||||
}
|
||||
|
||||
int lastRemoteSyncTime() {
|
||||
return entityService.lastSyncTime(type);
|
||||
}
|
||||
|
||||
Future<Map<int, SmartAlbumConfig>> getSmartConfigs() async {
|
||||
final lastRemoteSyncTimeValue = lastRemoteSyncTime();
|
||||
if (_lastCacheRefreshTime != lastRemoteSyncTimeValue) {
|
||||
_lastCacheRefreshTime = lastRemoteSyncTimeValue;
|
||||
_cachedConfigsFuture = null; // Invalidate cache
|
||||
}
|
||||
_cachedConfigsFuture ??= _fetchAndCacheSConfigs();
|
||||
return await _cachedConfigsFuture!;
|
||||
}
|
||||
|
||||
Future<Map<int, SmartAlbumConfig>> _fetchAndCacheSConfigs() async {
|
||||
_logger.finest("reading all smart configs from local db");
|
||||
|
||||
final entities = await entityService.getEntities(type);
|
||||
|
||||
final result = _decodeSConfigEntities(
|
||||
{"entity": entities},
|
||||
);
|
||||
|
||||
final sconfigs = result["sconfigs"] as Map<int, SmartAlbumConfig>;
|
||||
|
||||
final collectionToUpdate = result["collectionToUpdate"] as Set<int>;
|
||||
final idToDelete = result["idToDelete"] as Set<String>;
|
||||
|
||||
// update the merged config to remote db
|
||||
for (final collectionid in collectionToUpdate) {
|
||||
try {
|
||||
await saveConfig(sconfigs[collectionid]!);
|
||||
} catch (error, stackTrace) {
|
||||
_logger.severe(
|
||||
"Failed to update smart album config for collection $collectionid",
|
||||
error,
|
||||
stackTrace,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// delete all remote ids that are merged into the config
|
||||
for (final remoteId in idToDelete) {
|
||||
try {
|
||||
await _deleteEntry(id: remoteId);
|
||||
} catch (error, stackTrace) {
|
||||
_logger.severe(
|
||||
"Failed to delete smart album config for remote id $remoteId",
|
||||
error,
|
||||
stackTrace,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return sconfigs;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _decodeSConfigEntities(
|
||||
Map<String, dynamic> param,
|
||||
) {
|
||||
final entities = (param["entity"] as List<LocalEntityData>);
|
||||
|
||||
final Map<int, SmartAlbumConfig> sconfigs = {};
|
||||
final Set<int> collectionToUpdate = {};
|
||||
final Set<String> idToDelete = {};
|
||||
|
||||
for (final entity in entities) {
|
||||
try {
|
||||
var config = SmartAlbumConfig.fromJson(
|
||||
json.decode(entity.data),
|
||||
entity.id,
|
||||
entity.updatedAt,
|
||||
);
|
||||
|
||||
if (sconfigs.containsKey(config.collectionId)) {
|
||||
final existingConfig = sconfigs[config.collectionId]!;
|
||||
final collectionIdToKeep = config.updatedAt < existingConfig.updatedAt
|
||||
? config.collectionId
|
||||
: existingConfig.collectionId;
|
||||
final remoteIdToDelete = config.updatedAt < existingConfig.updatedAt
|
||||
? existingConfig.remoteId
|
||||
: config.remoteId;
|
||||
|
||||
config = config.merge(sconfigs[config.collectionId]!);
|
||||
|
||||
// Update the config to be updated and deleted list
|
||||
collectionToUpdate.add(collectionIdToKeep);
|
||||
idToDelete.add(remoteIdToDelete!);
|
||||
}
|
||||
|
||||
sconfigs[config.collectionId] = config;
|
||||
} catch (error, stackTrace) {
|
||||
_logger.severe(
|
||||
"Failed to decode smart album config",
|
||||
error,
|
||||
stackTrace,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"sconfigs": sconfigs,
|
||||
"collectionToUpdate": collectionToUpdate,
|
||||
"idToDelete": idToDelete,
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> syncSmartAlbums() async {
|
||||
final cachedConfigs = await _fetchAndCacheSConfigs();
|
||||
|
||||
for (final entry in cachedConfigs.entries) {
|
||||
final collectionId = entry.key;
|
||||
final config = entry.value;
|
||||
|
||||
final infoMap = config.infoMap;
|
||||
|
||||
// Person Id key mapped to updatedAt value
|
||||
final updatedAtMap = await entityService.getUpdatedAts(
|
||||
EntityType.cgroup,
|
||||
config.personIDs.toList(),
|
||||
);
|
||||
|
||||
for (final personId in config.personIDs) {
|
||||
// compares current updateAt with last added file's updatedAt
|
||||
if (updatedAtMap[personId] == null ||
|
||||
infoMap[personId] == null ||
|
||||
(updatedAtMap[personId]! <= infoMap[personId]!.updatedAt)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final toBeSynced = (await SearchService.instance
|
||||
.getClusterFilesForPersonID(personId))
|
||||
.entries
|
||||
.expand((e) => e.value)
|
||||
.toList()
|
||||
..removeWhere(
|
||||
(e) =>
|
||||
e.uploadedFileID == null ||
|
||||
config.infoMap[personId]!.addedFiles.contains(e.uploadedFileID),
|
||||
);
|
||||
|
||||
if (toBeSynced.isNotEmpty) {
|
||||
final CollectionActions collectionActions =
|
||||
CollectionActions(CollectionsService.instance);
|
||||
|
||||
final result = await collectionActions.addToCollection(
|
||||
null,
|
||||
collectionId,
|
||||
false,
|
||||
selectedFiles: toBeSynced,
|
||||
);
|
||||
|
||||
if (result) {
|
||||
final newConfig = await config.addFiles(
|
||||
personId,
|
||||
updatedAtMap[personId]!,
|
||||
toBeSynced.map((e) => e.uploadedFileID!).toSet(),
|
||||
);
|
||||
await saveConfig(newConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveConfig(SmartAlbumConfig config) async {
|
||||
await _addOrUpdateEntity(
|
||||
type,
|
||||
config.toJson(),
|
||||
id: config.remoteId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<SmartAlbumConfig?> getConfig(int collectionId) async {
|
||||
final cachedConfigs = await getSmartConfigs();
|
||||
return cachedConfigs[collectionId];
|
||||
}
|
||||
|
||||
Future<bool> removeFilesDialog(
|
||||
BuildContext context,
|
||||
) async {
|
||||
final completer = Completer<bool>();
|
||||
await showActionSheet(
|
||||
context: context,
|
||||
body: "Should the files related to the person be removed?",
|
||||
buttons: [
|
||||
ButtonWidget(
|
||||
labelText: S.of(context).yes,
|
||||
buttonType: ButtonType.neutral,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.first,
|
||||
shouldSurfaceExecutionStates: true,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
completer.complete(true);
|
||||
},
|
||||
),
|
||||
ButtonWidget(
|
||||
labelText: S.of(context).no,
|
||||
buttonType: ButtonType.secondary,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.cancel,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
completer.complete(false);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
/// Wrapper method for entityService.addOrUpdate that handles cache refresh
|
||||
Future<LocalEntityData> _addOrUpdateEntity(
|
||||
EntityType type,
|
||||
Map<String, dynamic> jsonMap, {
|
||||
String? id,
|
||||
}) async {
|
||||
final result = await entityService.addOrUpdate(
|
||||
type,
|
||||
jsonMap,
|
||||
id: id,
|
||||
);
|
||||
_lastCacheRefreshTime = 0; // Invalidate cache
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<void> _deleteEntry({
|
||||
required String id,
|
||||
}) async {
|
||||
await entityService.deleteEntry(id);
|
||||
_lastCacheRefreshTime = 0; // Invalidate cache
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import 'package:photos/events/trigger_logout_event.dart';
|
||||
import 'package:photos/models/file/file_type.dart';
|
||||
import "package:photos/services/language_service.dart";
|
||||
import 'package:photos/services/notification_service.dart';
|
||||
import "package:photos/services/smart_albums_service.dart";
|
||||
import 'package:photos/services/sync/local_sync_service.dart';
|
||||
import 'package:photos/services/sync/remote_sync_service.dart';
|
||||
import 'package:photos/utils/file_uploader.dart';
|
||||
@@ -199,6 +200,7 @@ class SyncService {
|
||||
if (shouldSync) {
|
||||
await _remoteSyncService.sync();
|
||||
}
|
||||
await SmartAlbumsService.instance.syncSmartAlbums();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import "dart:io";
|
||||
import "package:collection/collection.dart";
|
||||
import "package:dio/dio.dart";
|
||||
import "package:encrypt/encrypt.dart" as enc;
|
||||
import "package:ffmpeg_kit_flutter/ffmpeg_kit.dart";
|
||||
import "package:ffmpeg_kit_flutter/ffmpeg_session.dart";
|
||||
import "package:ffmpeg_kit_flutter/return_code.dart";
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
@@ -31,6 +29,7 @@ import "package:photos/models/preview/preview_item_status.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import "package:photos/services/filedata/model/file_data.dart";
|
||||
import "package:photos/services/isolated_ffmpeg_service.dart";
|
||||
import "package:photos/ui/notification/toast.dart";
|
||||
import "package:photos/utils/exif_util.dart";
|
||||
import "package:photos/utils/file_key.dart";
|
||||
@@ -222,8 +221,6 @@ class VideoPreviewService {
|
||||
'Generating HLS Playlist ${enteFile.displayName} at $prefix/output.m3u8}',
|
||||
);
|
||||
|
||||
FFmpegSession? session;
|
||||
|
||||
final reencodeVideo =
|
||||
!(isH264 && bitrate != null && bitrate <= 4000 * 1000);
|
||||
final rescaleVideo = !(bitrate != null && bitrate <= 2000 * 1000);
|
||||
@@ -270,21 +267,26 @@ class VideoPreviewService {
|
||||
|
||||
_logger.info(command);
|
||||
|
||||
session = await FFmpegKit.execute(
|
||||
final map = await IsolatedFfmpegService.runFfmpeg(
|
||||
// input file path
|
||||
'-i "${file.path}" ' +
|
||||
// main params for streaming
|
||||
command +
|
||||
// output file path
|
||||
'$prefix/output.m3u8',
|
||||
).onError(
|
||||
(error, stackTrace) {
|
||||
_logger.warning("FFmpeg command failed", error, stackTrace);
|
||||
return {};
|
||||
},
|
||||
);
|
||||
|
||||
final returnCode = await session.getReturnCode();
|
||||
final returnCode = map["returnCode"] as int?;
|
||||
|
||||
String? objectId;
|
||||
int? objectSize;
|
||||
|
||||
if (ReturnCode.isSuccess(returnCode)) {
|
||||
if (ReturnCode.success == returnCode) {
|
||||
try {
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.uploading,
|
||||
@@ -303,13 +305,22 @@ class VideoPreviewService {
|
||||
objectSize = result.$2;
|
||||
|
||||
// Fetch resolution of generated stream by decrypting a single frame
|
||||
final FFmpegSession session2 = await FFmpegKit.execute(
|
||||
final map2 = await IsolatedFfmpegService.runFfmpeg(
|
||||
'-allowed_extensions ALL -i "$prefix/output.m3u8" -frames:v 1 -c copy "$prefix/frame.ts"',
|
||||
).onError(
|
||||
(error, stackTrace) {
|
||||
_logger.warning(
|
||||
"FFmpeg command failed for frame",
|
||||
error,
|
||||
stackTrace,
|
||||
);
|
||||
return {};
|
||||
},
|
||||
);
|
||||
final returnCode2 = await session2.getReturnCode();
|
||||
final returnCode2 = map2["returnCode"] as int?;
|
||||
int? width, height;
|
||||
try {
|
||||
if (ReturnCode.isSuccess(returnCode2)) {
|
||||
if (ReturnCode.success == returnCode2) {
|
||||
FFProbeProps? props2;
|
||||
final file2 = File("$prefix/frame.ts");
|
||||
|
||||
@@ -335,11 +346,11 @@ class VideoPreviewService {
|
||||
error = "Failed to upload video preview\nError: $err";
|
||||
_logger.shout("Something went wrong with preview upload", err, sT);
|
||||
}
|
||||
} else if (ReturnCode.isCancel(returnCode)) {
|
||||
} else if (ReturnCode.cancel == returnCode) {
|
||||
_logger.warning("FFmpeg command cancelled");
|
||||
error = "FFmpeg command cancelled";
|
||||
} else {
|
||||
final output = await session.getOutput();
|
||||
final output = map["output"] as String?;
|
||||
_logger.shout(
|
||||
"FFmpeg command failed with return code $returnCode",
|
||||
output ?? "Error not found",
|
||||
@@ -376,7 +387,9 @@ class VideoPreviewService {
|
||||
if (uploadingFileId == enteFile.uploadedFileID!) {
|
||||
uploadingFileId = -1;
|
||||
}
|
||||
_logger.info("[chunk] Processing ${_items.length} items for streaming");
|
||||
_logger.info(
|
||||
"[chunk] Processing ${_items.length} items for streaming, $error",
|
||||
);
|
||||
// process next file
|
||||
if (fileQueue.isNotEmpty) {
|
||||
final entry = fileQueue.entries.first;
|
||||
|
||||
@@ -249,7 +249,11 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
||||
}
|
||||
_recoveryKeyFile.writeAsStringSync(recoveryKey);
|
||||
|
||||
await Share.shareXFiles([XFile(_recoveryKeyFile.path)]);
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
files: [XFile(_recoveryKeyFile.path)],
|
||||
),
|
||||
);
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
|
||||
@@ -83,7 +83,7 @@ class _SessionsPageState extends State<SessionsPage> {
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.8),
|
||||
.withValues(alpha: 0.8),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
@@ -96,7 +96,7 @@ class _SessionsPageState extends State<SessionsPage> {
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.8),
|
||||
.withValues(alpha: 0.8),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -101,8 +101,9 @@ class _TwoFactorRecoveryPageState extends State<TwoFactorRecoveryPage> {
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 12,
|
||||
color:
|
||||
getEnteColorScheme(context).textBase.withOpacity(0.9),
|
||||
color: getEnteColorScheme(context)
|
||||
.textBase
|
||||
.withValues(alpha: 0.9),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -160,14 +160,14 @@ class _TwoFactorSetupPageState extends State<TwoFactorSetupPage>
|
||||
padding: const EdgeInsets.only(left: 10, right: 10),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
color: textColor.withOpacity(0.1),
|
||||
color: textColor.withValues(alpha: 0.1),
|
||||
child: Center(
|
||||
child: Text(
|
||||
widget.secretCode,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
color: textColor.withOpacity(0.7),
|
||||
color: textColor.withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -176,7 +176,7 @@ class _TwoFactorSetupPageState extends State<TwoFactorSetupPage>
|
||||
const Padding(padding: EdgeInsets.all(6)),
|
||||
Text(
|
||||
S.of(context).tapToCopy,
|
||||
style: TextStyle(color: textColor.withOpacity(0.5)),
|
||||
style: TextStyle(color: textColor.withValues(alpha: 0.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -183,14 +183,14 @@ extension CollectionFileActions on CollectionActions {
|
||||
}
|
||||
|
||||
Future<bool> addToCollection(
|
||||
BuildContext context,
|
||||
BuildContext? context,
|
||||
int collectionID,
|
||||
bool showProgressDialog, {
|
||||
List<EnteFile>? selectedFiles,
|
||||
List<SharedMediaFile>? sharedFiles,
|
||||
List<AssetEntity>? picketAssets,
|
||||
}) async {
|
||||
ProgressDialog? dialog = showProgressDialog
|
||||
ProgressDialog? dialog = showProgressDialog && context != null
|
||||
? createProgressDialog(
|
||||
context,
|
||||
S.of(context).uploadingFilesToAlbum,
|
||||
@@ -246,7 +246,7 @@ extension CollectionFileActions on CollectionActions {
|
||||
final Collection? c =
|
||||
CollectionsService.instance.getCollectionByID(collectionID);
|
||||
if (c != null && c.owner.id != currentUserID) {
|
||||
if (!showProgressDialog) {
|
||||
if (!showProgressDialog && context != null) {
|
||||
dialog = createProgressDialog(
|
||||
context,
|
||||
S.of(context).uploadingFilesToAlbum,
|
||||
@@ -291,7 +291,9 @@ extension CollectionFileActions on CollectionActions {
|
||||
} catch (e, s) {
|
||||
logger.severe("Failed to add to album", e, s);
|
||||
await dialog?.hide();
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
if (context != null) {
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,8 +120,8 @@ class AlbumRowItemWidget extends StatelessWidget {
|
||||
child: isSelected
|
||||
? ColorFiltered(
|
||||
colorFilter: ColorFilter.mode(
|
||||
Colors.black.withOpacity(
|
||||
0.4,
|
||||
Colors.black.withValues(
|
||||
alpha: 0.4,
|
||||
),
|
||||
BlendMode.darken,
|
||||
),
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
import "dart:async";
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:photos/db/files_db.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/collection/smart_album_config.dart";
|
||||
import "package:photos/models/selected_people.dart";
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import "package:photos/services/smart_albums_service.dart";
|
||||
import "package:photos/ui/actions/collection/collection_sharing_actions.dart";
|
||||
import "package:photos/ui/components/buttons/button_widget.dart";
|
||||
import "package:photos/ui/components/models/button_type.dart";
|
||||
import 'package:photos/ui/components/title_bar_title_widget.dart';
|
||||
import 'package:photos/ui/components/title_bar_widget.dart';
|
||||
import "package:photos/ui/viewer/search/result/people_section_all_page.dart"
|
||||
show PeopleSectionAllWidget;
|
||||
import "package:photos/utils/dialog_util.dart";
|
||||
|
||||
class SmartAlbumPeople extends StatefulWidget {
|
||||
const SmartAlbumPeople({
|
||||
super.key,
|
||||
required this.collectionId,
|
||||
});
|
||||
|
||||
final int collectionId;
|
||||
|
||||
@override
|
||||
State<SmartAlbumPeople> createState() => _SmartAlbumPeopleState();
|
||||
}
|
||||
|
||||
class _SmartAlbumPeopleState extends State<SmartAlbumPeople> {
|
||||
final _selectedPeople = SelectedPeople();
|
||||
SmartAlbumConfig? currentConfig;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getSelections();
|
||||
}
|
||||
|
||||
Future<void> getSelections() async {
|
||||
currentConfig =
|
||||
await SmartAlbumsService.instance.getConfig(widget.collectionId);
|
||||
|
||||
if (currentConfig != null &&
|
||||
currentConfig!.personIDs.isNotEmpty &&
|
||||
mounted) {
|
||||
_selectedPeople.select(currentConfig!.personIDs);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
bottomNavigationBar: Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
16,
|
||||
8,
|
||||
16,
|
||||
8 + MediaQuery.viewPaddingOf(context).bottom,
|
||||
),
|
||||
child: ListenableBuilder(
|
||||
listenable: _selectedPeople,
|
||||
builder: (context, _) {
|
||||
return ButtonWidget(
|
||||
buttonType: ButtonType.primary,
|
||||
buttonSize: ButtonSize.large,
|
||||
labelText: S.of(context).save,
|
||||
shouldSurfaceExecutionStates: false,
|
||||
onTap: () async {
|
||||
final dialog = createProgressDialog(
|
||||
context,
|
||||
S.of(context).pleaseWait,
|
||||
isDismissible: true,
|
||||
);
|
||||
|
||||
if (_selectedPeople.personIds.length ==
|
||||
currentConfig?.personIDs.length &&
|
||||
_selectedPeople.personIds
|
||||
.toSet()
|
||||
.difference(currentConfig?.personIDs.toSet() ?? {})
|
||||
.isEmpty) {
|
||||
Navigator.pop(context);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await dialog.show();
|
||||
SmartAlbumConfig newConfig;
|
||||
|
||||
if (currentConfig == null) {
|
||||
final infoMap = <String, PersonInfo>{};
|
||||
|
||||
// Add files which are needed
|
||||
for (final personId in _selectedPeople.personIds) {
|
||||
infoMap[personId] = (updatedAt: 0, addedFiles: {});
|
||||
}
|
||||
|
||||
newConfig = SmartAlbumConfig(
|
||||
collectionId: widget.collectionId,
|
||||
personIDs: _selectedPeople.personIds,
|
||||
infoMap: infoMap,
|
||||
);
|
||||
} else {
|
||||
final removedPersonIds = currentConfig!.personIDs
|
||||
.toSet()
|
||||
.difference(_selectedPeople.personIds.toSet())
|
||||
.toList();
|
||||
|
||||
if (removedPersonIds.isNotEmpty) {
|
||||
final toDelete =
|
||||
await SmartAlbumsService.instance.removeFilesDialog(
|
||||
context,
|
||||
);
|
||||
await dialog.show();
|
||||
|
||||
if (toDelete) {
|
||||
for (final personId in removedPersonIds) {
|
||||
final files =
|
||||
currentConfig!.infoMap[personId]?.addedFiles;
|
||||
|
||||
final enteFiles = await FilesDB.instance
|
||||
.getAllFilesGroupByCollectionID(
|
||||
files?.toList() ?? [],
|
||||
);
|
||||
|
||||
final collection = CollectionsService.instance
|
||||
.getCollectionByID(widget.collectionId);
|
||||
|
||||
if (files?.isNotEmpty ?? false) {
|
||||
await CollectionActions(CollectionsService.instance)
|
||||
.moveFilesFromCurrentCollection(
|
||||
context,
|
||||
collection!,
|
||||
enteFiles[widget.collectionId] ?? [],
|
||||
isHidden: collection.isHidden(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newConfig = await currentConfig!.getUpdatedConfig(
|
||||
_selectedPeople.personIds,
|
||||
);
|
||||
}
|
||||
|
||||
await SmartAlbumsService.instance.saveConfig(newConfig);
|
||||
SmartAlbumsService.instance.syncSmartAlbums().ignore();
|
||||
|
||||
await dialog.hide();
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
await dialog.hide();
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
body: CustomScrollView(
|
||||
primary: false,
|
||||
slivers: <Widget>[
|
||||
TitleBarWidget(
|
||||
flexibleSpaceTitle: TitleBarTitleWidget(
|
||||
title: S.of(context).people,
|
||||
),
|
||||
expandedHeight: MediaQuery.textScalerOf(context).scale(120),
|
||||
flexibleSpaceCaption: S.of(context).peopleWidgetDesc,
|
||||
actionIcons: const [],
|
||||
),
|
||||
SliverFillRemaining(
|
||||
child: PeopleSectionAllWidget(
|
||||
selectedPeople: _selectedPeople,
|
||||
namedOnly: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class ArchivedCollectionsButton extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(0),
|
||||
side: BorderSide(
|
||||
width: 0.5,
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.24),
|
||||
color: Theme.of(context).iconTheme.color!.withValues(alpha: 0.24),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
|
||||
@@ -23,7 +23,7 @@ class HiddenCollectionsButtonWidget extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(0),
|
||||
side: BorderSide(
|
||||
width: 0.5,
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.24),
|
||||
color: Theme.of(context).iconTheme.color!.withValues(alpha: 0.24),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
|
||||
@@ -52,7 +52,7 @@ class _TrashSectionButtonState extends State<TrashSectionButton> {
|
||||
padding: const EdgeInsets.all(0),
|
||||
side: BorderSide(
|
||||
width: 0.5,
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.24),
|
||||
color: Theme.of(context).iconTheme.color!.withValues(alpha: 0.24),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user