Render markdown

Fixes: https://github.com/status-im/trailofbits-audit/issues/47
Fixes: https://github.com/status-im/trailofbits-audit/issues/46
Fixes: https://github.com/status-im/trailofbits-audit/issues/44
Fixes: https://github.com/status-im/security-reports/issues/13
Fixes: https://github.com/status-im/security-reports/issues/5
Fixes: https://github.com/status-im/status-react/issues/8995

This commits re-introduce rendering of markdown text and implent a few
changes:

1) Parsing of the message content is now in status-go, this includes
markdown, line-count, and rtl. Parsing is not nested, as there's some
rendering degradation involved as we nest components, unclear exactly if
it's react-native or clojure, haven't looked too deeply into it.
2) Emojii type messages are not parsed on the sending side, not the
receiving one, using the appropriate content-type
3) Fixes a few issues with chat input rendering, currrently we use
`chats/current-chat` subscription which is very heavy and should not be
used unless necessary, and means that
any change to chat will trigger a re-render, which caused re-rendering
of input container on each received message. Also to note that
input-container is fairly heavy to render, and it's rendered twice at
each keypress on input.

The inline markdow supported is:

*italic* or _italic_
**bold** or __bold__
`inline code`
http://test.com links
\#status-tag

The block markdown supported is:

\# Headers
```
code blocks
```
> Quotereply

The styling is very basic at the moment, but can be improved.
Adding other markdown (photo,mentions) is straightforward and should
come at little performance cost (unless the component to render is
heavy, i.e a photo for example).

There are some behavioral changes with this commit:

1) Links are only parsed if starting with http:// or https://, meaning that
blah.com won't be parsed, nor www.test.com. This behavior is consistent
with discord for example and allows faster parsing at little expense to
ser experience imo. Fixes a few security issues as well.

2) Content is not anymore capped (regression), that's due to the fact that
before we only rendered text and react-native allowed us easily to limit
the number of lines, but adding markdown support means that this
strategy is not viable anymore. Performance of rendering don't see to be
very much impacted by this, I would re-introduce it if necessary, but
I'd rather do that in a separate PR.

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2019-11-07 14:41:37 +01:00
parent 3127f2fcb2
commit 9a9c0ce526
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
20 changed files with 338 additions and 170 deletions

View File

@ -589,6 +589,8 @@ var TopLevel = {
"prev": function() {},
"hasNext": function() {},
"hasPrev": function() {},
"rtl": function() {},
"lineCount": function() {},
"key": function() {},
"keys": function() {},
"values": function() {},

View File

@ -7,6 +7,7 @@
[status-im.chat.commands.sending :as commands.sending]
[status-im.chat.constants :as chat.constants]
[status-im.chat.models :as chat]
[status-im.chat.models.message-content :as message-content]
[status-im.chat.models.message :as chat.message]
[status-im.constants :as constants]
[status-im.js-dependencies :as dependencies]
@ -125,11 +126,15 @@
(let [{:keys [message-id]}
(get-in db [:chats current-chat-id :metadata :responding-to-message])
show-name? (get-in db [:multiaccount :show-name?])
preferred-name (when show-name? (get-in db [:multiaccount :preferred-name]))]
preferred-name (when show-name? (get-in db [:multiaccount :preferred-name]))
emoji? (message-content/emoji-only-content? {:text input-text
:response-to message-id})]
(fx/merge cofx
{:db (assoc-in db [:chats current-chat-id :metadata :responding-to-message] nil)}
(chat.message/send-message {:chat-id current-chat-id
:content-type constants/content-type-text
:content-type (if emoji?
constants/content-type-emoji
constants/content-type-text)
:content (cond-> {:chat-id current-chat-id
:text input-text}
message-id
@ -140,15 +145,6 @@
(set-chat-input-text nil)
(process-cooldown)))))
(defn send-plain-text-message-fx
"no command detected, when not empty, proceed by sending text message without command processing"
[{:keys [db] :as cofx} message-text current-chat-id]
(when-not (string/blank? message-text)
(chat.message/send-message cofx {:chat-id current-chat-id
:content-type constants/content-type-text
:content (cond-> {:chat-id current-chat-id
:text message-text})})))
(fx/defn send-sticker-fx
[{:keys [db] :as cofx} {:keys [hash pack]} current-chat-id]
(when-not (string/blank? hash)

View File

@ -2,6 +2,7 @@
(:require [re-frame.core :as re-frame]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.chat.commands.receiving :as commands-receiving]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.chat.db :as chat.db]
[status-im.chat.models :as chat-model]
[status-im.chat.models.loading :as chat-loading]
@ -37,17 +38,14 @@
(defn- prepare-message
[{:keys [content content-type] :as message} chat-id current-chat?]
(let [emoji? (message-content/emoji-only-content? content)]
;; TODO janherich: enable the animations again once we can do them more efficiently
(cond-> message
current-chat?
(assoc :seen true)
(cond-> message
current-chat?
(assoc :seen true) (and (= constants/content-type-text content-type)
(message-content/should-collapse?
(:text content)
(:line-count content)))
emoji?
(assoc :content-type constants/content-type-emoji)
(and (= constants/content-type-text content-type) (not emoji?))
(update :content message-content/enrich-content))))
(assoc :should-collapse? true)))
(defn system-message? [{:keys [message-type]}]
(= :system-message message-type))
@ -250,24 +248,46 @@
:message message
:current-chat? (= (get-in cofx [:db :current-chat-id]) chat-id)})))
(fx/defn upsert-and-send
[{:keys [now] :as cofx} {:keys [chat-id from] :as message}]
(let [message (remove-icon message)
send-record (protocol/map->Message (select-keys message transport-keys))
(fx/defn prepare-message-content [cofx chat-id message]
{::json-rpc/call
[{:method "shhext_prepareContent"
:params [(:content message)]
:on-success #(re-frame/dispatch [::prepared-message chat-id message %])
:on-failure #(log/error "failed to prepare content" %)}]})
(fx/defn prepared-message
{:events [::prepared-message]}
[{:keys [now] :as cofx} chat-id message content]
(let [message-with-content
(update message :content
assoc
:parsed-text (:parsedText content)
:line-count (:lineCount content)
:should-collapse? (message-content/should-collapse?
(:text content)
(:lineCount content))
:rtl? (:rtl content))
send-record (protocol/map->Message
(select-keys message-with-content transport-keys))
wrapped-record (if (= (:message-type send-record) :group-user-message)
(wrap-group-message cofx chat-id send-record)
send-record)
message (assoc message :outgoing-status :sending)]
send-record)]
(fx/merge cofx
(chat-model/upsert-chat
{:chat-id chat-id
:timestamp now
:last-message-timestamp (:timestamp message)
:last-message-content (:content message)
:last-message-content-type (:content-type message)
:last-clock-value (:clock-value message)})
(send chat-id message wrapped-record))))
:last-message-timestamp (:timestamp message-with-content)
:last-message-content (:content message-with-content)
:last-message-content-type (:content-type message-with-content)
:last-clock-value (:clock-value message-with-content)})
(send chat-id message-with-content wrapped-record))))
(fx/defn upsert-and-send
[{:keys [now] :as cofx} {:keys [chat-id from] :as message}]
(let [message (remove-icon message)
message (assoc message :outgoing-status :sending)]
(prepare-message-content cofx chat-id message)))
(fx/defn update-message-status
[{:keys [db] :as cofx} chat-id message-id status]

View File

@ -40,9 +40,9 @@
(and (seq text)
(re-matches constants/regx-rtl-characters (first text))))
(defn- should-collapse? [text]
(defn should-collapse? [text line-count]
(or (<= constants/chars-collapse-threshold (count text))
(<= constants/lines-collapse-threshold (inc (count (query-regex #"\n" text))))))
(<= constants/lines-collapse-threshold (inc line-count))))
(defn- sorted-ranges [{:keys [metadata text]} metadata-keys]
(->> (if metadata-keys
@ -81,28 +81,6 @@
(cond-> builder
end-record (conj end-record))))))
(defn enrich-content
"Enriches message content with `:metadata`, `:render-recipe` and `:rtl?` information.
Metadata map keys can by any of the `:link`, `:tag`, `:mention` actions
or `:bold` and `:italic` stylings.
Value for each key is sequence of tuples representing ranges in original
`:text` content. "
[{:keys [text] :as content}]
(let [[_ metadata] (reduce (fn [[text metadata] [type regex]]
(if-let [matches (query-regex regex text)]
[(clear-ranges matches text) (assoc metadata type matches)]
[text metadata]))
[text {}]
(if platform/desktop?
(into stylings actions)
actions))]
(cond-> content
(seq metadata) (as-> content
(assoc content :metadata metadata)
(assoc content :render-recipe (build-render-recipe content)))
(right-to-left-text? text) (assoc :rtl? true)
(should-collapse? text) (assoc :should-collapse? true))))
(defn emoji-only-content?
"Determines if text is just an emoji"
[{:keys [text response-to]}]

View File

@ -51,6 +51,7 @@
"shhext_chatMessages" {}
"shhext_saveChat" {}
"shhext_contacts" {}
"shhext_prepareContent" {}
"shhext_blockContact" {}
;;TODO not used anywhere?
"shhext_deleteChat" {}

View File

@ -616,11 +616,6 @@
{:db (assoc db :view-id view-id)}
#(mark-messages-seen %))))
(handlers/register-handler-fx
:chat/send-plain-text-message
(fn [{{:keys [current-chat-id]} :db :as cofx} [_ message-text]]
(chat.input/send-plain-text-message-fx cofx message-text current-chat-id)))
(handlers/register-handler-fx
:chat/send-sticker
(fn [{{:keys [current-chat-id multiaccount]} :db :as cofx} [_ {:keys [hash] :as sticker}]]

View File

@ -3,7 +3,9 @@
(:require [clojure.set :as clojure.set]
[clojure.spec.alpha :as spec]
[clojure.string :as string]
[status-im.ethereum.json-rpc :as json-rpc]
[re-frame.core :as re-frame]
[status-im.chat.models.message-content :as message-content]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.utils.pairing :as pairing.utils]
@ -463,6 +465,43 @@
(transport.filters/upsert-group-chat-topics)
(transport.filters/load-members members)))))
(fx/defn prepared-message
{:events [::prepared-message]}
[{:keys [now] :as cofx}
chat-id message
content
sender-signature
whisper-timestamp
metadata]
(let [message-with-content
(update message :content
assoc
:parsed-text (:parsedText content)
:line-count (:lineCount content)
:should-collapse? (message-content/should-collapse?
(:text content)
(:lineCount content))
:rtl? (:rtl content))]
(protocol/receive message-with-content
chat-id
sender-signature
whisper-timestamp
(assoc cofx :metadata metadata))))
(fx/defn prepare-message-content
[cofx chat-id message sender-signature whisper-timestamp metadata]
{::json-rpc/call
[{:method "shhext_prepareContent"
:params [(:content message)]
:on-success #(re-frame/dispatch [::prepared-message
chat-id
message
%
sender-signature
whisper-timestamp
metadata])
:on-failure #(log/error "failed to prepare content" %)}]})
(fx/defn handle-membership-update
"Upsert chat and receive message if valid"
;; Care needs to be taken here as chat-id is not coming from a whisper filter
@ -498,11 +537,13 @@
;; don't allow anything but group messages
(instance? protocol/Message message)
(= :group-user-message (:message-type message)))
(protocol/receive message
chat-id
sender-signature
whisper-timestamp
(assoc % :metadata metadata))))))))
(prepare-message-content
%
chat-id
message
sender-signature
whisper-timestamp
metadata)))))))
(defn handle-sign-success
"Upsert chat and send signed payload to group members"

View File

@ -505,9 +505,9 @@
(re-frame/reg-sub
::show-suggestions-view?
:<- [:chats/current-chat-ui-prop :show-suggestions?]
:<- [:chats/current-chat]
:<- [:chats/current-chat-input-text]
:<- [:chats/all-available-commands]
(fn [[show-suggestions? {:keys [input-text]} commands]]
(fn [[show-suggestions? input-text commands]]
(and (or show-suggestions?
(commands.input/starts-as-command? (string/trim (or input-text ""))))
(seq commands))))
@ -523,7 +523,7 @@
::get-commands-for-chat
:<- [:chats/id->command]
:<- [::access-scope->command-id]
:<- [:chats/current-chat]
:<- [:chats/current-raw-chat]
(fn [[id->command access-scope->command-id chat]]
(commands/chat-commands id->command access-scope->command-id chat)))
@ -666,9 +666,21 @@
:messages))))
(re-frame/reg-sub
:chats/current-chat
:chats/current-raw-chat
:<- [:chats/active-chats]
:<- [:chats/current-chat-id]
(fn [[chats current-chat-id]]
(get chats current-chat-id)))
(re-frame/reg-sub
:chats/current-chat-input-text
:<- [:chats/current-raw-chat]
(fn [chat]
(:input-text chat)))
(re-frame/reg-sub
:chats/current-chat
:<- [:chats/current-raw-chat]
:<- [:multiaccount/public-key]
:<- [:mailserver/ranges]
:<- [:chats/content-layout-height]
@ -677,27 +689,28 @@
:<- [:ethereum/chain-keyword]
:<- [:prices]
:<- [:wallet/currency]
(fn [[chats current-chat-id my-public-key ranges height
(fn [[{:keys [group-chat
chat-id
contact
messages]
:as current-chat} my-public-key ranges height
input-height ttt-settings chain-keyword prices currency]]
(let [{:keys [group-chat contact messages]
:as current-chat}
(get chats current-chat-id)]
(when current-chat
(cond-> (enrich-current-chat current-chat ranges height input-height)
(empty? messages)
(assoc :universal-link
(links/generate-link :public-chat :external current-chat-id))
(when current-chat
(cond-> (enrich-current-chat current-chat ranges height input-height)
(empty? messages)
(assoc :universal-link
(links/generate-link :public-chat :external chat-id))
(chat.models/public-chat? current-chat)
(assoc :show-input? true)
(chat.models/public-chat? current-chat)
(assoc :show-input? true)
(and (chat.models/group-chat? current-chat)
(group-chats.db/joined? my-public-key current-chat))
(assoc :show-input? true)
(and (chat.models/group-chat? current-chat)
(group-chats.db/joined? my-public-key current-chat))
(assoc :show-input? true)
(not group-chat)
(enrich-current-one-to-one-chat my-public-key ttt-settings
chain-keyword prices currency))))))
(not group-chat)
(enrich-current-one-to-one-chat my-public-key ttt-settings
chain-keyword prices currency)))))
(re-frame/reg-sub
:chats/current-chat-message
@ -798,10 +811,10 @@
(re-frame/reg-sub
:chats/selected-chat-command
:<- [:chats/current-chat]
:<- [:chats/current-chat-input-text]
:<- [:chats/current-chat-ui-prop :selection]
:<- [::get-commands-for-chat]
(fn [[{:keys [input-text]} selection commands]]
(fn [[input-text selection commands]]
(commands.input/selected-chat-command input-text selection commands)))
(re-frame/reg-sub

View File

@ -59,6 +59,7 @@
(spec/def :message.content/params (spec/map-of keyword? any?))
(spec/def ::content-type #{constants/content-type-text constants/content-type-command
constants/content-type-emoji
constants/content-type-command-request constants/content-type-sticker})
(spec/def ::message-type #{:group-user-message :public-group-user-message :user-message})
(spec/def ::clock-value (spec/and pos-int?

View File

@ -12,6 +12,7 @@
[status-im.transport.message.transit :as transit]
[status-im.transport.utils :as transport.utils]
[status-im.tribute-to-talk.whitelist :as whitelist]
[cljs-bean.core :as clj-bean]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
@ -19,14 +20,20 @@
(def message-type-message 1)
(defn build-content [content-js]
{:text (.-text content-js)
:line-count (.-lineCount content-js)
:parsed-text (clj-bean/->clj (.-parsedText content-js))
:name (.-name content-js)
:rtl? (.-rtl content-js)
:response-to (aget content-js "response-to")
:chat-id (.-chat_id content-js)})
(defn build-message [parsed-message-js]
(let [content (.-content parsed-message-js)
built-message
(protocol/Message.
{:text (.-text content)
:response-to (aget content "response-to")
:name (.-name content)
:chat-id (.-chat_id content)}
(build-content content)
(.-content_type parsed-message-js)
(keyword (.-message_type parsed-message-js))
(.-clock parsed-message-js)

View File

@ -25,7 +25,7 @@
(def black-transparent (alpha black 0.1)) ;; Used as background color for rounded button on dark background and as background color for containers like "Backup recovery phrase"
(def black-transparent-20 (alpha black 0.2)) ; accounts divider
(def black-transparent-40 (alpha black 0.4))
(def black-transparent-50 (alpha black 0.5))
(def black-light "#2d2d2d") ;; sign-with-keycard-button
;; DARK GREY

View File

@ -24,7 +24,7 @@
[status-im.ui.screens.chat.stickers.views :as stickers]))
(defview basic-text-input [{:keys [set-container-width-fn height single-line-input?]}]
(letsubs [{:keys [input-text]} [:chats/current-chat]
(letsubs [input-text [:chats/current-chat-input-text]
cooldown-enabled? [:chats/cooldown-enabled?]]
[react/text-input
(merge
@ -91,7 +91,7 @@
{:placeholder (i18n/label :cooldown/text-input-disabled)}))]))
(defview invisible-input [{:keys [set-layout-width-fn value]}]
(letsubs [{:keys [input-text]} [:chats/current-chat]]
(letsubs [input-text [:chats/current-chat-input-text]]
[react/text {:style style/invisible-input-text
:on-layout #(let [w (-> (.-nativeEvent %)
(.-layout)
@ -184,7 +184,7 @@
(defview input-container []
(letsubs [margin [:chats/input-margin]
mainnet? [:mainnet?]
{:keys [input-text]} [:chats/current-chat]
input-text [:chats/current-chat-input-text]
result-box [:chats/current-chat-ui-prop :result-box]
show-stickers? [:chats/current-chat-ui-prop :show-stickers?]
state-text (reagent/atom "")]

View File

@ -2,8 +2,11 @@
(:require [re-frame.core :as re-frame]
[status-im.chat.commands.receiving :as commands-receiving]
[status-im.constants :as constants]
[status-im.utils.http :as http]
[status-im.i18n :as i18n]
[reagent.core :as reagent]
[status-im.ui.components.colors :as colors]
[status-im.utils.security :as security]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.popup-menu.views :as desktop.pop-up]
@ -66,32 +69,89 @@
:on-press #(re-frame/dispatch [:chat.ui/message-expand-toggled chat-id message-id])}
(i18n/label (if expanded? :show-less :show-more))])
(defn render-inline [message-text outgoing acc {:keys [type literal destination] :as node}]
(case type
""
(conj acc literal)
"code"
(conj acc [react/text-class style/inline-code-style literal])
"emph"
(conj acc [react/text-class (style/emph-style outgoing) literal])
"strong"
(conj acc [react/text-class (style/strong-style outgoing) literal])
"link"
(conj acc
[react/text-class
{:style
{:color (if outgoing colors/white colors/blue)
:text-decoration-line :underline}
:on-press
#(when (and (security/safe-link? destination)
(security/safe-link-text? message-text))
(if platform/desktop?
(.openURL react/linking (http/normalize-url destination))
(re-frame/dispatch
[:browser.ui/message-link-pressed destination])))}
destination])
"status-tag"
(conj acc [react/text-class
{:style {:color (if outgoing colors/white colors/blue)
:text-decoration-line :underline}
:on-press
#(re-frame/dispatch
[:chat.ui/start-public-chat literal {:navigation-reset? true}])}
"#"
literal])
(conj acc literal)))
(defn render-block [{:keys [chat-id message-id content
timestamp-str group-chat outgoing
current-public-key expanded?] :as message}
acc
{:keys [type literal children]}]
(case type
"paragraph"
(conj acc (reduce
(fn [acc e] (render-inline (:text content) outgoing acc e))
[react/text-class (style/text-style outgoing)]
children))
"blockquote"
(conj acc [react/view (style/blockquote-style outgoing)
[react/text-class (style/blockquote-text-style outgoing)
(.substring literal 0 (dec (.-length literal)))]])
"codeblock"
(conj acc [react/view style/codeblock-style
[react/text-class style/codeblock-text-style
(.substring literal 0 (dec (.-length literal)))]])
acc))
(defn render-parsed-text [{:keys [timestamp-str
outgoing] :as message}
tree]
(conj (reduce (fn [acc e] (render-block message acc e)) [react/view {}] tree)
[react/text {:style (style/message-timestamp-placeholder outgoing)}
(str " " timestamp-str)]))
(defn text-message
[{:keys [chat-id message-id content
timestamp-str group-chat outgoing current-public-key expanded?] :as message}]
[message-view message
(let [response-to (:response-to content)
collapsible? (and (:should-collapse? content) group-chat)]
(let [response-to (:response-to content)]
[react/view
(when response-to
(when (seq response-to)
[quoted-message response-to (:quoted-message message) outgoing current-public-key])
(apply react/nested-text
(cond-> {:style (style/text-message collapsible? outgoing)
:text-break-strategy :balanced
:parseBasicMarkdown true
:markdownCodeBackgroundColor colors/black
:markdownCodeForegroundColor colors/green}
(and collapsible? (not expanded?))
(assoc :number-of-lines constants/lines-collapse-threshold))
(conj (if-let [render-recipe (:render-recipe content)]
(chat.utils/render-chunks render-recipe message)
[(:text content)])
[{:style (style/message-timestamp-placeholder outgoing)}
(str " " timestamp-str)]))
(when collapsible?
[expand-button expanded? chat-id message-id])])
[render-parsed-text message (:parsed-text content)]])
{:justify-timestamp? true}])
(defn emoji-message

View File

@ -1,8 +1,10 @@
(ns status-im.ui.screens.chat.styles.message.message
(:require [status-im.constants :as constants]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.react :as react]
[status-im.ui.screens.chat.styles.photos :as photos]
[status-im.utils.platform :as platform]
[status-im.ui.components.typography :as typography]
[status-im.utils.styles :as styles]))
(defn style-message-text
@ -216,3 +218,98 @@
:color (if outgoing
colors/white-transparent-70
colors/gray)})
;; Markdown styles
(def default-text-style
{:max-font-size-multiplier react/max-font-size-multiplier
:style (assoc typography/default-style
:line-height 22)})
(def outgoing-text-style
(update default-text-style :style
assoc :color colors/white))
(defn text-style [outgoing]
(if outgoing
outgoing-text-style
default-text-style))
(def emph-text-style
(update default-text-style :style
assoc :font-style :italic))
(def outgoing-emph-text-style
(update emph-text-style :style
assoc :color colors/white))
(defn emph-style [outgoing]
(if outgoing
outgoing-emph-text-style
emph-text-style))
(def strong-text-style
(update default-text-style :style
assoc :font-weight "700"))
(def outgoing-strong-text-style
(update strong-text-style :style
assoc :color colors/white))
(defn strong-style [outgoing]
(if outgoing
outgoing-strong-text-style
strong-text-style))
(def monospace-fonts (if platform/ios? "Courier" "monospace"))
(def code-block-background "#2E386B")
(def inline-code-style
(update default-text-style :style
assoc
:font-family monospace-fonts
:color colors/white
:background-color code-block-background))
(def codeblock-style {:style {:padding 10
:background-color code-block-background
:border-radius 4}})
(def codeblock-text-style
(update default-text-style :style
assoc
:font-family monospace-fonts
:color colors/white))
(def default-blockquote-style
{:style {:border-left-width 2
:padding-left 3
:border-left-color colors/gray-transparent-40}})
(def outgoing-blockquote-style
(update default-blockquote-style :style
assoc
:border-left-color colors/white-transparent))
(defn blockquote-style [outgoing]
(if outgoing
outgoing-blockquote-style
default-blockquote-style))
(def default-blockquote-text-style
(update default-text-style :style
assoc
:line-height 19
:font-size 14
:color colors/black-transparent-50))
(def outgoing-blockquote-text-style
(update default-blockquote-text-style :style
assoc
:color colors/white-transparent-70))
(defn blockquote-text-style [outgoing]
(if outgoing
outgoing-blockquote-text-style
default-blockquote-text-style))

View File

@ -24,7 +24,7 @@
data))
;; Links starting with javascript:// should not be handled at all
(def javascript-link-regex #"javascript://.*")
(def javascript-link-regex #"(?i)javascript://.*")
;; Anything with rtlo character we don't handle as it might be a spoofed url
(def rtlo-link-regex #".*\u202e.*")

View File

@ -2,7 +2,7 @@
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
"owner": "status-im",
"repo": "status-go",
"version": "v0.34.0-beta.7",
"commit-sha1": "89659f85b49b48f4409ed9f522397b99728b214b",
"src-sha256": "0kyk3r2wl3qxz28ifgnk2r8lh5116q8s58pk9x044dsrl0zvn5qv"
"version": "v0.34.0-beta.8",
"commit-sha1": "9d7c570593b1f88e9688204d8e1041d57b98da1f",
"src-sha256": "1kwxf0h80vdj493c7s3lpmdjpbc72plbll0rj1vv7kzf8a84ff2k"
}

View File

@ -207,7 +207,7 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
home_2.home_button.click()
chat_1 = home_1.add_contact(public_key_2)
url_message = 'status.im'
url_message = 'http://status.im'
chat_1.chat_message_input.send_keys(url_message)
chat_1.send_message_button.click()
chat_1.get_back_to_home_view()

View File

@ -1,45 +0,0 @@
(ns status-im.test.chat.models.message-content
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.platform :as platform]
[status-im.chat.models.message-content :as message-content]))
(deftest enrich-string-content-test
(if platform/desktop?
(testing "Text content of the message is enriched correctly"
(is (not (:metadata (message-content/enrich-content {:text "Plain message"}))))
(is (= {:bold [[5 14]]}
(:metadata (message-content/enrich-content {:text "Some *styling* present"}))))
(is (= {:bold [[5 14]]
:tag [[28 33] [38 43]]}
(:metadata (message-content/enrich-content {:text "Some *styling* present with #tag1 and #tag2 as well"}))))))
(testing "right to left is correctly identified"
(is (not (:rtl? (message-content/enrich-content {:text "You are lucky today!"}))))
(is (not (:rtl? (message-content/enrich-content {:text "42"}))))
(is (not (:rtl? (message-content/enrich-content {:text "You are lucky today! أنت محظوظ اليوم!"}))))
(is (not (:rtl? (message-content/enrich-content {:text "۱۲۳۴۵۶۷۸۹"}))))
(is (not (:rtl? (message-content/enrich-content {:text "۱۲۳۴۵۶۷۸۹أنت محظوظ اليوم!"}))))
(is (:rtl? (message-content/enrich-content {:text "أنت محظوظ اليوم!"})))
(is (:rtl? (message-content/enrich-content {:text "أنت محظوظ اليوم! You are lucky today"})))
(is (:rtl? (message-content/enrich-content {:text "יש לך מזל היום!"})))))
(deftest build-render-recipe-test
(testing "Render tree is build from text"
(is (not (:render-recipe (message-content/enrich-content {:text "Plain message"}))))
(is (= (if platform/desktop?
'(["Test " :text]
["#status" :tag]
[" one three " :text]
["#core-chat (@developer)!" :bold]
[" By the way, " :text]
["nice link(https://link.com)" :italic])
'(["Test " :text]
["#status" :tag]
[" one three *" :text]
["#core-chat" :tag]
[" (" :text]
["@developer" :mention]
[")!* By the way, ~nice link(" :text]
["https://link.com" :link]
[")~" :text]))
(:render-recipe (message-content/enrich-content {:text "Test #status one three *#core-chat (@developer)!* By the way, ~nice link(https://link.com)~"}))))))

View File

@ -7,7 +7,6 @@
[status-im.test.chat.commands.input]
[status-im.test.chat.db]
[status-im.test.chat.models.input]
[status-im.test.chat.models.message-content]
[status-im.test.chat.models.message]
[status-im.test.chat.models.message-list]
[status-im.test.chat.models]
@ -87,7 +86,6 @@
'status-im.test.chat.models.input
'status-im.test.chat.models.message
'status-im.test.chat.models.message-list
'status-im.test.chat.models.message-content
'status-im.test.chat.views.photos
'status-im.test.transport.filters.core
'status-im.test.contacts.db

View File

@ -16,6 +16,10 @@
(deftest safe-link-test-exceptions
(testing "a javascript link"
(is (not (security/safe-link? "javascript://anything"))))
(testing "a javascript link mixed cases"
(is (not (security/safe-link? "JaVasCrIpt://anything"))))
(testing "a javascript link upper cases"
(is (not (security/safe-link? "JAVASCRIPT://anything"))))
(testing "rtlo links"
(is (not (security/safe-link? rtlo-link)))))