90a1390716
Fixes reported by testers and found in an app review: - First message right after install no longer fails. Before the app finished registering with the relay, the outgoing message could crash on a null delivery address. The profile field is now optional and the send path throws a clear "still connecting, try again shortly" error while registration completes in the background. - The QR code and share link now appear only once the delivery address is ready. Previously a link generated before the first connection lacked routing, so the other side could not send the first message. The screen now shows a connecting state and reactively swaps in the QR when ready. - Incomplete contact links are rejected on import instead of failing at first send, with a clear prompt to ask for a fresh link. - Auto-lock no longer silences the app: after locking and unlocking, messaging is restarted (it previously stayed dead until app restart). - Group messages are marked failed on a non-per-member send error instead of hanging in the "sending" state forever. - Hardened the relay client against a rare duplicate connection when toggling Tor or locking mid-connect, and fromShareString now rejects a delivery address of the wrong length. - Pin unifiedpush below 6.2.0 to drop the webcrypto dependency (its native framework lacks LC_ENCRYPTION_INFO and blocked the iOS upload). - Skip Android release lint (network dependency check hangs on the build host and adds no value for a Flutter app).
68 lines
3.0 KiB
Dart
68 lines
3.0 KiB
Dart
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:sepot/core/network/relay_client.dart';
|
|
import 'package:sepot/core/push/push_notifier.dart';
|
|
import 'package:sepot/core/services/messaging_service.dart';
|
|
import 'package:sepot/providers/contacts_provider.dart';
|
|
import 'package:sepot/providers/conversations_provider.dart';
|
|
import 'package:sepot/providers/groups_provider.dart';
|
|
import 'package:sepot/providers/identity_provider.dart';
|
|
import 'package:sepot/providers/storage_provider.dart';
|
|
|
|
/// Jediná instance messaging služby (krypto + relay + storage).
|
|
final messagingServiceProvider = Provider<MessagingService>((ref) {
|
|
final service = MessagingService(storage: ref.read(storageProvider));
|
|
ref.onDispose(service.stop);
|
|
return service;
|
|
});
|
|
|
|
/// Nastartuje službu, jakmile existuje identita. Watchovat z první
|
|
/// obrazovky po odemčení (chat list) — do té doby se nic nespojuje.
|
|
/// Předpokládá odemčený storage — na /chats se jde vždy až po unlocku.
|
|
final messagingStartProvider = FutureProvider<void>((ref) async {
|
|
final identity = await ref.watch(identityProvider.future);
|
|
if (identity == null) return;
|
|
|
|
// Po auto-locku se tenhle provider eager-rebuildne (drží ho pushInitProvider)
|
|
// ještě NAD zamčeným storage → service.start() by spadl na „Storage is
|
|
// locked" a provider by uvázl v AsyncError (žádný příjem/odeslání až do
|
|
// killu procesu). Nad zamčeným storage tiše nic nestartuj; po odemčení
|
|
// se provider invaliduje (viz unlock cesty) a spustí start znovu.
|
|
final storage = ref.read(storageProvider);
|
|
if (!storage.isUnlocked) return;
|
|
|
|
final service = ref.read(messagingServiceProvider);
|
|
if (service.isStarted) return;
|
|
|
|
service.onUpdate = () {
|
|
ref.invalidate(conversationsProvider);
|
|
// celá family — jinak se otevřená konverzace nedozví o nové zprávě
|
|
// ani o změně stavu přílohy
|
|
ref.invalidate(conversationProvider);
|
|
ref.invalidate(contactsProvider);
|
|
// příchozí ginv/gleave/gmeta mění skupiny
|
|
ref.invalidate(groupsProvider);
|
|
};
|
|
|
|
// Appka běží, ale není v popředí (jiná appka / zhasnutý displej se
|
|
// živým procesem) → systémová notifikace. Jen jméno, žádný obsah.
|
|
// Ztlumená konverzace notifikaci potlačí (unread badge běží dál).
|
|
service.onMessageReceived = (contact, message) async {
|
|
final visible = WidgetsBinding.instance.lifecycleState ==
|
|
AppLifecycleState.resumed;
|
|
if (visible) return;
|
|
final conv = await ref.read(storageProvider).loadConversation(contact.id);
|
|
if (conv?.isMuted == true) return;
|
|
PushNotifier.showMessageNotification(contact.displayName);
|
|
};
|
|
|
|
await service.start(identity);
|
|
});
|
|
|
|
/// Stav připojení k relayi pro UI indikátor.
|
|
final relayConnectionProvider = StreamProvider<RelayConnectionState>((ref) {
|
|
ref.watch(messagingStartProvider);
|
|
final service = ref.read(messagingServiceProvider);
|
|
return service.connectionStream ?? const Stream.empty();
|
|
});
|