fix: reliable first-message send, QR routing, auto-lock recovery
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).
This commit is contained in:
@@ -496,10 +496,14 @@ class MessagingService {
|
||||
|
||||
Map<String, dynamic> _profile() {
|
||||
final identity = _identity!;
|
||||
final da = myDeliveryAddress;
|
||||
return {
|
||||
'n': identity.displayName,
|
||||
'aid': accountId,
|
||||
'da': base64Encode(myDeliveryAddress!),
|
||||
'aid': ?accountId,
|
||||
// Do doběhnutí vlastní registrace na relay ještě routing nemáme —
|
||||
// pole vynecháme (protistrana ho doplní z pozdějšího profilu), NIKDY
|
||||
// `da!` (null-check pád = „Null check operator used on a null value").
|
||||
'da': ?(da == null ? null : base64Encode(da)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -694,21 +698,30 @@ class MessagingService {
|
||||
await storage.addMessage(group.id, message);
|
||||
onUpdate?.call();
|
||||
|
||||
final anyOk = await _fanoutToGroup(group, {
|
||||
'v': 1,
|
||||
't': 'txt',
|
||||
'id': message.id,
|
||||
'gid': group.id,
|
||||
'txt': text,
|
||||
're': ?replyToId,
|
||||
'ttl': ?ttl,
|
||||
'p': _profile(),
|
||||
});
|
||||
final sent = message.copyWith(
|
||||
status: anyOk ? MessageStatus.sent : MessageStatus.failed);
|
||||
await storage.updateMessage(group.id, sent);
|
||||
onUpdate?.call();
|
||||
return sent;
|
||||
try {
|
||||
final anyOk = await _fanoutToGroup(group, {
|
||||
'v': 1,
|
||||
't': 'txt',
|
||||
'id': message.id,
|
||||
'gid': group.id,
|
||||
'txt': text,
|
||||
're': ?replyToId,
|
||||
'ttl': ?ttl,
|
||||
'p': _profile(),
|
||||
});
|
||||
final sent = message.copyWith(
|
||||
status: anyOk ? MessageStatus.sent : MessageStatus.failed);
|
||||
await storage.updateMessage(group.id, sent);
|
||||
onUpdate?.call();
|
||||
return sent;
|
||||
} catch (e) {
|
||||
// Výjimka mimo per-member cestu (např. build obsahu) by jinak nechala
|
||||
// zprávu navždy ve stavu „odesílá se" (⏱). Označit jako selhalou.
|
||||
await storage.updateMessage(
|
||||
group.id, message.copyWith(status: MessageStatus.failed));
|
||||
onUpdate?.call();
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Odešle přílohu do skupiny — blob se šifruje a nahrává JEDNOU,
|
||||
@@ -741,21 +754,28 @@ class MessagingService {
|
||||
await storage.addMessage(group.id, message);
|
||||
onUpdate?.call();
|
||||
|
||||
final anyOk = await _fanoutToGroup(group, {
|
||||
'v': 1,
|
||||
't': 'att',
|
||||
'id': message.id,
|
||||
'gid': group.id,
|
||||
if (caption != null && caption.isNotEmpty) 'txt': caption,
|
||||
'ttl': ?ttl,
|
||||
'att': meta.toWire(),
|
||||
'p': _profile(),
|
||||
});
|
||||
final sent = message.copyWith(
|
||||
status: anyOk ? MessageStatus.sent : MessageStatus.failed);
|
||||
await storage.updateMessage(group.id, sent);
|
||||
onUpdate?.call();
|
||||
return sent;
|
||||
try {
|
||||
final anyOk = await _fanoutToGroup(group, {
|
||||
'v': 1,
|
||||
't': 'att',
|
||||
'id': message.id,
|
||||
'gid': group.id,
|
||||
if (caption != null && caption.isNotEmpty) 'txt': caption,
|
||||
'ttl': ?ttl,
|
||||
'att': meta.toWire(),
|
||||
'p': _profile(),
|
||||
});
|
||||
final sent = message.copyWith(
|
||||
status: anyOk ? MessageStatus.sent : MessageStatus.failed);
|
||||
await storage.updateMessage(group.id, sent);
|
||||
onUpdate?.call();
|
||||
return sent;
|
||||
} catch (e) {
|
||||
await storage.updateMessage(
|
||||
group.id, message.copyWith(status: MessageStatus.failed));
|
||||
onUpdate?.call();
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Reakce ve skupině (emoji == null = odebrat).
|
||||
@@ -1009,6 +1029,13 @@ class MessagingService {
|
||||
final crypto = _crypto ?? (throw StateError('Service not started'));
|
||||
final relay = _relay!;
|
||||
|
||||
// Vlastní registrace na relay ještě nedoběhla (čerstvá instalace,
|
||||
// pomalá síť, Tor bez Orbotu) → nemáme sender adresu ani sender cert.
|
||||
// Srozumitelná chyba místo null-check pádu; registrace běží na pozadí.
|
||||
if (accountId == null || myDeliveryAddress == null) {
|
||||
throw const NotYetRegisteredError();
|
||||
}
|
||||
|
||||
final peer = contact.publicIdentity;
|
||||
final peerAid = peer.accountId;
|
||||
final peerDa = peer.deliveryAddress;
|
||||
|
||||
Reference in New Issue
Block a user