9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
200 lines
6.0 KiB
Dart
200 lines
6.0 KiB
Dart
part of '../compose_bar.dart';
|
|
|
|
const _nativeAttachmentPopoverChannel = MethodChannel(
|
|
'buzz/native_attachment_popover',
|
|
);
|
|
|
|
final _iosAttachmentPopoverCoordinator = _IOSAttachmentPopoverCoordinator(
|
|
_nativeAttachmentPopoverChannel,
|
|
);
|
|
|
|
class _IOSAttachmentPopoverCallbacks {
|
|
final Future<void> Function(XFile image) onCapture;
|
|
final Future<void> Function(List<XFile> photos) onChoosePhotos;
|
|
final VoidCallback onAllPhotos;
|
|
final VoidCallback onVideo;
|
|
final VoidCallback onFiles;
|
|
|
|
const _IOSAttachmentPopoverCallbacks({
|
|
required this.onCapture,
|
|
required this.onChoosePhotos,
|
|
required this.onAllPhotos,
|
|
required this.onVideo,
|
|
required this.onFiles,
|
|
});
|
|
}
|
|
|
|
class _IOSAttachmentPopoverCoordinator {
|
|
static final Object _cancelledSupportCheck = Object();
|
|
|
|
final MethodChannel _channel;
|
|
|
|
Object? _activeOwner;
|
|
_IOSAttachmentPopoverCallbacks? _callbacks;
|
|
Completer<void>? _pendingSupportCancellation;
|
|
bool _didPresent = false;
|
|
bool _handlerInstalled = false;
|
|
|
|
_IOSAttachmentPopoverCoordinator(this._channel);
|
|
|
|
Future<bool> present({
|
|
required Object owner,
|
|
required BuildContext sourceContext,
|
|
required Future<void> Function(XFile image) onCapture,
|
|
required Future<void> Function(List<XFile> photos) onChoosePhotos,
|
|
required VoidCallback onAllPhotos,
|
|
required VoidCallback onVideo,
|
|
required VoidCallback onFiles,
|
|
}) async {
|
|
if (defaultTargetPlatform != TargetPlatform.iOS) return false;
|
|
if (_activeOwner case final activeOwner?) {
|
|
if (identical(activeOwner, owner)) return true;
|
|
if (!_didPresent) _clearOwner(activeOwner);
|
|
return _didPresent;
|
|
}
|
|
|
|
final supportCancellation = Completer<void>();
|
|
_activeOwner = owner;
|
|
_pendingSupportCancellation = supportCancellation;
|
|
_callbacks = _IOSAttachmentPopoverCallbacks(
|
|
onCapture: onCapture,
|
|
onChoosePhotos: onChoosePhotos,
|
|
onAllPhotos: onAllPhotos,
|
|
onVideo: onVideo,
|
|
onFiles: onFiles,
|
|
);
|
|
_ensureHandler();
|
|
|
|
try {
|
|
final supportResult = await Future.any<Object?>([
|
|
_channel.invokeMethod<bool>('isSupported'),
|
|
supportCancellation.future.then<Object?>((_) => _cancelledSupportCheck),
|
|
]);
|
|
if (identical(supportResult, _cancelledSupportCheck)) return true;
|
|
if (identical(_pendingSupportCancellation, supportCancellation)) {
|
|
_pendingSupportCancellation = null;
|
|
}
|
|
if (!identical(_activeOwner, owner)) return false;
|
|
final supported = supportResult == true;
|
|
if (!supported || !sourceContext.mounted) {
|
|
_clearOwner(owner);
|
|
return false;
|
|
}
|
|
|
|
final renderObject = sourceContext.findRenderObject();
|
|
if (renderObject is! RenderBox || !renderObject.hasSize) {
|
|
_clearOwner(owner);
|
|
return false;
|
|
}
|
|
final origin = renderObject.localToGlobal(Offset.zero);
|
|
|
|
_didPresent = true;
|
|
final didPresent =
|
|
await _channel.invokeMethod<bool>('present', {
|
|
'x': origin.dx,
|
|
'y': origin.dy,
|
|
'width': renderObject.size.width,
|
|
'height': renderObject.size.height,
|
|
}) ??
|
|
false;
|
|
if (!identical(_activeOwner, owner)) return false;
|
|
if (!didPresent) _clearOwner(owner);
|
|
return didPresent;
|
|
} on PlatformException {
|
|
_clearOwner(owner);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<void> disposeOwner(Object owner) async {
|
|
if (!identical(_activeOwner, owner)) return;
|
|
|
|
_callbacks = null;
|
|
if (!_didPresent) {
|
|
_clearOwner(owner);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await _channel.invokeMethod<void>('dismiss');
|
|
} on PlatformException {
|
|
// The native bridge is unavailable, so there is nothing left to dismiss.
|
|
} on MissingPluginException {
|
|
// The native bridge is unavailable, so there is nothing left to dismiss.
|
|
} finally {
|
|
_clearOwner(owner);
|
|
}
|
|
}
|
|
|
|
void _ensureHandler() {
|
|
if (_handlerInstalled) return;
|
|
_handlerInstalled = true;
|
|
_channel.setMethodCallHandler(_handleMethodCall);
|
|
}
|
|
|
|
Future<void> _handleMethodCall(MethodCall call) async {
|
|
final callbacks = _callbacks;
|
|
switch (call.method) {
|
|
case 'cameraCaptured':
|
|
if (call.arguments case final String path) {
|
|
final activeCallbacks = callbacks;
|
|
if (activeCallbacks != null) {
|
|
await processCapturedImage(XFile(path), activeCallbacks.onCapture);
|
|
}
|
|
}
|
|
case 'photosSelected':
|
|
final paths = (call.arguments as List<Object?>? ?? const [])
|
|
.whereType<String>()
|
|
.toList();
|
|
if (callbacks != null && paths.isNotEmpty) {
|
|
await processTemporaryImages([
|
|
for (final path in paths) XFile(path),
|
|
], callbacks.onChoosePhotos);
|
|
}
|
|
case 'pickAllPhotos':
|
|
callbacks?.onAllPhotos();
|
|
case 'pickVideo':
|
|
callbacks?.onVideo();
|
|
case 'pickFiles':
|
|
callbacks?.onFiles();
|
|
case 'dismissed':
|
|
_activeOwner = null;
|
|
_callbacks = null;
|
|
_didPresent = false;
|
|
}
|
|
}
|
|
|
|
void _clearOwner(Object owner) {
|
|
if (!identical(_activeOwner, owner)) return;
|
|
final supportCancellation = _pendingSupportCancellation;
|
|
_pendingSupportCancellation = null;
|
|
if (supportCancellation != null && !supportCancellation.isCompleted) {
|
|
supportCancellation.complete();
|
|
}
|
|
_activeOwner = null;
|
|
_callbacks = null;
|
|
_didPresent = false;
|
|
}
|
|
}
|
|
|
|
class _IOSAttachmentPopoverController {
|
|
Future<bool> present({
|
|
required BuildContext sourceContext,
|
|
required Future<void> Function(XFile image) onCapture,
|
|
required Future<void> Function(List<XFile> photos) onChoosePhotos,
|
|
required VoidCallback onAllPhotos,
|
|
required VoidCallback onVideo,
|
|
required VoidCallback onFiles,
|
|
}) => _iosAttachmentPopoverCoordinator.present(
|
|
owner: this,
|
|
sourceContext: sourceContext,
|
|
onCapture: onCapture,
|
|
onChoosePhotos: onChoosePhotos,
|
|
onAllPhotos: onAllPhotos,
|
|
onVideo: onVideo,
|
|
onFiles: onFiles,
|
|
);
|
|
|
|
Future<void> dispose() => _iosAttachmentPopoverCoordinator.disposeOwner(this);
|
|
}
|