import 'dart:async'; import 'dart:io' show Platform; import 'package:flutter/services.dart'; import '../../backend/api.dart'; import '../../frontend/screens/chats/chat_list_screen.dart'; import '../../frontend/widgets/swipe_route.dart'; import '../../main.dart'; import '../../models/shared_payload.dart'; import '../utils/logger.dart'; import 'shared_file_uploads.dart'; import 'share_inbox.dart'; class ShareIntentBridge { ShareIntentBridge._(); static final ShareIntentBridge instance = ShareIntentBridge._(); static const _method = MethodChannel('ru.ikar.app/share'); static const _events = EventChannel('ru.ikar.app/share_events'); static const _retryDelay = Duration(milliseconds: 300); static const _maxRetries = 100; bool _started = false; bool _ready = false; bool _presenting = false; SharedPayload? _pending; int _retriesLeft = 0; Timer? _retry; late final _inbox = ShareInbox( consume: () => _method.invokeMethod('consumeInitialShare'), acknowledge: (id) => _method.invokeMethod('acknowledgeShare', {'id': id}), release: (id) => _method.invokeMethod('releaseShare', {'id': id}), onShare: _receivePayload, ); bool get _native { try { return Platform.isAndroid; } catch (_) { return false; } } void init() { if (_started || !_native) return; _started = true; _events.receiveBroadcastStream().listen( _onEvent, onError: (e) => logger.w('ShareIntentBridge: events stream error: $e'), ); api.stateStream.listen((state) { if (state == SessionState.online) _flushPending(); }); } void markReady() { _ready = true; _flushPending(); } Future checkInitialShare() async { if (!_native) return; try { await _inbox.check(); } catch (e) { logger.w('ShareIntentBridge.checkInitialShare: $e'); } } Future clearCache(SharedPayload payload) async { if (!_native) return; final paths = payload.files.map((file) => file.path).toList(); await SharedFileUploads.instance.waitUntilIdle(paths); try { await _method.invokeMethod('clearCache', {'paths': paths}); } catch (e) { logger.w('ShareIntentBridge.clearCache: $e'); } } void _onEvent(Object? event) { unawaited(checkInitialShare()); } void _receivePayload(SharedPayload payload) { logger.i( 'Поделиться: получено ${payload.files.length} файлов' '${payload.text != null ? ' и текст' : ''}', ); _pending = payload; _retriesLeft = _maxRetries; _flushPending(); } void _flushPending() { final payload = _pending; if (payload == null || _presenting) return; final context = QlyraApp.navigatorKey.currentContext; if (!_ready || context == null || api.state != SessionState.online) { if (_retriesLeft <= 0) { // Keep the native record and payload until a session becomes ready. return; } _retriesLeft--; _retry ??= Timer(_retryDelay, () { _retry = null; _flushPending(); }); return; } _pending = null; _presenting = true; unawaited( pushSwipeable( context, (_) => ChatListScreen(sharePayload: payload), ).whenComplete(() async { _presenting = false; try { await _inbox.complete( waitForRelease: () => SharedFileUploads.instance.waitUntilIdle( payload.files.map((file) => file.path).toList(), ), ); } catch (e) { logger.w('ShareIntentBridge.complete: $e'); } _flushPending(); }), ); } }