Compare commits

..

1 Commits

Author SHA1 Message Date
Alex
9996a1ec3e fix: prevent database deadlock (eps 2) 2025-09-08 21:10:51 -05:00
20 changed files with 66 additions and 353 deletions

View File

@@ -39,7 +39,7 @@ class ImmichTestHelper {
static Future<void> loadApp(WidgetTester tester) async {
await EasyLocalization.ensureInitialized();
// Clear all data from Isar (reuse existing instance if available)
final (isar, drift, logDb) = await Bootstrap.initDB();
final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true);
await Bootstrap.initDomain(isar, drift, logDb);
await Store.clear();
await isar.writeTxn(() => isar.clear());

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -27,11 +27,9 @@
FAC6F89B2D287C890078CB2F /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = FAC6F8902D287C890078CB2F /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
FAC6F8B72D287F120078CB2F /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC6F8B52D287F120078CB2F /* ShareViewController.swift */; };
FAC6F8B92D287F120078CB2F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FAC6F8B32D287F120078CB2F /* MainInterface.storyboard */; };
FE619FDB2E6F5D0600D0B708 /* NativeImageViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE619FD52E6F5D0600D0B708 /* NativeImageViewFactory.swift */; };
FE619FDC2E6F5D0600D0B708 /* Thumbhash.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE619FD72E6F5D0600D0B708 /* Thumbhash.swift */; };
FE619FDD2E6F5D0600D0B708 /* Thumbnails.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE619FD82E6F5D0600D0B708 /* Thumbnails.g.swift */; };
FE619FDE2E6F5D0600D0B708 /* ThumbnailsImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE619FD92E6F5D0600D0B708 /* ThumbnailsImpl.swift */; };
FE619FDF2E6F5D0600D0B708 /* NativeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE619FD42E6F5D0600D0B708 /* NativeImageView.swift */; };
FEAFA8732E4D42F4001E47FE /* Thumbhash.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAFA8722E4D42F4001E47FE /* Thumbhash.swift */; };
FED3B1962E253E9B0030FD97 /* ThumbnailsImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */; };
FED3B1972E253E9B0030FD97 /* Thumbnails.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -113,11 +111,9 @@
FAC6F8B42D287F120078CB2F /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
FAC6F8B52D287F120078CB2F /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = "<group>"; };
FE619FD42E6F5D0600D0B708 /* NativeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeImageView.swift; sourceTree = "<group>"; };
FE619FD52E6F5D0600D0B708 /* NativeImageViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeImageViewFactory.swift; sourceTree = "<group>"; };
FE619FD72E6F5D0600D0B708 /* Thumbhash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbhash.swift; sourceTree = "<group>"; };
FE619FD82E6F5D0600D0B708 /* Thumbnails.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbnails.g.swift; sourceTree = "<group>"; };
FE619FD92E6F5D0600D0B708 /* ThumbnailsImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailsImpl.swift; sourceTree = "<group>"; };
FEAFA8722E4D42F4001E47FE /* Thumbhash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbhash.swift; sourceTree = "<group>"; };
FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbnails.g.swift; sourceTree = "<group>"; };
FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailsImpl.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
@@ -133,6 +129,8 @@
/* Begin PBXFileSystemSynchronizedRootGroup section */
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = Sync;
sourceTree = "<group>";
};
@@ -245,7 +243,6 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
FE619FDA2E6F5D0600D0B708 /* Images */,
B21E34A62E5AF9760031FDB9 /* Background */,
B2CF7F8C2DDE4EBB00744BF6 /* Sync */,
FA9973382CF6DF4B000EF859 /* Runner.entitlements */,
@@ -259,6 +256,7 @@
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
FED3B1952E253E9B0030FD97 /* Images */,
);
path = Runner;
sourceTree = "<group>";
@@ -284,22 +282,12 @@
path = ShareExtension;
sourceTree = "<group>";
};
FE619FD62E6F5D0600D0B708 /* Viewer */ = {
FED3B1952E253E9B0030FD97 /* Images */ = {
isa = PBXGroup;
children = (
FE619FD42E6F5D0600D0B708 /* NativeImageView.swift */,
FE619FD52E6F5D0600D0B708 /* NativeImageViewFactory.swift */,
);
path = Viewer;
sourceTree = "<group>";
};
FE619FDA2E6F5D0600D0B708 /* Images */ = {
isa = PBXGroup;
children = (
FE619FD62E6F5D0600D0B708 /* Viewer */,
FE619FD72E6F5D0600D0B708 /* Thumbhash.swift */,
FE619FD82E6F5D0600D0B708 /* Thumbnails.g.swift */,
FE619FD92E6F5D0600D0B708 /* ThumbnailsImpl.swift */,
FEAFA8722E4D42F4001E47FE /* Thumbhash.swift */,
FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */,
FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */,
);
path = Images;
sourceTree = "<group>";
@@ -570,14 +558,12 @@
65F32F31299BD2F800CE9261 /* BackgroundServicePlugin.swift in Sources */,
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
B21E34AC2E5B09190031FDB9 /* BackgroundWorker.swift in Sources */,
FEAFA8732E4D42F4001E47FE /* Thumbhash.swift in Sources */,
FED3B1962E253E9B0030FD97 /* ThumbnailsImpl.swift in Sources */,
B21E34AA2E5AFD2B0031FDB9 /* BackgroundWorkerApiImpl.swift in Sources */,
FED3B1972E253E9B0030FD97 /* Thumbnails.g.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */,
FE619FDB2E6F5D0600D0B708 /* NativeImageViewFactory.swift in Sources */,
FE619FDC2E6F5D0600D0B708 /* Thumbhash.swift in Sources */,
FE619FDD2E6F5D0600D0B708 /* Thumbnails.g.swift in Sources */,
FE619FDE2E6F5D0600D0B708 /* ThumbnailsImpl.swift in Sources */,
FE619FDF2E6F5D0600D0B708 /* NativeImageView.swift in Sources */,
65F32F33299D349D00CE9261 /* BackgroundSyncWorker.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -15,7 +15,7 @@ import UIKit
) -> Bool {
// Required for flutter_local_notification
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
GeneratedPluginRegistrant.register(with: self)
@@ -47,9 +47,6 @@ import UIKit
FPPNetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "org.cocoapods.network-info-plus")!)
}
}
let factory = NativeImageViewFactory(messenger: controller.binaryMessenger)
registrar(forPlugin: "NativeImageView")!.register(factory, withId: NativeImageViewFactory.id)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

View File

@@ -193,12 +193,12 @@ class ThumbnailApiImpl: ThumbnailApi {
}
}
static func requestAsset(assetId: String) -> PHAsset? {
private static func requestAsset(assetId: String) -> PHAsset? {
var asset: PHAsset?
assetQueue.sync { asset = assetCache.object(forKey: assetId as NSString) }
if asset != nil { return asset }
guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: fetchOptions).firstObject
guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: Self.fetchOptions).firstObject
else { return nil }
assetQueue.async { assetCache.setObject(asset, forKey: assetId as NSString) }
return asset

View File

@@ -1,219 +0,0 @@
import Flutter
import UIKit
import Photos
// TODO: the bounds this uses for scaling can change with the hero animation,
// so it doesn't display the image correctly until swiping to the next asset and back
class NativeImageView: NSObject, FlutterPlatformView {
private var _containerView: UIView
private var _scrollView: UIScrollView
private var _imageView: UIImageView
private var _image: UIImage?
private var _hasSetupZoom = false
private static let imageManager = PHImageManager.default()
private static let fetchOptions = {
let fetchOptions = PHFetchOptions()
fetchOptions.fetchLimit = 1
fetchOptions.wantsIncrementalChangeDetails = false
return fetchOptions
}()
private static let requestOptions = {
let requestOptions = PHImageRequestOptions()
requestOptions.isNetworkAccessAllowed = true
requestOptions.deliveryMode = .opportunistic
requestOptions.resizeMode = .none
requestOptions.isSynchronous = false
requestOptions.version = .current
return requestOptions
}()
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger
) {
_containerView = UIView(frame: frame)
_scrollView = UIScrollView()
_imageView = UIImageView()
super.init()
setupViews()
guard let arguments = args as? [String: Any],
let assetId = arguments["assetId"] as? String else {
print("Asset ID not provided")
return
}
guard let asset = ThumbnailApiImpl.requestAsset(assetId: assetId) else {
print("Asset not found for identifier: \(assetId)")
return
}
loadImage(from: asset)
}
func view() -> UIView {
return _containerView
}
private func setupViews() {
// Configure image view
_imageView.contentMode = .scaleAspectFit
_imageView.preferredImageDynamicRange = .high
if #available(iOS 17.0, *) {
_imageView.layer.wantsExtendedDynamicRangeContent = true
_imageView.layer.contentsFormat = .RGBA16Float
}
// Configure scroll view
_scrollView.delegate = self
_scrollView.showsVerticalScrollIndicator = false
_scrollView.showsHorizontalScrollIndicator = false
_scrollView.bouncesZoom = true
_scrollView.decelerationRate = .fast
_scrollView.contentInsetAdjustmentBehavior = .never
_scrollView.backgroundColor = .clear
// Add double tap gesture
let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(_:)))
doubleTapGesture.numberOfTapsRequired = 2
_scrollView.addGestureRecognizer(doubleTapGesture)
// Setup view hierarchy
_scrollView.addSubview(_imageView)
_containerView.addSubview(_scrollView)
// Setup constraints
_scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
_scrollView.topAnchor.constraint(equalTo: _containerView.topAnchor),
_scrollView.leadingAnchor.constraint(equalTo: _containerView.leadingAnchor),
_scrollView.trailingAnchor.constraint(equalTo: _containerView.trailingAnchor),
_scrollView.bottomAnchor.constraint(equalTo: _containerView.bottomAnchor)
])
// Observe bounds changes
_containerView.addObserver(self, forKeyPath: "bounds", options: [.new], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "bounds", let image = _image {
DispatchQueue.main.async { [weak self] in
self?.setupZoomScale(for: image)
}
}
}
deinit {
_containerView.removeObserver(self, forKeyPath: "bounds")
}
private func loadImage(from asset: PHAsset) {
Self.imageManager.requestImageDataAndOrientation(
for: asset,
options: Self.requestOptions
) { [weak self] data, uti, orientation, info in
guard let data = data else { return }
if #available(iOS 17.0, *) {
var config = UIImageReader.Configuration()
config.prefersHighDynamicRange = true
let imageReader = UIImageReader(configuration: config)
guard let image = imageReader.image(data: data) else { return }
DispatchQueue.main.async {
self?.setImage(image)
}
} else {
guard let image = UIImage(data: data) else { return }
DispatchQueue.main.async {
self?.setImage(image)
}
}
}
}
private func setImage(_ image: UIImage) {
_image = image
_imageView.image = image
// Wait for next run loop to ensure layout is complete
DispatchQueue.main.async { [weak self] in
self?.setupZoomScale(for: image)
}
}
private func setupZoomScale(for image: UIImage) {
guard _scrollView.bounds.size.width > 0, _scrollView.bounds.size.height > 0 else {
// View not laid out yet
return
}
// Set image view size to match image
_imageView.frame = CGRect(origin: .zero, size: image.size)
_scrollView.contentSize = image.size
// Calculate zoom scales
let scrollViewSize = _scrollView.bounds.size
let widthScale = scrollViewSize.width / image.size.width
let heightScale = scrollViewSize.height / image.size.height
let minScale = min(widthScale, heightScale)
_scrollView.minimumZoomScale = minScale
_scrollView.maximumZoomScale = max(2.0, minScale * 5.0)
_scrollView.zoomScale = minScale
centerImageView()
_hasSetupZoom = true
}
private func centerImageView() {
let scrollViewSize = _scrollView.bounds.size
let imageViewSize = _imageView.frame.size
let horizontalInset = max(0, (scrollViewSize.width - imageViewSize.width) / 2)
let verticalInset = max(0, (scrollViewSize.height - imageViewSize.height) / 2)
_scrollView.contentInset = UIEdgeInsets(
top: verticalInset,
left: horizontalInset,
bottom: verticalInset,
right: horizontalInset
)
}
@objc private func handleDoubleTap(_ gesture: UITapGestureRecognizer) {
guard _hasSetupZoom else { return }
if _scrollView.zoomScale > _scrollView.minimumZoomScale {
_scrollView.setZoomScale(_scrollView.minimumZoomScale, animated: true)
} else {
let tapLocation = gesture.location(in: _imageView)
let zoomScale = min(_scrollView.maximumZoomScale, _scrollView.minimumZoomScale * 3.0)
let zoomRect = zoomRectForScale(zoomScale, center: tapLocation)
_scrollView.zoom(to: zoomRect, animated: true)
}
}
private func zoomRectForScale(_ scale: CGFloat, center: CGPoint) -> CGRect {
var zoomRect = CGRect.zero
zoomRect.size.width = _scrollView.frame.size.width / scale
zoomRect.size.height = _scrollView.frame.size.height / scale
zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0)
zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0)
return zoomRect
}
}
// MARK: - UIScrollViewDelegate
extension NativeImageView: UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return _imageView
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
centerImageView()
}
}

View File

@@ -1,29 +0,0 @@
import Foundation
import Flutter
public class NativeImageViewFactory: NSObject, FlutterPlatformViewFactory {
public static let id = "native_image_view"
private let messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
}
public func create(
withFrame frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?
) -> FlutterPlatformView {
NativeImageView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger
)
}
public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}

View File

@@ -246,7 +246,7 @@ Future<void> backgroundSyncNativeEntrypoint() async {
WidgetsFlutterBinding.ensureInitialized();
DartPluginRegistrant.ensureInitialized();
final (isar, drift, logDB) = await Bootstrap.initDB();
final (isar, drift, logDB) = await Bootstrap.initDB(shareAcrossIsolates: false);
await Bootstrap.initDomain(isar, drift, logDB, shouldBufferLogs: false);
await BackgroundWorkerBgService(isar: isar, drift: drift, driftLogger: logDB).init();
}

View File

@@ -66,8 +66,14 @@ class IsarDatabaseRepository implements IDatabaseRepository {
include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'},
)
class Drift extends $Drift implements IDatabaseRepository {
Drift([QueryExecutor? executor])
: super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true)));
Drift({QueryExecutor? executor, bool shareAcrossIsolates = true})
: super(
executor ??
driftDatabase(
name: 'immich',
native: DriftNativeOptions(shareAcrossIsolates: shareAcrossIsolates),
),
);
@override
int get schemaVersion => 10;

View File

@@ -7,9 +7,13 @@ import 'logger_db.repository.drift.dart';
@DriftDatabase(tables: [LogMessageEntity])
class DriftLogger extends $DriftLogger implements IDatabaseRepository {
DriftLogger([QueryExecutor? executor])
DriftLogger({QueryExecutor? executor, bool shareAcrossIsolates = true})
: super(
executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)),
executor ??
driftDatabase(
name: 'immich_logs',
native: DriftNativeOptions(shareAcrossIsolates: shareAcrossIsolates),
),
);
@override

View File

@@ -42,7 +42,7 @@ import 'package:worker_manager/worker_manager.dart';
void main() async {
ImmichWidgetsBinding();
final (isar, drift, logDb) = await Bootstrap.initDB();
final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true);
await Bootstrap.initDomain(isar, drift, logDb);
await initApp();
// Warm-up isolate pool for worker manager

View File

@@ -19,7 +19,7 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet.wid
import 'package:immich_mobile/presentation/widgets/asset_viewer/top_app_bar.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/native_image.widget.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
@@ -533,21 +533,24 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
}
PhotoViewGalleryPageOptions _imageBuilder(BuildContext ctx, BaseAsset asset) {
return PhotoViewGalleryPageOptions.customChild(
disableScaleGestures: true,
final size = ctx.sizeData;
return PhotoViewGalleryPageOptions(
key: ValueKey(asset.heroTag),
imageProvider: getFullImageProvider(asset, size: size),
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
filterQuality: FilterQuality.high,
tightMode: true,
disableScaleGestures: showingBottomSheet,
onDragStart: _onDragStart,
onDragUpdate: _onDragUpdate,
onDragEnd: _onDragEnd,
onTapDown: _onTapDown,
onLongPressStart: asset.isMotionPhoto ? _onLongPress : null,
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
filterQuality: FilterQuality.high,
basePosition: Alignment.center,
child: NativeImageView(
key: _getVideoPlayerKey(asset.heroTag),
assetId: (asset as LocalAsset).id,
width: ctx.width,
height: ctx.height,
errorBuilder: (_, __, ___) => Container(
width: size.width,
height: size.height,
color: backgroundColor,
child: Thumbnail.fromAsset(asset: asset, fit: BoxFit.contain),
),
);
}

View File

@@ -1,40 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class NativeImageView extends StatelessWidget {
final String assetId;
final double width;
final double height;
const NativeImageView({super.key, required this.assetId, required this.width, required this.height});
@override
Widget build(BuildContext context) {
if (defaultTargetPlatform != TargetPlatform.iOS) {
return Container(
width: width,
height: height,
color: Colors.grey,
child: const Center(child: Text('PHAsset view only available on iOS')),
);
}
return SizedBox(
width: width,
height: height,
child: UiKitView(
viewType: 'native_image_view',
layoutDirection: TextDirection.ltr,
creationParams: {'assetId': assetId},
creationParamsCodec: const StandardMessageCodec(),
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(() => VerticalDragGestureRecognizer()),
Factory<HorizontalDragGestureRecognizer>(() => HorizontalDragGestureRecognizer()),
Factory<ScaleGestureRecognizer>(() => ScaleGestureRecognizer()),
},
),
);
}
}

View File

@@ -330,7 +330,7 @@ class BackgroundService {
}
Future<bool> _onAssetsChanged() async {
final (isar, drift, logDb) = await Bootstrap.initDB();
final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: false);
await Bootstrap.initDomain(isar, drift, logDb);
final ref = ProviderContainer(

View File

@@ -115,7 +115,7 @@ class BackupVerificationService {
assert(tuple.deleteCandidates.length == tuple.originals.length);
final List<Asset> result = [];
BackgroundIsolateBinaryMessenger.ensureInitialized(tuple.rootIsolateToken);
final (isar, drift, logDb) = await Bootstrap.initDB();
final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true);
await Bootstrap.initDomain(isar, drift, logDb);
await tuple.fileMediaRepository.enableBackgroundAccess();
final ApiService apiService = ApiService();

View File

@@ -56,9 +56,9 @@ void configureFileDownloaderNotifications() {
}
abstract final class Bootstrap {
static Future<(Isar isar, Drift drift, DriftLogger logDb)> initDB() async {
final drift = Drift();
final logDb = DriftLogger();
static Future<(Isar isar, Drift drift, DriftLogger logDb)> initDB({required bool shareAcrossIsolates}) async {
final drift = Drift(shareAcrossIsolates: shareAcrossIsolates);
final logDb = DriftLogger(shareAcrossIsolates: shareAcrossIsolates);
Isar? isar = Isar.getInstance();

View File

@@ -34,7 +34,7 @@ Cancelable<T?> runInIsolateGentle<T>({
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
DartPluginRegistrant.ensureInitialized();
final (isar, drift, logDb) = await Bootstrap.initDB();
final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true);
await Bootstrap.initDomain(isar, drift, logDb, shouldBufferLogs: false);
final ref = ProviderContainer(
overrides: [

View File

@@ -297,7 +297,9 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with
Widget build(BuildContext context) {
final syncStatus = ref.watch(syncStatusProvider);
final isSyncing = syncStatus.isRemoteSyncing || syncStatus.isLocalSyncing;
print(
"SyncStatusIndicator build - syncStatus.isRemoteSyncing: ${syncStatus.isRemoteSyncing} , isLocalSyncing: ${syncStatus.isLocalSyncing}, isHashing: ${syncStatus.isHashing}",
);
// Control animations based on sync status
if (isSyncing) {
if (!_rotationController.isAnimating) {

View File

@@ -27,7 +27,7 @@ void main() {
for (final toVersion in versions.skip(i + 1)) {
test('to $toVersion', () async {
final schema = await verifier.schemaAt(fromVersion);
final db = Drift(schema.newConnection());
final db = Drift(executor: schema.newConnection());
await verifier.migrateAndValidate(db, toVersion);
await db.close();
});

View File

@@ -12,7 +12,7 @@ void main() {
late MediumFactory mediumFactory;
setUp(() {
db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
db = Drift(executor: DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
mediumFactory = MediumFactory(db);
});

View File

@@ -62,7 +62,10 @@ void main() {
);
setUpAll(() async {
final loggerDb = DriftLogger(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
final loggerDb = DriftLogger(
executor: DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true),
shareAcrossIsolates: true,
);
final LogRepository logRepository = LogRepository(loggerDb);
WidgetsFlutterBinding.ensureInitialized();