From ae16cc36aaeb2bc96d236b88c5ff7f225acbde8d Mon Sep 17 00:00:00 2001 From: David Baker <dave@matrix.org> Date: Fri, 8 Feb 2019 14:57:36 +0000 Subject: [PATCH] Change SAS to decimal / emoji Requires https://github.com/matrix-org/matrix-js-sdk/pull/837 --- .../verification/_VerificationShowSas.scss | 28 +++- src/HtmlUtils.js | 18 ++- src/components/views/elements/EmojiText.js | 5 +- .../views/verification/VerificationShowSas.js | 124 ++++++++++++++++-- src/i18n/strings/en_EN.json | 67 +++++++++- 5 files changed, 224 insertions(+), 18 deletions(-) diff --git a/res/css/views/verification/_VerificationShowSas.scss b/res/css/views/verification/_VerificationShowSas.scss index 32ccf6b0bb..a0da7e2539 100644 --- a/res/css/views/verification/_VerificationShowSas.scss +++ b/res/css/views/verification/_VerificationShowSas.scss @@ -14,9 +14,35 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_VerificationShowSas_sas { +.mx_VerificationShowSas_decimalSas { text-align: center; font-weight: bold; padding-left: 3px; padding-right: 3px; } + +.mx_VerificationShowSas_decimalSas span { + margin-left: 5px; + margin-right: 5px; +} + +.mx_VerificationShowSas_emojiSas { + text-align: center; +} + +.mx_VerificationShowSas_emojiSas_block { + display: inline-block; + margin-left: 15px; + margin-right: 15px; + text-align: center; + margin-bottom: 20px; +} + +.mx_VerificationShowSas_emojiSas_emoji { + font-size: 48px; +} + +.mx_VerificationShowSas_emojiSas_label { + text-align: center; + font-weight: bold; +} diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 371804725d..2e08c059eb 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -68,8 +68,10 @@ export function containsEmoji(str) { /* modified from https://github.com/Ranks/emojione/blob/master/lib/js/emojione.js * because we want to include emoji shortnames in title text */ -function unicodeToImage(str) { - let replaceWith; let unicode; let alt; let short; let fname; +function unicodeToImage(str, addAlt) { + if (addAlt === undefined) addAlt = true; + + let replaceWith; let unicode; let short; let fname; const mappedUnicode = emojione.mapUnicodeToShort(); str = str.replace(emojione.regUnicode, function(unicodeChar) { @@ -84,10 +86,14 @@ function unicodeToImage(str) { fname = emojione.emojioneList[short].fname; // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname - alt = (emojione.unicodeAlt) ? emojione.convert(unicode.toUpperCase()) : mappedUnicode[unicode]; const title = mappedUnicode[unicode]; - replaceWith = `<img class="mx_emojione" title="${title}" alt="${alt}" src="${emojione.imagePathSVG}${fname}.svg${emojione.cacheBustParam}"/>`; + if (addAlt) { + const alt = (emojione.unicodeAlt) ? emojione.convert(unicode.toUpperCase()) : mappedUnicode[unicode]; + replaceWith = `<img class="mx_emojione" title="${title}" alt="${alt}" src="${emojione.imagePathSVG}${fname}.svg${emojione.cacheBustParam}"/>`; + } else { + replaceWith = `<img class="mx_emojione" src="${emojione.imagePathSVG}${fname}.svg${emojione.cacheBustParam}"/>`; + } return replaceWith; } }); @@ -508,9 +514,9 @@ export function bodyToHtml(content, highlights, opts={}) { <span className={className} dir="auto">{ strippedBody }</span>; } -export function emojifyText(text) { +export function emojifyText(text, addAlt) { return { - __html: unicodeToImage(escape(text)), + __html: unicodeToImage(escape(text), addAlt), }; } diff --git a/src/components/views/elements/EmojiText.js b/src/components/views/elements/EmojiText.js index 9fb650b2c3..b7f3e45321 100644 --- a/src/components/views/elements/EmojiText.js +++ b/src/components/views/elements/EmojiText.js @@ -20,12 +20,12 @@ import PropTypes from 'prop-types'; import {emojifyText, containsEmoji} from '../../../HtmlUtils'; export default function EmojiText(props) { - const {element, children, ...restProps} = props; + const {element, children, addAlt, ...restProps} = props; // fast path: simple regex to detect strings that don't contain // emoji and just return them if (containsEmoji(children)) { - restProps.dangerouslySetInnerHTML = emojifyText(children); + restProps.dangerouslySetInnerHTML = emojifyText(children, addAlt); return React.createElement(element, restProps); } else { return React.createElement(element, restProps, children); @@ -39,4 +39,5 @@ EmojiText.propTypes = { EmojiText.defaultProps = { element: 'span', + addAlt: true, }; diff --git a/src/components/views/verification/VerificationShowSas.js b/src/components/views/verification/VerificationShowSas.js index 0224571d9e..bca68e92d3 100644 --- a/src/components/views/verification/VerificationShowSas.js +++ b/src/components/views/verification/VerificationShowSas.js @@ -17,13 +17,17 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import sdk from '../../../index'; -import { _t } from '../../../languageHandler'; +import { _t, _td } from '../../../languageHandler'; + +function capFirst(s) { + return s.charAt(0).toUpperCase() + s.slice(1); +} export default class VerificationShowSas extends React.Component { static propTypes = { onDone: PropTypes.func.isRequired, onCancel: PropTypes.func.isRequired, - sas: PropTypes.string.isRequired, + sas: PropTypes.object.isRequired, } constructor() { @@ -32,17 +36,55 @@ export default class VerificationShowSas extends React.Component { render() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - return <div className="mx_VerificationShowSas"> - <p>{_t( + const EmojiText = sdk.getComponent('views.elements.EmojiText'); + + let sasDisplay; + let sasCaption; + if (this.props.sas.emoji) { + const emojiBlocks = this.props.sas.emoji.map( + (emoji, i) => <div className="mx_VerificationShowSas_emojiSas_block" key={i}> + <div className="mx_VerificationShowSas_emojiSas_emoji"> + <EmojiText addAlt={false}>{emoji[0]}</EmojiText> + </div> + <div className="mx_VerificationShowSas_emojiSas_label"> + {_t(capFirst(emoji[1]))} + </div> + </div>, + ); + sasDisplay = <div className="mx_VerificationShowSas_emojiSas"> + {emojiBlocks} + </div>; + sasCaption = _t( + "Verify this user by confirming the following emoji appear on their screen.", + ); + } else if (this.props.sas.decimal) { + const numberBlocks = this.props.sas.decimal.map((num, i) => <span key={i}> + {num} + </span>); + sasDisplay = <div className="mx_VerificationShowSas_decimalSas"> + {numberBlocks} + </div>; + sasCaption = _t( "Verify this user by confirming the following number appears on their screen.", - )}</p> + ); + } else { + return <div> + {_t("Unable to find a supported verification method.")} + <DialogButtons + primaryButton={_t('Cancel')} + hasCancel={false} + onPrimaryButtonClick={this.props.onCancel} + /> + </div>; + } + + return <div className="mx_VerificationShowSas"> + <p>{sasCaption}</p> <p>{_t( "For maximum security, we recommend you do this in person or use another " + "trusted means of communication.", )}</p> - <div className="mx_VerificationShowSas_sas"> - {this.props.sas} - </div> + {sasDisplay} <DialogButtons onPrimaryButtonClick={this.props.onDone} primaryButton={_t("Continue")} hasCancel={true} @@ -51,3 +93,69 @@ export default class VerificationShowSas extends React.Component { </div>; } } + +// List of Emoji strings from the js-sdk, for i18n +_td("Dog"); +_td("Cat"); +_td("Lion"); +_td("Horse"); +_td("Unicorn"); +_td("Pig"); +_td("Elephant"); +_td("Rabbit"); +_td("Panda"); +_td("Rooster"); +_td("Penguin"); +_td("Turtle"); +_td("Fish"); +_td("Octopus"); +_td("Butterfly"); +_td("Flower"); +_td("Tree"); +_td("Cactus"); +_td("Mushroom"); +_td("Globe"); +_td("Moon"); +_td("Cloud"); +_td("Fire"); +_td("Banana"); +_td("Apple"); +_td("Strawberry"); +_td("Corn"); +_td("Pizza"); +_td("Cake"); +_td("Heart"); +_td("Smiley"); +_td("Robot"); +_td("Hat"); +_td("Glasses"); +_td("Spanner"); +_td("Santa"); +_td("Thumbs up"); +_td("Umbrella"); +_td("Hourglass"); +_td("Clock"); +_td("Gift"); +_td("Light bulb"); +_td("Book"); +_td("Pencil"); +_td("Paperclip"); +_td("Scisors"); +_td("Padlock"); +_td("Key"); +_td("Hammer"); +_td("Telephone"); +_td("Flag"); +_td("Train"); +_td("Bicycle"); +_td("Aeroplane"); +_td("Rocket"); +_td("Trophy"); +_td("Ball"); +_td("Guitar"); +_td("Trumpet"); +_td("Bell"); +_td("Anchor"); +_td("Headphones"); +_td("Folder"); +_td("Pin"); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6fd9b4d190..0ff2df46ad 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -326,10 +326,75 @@ "You've successfully verified this user.": "You've successfully verified this user.", "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.", "Got It": "Got It", + "Verify this user by confirming the following emoji appear on their screen.": "Verify this user by confirming the following emoji appear on their screen.", "Verify this user by confirming the following number appears on their screen.": "Verify this user by confirming the following number appears on their screen.", + "Unable to find a supported verification method.": "Unable to find a supported verification method.", "For maximum security, we recommend you do this in person or use another trusted means of communication.": "For maximum security, we recommend you do this in person or use another trusted means of communication.", - "To continue, click on each pair to confirm it's correct.": "To continue, click on each pair to confirm it's correct.", "Continue": "Continue", + "Dog": "Dog", + "Cat": "Cat", + "Lion": "Lion", + "Horse": "Horse", + "Unicorn": "Unicorn", + "Pig": "Pig", + "Elephant": "Elephant", + "Rabbit": "Rabbit", + "Panda": "Panda", + "Rooster": "Rooster", + "Penguin": "Penguin", + "Turtle": "Turtle", + "Fish": "Fish", + "Octopus": "Octopus", + "Butterfly": "Butterfly", + "Flower": "Flower", + "Tree": "Tree", + "Cactus": "Cactus", + "Mushroom": "Mushroom", + "Globe": "Globe", + "Moon": "Moon", + "Cloud": "Cloud", + "Fire": "Fire", + "Banana": "Banana", + "Apple": "Apple", + "Strawberry": "Strawberry", + "Corn": "Corn", + "Pizza": "Pizza", + "Cake": "Cake", + "Heart": "Heart", + "Smiley": "Smiley", + "Robot": "Robot", + "Hat": "Hat", + "Glasses": "Glasses", + "Wrench": "Wrench", + "Santa": "Santa", + "Thumbs up": "Thumbs up", + "Umbrella": "Umbrella", + "Hourglass": "Hourglass", + "Clock": "Clock", + "Gift": "Gift", + "Light bulb": "Light bulb", + "Book": "Book", + "Pencil": "Pencil", + "Paperclip": "Paperclip", + "Scisors": "Scisors", + "Padlock": "Padlock", + "Key": "Key", + "Hammer": "Hammer", + "Telephone": "Telephone", + "Flag": "Flag", + "Train": "Train", + "Bicycle": "Bicycle", + "Aeroplane": "Aeroplane", + "Rocket": "Rocket", + "Trophy": "Trophy", + "Ball": "Ball", + "Guitar": "Guitar", + "Trumpet": "Trumpet", + "Bell": "Bell", + "Anchor": "Anchor", + "Headphones": "Headphones", + "Folder": "Folder", + "Pin": "Pin", "Failed to upload profile picture!": "Failed to upload profile picture!", "Upload new:": "Upload new:", "No display name": "No display name",