fix(#550): reliable CoreScope refresh (bigger badge, long-press action, feedback)

Owner testing: the inline badge needed pixel-accurate taps, the refresh gave no
feedback, and there was no refresh in the message action sheet. Now the badge is
a padded InkWell (real tap target), the long-press panel has a 'Refresh CoreScope
observers' action, both show a snackbar with the fetched count, and the auto-poll
is more patient (adds 90s tail steps, stabilises after 3 flat checks) so it climbs
closer to the total before you need to tap. Part of #524.

Agent: QuietSnow (session 31eaba02)
pull/556/head
Strycher 2 months ago
parent 08f3858dd8
commit 61b17a3aa4

@ -4761,8 +4761,8 @@ class MeshCoreConnector extends ChangeNotifier {
/// increase across [_coreScopeStableChecks] consecutive polls. There is no
/// true "final" count (it only ever grows), so this is a pragmatic stop, the
/// tappable badge covers re-checking later.
static const List<int> _coreScopePollSecs = [10, 20, 30, 60, 60, 60, 60];
static const int _coreScopeStableChecks = 2;
static const List<int> _coreScopePollSecs = [10, 20, 30, 60, 60, 60, 90, 90];
static const int _coreScopeStableChecks = 3;
Future<void> _fetchAndStoreCoreScopeCount(
int channelIndex,
@ -4814,20 +4814,21 @@ class MeshCoreConnector extends ChangeNotifier {
}
/// Tap-to-refresh: re-query CoreScope for a message's stored on-air hash and
/// bump the observer count if it grew (counts only ever climb). No-op without
/// a stored hash.
Future<void> refreshCoreScopeObserverCount(
/// bump the observer count if it grew (counts only ever climb). Returns the
/// fetched count (or null on failure / no stored hash) so the UI can confirm.
Future<int?> refreshCoreScopeObserverCount(
int channelIndex,
String messageId,
String hashHex,
) async {
final count = await _coreScopeService.fetchObserverCount(hashHex);
if (count == null) return;
if (count == null) return null;
_updateChannelMessageById(channelIndex, messageId, (m) {
final current = m.coreScopeObserverCount ?? 0;
return count > current ? m.copyWith(coreScopeObserverCount: count) : m;
});
notifyListeners();
return count;
}
void _updateChannelMessageById(

@ -884,6 +884,8 @@
}
}
},
"channel_coreScopeRefresh": "Refresh CoreScope observers",
"channel_coreScopeRefreshFailed": "CoreScope unreachable",
"chat_successes": "successes",
"chat_score": "Score",
"chat_removePath": "Remove path",

@ -3088,6 +3088,18 @@ abstract class AppLocalizations {
/// **'Seen by {count, plural, =1{1 CoreScope observer} other{{count} CoreScope observers}}'**
String channel_coreScopeTooltip(int count);
/// No description provided for @channel_coreScopeRefresh.
///
/// In en, this message translates to:
/// **'Refresh CoreScope observers'**
String get channel_coreScopeRefresh;
/// No description provided for @channel_coreScopeRefreshFailed.
///
/// In en, this message translates to:
/// **'CoreScope unreachable'**
String get channel_coreScopeRefreshFailed;
/// No description provided for @chat_successes.
///
/// In en, this message translates to:

@ -1713,6 +1713,12 @@ class AppLocalizationsBg extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Успехи';

@ -1711,6 +1711,12 @@ class AppLocalizationsDe extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Erfolgreich';

@ -1681,6 +1681,12 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'successes';

@ -1708,6 +1708,12 @@ class AppLocalizationsEs extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Éxitos';

@ -1715,6 +1715,12 @@ class AppLocalizationsFr extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Succès';

@ -1720,6 +1720,12 @@ class AppLocalizationsHu extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'sikerek';

@ -1710,6 +1710,12 @@ class AppLocalizationsIt extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'successi';

@ -1645,6 +1645,12 @@ class AppLocalizationsJa extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => '成功事例';

@ -1641,6 +1641,12 @@ class AppLocalizationsKo extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => '성공 사례';

@ -1698,6 +1698,12 @@ class AppLocalizationsNl extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Succesvol';

@ -1723,6 +1723,12 @@ class AppLocalizationsPl extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Sukcesy';

@ -1707,6 +1707,12 @@ class AppLocalizationsPt extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Sucessos';

@ -1711,6 +1711,12 @@ class AppLocalizationsRu extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'успешно';

@ -1700,6 +1700,12 @@ class AppLocalizationsSk extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Úspechy';

@ -1696,6 +1696,12 @@ class AppLocalizationsSl extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Uspešni';

@ -1691,6 +1691,12 @@ class AppLocalizationsSv extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'framgångar';

@ -1706,6 +1706,12 @@ class AppLocalizationsUk extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => 'Успішно';

@ -1620,6 +1620,12 @@ class AppLocalizationsZh extends AppLocalizations {
return 'Seen by $_temp0';
}
@override
String get channel_coreScopeRefresh => 'Refresh CoreScope observers';
@override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get chat_successes => '成功';

@ -1522,28 +1522,26 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
// re-query on demand (the count only ever climbs).
if (isOutgoing && (message.coreScopeObserverCount ?? 0) > 0) ...[
dot,
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: message.onAirHash == null
? null
: () => context
.read<MeshCoreConnector>()
.refreshCoreScopeObserverCount(
message.channelIndex ?? _currentChannel.index,
message.messageId,
message.onAirHash!,
),
child: Tooltip(
message: context.l10n.channel_coreScopeTooltip(
message.coreScopeObserverCount!,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.cloud_outlined, size: 12, color: metaColor),
const SizedBox(width: 4),
Text('${message.coreScopeObserverCount}', style: metaStyle),
],
Tooltip(
message: context.l10n.channel_coreScopeTooltip(
message.coreScopeObserverCount!,
),
child: InkWell(
onTap: message.onAirHash == null
? null
: () => _refreshCoreScope(message),
borderRadius: BorderRadius.circular(10),
child: Padding(
// Generous padding so the small badge is a reliable tap target.
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.cloud_outlined, size: 12, color: metaColor),
const SizedBox(width: 4),
Text('${message.coreScopeObserverCount}', style: metaStyle),
],
),
),
),
),
@ -1644,6 +1642,27 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
);
}
Future<void> _refreshCoreScope(ChannelMessage message) async {
final hash = message.onAirHash;
if (hash == null) return;
final connector = context.read<MeshCoreConnector>();
final l10n = context.l10n;
final count = await connector.refreshCoreScopeObserverCount(
message.channelIndex ?? _currentChannel.index,
message.messageId,
hash,
);
if (!mounted) return;
showDismissibleSnackBar(
context,
content: Text(
count == null
? l10n.channel_coreScopeRefreshFailed
: l10n.channel_coreScopeTooltip(count),
),
);
}
void _retryChannelMessage(ChannelMessage message) {
context.read<MeshCoreConnector>().sendChannelMessage(
_currentChannel,
@ -1710,6 +1729,24 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
_retryChannelMessage(message);
},
),
// CoreScope observer count refresh (#524), owner feature. A large,
// reliable tap target vs the small inline badge.
if (message.isOutgoing && message.onAirHash != null)
ListTile(
leading: const Icon(Icons.cloud_outlined),
title: Text(context.l10n.channel_coreScopeRefresh),
subtitle: message.coreScopeObserverCount != null
? Text(
context.l10n.channel_coreScopeTooltip(
message.coreScopeObserverCount!,
),
)
: null,
onTap: () {
Navigator.pop(sheetContext);
_refreshCoreScope(message);
},
),
// Can't react to your own messages
if (!message.isOutgoing)
ListTile(

@ -52,6 +52,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -152,6 +154,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -252,6 +256,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -352,6 +358,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -452,6 +460,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -552,6 +562,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -652,6 +664,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -752,6 +766,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -852,6 +868,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -952,6 +970,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -1052,6 +1072,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -1152,6 +1174,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -1252,6 +1276,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -1352,6 +1378,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -1452,6 +1480,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -1552,6 +1582,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
@ -1652,6 +1684,8 @@
"debugLog_logUnavailable",
"channel_heardTooltip",
"channel_coreScopeTooltip",
"channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",

Loading…
Cancel
Save

Powered by TurnKey Linux.