feat(#611): share your contact into a channel from a + picker

The composer GIF button becomes a +, opening a short picker with GIF and
My contact card. Owner decision after seeing the build: one affordance
beside the text entry rather than a button per attachable thing, because
the sendable set stays small when a channel message shares a 160-byte
payload with the Sender: prefix.

Sends the COMPACT format, not the meshcore:// URI:

  <{64-hex key}:{type}:{name}>

That shape was observed on the live mesh, twice, from different senders
in #test and #hamradio four weeks apart. It costs about 75 bytes against
117 for the equivalent URI, so on a 160-byte budget the difference is
airtime rather than tidiness. The URI form stays correct for a QR, a DM,
or an out-of-band paste; this is the channel idiom.

Angle brackets are stripped from an emitted name because they are the
delimiters, and a name carrying one would truncate the payload for every
parser reading it. A colon is left alone: the name is the final field,
so a correct parser splits on the first two colons and takes the rest.

Inserts into the composer rather than sending, matching the GIF picker,
so the card can be captioned and reviewed first. It appends, so a
caption already typed is not destroyed.

Parsing a received share is #610 and is not in this change, so an
inbound card still renders as raw text for now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix/652-release-prerelease-flag
Strycher 3 weeks ago
parent d5a306d4a4
commit c27bef0f73

@ -2521,6 +2521,11 @@
"contacts_verifiedByMessage": "Key confirmed. A message with this contact went through, which only works with the matching key.",
"contacts_verifiedKeyOnly": "Added from a key. Nothing has confirmed it on air yet.",
"contacts_lastSeenNever": "Not heard yet",
"chat_attachTooltip": "Add to message",
"chat_attachGif": "GIF",
"chat_attachMyContact": "My contact card",
"chat_attachMyContactSubtitle": "Puts your public key in the message so anyone here can add you",
"chat_contactCardNeedsConnection": "Connect to a radio first, so the app knows your public key.",
"contacts_ShareContact": "Copy contact to Clipboard",
"contacts_ShareContactZeroHop": "Share contact by advert",
"contacts_zeroHopContactAdvertSent": "Sent contact by advert.",

@ -7576,6 +7576,36 @@ abstract class AppLocalizations {
/// **'Not heard yet'**
String get contacts_lastSeenNever;
/// No description provided for @chat_attachTooltip.
///
/// In en, this message translates to:
/// **'Add to message'**
String get chat_attachTooltip;
/// No description provided for @chat_attachGif.
///
/// In en, this message translates to:
/// **'GIF'**
String get chat_attachGif;
/// No description provided for @chat_attachMyContact.
///
/// In en, this message translates to:
/// **'My contact card'**
String get chat_attachMyContact;
/// No description provided for @chat_attachMyContactSubtitle.
///
/// In en, this message translates to:
/// **'Puts your public key in the message so anyone here can add you'**
String get chat_attachMyContactSubtitle;
/// No description provided for @chat_contactCardNeedsConnection.
///
/// In en, this message translates to:
/// **'Connect to a radio first, so the app knows your public key.'**
String get chat_contactCardNeedsConnection;
/// No description provided for @contacts_ShareContact.
///
/// In en, this message translates to:

@ -4425,6 +4425,23 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Копирай контакт в клипборда';

@ -4437,6 +4437,23 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Kontakt in die Zwischenablage kopieren';

@ -4359,6 +4359,23 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Copy contact to Clipboard';

@ -4426,6 +4426,23 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Copiar contacto al Portapapeles';

@ -4448,6 +4448,23 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact =>
'Copier le contact dans le presse-papiers';

@ -4443,6 +4443,23 @@ class AppLocalizationsHu extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Másolja a kapcsolatot a kiválasztóba';

@ -4431,6 +4431,23 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Copia contatto negli Appunti';

@ -4228,6 +4228,23 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => '連絡先をクリップボードにコピー';

@ -4230,6 +4230,23 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => '연락처를 복사';

@ -4411,6 +4411,23 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Contact naar Klembord kopiëren';

@ -4441,6 +4441,23 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Kopiuj kontakt do schowka';

@ -4423,6 +4423,23 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact =>
'Copiar contato para Área de Transferência';

@ -4433,6 +4433,23 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Копировать контакт в буфер обмена';

@ -4407,6 +4407,23 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Kopírovať kontakt do schránky';

@ -4401,6 +4401,23 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Kopiraj stik v Odložišče';

@ -4380,6 +4380,23 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Kopiera kontakt till Urklipp';

@ -4432,6 +4432,23 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => 'Копіювати контакт у буфер обміну';

@ -4131,6 +4131,23 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get contacts_lastSeenNever => 'Not heard yet';
@override
String get chat_attachTooltip => 'Add to message';
@override
String get chat_attachGif => 'GIF';
@override
String get chat_attachMyContact => 'My contact card';
@override
String get chat_attachMyContactSubtitle =>
'Puts your public key in the message so anyone here can add you';
@override
String get chat_contactCardNeedsConnection =>
'Connect to a radio first, so the app knows your public key.';
@override
String get contacts_ShareContact => '复制联系人信息到剪贴板';

@ -296,6 +296,35 @@ class Contact {
'&public_key=$publicKeyHex'
'&type=$type';
/// Compact contact share for a CHANNEL message, `<key:type:name>`. (#611)
///
/// This is a second, different format from [toShareUri], and deliberately so.
/// It is what real clients put on the air, observed live in `#test` and
/// `#hamradio`, and it is far cheaper: about 75 bytes against 117 for the
/// equivalent URI. Channel text shares a 160-byte payload with the
/// `Sender: ` prefix, so that difference is airtime, not neatness.
///
/// Use the URI form for a QR, a DM, or an out-of-band paste. Use this for a
/// channel.
String toChannelShare() =>
buildChannelShare(publicKeyHex: publicKeyHex, name: name, type: type);
/// Builds the compact channel share from raw parts, so this device can share
/// its OWN identity without constructing a [Contact]. (#611)
///
/// Angle brackets are the delimiters, so any in [name] are dropped: a name
/// carrying one would truncate the payload for every parser reading it. A
/// colon is left alone, because the name is the final field and a correct
/// parser splits on the first two colons only.
static String buildChannelShare({
required String publicKeyHex,
required String name,
int type = advTypeChat,
}) {
final safeName = name.replaceAll('<', '').replaceAll('>', '');
return '<$publicKeyHex:$type:$safeName>';
}
/// Parses a contact from the reference-app share URI, or null if malformed.
///
/// `meshcore://contact/add?name=<url-encoded>&public_key=<64 hex>&type=<1-4>`

@ -1243,6 +1243,76 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
);
}
/// The composer `+` picker. (#611)
///
/// Owner decision: one `+` beside the text entry, offering GIF and Contact
/// for now, rather than a dedicated button per thing you can attach. The
/// sendable set stays small because a channel message shares a 160-byte
/// payload with the `Sender: ` prefix.
void _showAttachPicker(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (sheetContext) => SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.gif_box),
title: Text(sheetContext.l10n.chat_attachGif),
onTap: () {
Navigator.pop(sheetContext);
_showGifPicker(context);
},
),
ListTile(
leading: const Icon(Icons.person_outline),
title: Text(sheetContext.l10n.chat_attachMyContact),
subtitle: Text(sheetContext.l10n.chat_attachMyContactSubtitle),
onTap: () {
Navigator.pop(sheetContext);
_insertMyContactCard(context);
},
),
],
),
),
);
}
/// Puts this device's own contact card into the composer. (#611)
///
/// Inserts rather than sends, matching the GIF picker, so the text can be
/// reviewed and captioned before it goes out.
///
/// Emits the compact `<key:type:name>` form observed on the live mesh, NOT
/// the `meshcore://` URI. The URI is ~117 bytes against ~75 here, and the
/// channel payload budget is 160 including the `Sender: ` prefix.
void _insertMyContactCard(BuildContext context) {
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
final keyHex = connector.selfPublicKeyHex;
if (keyHex.length != pubKeySize * 2) {
showDismissibleSnackBar(
context,
content: Text(context.l10n.chat_contactCardNeedsConnection),
);
return;
}
final card = Contact.buildChannelShare(
publicKeyHex: keyHex,
name: connector.selfName ?? '',
// This device is a companion.
type: advTypeChat,
);
// Append rather than replace, so a caption already typed is not lost.
final existing = _textController.text.trimRight();
_textController.text = existing.isEmpty ? card : '$existing $card';
_textController.selection = TextSelection.collapsed(
offset: _textController.text.length,
);
}
Widget _buildAvatar(String senderName) {
final initial = _getFirstCharacterOrEmoji(senderName);
final color = _getColorForName(senderName);
@ -1382,9 +1452,9 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
child: Row(
children: [
IconButton(
icon: const Icon(Icons.gif_box),
onPressed: () => _showGifPicker(context),
tooltip: context.l10n.chat_sendGif,
icon: const Icon(Icons.add),
onPressed: () => _showAttachPicker(context),
tooltip: context.l10n.chat_attachTooltip,
),
if (settings.translationEnabled)
MessageTranslationButton(

@ -228,4 +228,92 @@ void main() {
);
});
});
group('Compact channel contact share (#611)', () {
Contact stub(String name, int type) => Contact(
publicKey: hex2Uint8List(_key),
name: name,
type: type,
pathLength: -1,
path: Uint8List(0),
lastSeen: DateTime.fromMillisecondsSinceEpoch(0),
);
test('matches the shape observed on the live mesh', () {
// Real traffic in #test and #hamradio carries <key:type:name>, with the
// angle brackets as literal delimiters. Key is synthetic here; the repo
// is public.
expect(
Contact.buildChannelShare(publicKeyHex: _key, name: 'KE8AFF', type: 1),
'<$_key:1:KE8AFF>',
);
});
test('a name with spaces is carried verbatim', () {
// One of the two observed samples was "Roger KY4RS".
expect(
Contact.buildChannelShare(
publicKeyHex: _key,
name: 'Roger KY4RS',
type: advTypeChat,
),
'<$_key:1:Roger KY4RS>',
);
});
test('angle brackets are stripped from the name', () {
// A bracket in the name would truncate the payload for every parser
// reading it, so the emitter must not produce one.
final out = Contact.buildChannelShare(
publicKeyHex: _key,
name: 'we<ird>name',
type: advTypeChat,
);
expect(out, '<$_key:1:weirdname>');
expect('>'.allMatches(out).length, 1);
expect('<'.allMatches(out).length, 1);
});
test('a colon in the name survives, since the name is the final field', () {
// A correct parser splits on the first two colons and takes the rest as
// the name, so this needs no escaping.
expect(
Contact.buildChannelShare(
publicKeyHex: _key,
name: 'a:b',
type: advTypeChat,
),
'<$_key:1:a:b>',
);
});
test('every documented type is emitted numerically', () {
for (final t in [
advTypeChat,
advTypeRepeater,
advTypeRoom,
advTypeSensor,
]) {
expect(
Contact.buildChannelShare(publicKeyHex: _key, name: 'N', type: t),
'<$_key:$t:N>',
);
}
});
test('toChannelShare uses the contact own key, type and name', () {
expect(stub('Bob', advTypeRepeater).toChannelShare(), '<$_key:2:Bob>');
});
test('the compact form is materially cheaper than the URI', () {
// This is the whole reason both formats exist. Channel text shares a
// 160-byte payload with the "Sender: " prefix, so the difference is
// airtime, not tidiness.
final c = stub('KE8AFF', advTypeChat);
final compact = c.toChannelShare().length;
final uri = c.toShareUri().length;
expect(compact, lessThan(uri));
expect(uri - compact, greaterThan(30));
});
});
}

@ -115,6 +115,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -256,6 +261,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -397,6 +407,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -538,6 +553,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -679,6 +699,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -820,6 +845,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -961,6 +991,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -1102,6 +1137,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -1243,6 +1283,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -1384,6 +1429,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -1525,6 +1575,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -1666,6 +1721,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -1807,6 +1867,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -1948,6 +2013,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -2089,6 +2159,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -2230,6 +2305,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",
@ -2371,6 +2451,11 @@
"contacts_verifiedByMessage",
"contacts_verifiedKeyOnly",
"contacts_lastSeenNever",
"chat_attachTooltip",
"chat_attachGif",
"chat_attachMyContact",
"chat_attachMyContactSubtitle",
"chat_contactCardNeedsConnection",
"snrIndicator_noNeighbors",
"block_block",
"block_unblock",

Loading…
Cancel
Save

Powered by TurnKey Linux.