fix(#678): distinguish CoreScope not-found from unreachable (#571)

CoreScopeService.fetchCounts returns a three-state CoreScopeResult { found | notFound | unreachable } instead of a nullable count. Reachable-but-empty -> notFound ('Not seen by CoreScope yet'); network/HTTP/parse error -> unreachable. Channel-chat tap-refresh banner switches on status; adaptive poll reads .counts.

Epic #677. Closes #571.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pull/680/head
Strycher 2 months ago
parent edffd10555
commit 0b5f4ea36f

@ -4948,7 +4948,9 @@ class MeshCoreConnector extends ChangeNotifier {
for (final secs in _coreScopePollSecs) { for (final secs in _coreScopePollSecs) {
await Future<void>.delayed(Duration(seconds: secs)); await Future<void>.delayed(Duration(seconds: secs));
if (!isConnected) return; if (!isConnected) return;
final counts = await _coreScopeService.fetchCounts(pkt.hashHex); final counts = (await _coreScopeService.fetchCounts(
pkt.hashHex,
)).counts;
if (counts == null) continue; if (counts == null) continue;
if (counts.observers > best) { if (counts.observers > best) {
best = counts.observers; best = counts.observers;
@ -4979,26 +4981,28 @@ class MeshCoreConnector extends ChangeNotifier {
} }
/// Tap-to-refresh: re-query CoreScope for a message's stored on-air hash and /// Tap-to-refresh: re-query CoreScope for a message's stored on-air hash and
/// bump the counts if they grew (they only ever climb). Returns the fetched /// bump the counts if they grew (they only ever climb). Returns the full
/// counts (or null on failure / no stored hash) so the UI can confirm. /// result so the UI can distinguish found / notFound / unreachable (#571).
Future<CoreScopeCounts?> refreshCoreScopeCounts( Future<CoreScopeResult> refreshCoreScopeCounts(
int channelIndex, int channelIndex,
String messageId, String messageId,
String hashHex, String hashHex,
) async { ) async {
final counts = await _coreScopeService.fetchCounts(hashHex); final result = await _coreScopeService.fetchCounts(hashHex);
if (counts == null) return null; final counts = result.counts;
_updateChannelMessageById(channelIndex, messageId, (m) { if (counts != null) {
final current = m.coreScopeObserverCount ?? 0; _updateChannelMessageById(channelIndex, messageId, (m) {
return counts.observers >= current final current = m.coreScopeObserverCount ?? 0;
? m.copyWith( return counts.observers >= current
coreScopeObserverCount: counts.observers, ? m.copyWith(
coreScopeObservationCount: counts.observations, coreScopeObserverCount: counts.observers,
) coreScopeObservationCount: counts.observations,
: m; )
}); : m;
notifyListeners(); });
return counts; notifyListeners();
}
return result;
} }
void _updateChannelMessageById( void _updateChannelMessageById(

@ -891,6 +891,7 @@
}, },
"channel_coreScopeRefresh": "Refresh CoreScope observers", "channel_coreScopeRefresh": "Refresh CoreScope observers",
"channel_coreScopeRefreshFailed": "CoreScope unreachable", "channel_coreScopeRefreshFailed": "CoreScope unreachable",
"channel_coreScopeNotFound": "Not seen by CoreScope yet",
"channel_coreScopeCounts": "{observers, plural, =1{1 observer} other{{observers} observers}} · {observations} observations", "channel_coreScopeCounts": "{observers, plural, =1{1 observer} other{{observers} observers}} · {observations} observations",
"@channel_coreScopeCounts": { "@channel_coreScopeCounts": {
"placeholders": { "placeholders": {

@ -3130,6 +3130,12 @@ abstract class AppLocalizations {
/// **'CoreScope unreachable'** /// **'CoreScope unreachable'**
String get channel_coreScopeRefreshFailed; String get channel_coreScopeRefreshFailed;
/// No description provided for @channel_coreScopeNotFound.
///
/// In en, this message translates to:
/// **'Not seen by CoreScope yet'**
String get channel_coreScopeNotFound;
/// No description provided for @channel_coreScopeCounts. /// No description provided for @channel_coreScopeCounts.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

@ -1734,6 +1734,9 @@ class AppLocalizationsBg extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1732,6 +1732,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1703,6 +1703,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1729,6 +1729,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1736,6 +1736,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1741,6 +1741,9 @@ class AppLocalizationsHu extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1731,6 +1731,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1666,6 +1666,9 @@ class AppLocalizationsJa extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1662,6 +1662,9 @@ class AppLocalizationsKo extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1719,6 +1719,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1744,6 +1744,9 @@ class AppLocalizationsPl extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1728,6 +1728,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1732,6 +1732,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1721,6 +1721,9 @@ class AppLocalizationsSk extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1717,6 +1717,9 @@ class AppLocalizationsSl extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1712,6 +1712,9 @@ class AppLocalizationsSv extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1727,6 +1727,9 @@ class AppLocalizationsUk extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -1641,6 +1641,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get channel_coreScopeRefreshFailed => 'CoreScope unreachable'; String get channel_coreScopeRefreshFailed => 'CoreScope unreachable';
@override
String get channel_coreScopeNotFound => 'Not seen by CoreScope yet';
@override @override
String channel_coreScopeCounts(int observers, int observations) { String channel_coreScopeCounts(int observers, int observations) {
String _temp0 = intl.Intl.pluralLogic( String _temp0 = intl.Intl.pluralLogic(

@ -27,6 +27,7 @@ import '../models/contact.dart';
import '../models/translation_support.dart'; import '../models/translation_support.dart';
import '../models/app_settings.dart'; import '../models/app_settings.dart';
import '../services/app_settings_service.dart'; import '../services/app_settings_service.dart';
import '../services/corescope_service.dart';
import '../widgets/channel_notify_mode.dart'; import '../widgets/channel_notify_mode.dart';
import '../services/block_service.dart'; import '../services/block_service.dart';
import '../services/chat_text_scale_service.dart'; import '../services/chat_text_scale_service.dart';
@ -1920,17 +1921,21 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
if (hash == null) return; if (hash == null) return;
final connector = context.read<MeshCoreConnector>(); final connector = context.read<MeshCoreConnector>();
final l10n = context.l10n; final l10n = context.l10n;
final counts = await connector.refreshCoreScopeCounts( final result = await connector.refreshCoreScopeCounts(
message.channelIndex ?? _currentChannel.index, message.channelIndex ?? _currentChannel.index,
message.messageId, message.messageId,
hash, hash,
); );
if (!mounted) return; if (!mounted) return;
_showCoreScopeBanner( final text = switch (result.status) {
counts == null CoreScopeStatus.found => l10n.channel_coreScopeCounts(
? l10n.channel_coreScopeRefreshFailed result.counts!.observers,
: l10n.channel_coreScopeCounts(counts.observers, counts.observations), result.counts!.observations,
); ),
CoreScopeStatus.notFound => l10n.channel_coreScopeNotFound,
CoreScopeStatus.unreachable => l10n.channel_coreScopeRefreshFailed,
};
_showCoreScopeBanner(text);
} }
/// Top banner (out of the way of the composer), auto-dismissed after a few /// Top banner (out of the way of the composer), auto-dismissed after a few

@ -17,6 +17,25 @@ class CoreScopeCounts {
final int observations; final int observations;
} }
/// Outcome of a CoreScope query, so the UI can tell "no record of this packet
/// yet" apart from "couldn't reach CoreScope" (#571).
enum CoreScopeStatus { found, notFound, unreachable }
class CoreScopeResult {
const CoreScopeResult(this.status, [this.counts]);
final CoreScopeStatus status;
final CoreScopeCounts? counts;
static const CoreScopeResult notFound = CoreScopeResult(
CoreScopeStatus.notFound,
);
static const CoreScopeResult unreachable = CoreScopeResult(
CoreScopeStatus.unreachable,
);
factory CoreScopeResult.found(CoreScopeCounts counts) =>
CoreScopeResult(CoreScopeStatus.found, counts);
}
/// Queries a CoreScope instance for how many observers reported a given packet, /// Queries a CoreScope instance for how many observers reported a given packet,
/// keyed by the firmware/mesh packet hash (#524). Read-only and best-effort: /// keyed by the firmware/mesh packet hash (#524). Read-only and best-effort:
/// any failure (offline, timeout, non-200, bad body, unknown packet) returns /// any failure (offline, timeout, non-200, bad body, unknown packet) returns
@ -35,10 +54,10 @@ class CoreScopeService {
final Duration timeout; final Duration timeout;
/// Distinct observers and total observations for [packetHash] (16 lowercase /// Distinct observers and total observations for [packetHash] (16 lowercase
/// hex chars). Returns null on any error, or when CoreScope has no record of /// hex chars). `notFound` = CoreScope reachable but has no record of the hash
/// the hash yet. /// yet; `unreachable` = network/HTTP/parse error. Never throws.
Future<CoreScopeCounts?> fetchCounts(String packetHash) async { Future<CoreScopeResult> fetchCounts(String packetHash) async {
if (packetHash.isEmpty) return null; if (packetHash.isEmpty) return CoreScopeResult.unreachable;
final uri = Uri( final uri = Uri(
scheme: useTls ? 'https' : 'http', scheme: useTls ? 'https' : 'http',
host: host, host: host,
@ -57,19 +76,20 @@ class CoreScopeService {
'HTTP ${resp.statusCode} for hash $packetHash', 'HTTP ${resp.statusCode} for hash $packetHash',
tag: 'CoreScope', tag: 'CoreScope',
); );
return null; return CoreScopeResult.unreachable;
} }
final body = jsonDecode(resp.body); final body = jsonDecode(resp.body);
if (body is! Map) return null; if (body is! Map) return CoreScopeResult.unreachable;
final packets = body['packets']; final packets = body['packets'];
if (packets is! List || packets.isEmpty) { if (packets is! List) return CoreScopeResult.unreachable;
if (packets.isEmpty) {
appLogger.info('no record yet for $packetHash', tag: 'CoreScope'); appLogger.info('no record yet for $packetHash', tag: 'CoreScope');
return null; return CoreScopeResult.notFound;
} }
final first = packets.first; final first = packets.first;
if (first is! Map) return null; if (first is! Map) return CoreScopeResult.unreachable;
final observers = first['observer_count']; final observers = first['observer_count'];
if (observers is! num) return null; if (observers is! num) return CoreScopeResult.unreachable;
final observations = first['observation_count']; final observations = first['observation_count'];
final counts = CoreScopeCounts( final counts = CoreScopeCounts(
observers: observers.toInt(), observers: observers.toInt(),
@ -82,10 +102,10 @@ class CoreScopeService {
'for $packetHash', 'for $packetHash',
tag: 'CoreScope', tag: 'CoreScope',
); );
return counts; return CoreScopeResult.found(counts);
} catch (e) { } catch (e) {
appLogger.warn('Query failed for $packetHash: $e', tag: 'CoreScope'); appLogger.warn('Query failed for $packetHash: $e', tag: 'CoreScope');
return null; return CoreScopeResult.unreachable;
} }
} }

@ -5,87 +5,96 @@ import 'package:meshcore_open/services/corescope_service.dart';
void main() { void main() {
group('CoreScopeService.fetchCounts', () { group('CoreScopeService.fetchCounts', () {
test( test('found: parses observer + observation counts', () async {
'parses observer + observation counts from a grouped response', late Uri captured;
() async { final service = CoreScopeService(
late Uri captured; host: 'map.okimesh.org',
final service = CoreScopeService( client: MockClient((req) async {
host: 'map.okimesh.org', captured = req.url;
client: MockClient((req) async { return http.Response(
captured = req.url; '{"packets":[{"hash":"abc123","observer_count":15,'
return http.Response( '"observation_count":34}],"total":1}',
'{"packets":[{"hash":"abc123","observer_count":15,' 200,
'"observation_count":34}],"total":1}', );
200, }),
); );
}),
);
final counts = await service.fetchCounts('abc123'); final result = await service.fetchCounts('abc123');
expect(counts, isNotNull); expect(result.status, CoreScopeStatus.found);
expect(counts!.observers, 15); expect(result.counts!.observers, 15);
expect(counts.observations, 34); expect(result.counts!.observations, 34);
expect(captured.host, 'map.okimesh.org'); expect(captured.host, 'map.okimesh.org');
expect(captured.path, '/api/packets'); expect(captured.path, '/api/packets');
expect(captured.queryParameters['hash'], 'abc123'); expect(captured.queryParameters['hash'], 'abc123');
expect(captured.queryParameters['groupByHash'], 'true'); expect(captured.queryParameters['groupByHash'], 'true');
}, });
);
test('falls back to observers when observation_count is absent', () async { test('found: observations falls back to observers when absent', () async {
final service = CoreScopeService( final service = CoreScopeService(
client: MockClient( client: MockClient(
(_) async => http.Response('{"packets":[{"observer_count":9}]}', 200), (_) async => http.Response('{"packets":[{"observer_count":9}]}', 200),
), ),
); );
final counts = await service.fetchCounts('abc123'); final result = await service.fetchCounts('abc123');
expect(counts!.observers, 9); expect(result.status, CoreScopeStatus.found);
expect(counts.observations, 9); expect(result.counts!.observers, 9);
expect(result.counts!.observations, 9);
}); });
test('returns null when CoreScope has no record (empty packets)', () async { test('notFound: reachable but empty packets (the #571 case)', () async {
final service = CoreScopeService( final service = CoreScopeService(
client: MockClient( client: MockClient(
(_) async => http.Response('{"packets":[],"total":0}', 200), (_) async => http.Response('{"packets":[],"total":0}', 200),
), ),
); );
expect(await service.fetchCounts('deadbeef'), isNull); final result = await service.fetchCounts('deadbeef');
expect(result.status, CoreScopeStatus.notFound);
expect(result.counts, isNull);
}); });
test('returns null on non-200', () async { test('unreachable on non-200', () async {
final service = CoreScopeService( final service = CoreScopeService(
client: MockClient((_) async => http.Response('nope', 503)), client: MockClient((_) async => http.Response('nope', 503)),
); );
expect(await service.fetchCounts('abc123'), isNull); expect(
(await service.fetchCounts('abc123')).status,
CoreScopeStatus.unreachable,
);
}); });
test('returns null on malformed body', () async { test('unreachable on malformed body', () async {
final service = CoreScopeService( final service = CoreScopeService(
client: MockClient((_) async => http.Response('not json', 200)), client: MockClient((_) async => http.Response('not json', 200)),
); );
expect(await service.fetchCounts('abc123'), isNull); expect(
(await service.fetchCounts('abc123')).status,
CoreScopeStatus.unreachable,
);
}); });
test('returns null (never throws) on transport failure', () async { test('unreachable (never throws) on transport failure', () async {
final service = CoreScopeService( final service = CoreScopeService(
client: MockClient((_) async => throw Exception('offline')), client: MockClient((_) async => throw Exception('offline')),
); );
expect(await service.fetchCounts('abc123'), isNull); expect(
(await service.fetchCounts('abc123')).status,
CoreScopeStatus.unreachable,
);
}); });
test( test('empty hash: unreachable without hitting the network', () async {
'returns null for an empty hash without hitting the network', var called = false;
() async { final service = CoreScopeService(
var called = false; client: MockClient((_) async {
final service = CoreScopeService( called = true;
client: MockClient((_) async { return http.Response('{}', 200);
called = true; }),
return http.Response('{}', 200); );
}), expect(
); (await service.fetchCounts('')).status,
expect(await service.fetchCounts(''), isNull); CoreScopeStatus.unreachable,
expect(called, isFalse); );
}, expect(called, isFalse);
); });
}); });
} }

@ -59,6 +59,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -205,6 +206,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -351,6 +353,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -497,6 +500,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -643,6 +647,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -789,6 +794,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -935,6 +941,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -1081,6 +1088,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -1227,6 +1235,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -1373,6 +1382,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -1519,6 +1529,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -1665,6 +1676,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -1811,6 +1823,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -1957,6 +1970,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -2103,6 +2117,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -2249,6 +2264,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",
@ -2395,6 +2411,7 @@
"channel_coreScopeTooltip", "channel_coreScopeTooltip",
"channel_coreScopeRefresh", "channel_coreScopeRefresh",
"channel_coreScopeRefreshFailed", "channel_coreScopeRefreshFailed",
"channel_coreScopeNotFound",
"channel_coreScopeCounts", "channel_coreScopeCounts",
"channel_senderNameUnknown", "channel_senderNameUnknown",
"channel_senderNameUnknownHint", "channel_senderNameUnknownHint",

Loading…
Cancel
Save

Powered by TurnKey Linux.