feat: UX polish, localization, advert relocation, and map filter fixes

- i18n: translate connection screen + chat tab keys into 13 locales;
  add "Send my contact" (advert) strings in all locales
- feat: move self-advert from home header into composer "+" action menu
  (new lib/utils/advert_helper.dart, always shows Flood/Direct sheet)
- fix: hide-repeaters map toggle was bypassed in simple mode
- fix: simple mode map now shows only favourite chat contacts
- fix: wrap ListTiles in Material (contact_tile secondary actions,
  inferred contact group card) to satisfy Flutter ink assertions
- device-authoritative contact/channel sync and UX review fixes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Janez T
2026-06-12 10:01:10 +02:00
parent 3e1113035d
commit ba27843166
62 changed files with 12546 additions and 2521 deletions

View File

@@ -100,10 +100,21 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
});
try {
await context.read<ConnectionProvider>().discoverNodeType(
advertType: advertType,
);
final connectionProvider = context.read<ConnectionProvider>();
connectionProvider.clearError();
await connectionProvider.discoverNodeType(advertType: advertType);
if (!mounted) return;
final discoveryError = connectionProvider.error;
if (discoveryError != null) {
connectionProvider.clearError();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(discoveryError),
backgroundColor: Colors.red,
),
);
return;
}
final l10n = AppLocalizations.of(context)!;
final label = switch (advertType) {
_repeaterAdvertType => l10n.repeaterDiscoverySent,
@@ -153,8 +164,10 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
return;
}
} else {
connectionProvider.clearError();
await connectionProvider.getContact(advert.publicKey);
if (connectionProvider.error == 'Not found') {
final resolveError = connectionProvider.error;
if (resolveError == 'Not found') {
connectionProvider.clearError();
final added = await _addPendingAdvertToRadio(
advert,
@@ -164,6 +177,23 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
if (!added) {
return;
}
} else if (resolveError != null) {
// Other failures (timeout, disconnect, ...): surface the error and
// leave the advert pending so the user can retry.
connectionProvider.clearError();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(
context,
)!.failedToAddContact(resolveError),
),
backgroundColor: Colors.red,
),
);
}
return;
}
}
if ((advert.typeValue ?? 0) == _sensorAdvertType) {
@@ -248,10 +278,12 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
required ContactsProvider contactsProvider,
}) async {
final contact = _contactFromPendingAdvert(advert);
connectionProvider.clearError();
await connectionProvider.addOrUpdateContact(contact);
final addError = connectionProvider.error;
if (addError != null) {
connectionProvider.clearError();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.failedToAddContact(addError.toString()))),