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:
@@ -4,6 +4,33 @@ Všechny podstatné změny v aplikaci Šepot. Formát dle
|
|||||||
[Keep a Changelog](https://keepachangelog.com/cs/1.1.0/),
|
[Keep a Changelog](https://keepachangelog.com/cs/1.1.0/),
|
||||||
verzování dle [ajtak standardu](#verzování).
|
verzování dle [ajtak standardu](#verzování).
|
||||||
|
|
||||||
|
## [1.1.1] – 2026-07-18
|
||||||
|
|
||||||
|
### Opraveno
|
||||||
|
- **Odeslání první zprávy hned po instalaci už neselže.** Krátce po
|
||||||
|
založení účtu, než se aplikace stihla připojit k relayi, mohla zpráva
|
||||||
|
spadnout s hláškou „Odeslání selhalo". Nově se v tu chvíli ukáže
|
||||||
|
srozumitelné „Šepot se ještě připojuje — zkus to za chvíli" a připojení
|
||||||
|
proběhne na pozadí.
|
||||||
|
- **QR kód a odkaz se ukážou až s doručovací adresou.** Když sis chtěl
|
||||||
|
ukázat vlastní QR dřív, než se aplikace poprvé připojila, vygeneroval se
|
||||||
|
neúplný odkaz — protistrana pak nemohla poslat první zprávu („Kontakt
|
||||||
|
nemá doručovací adresu"). Nově se místo neúplného kódu ukáže stav
|
||||||
|
připojování a QR se objeví, jakmile je hotové.
|
||||||
|
- **Neúplný odkaz kontaktu se odmítne při přidání**, ne až při prvním
|
||||||
|
odeslání — s jasnou výzvou požádat protistranu o nový odkaz.
|
||||||
|
- **Zamčení po nečinnosti už neumlčí aplikaci.** Po automatickém zamknutí
|
||||||
|
a opětovném odemčení se zprávy zase přijímají i odesílají — dřív mohl
|
||||||
|
messaging zůstat po odemčení vypnutý až do restartu aplikace.
|
||||||
|
- **Skupinová zpráva se při chybě neodeslání označí jako neodeslaná**
|
||||||
|
místo aby zůstala navždy ve stavu „odesílá se".
|
||||||
|
- Ošetřeno vzácné zdvojení spojení s relayem při rychlém přepnutí Toru
|
||||||
|
nebo zamčení během připojování.
|
||||||
|
|
||||||
|
### Změněno
|
||||||
|
- Interní úklid závislostí notifikací (odstraněna nepoužívaná větev, která
|
||||||
|
komplikovala sestavení verze pro iOS).
|
||||||
|
|
||||||
## [1.1.0] – 2026-07-13
|
## [1.1.0] – 2026-07-13
|
||||||
|
|
||||||
### Přidáno
|
### Přidáno
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ android {
|
|||||||
jvmTarget = JavaVersion.VERSION_17.toString()
|
jvmTarget = JavaVersion.VERSION_17.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lint na release buildu vynechán: jeho síťové kontroly (novější verze
|
||||||
|
// závislostí) na omezené build síti visí donekonečna a pro Flutter appku
|
||||||
|
// nemají přínos. Analýzu Dartu dělá `flutter analyze`, Kotlin je minimální.
|
||||||
|
lint {
|
||||||
|
checkReleaseBuilds = false
|
||||||
|
abortOnError = false
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "cz.sepot.sepot"
|
applicationId = "cz.sepot.sepot"
|
||||||
// libsignal binárky vyžadují Android 7.0+ (API 24)
|
// libsignal binárky vyžadují Android 7.0+ (API 24)
|
||||||
|
|||||||
+5
-1
@@ -196,8 +196,12 @@ class _AutoLockGateState extends ConsumerState<_AutoLockGate>
|
|||||||
final kde = _router.state.uri.path;
|
final kde = _router.state.uri.path;
|
||||||
if (_bezZamku.contains(kde)) return;
|
if (_bezZamku.contains(kde)) return;
|
||||||
|
|
||||||
|
// Zastavit službu a zamknout DB. messagingStartProvider ZÁMĚRNĚ
|
||||||
|
// neinvalidujeme tady — invalidace by ho eager-rebuildla ještě nad
|
||||||
|
// odemčeným storage (těsně před lock) a messaging by nastartoval nad DB,
|
||||||
|
// kterou hned zavíráme. Restart řeší odemykací obrazovky přes
|
||||||
|
// ref.invalidate(messagingStartProvider) až po úspěšném unlocku.
|
||||||
await ref.read(messagingServiceProvider).stop();
|
await ref.read(messagingServiceProvider).stop();
|
||||||
ref.invalidate(messagingStartProvider);
|
|
||||||
storage.lock();
|
storage.lock();
|
||||||
_router.go('/pin-lock');
|
_router.go('/pin-lock');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,24 @@ class RelayUnavailableError extends SepotUserError {
|
|||||||
String toString() => 'relay unavailable';
|
String toString() => 'relay unavailable';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Vlastní registrace na relay ještě nedoběhla — nemáme sender adresu.
|
||||||
|
/// Přechodný stav po čerstvé instalaci; registrace běží na pozadí.
|
||||||
|
class NotYetRegisteredError extends SepotUserError {
|
||||||
|
const NotYetRegisteredError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'not registered on relay yet';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Odkaz/QR kontaktu je neúplný (chybí routing aid+da) — vygenerovaný
|
||||||
|
/// dřív, než měla protistrana dokončenou registraci na relay.
|
||||||
|
class ContactLinkIncompleteError extends SepotUserError {
|
||||||
|
const ContactLinkIncompleteError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'contact link is missing routing (incomplete)';
|
||||||
|
}
|
||||||
|
|
||||||
/// Příloha přesahuje serverový limit.
|
/// Příloha přesahuje serverový limit.
|
||||||
class AttachmentTooLargeError extends SepotUserError {
|
class AttachmentTooLargeError extends SepotUserError {
|
||||||
const AttachmentTooLargeError(this.maxBytes);
|
const AttachmentTooLargeError(this.maxBytes);
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import 'package:sepot/l10n/generated/app_localizations.dart';
|
|||||||
String describeError(AppLocalizations l10n, Object error) => switch (error) {
|
String describeError(AppLocalizations l10n, Object error) => switch (error) {
|
||||||
TrustRootChangedError() => l10n.errorTrustRootChanged,
|
TrustRootChangedError() => l10n.errorTrustRootChanged,
|
||||||
RelayUnavailableError() => l10n.errorRelayUnavailable,
|
RelayUnavailableError() => l10n.errorRelayUnavailable,
|
||||||
|
NotYetRegisteredError() => l10n.errorNotYetRegistered,
|
||||||
|
ContactLinkIncompleteError() => l10n.errorContactLinkIncomplete,
|
||||||
AttachmentTooLargeError(:final maxMb) =>
|
AttachmentTooLargeError(:final maxMb) =>
|
||||||
l10n.errorAttachmentTooLarge(maxMb),
|
l10n.errorAttachmentTooLarge(maxMb),
|
||||||
GroupNeedsMemberError() => l10n.errorGroupNeedsMember,
|
GroupNeedsMemberError() => l10n.errorGroupNeedsMember,
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ class RelayClient {
|
|||||||
|
|
||||||
void _setState(RelayConnectionState s) {
|
void _setState(RelayConnectionState s) {
|
||||||
_state = s;
|
_state = s;
|
||||||
|
// Po dispose() je controller zavřený — pozdní přechod z doběhnutého
|
||||||
|
// _connect (zombie) by jinak hodil StateError do listenerů.
|
||||||
|
if (_stateController.isClosed) return;
|
||||||
_stateController.add(s);
|
_stateController.add(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +295,14 @@ class RelayClient {
|
|||||||
try {
|
try {
|
||||||
final channel = _transport.connectWs(Uri.parse('$_wsUrl/v1/ws'));
|
final channel = _transport.connectWs(Uri.parse('$_wsUrl/v1/ws'));
|
||||||
await channel.ready;
|
await channel.ready;
|
||||||
|
// Během `ready` (přes Tor může viset dlouho) mohl přijít stop()/dispose()
|
||||||
|
// — Tor toggle nebo auto-lock. Bez téhle kontroly by vzniklo zombie
|
||||||
|
// autentizované spojení (relay má 2× jedno zařízení, DELIVERy padají do
|
||||||
|
// zavřených controllerů). Nezakládat ho, jen zavřít.
|
||||||
|
if (!_shouldRun) {
|
||||||
|
await channel.sink.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
|
||||||
// AUTH: výzva-odpověď podepsaná identity klíčem (XEdDSA)
|
// AUTH: výzva-odpověď podepsaná identity klíčem (XEdDSA)
|
||||||
@@ -322,9 +333,13 @@ class RelayClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (frame.hasDeliver()) {
|
if (frame.hasDeliver()) {
|
||||||
_deliverController.add(frame.deliver);
|
if (!_deliverController.isClosed) {
|
||||||
|
_deliverController.add(frame.deliver);
|
||||||
|
}
|
||||||
} else if (frame.hasReceipt()) {
|
} else if (frame.hasReceipt()) {
|
||||||
_receiptController.add(frame.receipt);
|
if (!_receiptController.isClosed) {
|
||||||
|
_receiptController.add(frame.receipt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDone: _onDisconnected,
|
onDone: _onDisconnected,
|
||||||
|
|||||||
@@ -496,10 +496,14 @@ class MessagingService {
|
|||||||
|
|
||||||
Map<String, dynamic> _profile() {
|
Map<String, dynamic> _profile() {
|
||||||
final identity = _identity!;
|
final identity = _identity!;
|
||||||
|
final da = myDeliveryAddress;
|
||||||
return {
|
return {
|
||||||
'n': identity.displayName,
|
'n': identity.displayName,
|
||||||
'aid': accountId,
|
'aid': ?accountId,
|
||||||
'da': base64Encode(myDeliveryAddress!),
|
// 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);
|
await storage.addMessage(group.id, message);
|
||||||
onUpdate?.call();
|
onUpdate?.call();
|
||||||
|
|
||||||
final anyOk = await _fanoutToGroup(group, {
|
try {
|
||||||
'v': 1,
|
final anyOk = await _fanoutToGroup(group, {
|
||||||
't': 'txt',
|
'v': 1,
|
||||||
'id': message.id,
|
't': 'txt',
|
||||||
'gid': group.id,
|
'id': message.id,
|
||||||
'txt': text,
|
'gid': group.id,
|
||||||
're': ?replyToId,
|
'txt': text,
|
||||||
'ttl': ?ttl,
|
're': ?replyToId,
|
||||||
'p': _profile(),
|
'ttl': ?ttl,
|
||||||
});
|
'p': _profile(),
|
||||||
final sent = message.copyWith(
|
});
|
||||||
status: anyOk ? MessageStatus.sent : MessageStatus.failed);
|
final sent = message.copyWith(
|
||||||
await storage.updateMessage(group.id, sent);
|
status: anyOk ? MessageStatus.sent : MessageStatus.failed);
|
||||||
onUpdate?.call();
|
await storage.updateMessage(group.id, sent);
|
||||||
return 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,
|
/// 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);
|
await storage.addMessage(group.id, message);
|
||||||
onUpdate?.call();
|
onUpdate?.call();
|
||||||
|
|
||||||
final anyOk = await _fanoutToGroup(group, {
|
try {
|
||||||
'v': 1,
|
final anyOk = await _fanoutToGroup(group, {
|
||||||
't': 'att',
|
'v': 1,
|
||||||
'id': message.id,
|
't': 'att',
|
||||||
'gid': group.id,
|
'id': message.id,
|
||||||
if (caption != null && caption.isNotEmpty) 'txt': caption,
|
'gid': group.id,
|
||||||
'ttl': ?ttl,
|
if (caption != null && caption.isNotEmpty) 'txt': caption,
|
||||||
'att': meta.toWire(),
|
'ttl': ?ttl,
|
||||||
'p': _profile(),
|
'att': meta.toWire(),
|
||||||
});
|
'p': _profile(),
|
||||||
final sent = message.copyWith(
|
});
|
||||||
status: anyOk ? MessageStatus.sent : MessageStatus.failed);
|
final sent = message.copyWith(
|
||||||
await storage.updateMessage(group.id, sent);
|
status: anyOk ? MessageStatus.sent : MessageStatus.failed);
|
||||||
onUpdate?.call();
|
await storage.updateMessage(group.id, sent);
|
||||||
return 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).
|
/// Reakce ve skupině (emoji == null = odebrat).
|
||||||
@@ -1009,6 +1029,13 @@ class MessagingService {
|
|||||||
final crypto = _crypto ?? (throw StateError('Service not started'));
|
final crypto = _crypto ?? (throw StateError('Service not started'));
|
||||||
final relay = _relay!;
|
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 peer = contact.publicIdentity;
|
||||||
final peerAid = peer.accountId;
|
final peerAid = peer.accountId;
|
||||||
final peerDa = peer.deliveryAddress;
|
final peerDa = peer.deliveryAddress;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import 'package:local_auth/local_auth.dart';
|
|||||||
import 'package:sepot/core/crypto/dek_wrap.dart';
|
import 'package:sepot/core/crypto/dek_wrap.dart';
|
||||||
import 'package:sepot/core/l10n/l10n.dart';
|
import 'package:sepot/core/l10n/l10n.dart';
|
||||||
import 'package:sepot/core/theme/app_theme.dart';
|
import 'package:sepot/core/theme/app_theme.dart';
|
||||||
|
import 'package:sepot/providers/messaging_provider.dart';
|
||||||
|
import 'package:sepot/providers/push_provider.dart';
|
||||||
import 'package:sepot/providers/storage_provider.dart';
|
import 'package:sepot/providers/storage_provider.dart';
|
||||||
import 'package:sepot/widgets/sepot_logo.dart';
|
import 'package:sepot/widgets/sepot_logo.dart';
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ class _PinLockScreenState extends ConsumerState<PinLockScreen> {
|
|||||||
);
|
);
|
||||||
if (!ok) return; // uživatel zrušil → zůstává PIN
|
if (!ok) return; // uživatel zrušil → zůstává PIN
|
||||||
await storage.unlockWithBiometrics();
|
await storage.unlockWithBiometrics();
|
||||||
|
_restartMessagingAfterUnlock();
|
||||||
if (mounted) context.go('/chats');
|
if (mounted) context.go('/chats');
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// biometrie nedostupná/selhala — PIN je vždy fallback
|
// biometrie nedostupná/selhala — PIN je vždy fallback
|
||||||
@@ -211,6 +214,16 @@ class _PinLockScreenState extends ConsumerState<PinLockScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Auto-lock službu zastavil a storage zamkl; po odemčení je nutné
|
||||||
|
/// messaging znovu nastartovat. messagingStartProvider drží starou
|
||||||
|
/// „hotovou" hodnotu (start se sám neprovede) → invalidace ho přepočítá
|
||||||
|
/// nad odemčeným storage a spustí start() znovu (jinak žádný příjem ani
|
||||||
|
/// odesílání až do restartu appky).
|
||||||
|
void _restartMessagingAfterUnlock() {
|
||||||
|
ref.invalidate(messagingStartProvider);
|
||||||
|
ref.invalidate(pushInitProvider);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _verifyPin() async {
|
Future<void> _verifyPin() async {
|
||||||
setState(() => _isVerifying = true);
|
setState(() => _isVerifying = true);
|
||||||
|
|
||||||
@@ -218,6 +231,7 @@ class _PinLockScreenState extends ConsumerState<PinLockScreen> {
|
|||||||
// „Ověření" PINu = Argon2id unwrap DEK + otevření SQLCipher DB.
|
// „Ověření" PINu = Argon2id unwrap DEK + otevření SQLCipher DB.
|
||||||
// Špatný PIN = GCM tag nesedí = DB zůstává nečitelná.
|
// Špatný PIN = GCM tag nesedí = DB zůstává nečitelná.
|
||||||
await ref.read(storageProvider).unlockWithPin(_pin);
|
await ref.read(storageProvider).unlockWithPin(_pin);
|
||||||
|
_restartMessagingAfterUnlock();
|
||||||
if (mounted) context.go('/chats');
|
if (mounted) context.go('/chats');
|
||||||
} on WrongPinException {
|
} on WrongPinException {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:share_plus/share_plus.dart';
|
|||||||
import 'package:sepot/core/l10n/error_messages.dart';
|
import 'package:sepot/core/l10n/error_messages.dart';
|
||||||
import 'package:sepot/core/l10n/l10n.dart';
|
import 'package:sepot/core/l10n/l10n.dart';
|
||||||
import 'package:sepot/core/theme/app_theme.dart';
|
import 'package:sepot/core/theme/app_theme.dart';
|
||||||
|
import 'package:sepot/models/identity.dart';
|
||||||
import 'package:sepot/providers/identity_provider.dart';
|
import 'package:sepot/providers/identity_provider.dart';
|
||||||
import 'package:sepot/providers/messaging_provider.dart';
|
import 'package:sepot/providers/messaging_provider.dart';
|
||||||
|
|
||||||
@@ -27,12 +28,24 @@ class QrShareScreen extends ConsumerWidget {
|
|||||||
return Center(child: Text(context.l10n.qrIdentityNotFound));
|
return Center(child: Text(context.l10n.qrIdentityNotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sdílet s routingem (accountId + delivery address z relaye),
|
// Sledovat stav připojení: po dokončení vlastní registrace se
|
||||||
// jinak protistrana nemá kam poslat první zprávu
|
// routing (aid+da) zapíše do DB a relay se připojí → tenhle watch
|
||||||
|
// obrazovku překreslí a QR se z „připojuji" přepne na hotový kód.
|
||||||
|
ref.watch(relayConnectionProvider);
|
||||||
final service = ref.watch(messagingServiceProvider);
|
final service = ref.watch(messagingServiceProvider);
|
||||||
final shareString = (service.shareableIdentity ??
|
final shareable = service.shareableIdentity;
|
||||||
identity.publicIdentity)
|
final hasRouting = shareable != null &&
|
||||||
.toShareString();
|
shareable.accountId != null &&
|
||||||
|
shareable.deliveryAddress != null;
|
||||||
|
|
||||||
|
// Bez routingu by QR/odkaz byl neúplný (protistrana by neměla kam
|
||||||
|
// poslat první zprávu → „Kontakt nemá doručovací adresu"). Radši
|
||||||
|
// než mrtvý kód ukázat stav připojování.
|
||||||
|
if (!hasRouting) {
|
||||||
|
return _ConnectingPlaceholder(identity: identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
final shareString = shareable.toShareString();
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(32),
|
padding: const EdgeInsets.all(32),
|
||||||
@@ -157,3 +170,56 @@ class QrShareScreen extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stav obrazovky, dokud vlastní registrace na relay nedoběhne — místo
|
||||||
|
/// neúplného QR (bez routingu) ukáže identitu a hlášku o připojování.
|
||||||
|
class _ConnectingPlaceholder extends StatelessWidget {
|
||||||
|
const _ConnectingPlaceholder({required this.identity});
|
||||||
|
|
||||||
|
final LocalIdentity identity;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(color: SepotColors.ember),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
Text(
|
||||||
|
context.l10n.qrShareConnectingTitle,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontFamily: kFontSans,
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: SepotColors.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
context.l10n.qrShareConnectingBody,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: SepotColors.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Text(
|
||||||
|
identity.sepotId,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontFamily: kFontMono,
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: SepotColors.ember,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -375,6 +375,14 @@
|
|||||||
"@errorRelayUnavailable": {
|
"@errorRelayUnavailable": {
|
||||||
"description": "StateError z _senderCertificate, když se nepodařilo získat sender certificate (offline relay); propaguje se do odesílací cesty, zobrazuje chat UI při selhání odeslání."
|
"description": "StateError z _senderCertificate, když se nepodařilo získat sender certificate (offline relay); propaguje se do odesílací cesty, zobrazuje chat UI při selhání odeslání."
|
||||||
},
|
},
|
||||||
|
"errorNotYetRegistered": "Šepot se ještě připojuje k relayi — zkus zprávu poslat za chvíli.",
|
||||||
|
"@errorNotYetRegistered": {
|
||||||
|
"description": "NotYetRegisteredError z _sendContent, když vlastní registrace na relay ještě nedoběhla (čerstvá instalace). 'Šepot' je značka, nepřekládat."
|
||||||
|
},
|
||||||
|
"errorContactLinkIncomplete": "Odkaz je neúplný — protistrana ho vytvořila dřív, než se stihla připojit. Požádej ji o nový sepot:// odkaz.",
|
||||||
|
"@errorContactLinkIncomplete": {
|
||||||
|
"description": "ContactLinkIncompleteError při přidání kontaktu z odkazu/QR bez routingu (aid+da). 'sepot://' je schéma odkazu, zachovat."
|
||||||
|
},
|
||||||
"errorAttachmentTooLarge": "Příloha je příliš velká (max {maxMb} MB)",
|
"errorAttachmentTooLarge": "Příloha je příliš velká (max {maxMb} MB)",
|
||||||
"@errorAttachmentTooLarge": {
|
"@errorAttachmentTooLarge": {
|
||||||
"description": "StateError z _prepareAttachment při překročení limitu velikosti přílohy; zobrazuje UI výběru/odeslání přílohy. POZOR: v kódu jde o dva adjacentní literály 'Příloha je příliš velká ' (ř. 448) + '(max ${…} MB)' (ř. 449) — sloučit do jednoho klíče.",
|
"description": "StateError z _prepareAttachment při překročení limitu velikosti přílohy; zobrazuje UI výběru/odeslání přílohy. POZOR: v kódu jde o dva adjacentní literály 'Příloha je příliš velká ' (ř. 448) + '(max ${…} MB)' (ř. 449) — sloučit do jednoho klíče.",
|
||||||
@@ -1419,6 +1427,14 @@
|
|||||||
"@qrShareTitle": {
|
"@qrShareTitle": {
|
||||||
"description": "AppBar titulek obrazovky sdílení vlastního QR/Šepot ID"
|
"description": "AppBar titulek obrazovky sdílení vlastního QR/Šepot ID"
|
||||||
},
|
},
|
||||||
|
"qrShareConnectingTitle": "Připojuji k relayi…",
|
||||||
|
"@qrShareConnectingTitle": {
|
||||||
|
"description": "Titulek na QR obrazovce, když vlastní registrace na relay ještě nedoběhla — routing (aid+da) není hotový, QR by byl neúplný."
|
||||||
|
},
|
||||||
|
"qrShareConnectingBody": "Až se Šepot poprvé připojí, objeví se tvůj QR kód a odkaz. Chvilku strpení — stačí pár sekund.",
|
||||||
|
"@qrShareConnectingBody": {
|
||||||
|
"description": "Vysvětlení pod titulkem qrShareConnectingTitle. 'Šepot' je značka, nepřekládat."
|
||||||
|
},
|
||||||
"qrIdentityNotFound": "Identita nebyla nalezena",
|
"qrIdentityNotFound": "Identita nebyla nalezena",
|
||||||
"@qrIdentityNotFound": {
|
"@qrIdentityNotFound": {
|
||||||
"description": "Text uprostřed obrazovky, když identityProvider vrátí null (nemělo by běžně nastat)"
|
"description": "Text uprostřed obrazovky, když identityProvider vrátí null (nemělo by běžně nastat)"
|
||||||
|
|||||||
@@ -75,6 +75,8 @@
|
|||||||
"chatEdited": "bearbeitet",
|
"chatEdited": "bearbeitet",
|
||||||
"errorTrustRootChanged": "Der Trust Root des Relays hat sich geändert! Kommunikation wird verweigert.",
|
"errorTrustRootChanged": "Der Trust Root des Relays hat sich geändert! Kommunikation wird verweigert.",
|
||||||
"errorRelayUnavailable": "Relay nicht erreichbar (Sender-Zertifikat)",
|
"errorRelayUnavailable": "Relay nicht erreichbar (Sender-Zertifikat)",
|
||||||
|
"errorNotYetRegistered": "Šepot verbindet sich noch mit dem Relay — versuch es gleich noch einmal.",
|
||||||
|
"errorContactLinkIncomplete": "Dieser Link ist unvollständig — die andere Person hat ihn erstellt, bevor sie verbunden war. Bitte sie um einen neuen sepot://-Link.",
|
||||||
"errorAttachmentTooLarge": "Anhang ist zu groß (max. {maxMb} MB)",
|
"errorAttachmentTooLarge": "Anhang ist zu groß (max. {maxMb} MB)",
|
||||||
"errorGroupNeedsMember": "Die Gruppe braucht mindestens 1 Mitglied",
|
"errorGroupNeedsMember": "Die Gruppe braucht mindestens 1 Mitglied",
|
||||||
"errorGroupMaxMembers": "Die Gruppe kann höchstens {maxMembers} Mitglieder haben",
|
"errorGroupMaxMembers": "Die Gruppe kann höchstens {maxMembers} Mitglieder haben",
|
||||||
@@ -294,6 +296,8 @@
|
|||||||
"chatListArchive": "Archivieren",
|
"chatListArchive": "Archivieren",
|
||||||
"qrShareTitle": "Meine Šepot-ID",
|
"qrShareTitle": "Meine Šepot-ID",
|
||||||
"qrIdentityNotFound": "Identität nicht gefunden",
|
"qrIdentityNotFound": "Identität nicht gefunden",
|
||||||
|
"qrShareConnectingTitle": "Verbinde mit dem Relay…",
|
||||||
|
"qrShareConnectingBody": "Dein QR-Code und Link erscheinen, sobald sich Šepot zum ersten Mal verbindet. Einen Moment — es dauert nur ein paar Sekunden.",
|
||||||
"commonCopiedToClipboard": "In die Zwischenablage kopiert",
|
"commonCopiedToClipboard": "In die Zwischenablage kopiert",
|
||||||
"qrShareInviteMessage": "Füg mich auf Šepot hinzu!\n\n{shareString}",
|
"qrShareInviteMessage": "Füg mich auf Šepot hinzu!\n\n{shareString}",
|
||||||
"commonShare": "Teilen",
|
"commonShare": "Teilen",
|
||||||
|
|||||||
@@ -75,6 +75,8 @@
|
|||||||
"chatEdited": "edited",
|
"chatEdited": "edited",
|
||||||
"errorTrustRootChanged": "The relay's trust root has changed! Refusing to communicate.",
|
"errorTrustRootChanged": "The relay's trust root has changed! Refusing to communicate.",
|
||||||
"errorRelayUnavailable": "Relay unavailable (sender certificate)",
|
"errorRelayUnavailable": "Relay unavailable (sender certificate)",
|
||||||
|
"errorNotYetRegistered": "Šepot is still connecting to the relay — try sending again in a moment.",
|
||||||
|
"errorContactLinkIncomplete": "This link is incomplete — the other person created it before connecting. Ask them for a new sepot:// link.",
|
||||||
"errorAttachmentTooLarge": "Attachment is too large (max {maxMb} MB)",
|
"errorAttachmentTooLarge": "Attachment is too large (max {maxMb} MB)",
|
||||||
"errorGroupNeedsMember": "A group needs at least 1 member",
|
"errorGroupNeedsMember": "A group needs at least 1 member",
|
||||||
"errorGroupMaxMembers": "A group can have at most {maxMembers} members",
|
"errorGroupMaxMembers": "A group can have at most {maxMembers} members",
|
||||||
@@ -294,6 +296,8 @@
|
|||||||
"chatListArchive": "Archive",
|
"chatListArchive": "Archive",
|
||||||
"qrShareTitle": "My Šepot ID",
|
"qrShareTitle": "My Šepot ID",
|
||||||
"qrIdentityNotFound": "Identity not found",
|
"qrIdentityNotFound": "Identity not found",
|
||||||
|
"qrShareConnectingTitle": "Connecting to the relay…",
|
||||||
|
"qrShareConnectingBody": "Your QR code and link will appear once Šepot connects for the first time. Just a moment — it only takes a few seconds.",
|
||||||
"commonCopiedToClipboard": "Copied to clipboard",
|
"commonCopiedToClipboard": "Copied to clipboard",
|
||||||
"qrShareInviteMessage": "Add me on Šepot!\n\n{shareString}",
|
"qrShareInviteMessage": "Add me on Šepot!\n\n{shareString}",
|
||||||
"commonShare": "Share",
|
"commonShare": "Share",
|
||||||
|
|||||||
@@ -75,6 +75,8 @@
|
|||||||
"chatEdited": "modificato",
|
"chatEdited": "modificato",
|
||||||
"errorTrustRootChanged": "Il trust root del relay è cambiato! Comunicazione rifiutata.",
|
"errorTrustRootChanged": "Il trust root del relay è cambiato! Comunicazione rifiutata.",
|
||||||
"errorRelayUnavailable": "Relay non disponibile (sender certificate)",
|
"errorRelayUnavailable": "Relay non disponibile (sender certificate)",
|
||||||
|
"errorNotYetRegistered": "Šepot si sta ancora connettendo al relay — riprova a inviare tra un istante.",
|
||||||
|
"errorContactLinkIncomplete": "Questo link è incompleto — l'altra persona l'ha creato prima di connettersi. Chiedile un nuovo link sepot://.",
|
||||||
"errorAttachmentTooLarge": "L'allegato è troppo grande (max {maxMb} MB)",
|
"errorAttachmentTooLarge": "L'allegato è troppo grande (max {maxMb} MB)",
|
||||||
"errorGroupNeedsMember": "Il gruppo ha bisogno di almeno 1 membro",
|
"errorGroupNeedsMember": "Il gruppo ha bisogno di almeno 1 membro",
|
||||||
"errorGroupMaxMembers": "Il gruppo può avere al massimo {maxMembers} membri",
|
"errorGroupMaxMembers": "Il gruppo può avere al massimo {maxMembers} membri",
|
||||||
@@ -294,6 +296,8 @@
|
|||||||
"chatListArchive": "Archivia",
|
"chatListArchive": "Archivia",
|
||||||
"qrShareTitle": "Il mio Šepot ID",
|
"qrShareTitle": "Il mio Šepot ID",
|
||||||
"qrIdentityNotFound": "Identità non trovata",
|
"qrIdentityNotFound": "Identità non trovata",
|
||||||
|
"qrShareConnectingTitle": "Connessione al relay…",
|
||||||
|
"qrShareConnectingBody": "Il tuo codice QR e il link appariranno appena Šepot si connette per la prima volta. Un istante — bastano pochi secondi.",
|
||||||
"commonCopiedToClipboard": "Copiato negli appunti",
|
"commonCopiedToClipboard": "Copiato negli appunti",
|
||||||
"qrShareInviteMessage": "Aggiungimi su Šepot!\n\n{shareString}",
|
"qrShareInviteMessage": "Aggiungimi su Šepot!\n\n{shareString}",
|
||||||
"commonShare": "Condividi",
|
"commonShare": "Condividi",
|
||||||
|
|||||||
@@ -75,6 +75,8 @@
|
|||||||
"chatEdited": "изменено",
|
"chatEdited": "изменено",
|
||||||
"errorTrustRootChanged": "Trust root релея изменился! Соединение отклонено.",
|
"errorTrustRootChanged": "Trust root релея изменился! Соединение отклонено.",
|
||||||
"errorRelayUnavailable": "Релей недоступен (sender certificate)",
|
"errorRelayUnavailable": "Релей недоступен (sender certificate)",
|
||||||
|
"errorNotYetRegistered": "Šepot ещё подключается к релею — попробуйте отправить сообщение через пару секунд.",
|
||||||
|
"errorContactLinkIncomplete": "Ссылка неполная — собеседник создал её до подключения. Попросите у него новую ссылку sepot://.",
|
||||||
"errorAttachmentTooLarge": "Вложение слишком большое (макс. {maxMb} МБ)",
|
"errorAttachmentTooLarge": "Вложение слишком большое (макс. {maxMb} МБ)",
|
||||||
"errorGroupNeedsMember": "В группе должен быть хотя бы 1 участник",
|
"errorGroupNeedsMember": "В группе должен быть хотя бы 1 участник",
|
||||||
"errorGroupMaxMembers": "Максимальное число участников группы — {maxMembers}",
|
"errorGroupMaxMembers": "Максимальное число участников группы — {maxMembers}",
|
||||||
@@ -294,6 +296,8 @@
|
|||||||
"chatListArchive": "Архивировать",
|
"chatListArchive": "Архивировать",
|
||||||
"qrShareTitle": "Мой Šepot ID",
|
"qrShareTitle": "Мой Šepot ID",
|
||||||
"qrIdentityNotFound": "Идентичность не найдена",
|
"qrIdentityNotFound": "Идентичность не найдена",
|
||||||
|
"qrShareConnectingTitle": "Подключение к релею…",
|
||||||
|
"qrShareConnectingBody": "Ваш QR-код и ссылка появятся, как только Šepot подключится в первый раз. Подождите пару секунд.",
|
||||||
"commonCopiedToClipboard": "Скопировано в буфер обмена",
|
"commonCopiedToClipboard": "Скопировано в буфер обмена",
|
||||||
"qrShareInviteMessage": "Добавьте меня в Šepot!\n\n{shareString}",
|
"qrShareInviteMessage": "Добавьте меня в Šepot!\n\n{shareString}",
|
||||||
"commonShare": "Поделиться",
|
"commonShare": "Поделиться",
|
||||||
|
|||||||
@@ -554,6 +554,18 @@ abstract class AppLocalizations {
|
|||||||
/// **'Relay nedostupný (sender certificate)'**
|
/// **'Relay nedostupný (sender certificate)'**
|
||||||
String get errorRelayUnavailable;
|
String get errorRelayUnavailable;
|
||||||
|
|
||||||
|
/// NotYetRegisteredError z _sendContent, když vlastní registrace na relay ještě nedoběhla (čerstvá instalace). 'Šepot' je značka, nepřekládat.
|
||||||
|
///
|
||||||
|
/// In cs, this message translates to:
|
||||||
|
/// **'Šepot se ještě připojuje k relayi — zkus zprávu poslat za chvíli.'**
|
||||||
|
String get errorNotYetRegistered;
|
||||||
|
|
||||||
|
/// ContactLinkIncompleteError při přidání kontaktu z odkazu/QR bez routingu (aid+da). 'sepot://' je schéma odkazu, zachovat.
|
||||||
|
///
|
||||||
|
/// In cs, this message translates to:
|
||||||
|
/// **'Odkaz je neúplný — protistrana ho vytvořila dřív, než se stihla připojit. Požádej ji o nový sepot:// odkaz.'**
|
||||||
|
String get errorContactLinkIncomplete;
|
||||||
|
|
||||||
/// StateError z _prepareAttachment při překročení limitu velikosti přílohy; zobrazuje UI výběru/odeslání přílohy. POZOR: v kódu jde o dva adjacentní literály 'Příloha je příliš velká ' (ř. 448) + '(max ${…} MB)' (ř. 449) — sloučit do jednoho klíče.
|
/// StateError z _prepareAttachment při překročení limitu velikosti přílohy; zobrazuje UI výběru/odeslání přílohy. POZOR: v kódu jde o dva adjacentní literály 'Příloha je příliš velká ' (ř. 448) + '(max ${…} MB)' (ř. 449) — sloučit do jednoho klíče.
|
||||||
///
|
///
|
||||||
/// In cs, this message translates to:
|
/// In cs, this message translates to:
|
||||||
@@ -1862,6 +1874,18 @@ abstract class AppLocalizations {
|
|||||||
/// **'Můj Šepot ID'**
|
/// **'Můj Šepot ID'**
|
||||||
String get qrShareTitle;
|
String get qrShareTitle;
|
||||||
|
|
||||||
|
/// Titulek na QR obrazovce, když vlastní registrace na relay ještě nedoběhla — routing (aid+da) není hotový, QR by byl neúplný.
|
||||||
|
///
|
||||||
|
/// In cs, this message translates to:
|
||||||
|
/// **'Připojuji k relayi…'**
|
||||||
|
String get qrShareConnectingTitle;
|
||||||
|
|
||||||
|
/// Vysvětlení pod titulkem qrShareConnectingTitle. 'Šepot' je značka, nepřekládat.
|
||||||
|
///
|
||||||
|
/// In cs, this message translates to:
|
||||||
|
/// **'Až se Šepot poprvé připojí, objeví se tvůj QR kód a odkaz. Chvilku strpení — stačí pár sekund.'**
|
||||||
|
String get qrShareConnectingBody;
|
||||||
|
|
||||||
/// Text uprostřed obrazovky, když identityProvider vrátí null (nemělo by běžně nastat)
|
/// Text uprostřed obrazovky, když identityProvider vrátí null (nemělo by běžně nastat)
|
||||||
///
|
///
|
||||||
/// In cs, this message translates to:
|
/// In cs, this message translates to:
|
||||||
|
|||||||
@@ -276,6 +276,14 @@ class AppLocalizationsCs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get errorRelayUnavailable => 'Relay nedostupný (sender certificate)';
|
String get errorRelayUnavailable => 'Relay nedostupný (sender certificate)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorNotYetRegistered =>
|
||||||
|
'Šepot se ještě připojuje k relayi — zkus zprávu poslat za chvíli.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorContactLinkIncomplete =>
|
||||||
|
'Odkaz je neúplný — protistrana ho vytvořila dřív, než se stihla připojit. Požádej ji o nový sepot:// odkaz.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String errorAttachmentTooLarge(int maxMb) {
|
String errorAttachmentTooLarge(int maxMb) {
|
||||||
return 'Příloha je příliš velká (max $maxMb MB)';
|
return 'Příloha je příliš velká (max $maxMb MB)';
|
||||||
@@ -1048,6 +1056,13 @@ class AppLocalizationsCs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get qrShareTitle => 'Můj Šepot ID';
|
String get qrShareTitle => 'Můj Šepot ID';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingTitle => 'Připojuji k relayi…';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingBody =>
|
||||||
|
'Až se Šepot poprvé připojí, objeví se tvůj QR kód a odkaz. Chvilku strpení — stačí pár sekund.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get qrIdentityNotFound => 'Identita nebyla nalezena';
|
String get qrIdentityNotFound => 'Identita nebyla nalezena';
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get errorRelayUnavailable =>
|
String get errorRelayUnavailable =>
|
||||||
'Relay nicht erreichbar (Sender-Zertifikat)';
|
'Relay nicht erreichbar (Sender-Zertifikat)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorNotYetRegistered =>
|
||||||
|
'Šepot verbindet sich noch mit dem Relay — versuch es gleich noch einmal.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorContactLinkIncomplete =>
|
||||||
|
'Dieser Link ist unvollständig — die andere Person hat ihn erstellt, bevor sie verbunden war. Bitte sie um einen neuen sepot://-Link.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String errorAttachmentTooLarge(int maxMb) {
|
String errorAttachmentTooLarge(int maxMb) {
|
||||||
return 'Anhang ist zu groß (max. $maxMb MB)';
|
return 'Anhang ist zu groß (max. $maxMb MB)';
|
||||||
@@ -1067,6 +1075,13 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get qrShareTitle => 'Meine Šepot-ID';
|
String get qrShareTitle => 'Meine Šepot-ID';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingTitle => 'Verbinde mit dem Relay…';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingBody =>
|
||||||
|
'Dein QR-Code und Link erscheinen, sobald sich Šepot zum ersten Mal verbindet. Einen Moment — es dauert nur ein paar Sekunden.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get qrIdentityNotFound => 'Identität nicht gefunden';
|
String get qrIdentityNotFound => 'Identität nicht gefunden';
|
||||||
|
|
||||||
|
|||||||
@@ -274,6 +274,14 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get errorRelayUnavailable => 'Relay unavailable (sender certificate)';
|
String get errorRelayUnavailable => 'Relay unavailable (sender certificate)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorNotYetRegistered =>
|
||||||
|
'Šepot is still connecting to the relay — try sending again in a moment.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorContactLinkIncomplete =>
|
||||||
|
'This link is incomplete — the other person created it before connecting. Ask them for a new sepot:// link.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String errorAttachmentTooLarge(int maxMb) {
|
String errorAttachmentTooLarge(int maxMb) {
|
||||||
return 'Attachment is too large (max $maxMb MB)';
|
return 'Attachment is too large (max $maxMb MB)';
|
||||||
@@ -1044,6 +1052,13 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get qrShareTitle => 'My Šepot ID';
|
String get qrShareTitle => 'My Šepot ID';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingTitle => 'Connecting to the relay…';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingBody =>
|
||||||
|
'Your QR code and link will appear once Šepot connects for the first time. Just a moment — it only takes a few seconds.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get qrIdentityNotFound => 'Identity not found';
|
String get qrIdentityNotFound => 'Identity not found';
|
||||||
|
|
||||||
|
|||||||
@@ -276,6 +276,14 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
String get errorRelayUnavailable =>
|
String get errorRelayUnavailable =>
|
||||||
'Relay non disponibile (sender certificate)';
|
'Relay non disponibile (sender certificate)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorNotYetRegistered =>
|
||||||
|
'Šepot si sta ancora connettendo al relay — riprova a inviare tra un istante.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorContactLinkIncomplete =>
|
||||||
|
'Questo link è incompleto — l\'altra persona l\'ha creato prima di connettersi. Chiedile un nuovo link sepot://.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String errorAttachmentTooLarge(int maxMb) {
|
String errorAttachmentTooLarge(int maxMb) {
|
||||||
return 'L\'allegato è troppo grande (max $maxMb MB)';
|
return 'L\'allegato è troppo grande (max $maxMb MB)';
|
||||||
@@ -1055,6 +1063,13 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get qrShareTitle => 'Il mio Šepot ID';
|
String get qrShareTitle => 'Il mio Šepot ID';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingTitle => 'Connessione al relay…';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingBody =>
|
||||||
|
'Il tuo codice QR e il link appariranno appena Šepot si connette per la prima volta. Un istante — bastano pochi secondi.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get qrIdentityNotFound => 'Identità non trovata';
|
String get qrIdentityNotFound => 'Identità non trovata';
|
||||||
|
|
||||||
|
|||||||
@@ -276,6 +276,14 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get errorRelayUnavailable => 'Релей недоступен (sender certificate)';
|
String get errorRelayUnavailable => 'Релей недоступен (sender certificate)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorNotYetRegistered =>
|
||||||
|
'Šepot ещё подключается к релею — попробуйте отправить сообщение через пару секунд.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get errorContactLinkIncomplete =>
|
||||||
|
'Ссылка неполная — собеседник создал её до подключения. Попросите у него новую ссылку sepot://.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String errorAttachmentTooLarge(int maxMb) {
|
String errorAttachmentTooLarge(int maxMb) {
|
||||||
return 'Вложение слишком большое (макс. $maxMb МБ)';
|
return 'Вложение слишком большое (макс. $maxMb МБ)';
|
||||||
@@ -1072,6 +1080,13 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get qrShareTitle => 'Мой Šepot ID';
|
String get qrShareTitle => 'Мой Šepot ID';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingTitle => 'Подключение к релею…';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get qrShareConnectingBody =>
|
||||||
|
'Ваш QR-код и ссылка появятся, как только Šepot подключится в первый раз. Подождите пару секунд.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get qrIdentityNotFound => 'Идентичность не найдена';
|
String get qrIdentityNotFound => 'Идентичность не найдена';
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,15 @@ class ContactsNotifier extends AsyncNotifier<List<Contact>> {
|
|||||||
final storage = ref.read(storageProvider);
|
final storage = ref.read(storageProvider);
|
||||||
final contacts = await future;
|
final contacts = await future;
|
||||||
|
|
||||||
|
// Odkaz bez routingu (aid+da) = protistrana ho vytvořila dřív, než měla
|
||||||
|
// hotovou registraci. Přidat takový kontakt = časovaná bomba: první
|
||||||
|
// odeslání spadne na „nemá doručovací adresu". Odmítnout hned s
|
||||||
|
// jasnou hláškou. (Nullable routing má smysl jen na interních příjmových
|
||||||
|
// cestách — roster/_findOrCreateContact —, ne při ručním importu.)
|
||||||
|
if (identity.accountId == null || identity.deliveryAddress == null) {
|
||||||
|
throw const ContactLinkIncompleteError();
|
||||||
|
}
|
||||||
|
|
||||||
// Kontrola duplicity
|
// Kontrola duplicity
|
||||||
if (contacts.any((c) => c.publicIdentity.id == identity.id)) {
|
if (contacts.any((c) => c.publicIdentity.id == identity.id)) {
|
||||||
throw const ContactAlreadyExistsError();
|
throw const ContactAlreadyExistsError();
|
||||||
|
|||||||
@@ -23,6 +23,14 @@ final messagingStartProvider = FutureProvider<void>((ref) async {
|
|||||||
final identity = await ref.watch(identityProvider.future);
|
final identity = await ref.watch(identityProvider.future);
|
||||||
if (identity == null) return;
|
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);
|
final service = ref.read(messagingServiceProvider);
|
||||||
if (service.isStarted) return;
|
if (service.isStarted) return;
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,7 @@
|
|||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
#include <record_linux/record_linux_plugin.h>
|
#include <record_linux/record_linux_plugin.h>
|
||||||
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||||
@@ -27,13 +25,7 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||||||
g_autoptr(FlPluginRegistrar) record_linux_registrar =
|
g_autoptr(FlPluginRegistrar) record_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
|
||||||
record_linux_plugin_register_with_registrar(record_linux_registrar);
|
record_linux_plugin_register_with_registrar(record_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
|
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
|
|
||||||
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
|
|
||||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) window_manager_registrar =
|
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
|
|
||||||
window_manager_plugin_register_with_registrar(window_manager_registrar);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
file_selector_linux
|
file_selector_linux
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
record_linux
|
record_linux
|
||||||
screen_retriever_linux
|
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
+6
-102
@@ -193,14 +193,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.12.3"
|
version: "8.12.3"
|
||||||
change_case:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: change_case
|
|
||||||
sha256: e41ef3df58521194ef8d7649928954805aeb08061917cf658322305e61568003
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -924,14 +916,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.6"
|
version: "0.17.6"
|
||||||
native_toolchain_cmake:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: native_toolchain_cmake
|
|
||||||
sha256: "9960e1e926ec210a3ba6fe61720d48e1dd6cbab00d05890a6e147ee45b7e47c5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.6"
|
|
||||||
objective_c:
|
objective_c:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1196,46 +1180,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.2"
|
version: "1.5.2"
|
||||||
screen_retriever:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_retriever
|
|
||||||
sha256: "42cc3b402a0f67d2455a0d067553d0f13453f6a008d98eababf8b63958d506bd"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1"
|
|
||||||
screen_retriever_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_retriever_linux
|
|
||||||
sha256: "2a476f1a5538065bc5badf376cfdc83d6ecf07d77eb2391b9c2bff5a76970048"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1"
|
|
||||||
screen_retriever_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_retriever_macos
|
|
||||||
sha256: b5abb900fcb86614ff10b738b34e37b9e1d03b0447280668e2bc8a98bdc7bd59
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1"
|
|
||||||
screen_retriever_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_retriever_platform_interface
|
|
||||||
sha256: "3af22d926bedf20c2caa308eea376776451a3af125919ce072e56525fded8901"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1"
|
|
||||||
screen_retriever_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_retriever_windows
|
|
||||||
sha256: c44b38a4c4bab34af259180a70a4eee1e29384e7b82e627c9faa68afcdab2e73
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1"
|
|
||||||
share_plus:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1413,42 +1357,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: unifiedpush
|
name: unifiedpush
|
||||||
sha256: "8ed9767f750a1dc6159a77e2171641d0cb825dc87682d1ce1b8618689b79f58e"
|
sha256: b52a0eff848d0446b9d521e0ad2f7bc76c4dda88d0f6ac0a79f1765ccfe37939
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.0"
|
version: "6.1.0"
|
||||||
unifiedpush_android:
|
unifiedpush_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: unifiedpush_android
|
name: unifiedpush_android
|
||||||
sha256: "2e6684e0a1a39ad8d6c7bc0d197954d50686acb1b1a7614b18ab0e0126f5492a"
|
sha256: "2af6a09a5fb9fdd97b61e704a587e88abcdb5f7ee2aea9a2c35e80159f9d586d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.3.1"
|
||||||
unifiedpush_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: unifiedpush_linux
|
|
||||||
sha256: c062d5eedd1cec70bcd33270cc4e01ae0ff6501f33d471167c06b34a968adfeb
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.0"
|
|
||||||
unifiedpush_platform_interface:
|
unifiedpush_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: unifiedpush_platform_interface
|
name: unifiedpush_platform_interface
|
||||||
sha256: "83372bc8d794b8b12ef6993b518d7be907dcfc2191bdf6de0ece5c4445d89880"
|
sha256: "5aa3eefc2699e4d8db6d723f0cfdfb56ac850487549a0b3b83574f4a89f33aab"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "3.1.0"
|
||||||
unifiedpush_storage_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: unifiedpush_storage_interface
|
|
||||||
sha256: b8d423a4695efc616aa21d8ab48fb5ef99d6288c68b56282b8faac1579ceabd9
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.0"
|
|
||||||
universal_io:
|
universal_io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1585,14 +1513,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
webcrypto:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: webcrypto
|
|
||||||
sha256: "9885ed99a846191542dd6ca820c83a098117df56be57453832233a3fef68f3f2"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.6.1"
|
|
||||||
webdriver:
|
webdriver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1601,14 +1521,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
webpush_encryption:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: webpush_encryption
|
|
||||||
sha256: "5b83272b91acda6ae515fcd980c94f06bf413702282497c5a68f5dfc64fed27f"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1617,14 +1529,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.15.0"
|
version: "5.15.0"
|
||||||
window_manager:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: window_manager
|
|
||||||
sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.5.1"
|
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
+5
-2
@@ -1,7 +1,7 @@
|
|||||||
name: sepot
|
name: sepot
|
||||||
description: "Šepot — Soukromý messenger s E2E šifrováním"
|
description: "Šepot — Soukromý messenger s E2E šifrováním"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.1.0+20
|
version: 1.1.1+21
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.10.8
|
sdk: ^3.10.8
|
||||||
@@ -37,7 +37,10 @@ dependencies:
|
|||||||
pointycastle: ^3.9.1
|
pointycastle: ^3.9.1
|
||||||
protobuf: ^6.0.0
|
protobuf: ^6.0.0
|
||||||
sqlite3: ^3.3.4
|
sqlite3: ^3.3.4
|
||||||
unifiedpush: ^6.2.0
|
# Pin pod 6.2.0: od 6.2.0 táhne unifiedpush_linux → webpush_encryption →
|
||||||
|
# webcrypto, jehož native framework na iOS chybí LC_ENCRYPTION_INFO (blokuje
|
||||||
|
# upload). Linux desktop neshipujeme, na Android/iOS je 6.1.x funkčně shodná.
|
||||||
|
unifiedpush: ">=6.1.0 <6.2.0"
|
||||||
image: ^4.8.0
|
image: ^4.8.0
|
||||||
mime: ^2.0.0
|
mime: ^2.0.0
|
||||||
image_picker: ^1.2.3
|
image_picker: ^1.2.3
|
||||||
|
|||||||
@@ -133,5 +133,28 @@ void main() {
|
|||||||
expect(PublicIdentity.fromShareString('sepot://add/%%%'), isNull);
|
expect(PublicIdentity.fromShareString('sepot://add/%%%'), isNull);
|
||||||
expect(PublicIdentity.fromShareString(''), isNull);
|
expect(PublicIdentity.fromShareString(''), isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('share link s routingem: aid+da přežijí roundtrip', () async {
|
||||||
|
final base = await LocalIdentity.create('Routovaná');
|
||||||
|
final withRouting = base.publicIdentity.withRouting(
|
||||||
|
accountId: 'deadbeef',
|
||||||
|
deliveryAddress: Uint8List.fromList(List.generate(32, (i) => i)),
|
||||||
|
);
|
||||||
|
final parsed =
|
||||||
|
PublicIdentity.fromShareString(withRouting.toShareString());
|
||||||
|
expect(parsed, isNotNull);
|
||||||
|
expect(parsed!.accountId, 'deadbeef');
|
||||||
|
expect(parsed.deliveryAddress,
|
||||||
|
Uint8List.fromList(List.generate(32, (i) => i)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fromShareString: da špatné délky → null (ne vadný kontakt)', () async {
|
||||||
|
final base = await LocalIdentity.create('Krátká');
|
||||||
|
final bad = base.publicIdentity.withRouting(
|
||||||
|
accountId: 'deadbeef',
|
||||||
|
deliveryAddress: Uint8List.fromList(List.generate(8, (i) => i)),
|
||||||
|
);
|
||||||
|
expect(PublicIdentity.fromShareString(bad.toShareString()), isNull);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,8 @@
|
|||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <local_auth_windows/local_auth_plugin.h>
|
#include <local_auth_windows/local_auth_plugin.h>
|
||||||
#include <record_windows/record_windows_plugin_c_api.h>
|
#include <record_windows/record_windows_plugin_c_api.h>
|
||||||
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
|
|
||||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||||
@@ -30,12 +28,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
||||||
RecordWindowsPluginCApiRegisterWithRegistrar(
|
RecordWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
|
||||||
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
|
|
||||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
WindowManagerPluginRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
local_auth_windows
|
local_auth_windows
|
||||||
record_windows
|
record_windows
|
||||||
screen_retriever_windows
|
|
||||||
share_plus
|
share_plus
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
window_manager
|
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Reference in New Issue
Block a user