Merge branch 'main' into video_no_thum
This commit is contained in:
commit
23ff7e4b69
10 changed files with 147 additions and 113 deletions
|
@ -48,6 +48,19 @@ class EmbeddingsDB {
|
|||
return await _isar.embeddings.filter().updationTimeEqualTo(null).findAll();
|
||||
}
|
||||
|
||||
Future<void> deleteEmbeddings(List<int> fileIDs) async {
|
||||
await _isar.writeTxn(() async {
|
||||
final embeddings = <Embedding>[];
|
||||
for (final fileID in fileIDs) {
|
||||
embeddings.addAll(
|
||||
await _isar.embeddings.filter().fileIDEqualTo(fileID).findAll(),
|
||||
);
|
||||
}
|
||||
await _isar.embeddings.deleteAll(embeddings.map((e) => e.id).toList());
|
||||
Bus.instance.fire(EmbeddingUpdatedEvent());
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> deleteAllForModel(Model model) async {
|
||||
await _isar.writeTxn(() async {
|
||||
final embeddings =
|
||||
|
|
|
@ -53,13 +53,22 @@ class EmbeddingStore {
|
|||
final fileMap = await FilesDB.instance
|
||||
.getFilesFromIDs(pendingItems.map((e) => e.fileID).toList());
|
||||
_logger.info("Pushing ${pendingItems.length} embeddings");
|
||||
final deletedEntries = <int>[];
|
||||
for (final item in pendingItems) {
|
||||
try {
|
||||
await _pushEmbedding(fileMap[item.fileID]!, item);
|
||||
final file = fileMap[item.fileID];
|
||||
if (file != null) {
|
||||
await _pushEmbedding(file, item);
|
||||
} else {
|
||||
deletedEntries.add(item.fileID);
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.severe(e, s);
|
||||
}
|
||||
}
|
||||
if (deletedEntries.isNotEmpty) {
|
||||
await EmbeddingsDB.instance.deleteEmbeddings(deletedEntries);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> storeEmbedding(EnteFile file, Embedding embedding) async {
|
||||
|
|
|
@ -243,15 +243,23 @@ class SemanticSearchService {
|
|||
|
||||
final ignoredCollections =
|
||||
CollectionsService.instance.getHiddenCollectionIds();
|
||||
final deletedEntries = <int>[];
|
||||
for (final result in queryResults) {
|
||||
final file = filesMap[result.id];
|
||||
if (file != null && !ignoredCollections.contains(file.collectionID)) {
|
||||
results.add(filesMap[result.id]!);
|
||||
results.add(file);
|
||||
}
|
||||
if (file == null) {
|
||||
deletedEntries.add(result.id);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.info(results.length.toString() + " results");
|
||||
|
||||
if (deletedEntries.isNotEmpty) {
|
||||
unawaited(EmbeddingsDB.instance.deleteEmbeddings(deletedEntries));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
_passwordController.text = _volatilePassword!;
|
||||
Future.delayed(
|
||||
Duration.zero,
|
||||
() => verifyPassword(_volatilePassword!),
|
||||
() => verifyPassword(_volatilePassword!),
|
||||
);
|
||||
}
|
||||
_passwordFocusNode.addListener(() {
|
||||
|
@ -100,69 +100,68 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
}
|
||||
|
||||
Future<void> verifyPassword(String password) async {
|
||||
FocusScope.of(context).unfocus();
|
||||
final dialog =
|
||||
createProgressDialog(context, S.of(context).pleaseWait);
|
||||
await dialog.show();
|
||||
try {
|
||||
final kek = await Configuration.instance.decryptSecretsAndGetKeyEncKey(
|
||||
password,
|
||||
Configuration.instance.getKeyAttributes()!,
|
||||
);
|
||||
_registerSRPForExistingUsers(kek).ignore();
|
||||
} on KeyDerivationError catch (e, s) {
|
||||
_logger.severe("Password verification failed", e, s);
|
||||
await dialog.hide();
|
||||
final dialogChoice = await showChoiceDialog(
|
||||
context,
|
||||
title: S.of(context).recreatePasswordTitle,
|
||||
body: S.of(context).recreatePasswordBody,
|
||||
firstButtonLabel: S.of(context).useRecoveryKey,
|
||||
);
|
||||
if (dialogChoice!.action == ButtonAction.first) {
|
||||
// ignore: unawaited_futures
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return const RecoveryPage();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Password verification failed", e, s);
|
||||
await dialog.hide();
|
||||
final dialogChoice = await showChoiceDialog(
|
||||
context,
|
||||
title: S.of(context).incorrectPasswordTitle,
|
||||
body: S.of(context).pleaseTryAgain,
|
||||
firstButtonLabel: S.of(context).contactSupport,
|
||||
secondButtonLabel: S.of(context).ok,
|
||||
);
|
||||
if (dialogChoice!.action == ButtonAction.first) {
|
||||
await sendLogs(
|
||||
context,
|
||||
S.of(context).contactSupport,
|
||||
"support@ente.io",
|
||||
postShare: () {},
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
FocusScope.of(context).unfocus();
|
||||
final dialog = createProgressDialog(context, S.of(context).pleaseWait);
|
||||
await dialog.show();
|
||||
try {
|
||||
final kek = await Configuration.instance.decryptSecretsAndGetKeyEncKey(
|
||||
password,
|
||||
Configuration.instance.getKeyAttributes()!,
|
||||
);
|
||||
_registerSRPForExistingUsers(kek).ignore();
|
||||
} on KeyDerivationError catch (e, s) {
|
||||
_logger.severe("Password verification failed", e, s);
|
||||
await dialog.hide();
|
||||
Configuration.instance.setVolatilePassword(null);
|
||||
Bus.instance.fire(SubscriptionPurchasedEvent());
|
||||
unawaited(
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
final dialogChoice = await showChoiceDialog(
|
||||
context,
|
||||
title: S.of(context).recreatePasswordTitle,
|
||||
body: S.of(context).recreatePasswordBody,
|
||||
firstButtonLabel: S.of(context).useRecoveryKey,
|
||||
);
|
||||
if (dialogChoice!.action == ButtonAction.first) {
|
||||
// ignore: unawaited_futures
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return const HomeWidget();
|
||||
return const RecoveryPage();
|
||||
},
|
||||
),
|
||||
(route) => false,
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Password verification failed", e, s);
|
||||
await dialog.hide();
|
||||
final dialogChoice = await showChoiceDialog(
|
||||
context,
|
||||
title: S.of(context).incorrectPasswordTitle,
|
||||
body: S.of(context).pleaseTryAgain,
|
||||
firstButtonLabel: S.of(context).contactSupport,
|
||||
secondButtonLabel: S.of(context).ok,
|
||||
);
|
||||
if (dialogChoice!.action == ButtonAction.first) {
|
||||
await sendLogs(
|
||||
context,
|
||||
S.of(context).contactSupport,
|
||||
"support@ente.io",
|
||||
postShare: () {},
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
await dialog.hide();
|
||||
Configuration.instance.setVolatilePassword(null);
|
||||
Bus.instance.fire(SubscriptionPurchasedEvent());
|
||||
unawaited(
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return const HomeWidget();
|
||||
},
|
||||
),
|
||||
(route) => false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _registerSRPForExistingUsers(Uint8List key) async {
|
||||
|
@ -266,8 +265,8 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
|
@ -280,17 +279,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
),
|
||||
);
|
||||
},
|
||||
child: Center(
|
||||
child: Text(
|
||||
S.of(context).forgotPassword,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).forgotPassword,
|
||||
style:
|
||||
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
|
@ -306,17 +301,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
Navigator.of(context)
|
||||
.popUntil((route) => route.isFirst);
|
||||
},
|
||||
child: Center(
|
||||
child: Text(
|
||||
S.of(context).changeEmail,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).changeEmail,
|
||||
style:
|
||||
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -60,7 +60,6 @@ class DynamicFAB extends StatelessWidget {
|
|||
} else {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: OutlinedButton(
|
||||
onPressed:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/services/feature_flag_service.dart';
|
||||
import 'package:photos/services/update_service.dart';
|
||||
import "package:photos/ui/payment/store_subscription_page.dart";
|
||||
|
@ -9,18 +8,9 @@ StatefulWidget getSubscriptionPage({bool isOnBoarding = false}) {
|
|||
if (UpdateService.instance.isIndependentFlavor()) {
|
||||
return StripeSubscriptionPage(isOnboarding: isOnBoarding);
|
||||
}
|
||||
if (FeatureFlagService.instance.enableStripe() &&
|
||||
_isUserCreatedPostStripeSupport()) {
|
||||
if (FeatureFlagService.instance.enableStripe()) {
|
||||
return StripeSubscriptionPage(isOnboarding: isOnBoarding);
|
||||
} else {
|
||||
return StoreSubscriptionPage(isOnboarding: isOnBoarding);
|
||||
}
|
||||
}
|
||||
|
||||
// return true if the user was created after we added support for stripe payment
|
||||
// on frame. We do this check to avoid showing Stripe payment option for earlier
|
||||
// users who might have paid via playStore. This method should be removed once
|
||||
// we have better handling for active play/app store subscription & stripe plans.
|
||||
bool _isUserCreatedPostStripeSupport() {
|
||||
return Configuration.instance.getUserID()! > 1580559962386460;
|
||||
}
|
||||
|
|
|
@ -36,13 +36,9 @@ class _SubscriptionHeaderWidgetState extends State<SubscriptionHeaderWidget> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).selectYourPlan,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
],
|
||||
Text(
|
||||
S.of(context).selectYourPlan,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
|
|
|
@ -252,10 +252,14 @@ class _VideoWidgetNewState extends State<VideoWidgetNew>
|
|||
|
||||
void _setVideoController(String url) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
player.setPlaylistMode(PlaylistMode.single);
|
||||
controller = VideoController(player);
|
||||
player.open(Media(url), play: _isAppInFG);
|
||||
setState(() async {
|
||||
try {
|
||||
await player.setPlaylistMode(PlaylistMode.single);
|
||||
controller = VideoController(player);
|
||||
await player.open(Media(url), play: _isAppInFG);
|
||||
} catch (e, s) {
|
||||
_logger.severe("failed to set video url", e, s);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,7 +129,6 @@ class SearchWidgetState extends State<SearchWidget> {
|
|||
child: Container(
|
||||
color: colorScheme.backgroundBase,
|
||||
child: Container(
|
||||
height: 44,
|
||||
color: colorScheme.fillFaint,
|
||||
child: TextFormField(
|
||||
controller: textController,
|
||||
|
|
|
@ -35,6 +35,7 @@ import "package:photos/services/user_service.dart";
|
|||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/file_download_util.dart';
|
||||
import 'package:photos/utils/file_uploader_util.dart';
|
||||
import "package:photos/utils/file_util.dart";
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import "package:uuid/uuid.dart";
|
||||
|
@ -309,12 +310,36 @@ class FileUploader {
|
|||
return file.path.contains(kUploadTempPrefix) &&
|
||||
file.path.contains(".encrypted");
|
||||
});
|
||||
if (filesToDelete.isEmpty) {
|
||||
return;
|
||||
if (filesToDelete.isNotEmpty) {
|
||||
_logger.info('cleaning up state files ${filesToDelete.length}');
|
||||
for (final file in filesToDelete) {
|
||||
await file.delete();
|
||||
}
|
||||
}
|
||||
_logger.info('cleaning up state files ${filesToDelete.length}');
|
||||
for (final file in filesToDelete) {
|
||||
await file.delete();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
final sharedMediaDir =
|
||||
Configuration.instance.getSharedMediaDirectory() + "/";
|
||||
final sharedFiles = await Directory(sharedMediaDir).list().toList();
|
||||
if (sharedFiles.isNotEmpty) {
|
||||
_logger.info('Shared media directory cleanup ${sharedFiles.length}');
|
||||
final int ownerID = Configuration.instance.getUserID()!;
|
||||
final existingLocalFileIDs =
|
||||
await FilesDB.instance.getExistingLocalFileIDs(ownerID);
|
||||
final Set<String> trackedSharedFilePaths = {};
|
||||
for (String localID in existingLocalFileIDs) {
|
||||
if (localID.contains(sharedMediaIdentifier)) {
|
||||
trackedSharedFilePaths
|
||||
.add(getSharedMediaPathFromLocalID(localID));
|
||||
}
|
||||
}
|
||||
for (final file in sharedFiles) {
|
||||
if (!trackedSharedFilePaths.contains(file.path)) {
|
||||
_logger.info('Deleting stale shared media file ${file.path}');
|
||||
await file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.severe("Failed to remove stale files", e, s);
|
||||
|
|
Loading…
Add table
Reference in a new issue