feat(crypto): Add method to format emojis

This patch adds a method to format a list of emojis in a a terminal
friendly way.

This method was borrowed from weechat-matrix but it's also quite useful
in our own emoji verification example.
This commit is contained in:
Damir Jelić
2022-09-05 13:32:11 +02:00
parent b2452ae92c
commit a640312503
7 changed files with 52 additions and 12 deletions

View File

@@ -87,7 +87,9 @@ pub use requests::{
OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest, UploadSigningKeysRequest,
};
pub use store::{CrossSigningKeyExport, CryptoStoreError, SecretImportError, SecretInfo};
pub use verification::{AcceptSettings, CancelInfo, Emoji, Sas, Verification, VerificationRequest};
pub use verification::{
format_emojis, AcceptSettings, CancelInfo, Emoji, Sas, Verification, VerificationRequest,
};
#[cfg(feature = "qrcode")]
pub use verification::{QrVerification, ScanError};

View File

@@ -83,6 +83,43 @@ pub struct Emoji {
pub description: &'static str,
}
/// Format the the list of emojis as a two line string.
///
/// The first line will contain the emojis spread out so the second line can
/// contain the descriptions centered bellow the emoji.
pub fn format_emojis(emojis: [Emoji; 7]) -> String {
let (emojis, descriptions): (Vec<_>, Vec<_>) =
emojis.iter().map(|e| (e.symbol, e.description)).unzip();
let center_emoji = |emoji: &str| -> String {
const EMOJI_WIDTH: usize = 2;
// These are emojis that need VARIATION-SELECTOR-16 (U+FE0F) so that they are
// rendered with coloured glyphs. For these, we need to add an extra
// space after them so that they are rendered properly in terminals.
const VARIATION_SELECTOR_EMOJIS: [&str; 7] = ["☁️", "❤️", "☂️", "✏️", "✂️", "☎️", "✈️"];
// Hack to make terminals behave properly when one of the above is printed.
let emoji = if VARIATION_SELECTOR_EMOJIS.contains(&emoji) {
format!("{} ", emoji)
} else {
emoji.to_owned()
};
// This is a trick to account for the fact that emojis are wider than other
// monospace characters.
let placeholder = ".".repeat(EMOJI_WIDTH);
format!("{:^12}", placeholder).replace(&placeholder, &emoji)
};
let emoji_string = emojis.iter().map(|e| center_emoji(e)).collect::<Vec<_>>().join("");
let description =
descriptions.iter().map(|d| format!("{:^12}", d)).collect::<Vec<_>>().join("");
format!("{emoji_string}\n{description}")
}
impl VerificationStore {
pub async fn get_device(
&self,

View File

@@ -32,9 +32,9 @@ use crate::{
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
verification::{
event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent},
Cancelled,
Cancelled, Emoji,
},
Emoji, ReadOnlyAccount, ReadOnlyOwnUserIdentity,
ReadOnlyAccount, ReadOnlyOwnUserIdentity,
};
#[derive(Clone, Debug)]

View File

@@ -61,9 +61,9 @@ use crate::{
AcceptContent, DoneContent, KeyContent, MacContent, OwnedAcceptContent,
OwnedStartContent, StartContent,
},
Cancelled, FlowId,
Cancelled, Emoji, FlowId,
},
Emoji, ReadOnlyAccount, ReadOnlyOwnUserIdentity,
ReadOnlyAccount, ReadOnlyOwnUserIdentity,
};
const KEY_AGREEMENT_PROTOCOLS: &[KeyAgreementProtocol] =

View File

@@ -35,9 +35,9 @@ mod qrcode;
mod requests;
mod sas;
pub use matrix_sdk_base::crypto::{format_emojis, AcceptSettings, CancelInfo, Emoji};
#[cfg(feature = "qrcode")]
pub use matrix_sdk_base::crypto::{matrix_sdk_qrcode::QrVerificationData, ScanError};
pub use matrix_sdk_base::crypto::{AcceptSettings, CancelInfo, Emoji};
#[cfg(feature = "qrcode")]
pub use qrcode::QrVerification;
pub use requests::VerificationRequest;

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use matrix_sdk_base::crypto::{AcceptSettings, CancelInfo, ReadOnlyDevice, Sas as BaseSas};
use matrix_sdk_base::crypto::{AcceptSettings, CancelInfo, Emoji, ReadOnlyDevice, Sas as BaseSas};
use ruma::{events::key::verification::cancel::CancelCode, UserId};
use crate::{error::Result, Client};
@@ -151,7 +151,7 @@ impl SasVerification {
/// }
/// # anyhow::Ok(()) });
/// ```
pub fn emoji(&self) -> Option<[super::Emoji; 7]> {
pub fn emoji(&self) -> Option<[Emoji; 7]> {
self.inner.emoji()
}

View File

@@ -1,5 +1,5 @@
use std::{
io,
io::{self, Write},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
@@ -11,7 +11,7 @@ use clap::Parser;
use matrix_sdk::{
self,
config::SyncSettings,
encryption::verification::{SasVerification, Verification},
encryption::verification::{format_emojis, SasVerification, Verification},
ruma::{
events::{
room::message::MessageType, AnySyncMessageLikeEvent, AnySyncTimelineEvent,
@@ -25,9 +25,10 @@ use url::Url;
async fn wait_for_confirmation(client: Client, sas: SasVerification) {
let emoji = sas.emoji().expect("The emoji should be available now");
let emoji: Vec<&str> = emoji.iter().map(|e| e.symbol).collect();
println!("Does the emoji match: {:?}", emoji);
println!("\nDo the emojis match: \n{}", format_emojis(emoji));
print!("Confirm with `yes` or cancel with `no`: ");
std::io::stdout().flush().expect("We should be able to flush stdout");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("error: unable to read user input");