From 087dd0f758a977dc727acef4729db1719c5d91ec Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Thu, 12 Apr 2018 12:21:37 -0700 Subject: [PATCH] Support for iOS theme --- _locales/en/messages.json | 18 ++++ js/views/message_view.js | 15 ++-- stylesheets/_conversation.scss | 14 +-- stylesheets/_ios.scss | 124 +++++++++++++++++++++++++++ ts/components/conversation/Quote.md | 33 +++++++ ts/components/conversation/Quote.tsx | 33 +++++-- ts/styleguide/StyleGuideUtil.ts | 8 ++ 7 files changed, 226 insertions(+), 19 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 762177d7e..389f3ff1a 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -428,6 +428,24 @@ "selectAContact": { "message": "Select a contact or group to start chatting." }, + "replyingToYourself": { + "message": "Replying to Yourself", + "description": "Shown in iOS theme when you quote yourself" + }, + "replyingToYou": { + "message": "Replying to You", + "description": "Shown in iOS theme when someone else quotes a message from you" + }, + "replyingTo": { + "message": "Replying to $name`$", + "description": "Shown in iOS theme when you or someone quotes to a message which is not from you", + "placeholders": { + "name": { + "content": "$1", + "example": "John" + } + } + }, "audio": { "message": "Audio", "description": "Shown in a quotation of a message containing an audio attachment if no text was originally provided with that attachment" diff --git a/js/views/message_view.js b/js/views/message_view.js index ae37c451d..b1a764119 100644 --- a/js/views/message_view.js +++ b/js/views/message_view.js @@ -400,29 +400,30 @@ }); } + const OUR_NUMBER = textsecure.storage.user.getNumber(); const { author } = quote; const contact = ConversationController.get(author); + const authorTitle = contact ? contact.getTitle() : author; const authorProfileName = contact ? contact.getProfileName() : null; const authorColor = contact ? contact.getColor() : 'grey'; + const isFromMe = contact ? contact.id === OUR_NUMBER : false; const isIncoming = this.model.isIncoming(); - const quoterContact = this.model.getContact(); - const quoterAuthorColor = quoterContact ? quoterContact.getColor() : null; const props = { - authorTitle, - authorProfileName, + attachments: quote.attachments && quote.attachments.map(processAttachment), authorColor, + authorProfileName, + authorTitle, + isFromMe, isIncoming, - quoterAuthorColor, - openQuotedMessage: () => { + onClick: () => { const { quotedMessage } = this.model; if (quotedMessage) { this.trigger('scroll-to-message', { id: quotedMessage.id }); } }, text: quote.text, - attachments: quote.attachments && quote.attachments.map(processAttachment), }; if (!this.replyView) { diff --git a/stylesheets/_conversation.scss b/stylesheets/_conversation.scss index 3e56d985f..24935a3c1 100644 --- a/stylesheets/_conversation.scss +++ b/stylesheets/_conversation.scss @@ -379,11 +379,7 @@ li.entry .error-icon-container { display: none; } -.message-list .outgoing .bubble .quote { - margin-top: $android-bubble-quote-padding - $android-bubble-padding-vertical; -} - -.private .message-list .incoming .bubble .quote { +.message-list .outgoing .bubble .quote, .private .message-list .incoming .bubble .quote { margin-top: $android-bubble-quote-padding - $android-bubble-padding-vertical; } @@ -479,7 +475,7 @@ span.status { margin-bottom: 0.5em; // Accent color border: - border-left-width: 3; + border-left-width: 3px; border-left-style: solid; .primary { @@ -489,6 +485,12 @@ span.status { padding-top: 6px; padding-bottom: 6px; + // Will turn on in the iOS theme. This extra element is necessary because the iOS + // theme requires text that isn't used at all in the Android Theme + .ios-label { + display: none; + } + .author { font-weight: bold; margin-bottom: 0.3em; diff --git a/stylesheets/_ios.scss b/stylesheets/_ios.scss index a9ab48b96..ba613e37f 100644 --- a/stylesheets/_ios.scss +++ b/stylesheets/_ios.scss @@ -106,6 +106,130 @@ $ios-border-color: rgba(0,0,0,0.1); padding: 10px; } + .message-list { + .quote { + border-top-left-radius: 15px; + border-top-right-radius: 15px; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + + // Not ideal, but necessary to override the specificity of the android theme color + // classes used in conversations.scss + background-color: white !important; + border: 1px solid lightgray !important; + border-bottom: none !important; + + margin-top: 0px; + margin-bottom: 0px; + margin-left: 0px; + margin-right: 0px; + + .primary { + padding: 10px; + + .text, + .filename-label, + .type-label { + border-left: 2px solid lightgray; + padding: 5px; + padding-left: 7px; + // Without this smaller bottom padding, text beyond four lines still shows up! + padding-bottom: 2px; + color: black; + } + + .author { + display: none; + } + + .ios-label { + display: block; + color: lightgray; + font-size: smaller; + margin-bottom: 3px; + } + } + + .icon-container { + height: 61px; + width: 61px; + + .circle-background { + left: 12px; + right: 12px; + top: 12px; + bottom: 12px; + + background-color: $blue !important; + } + + .icon { + left: 18px; + right: 18px; + top: 18px; + bottom: 18px; + + background-color: white !important; + } + + .inner { + padding: 12px; + } + } + + .from-me { + .primary { + .text, + .filename-label, + .type-label { + border-left: 2px solid $blue; + } + } + } + } + + .incoming { + .bubble { + .quote { + background-color: lightgray !important; + border-left: none; + + .ios-label { + color: white; + } + + .primary { + .text, + .filename-label, + .type-label { + border-left: 2px solid white; + } + } + } + } + } + + .bubble { + .quote.from-me { + .primary { + .text, + .filename-label, + .type-label { + border-left: 2px solid $blue; + } + } + } + } + + .outgoing .bubble .quote, + .private .message-list .incoming .bubble .quote { + margin-top: 0px; + } + + .outgoing .bubble .quote .icon-container .circle-background { + background-color: lightgray !important; + } + } .attachments .bubbled { border-radius: 15px; diff --git a/ts/components/conversation/Quote.md b/ts/components/conversation/Quote.md index 609e12cdb..fb6920e94 100644 --- a/ts/components/conversation/Quote.md +++ b/ts/components/conversation/Quote.md @@ -34,6 +34,39 @@ const View = Whisper.MessageView; ``` +#### Replies to you or yourself + +```jsx +const outgoing = new Whisper.Message({ + type: 'outgoing', + body: 'About six', + sent_at: Date.now() - 18000000, + quote: { + text: 'How many ferrets do you have?', + author: util.ourNumber, + id: Date.now() - 1000, + }, +}); +const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, { + source: '+12025550011', + type: 'incoming', + quote: Object.assign({}, outgoing.attributes.quote, { + author: util.ourNumber, + }), +})); +const View = Whisper.MessageView; + + + + +``` + #### In a group conversation ```jsx diff --git a/ts/components/conversation/Quote.tsx b/ts/components/conversation/Quote.tsx index b19ec1b73..a3e78da7e 100644 --- a/ts/components/conversation/Quote.tsx +++ b/ts/components/conversation/Quote.tsx @@ -11,9 +11,9 @@ interface Props { authorProfileName?: string; authorTitle: string; i18n: (key: string, values?: Array) => string; + isFromMe: string; isIncoming: boolean; - openQuotedMessage?: () => void; - quoterAuthorColor?: string; + onClick?: () => void; text: string; } @@ -68,10 +68,10 @@ export class Quote extends React.Component { } public renderIcon(icon: string) { - const { authorColor, isIncoming, quoterAuthorColor } = this.props; + const { authorColor, isIncoming } = this.props; const backgroundColor = isIncoming ? 'white' : authorColor; - const iconColor = isIncoming ? quoterAuthorColor : 'white'; + const iconColor = isIncoming ? authorColor : 'white'; return (
@@ -138,12 +138,28 @@ export class Quote extends React.Component { return
{fileName}
; } + public renderIOSLabel() { + const { i18n, isIncoming, isFromMe, authorTitle, authorProfileName } = this.props; + + const profileString = authorProfileName ? ` ~${authorProfileName}` : ''; + const authorName = `${authorTitle}${profileString}`; + + const label = isFromMe + ? isIncoming + ? i18n('replyingToYou') + : i18n('replyingToYourself') + : i18n('replyingTo', [authorName]); + + return
{label}
; + } + public render() { const { authorTitle, authorProfileName, authorColor, - openQuotedMessage, + onClick, + isFromMe, } = this.props; if (!validateQuote(this.props)) { @@ -155,8 +171,13 @@ export class Quote extends React.Component { : null; return ( -
+
+ {this.renderIOSLabel()}
{authorTitle}{' '}{authorProfileElement}
diff --git a/ts/styleguide/StyleGuideUtil.ts b/ts/styleguide/StyleGuideUtil.ts index be8075e20..28dfb463f 100644 --- a/ts/styleguide/StyleGuideUtil.ts +++ b/ts/styleguide/StyleGuideUtil.ts @@ -141,9 +141,17 @@ const CONTACTS = COLORS.map((color, index) => { return parent.ConversationController.dangerouslyCreateAndAdd(contact); }); +const me = parent.ConversationController.dangerouslyCreateAndAdd({ + id: ourNumber, + name: 'Me!', + type: 'private', + color: 'light_blue', +}); + export { COLORS, CONTACTS, + me, }; parent.textsecure.storage.user.getNumber = () => ourNumber;