Skip to content

Commit

Permalink
[mentions] Replace all mentions with public keys before sending message
Browse files Browse the repository at this point in the history
  • Loading branch information
rasom committed Aug 24, 2020
1 parent 001789f commit 2934de9
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 8 deletions.
8 changes: 5 additions & 3 deletions src/status_im/chat/models/input.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
[status-im.i18n :as i18n]
[status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx]
["emojilib" :as emojis]))
["emojilib" :as emojis]
[status-im.chat.models.mentions :as mentions]))

(defn text->emoji
"Replaces emojis in a specified `text`"
Expand Down Expand Up @@ -130,7 +131,8 @@
(fx/defn send-current-message
"Sends message from current chat input"
[{{:keys [current-chat-id] :as db} :db :as cofx}]
(let [{:keys [input-text]} (get-in db [:chat/inputs current-chat-id])]
(let [{:keys [input-text]} (get-in db [:chat/inputs current-chat-id])
input-text-with-mentions (mentions/check-mentions cofx input-text)]
(fx/merge cofx
(send-image)
(send-plain-text-message input-text current-chat-id))))
(send-plain-text-message input-text-with-mentions current-chat-id))))
123 changes: 123 additions & 0 deletions src/status_im/chat/models/mentions.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
(ns status-im.chat.models.mentions
(:require [clojure.string :as string]
[status-im.contact.db :as contact.db]))

(defn get-mentionable-users
[{{:keys [current-chat-id messages]
:contacts/keys [contacts] :as db} :db}]
(let [{:keys [group-chat public?] :as chat}
(get-in db [:chats current-chat-id])
chat-specific-suggestions
(cond
(and group-chat (not public?))
(let [{:keys [public-key]} (:multiaccount db)
all-contacts (:contacts/contacts db)
group-contacts
(contact.db/get-all-contacts-in-group-chat
(disj (:contacts chat) public-key)
nil
all-contacts
nil)]
(reduce
(fn [acc {:keys [alias public-key identicon name]}]
(assoc acc alias
{:alias alias
:identicon identicon
:public-key public-key
:name name}))
{}
group-contacts))

(and group-chat public?)
(reduce
(fn [acc [_ {:keys [alias name identicon from]}]]
(assoc acc alias {:alias alias
:name (or name alias)
:identicon identicon
:public-key from}))
nil
(get messages current-chat-id))

:else {})]
(reduce
(fn [acc [key {:keys [alias name identicon]}]]
(let [name (string/replace name ".stateofus.eth" "")]
(assoc acc alias {:alias alias
:name (or name alias)
:identicon identicon
:public-key key})))
chat-specific-suggestions
contacts)))

(def word-regex #"^[\S]*\s|^[\S]*$")

(defn mentioned? [{:keys [alias name]} text]
(let [lcase-name (string/lower-case name)
lcase-alias (string/lower-case alias)
regex (re-pattern
(string/join
"|"
[(str "^" lcase-name "\\s")
(str "^" lcase-name "$")
(str "^" lcase-alias "\\s")
(str "^" lcase-alias "$")]))
lcase-text (string/lower-case text)]
(re-find regex lcase-text)))

(defn match-mention
([text users mention-key-idx]
(match-mention text users mention-key-idx (inc mention-key-idx) []))
([text users mention-key-idx next-word-idx words]
(let [trimmed-text (subs text next-word-idx)]
(when-let [word (string/trim (re-find word-regex trimmed-text))]
(let [words (conj words word)
searched-text (string/lower-case (string/join " " words))
suggestions (filter
(fn [[_ {:keys [alias name]}]]
(let [names (set [alias name])]
(some
(fn [username]
(string/starts-with?
(string/lower-case username)
searched-text))
names)))
users)
suggestions-cnt (count suggestions)]
(cond (zero? suggestions-cnt)
nil

(and (= 1 suggestions-cnt)
(mentioned? (second (first suggestions))
(subs text (inc mention-key-idx))))
(second (first suggestions))

(> suggestions-cnt 1)
(let [word-len (count word)
text-len (count text)
next-word-start (+ next-word-idx (inc word-len))]
(when (> text-len next-word-start)
(match-mention text users mention-key-idx
next-word-start words)))))))))

(defn replace-mentions
([text users-fn]
(replace-mentions text users-fn 0))
([text users-fn idx]
(let [mention-key-idx (string/index-of text "@" idx)]
(if-not mention-key-idx
text
(let [users (users-fn)
{:keys [public-key alias]}
(match-mention text users mention-key-idx)]
(if-not alias
(recur text (fn [] users) (inc mention-key-idx))
(let [new-text (string/join
[(subs text 0 (inc mention-key-idx))
public-key
(subs text (+ (inc mention-key-idx)
(count alias)))])
mention-end (+ (inc mention-key-idx) (count public-key))]
(recur new-text (fn [] users) mention-end))))))))

(defn check-mentions [cofx text]
(replace-mentions text #(get-mentionable-users cofx)))
78 changes: 78 additions & 0 deletions src/status_im/chat/models/mentions_test.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
(ns status-im.chat.models.mentions-test
(:require [status-im.chat.models.mentions :as mentions]
[clojure.string :as string]
[cljs.test :as test :include-macros true]))

(test/deftest test-replace-mentions
(let [users (fn []
{"User Number One"
{:name "User Number One"
:alias "User Number One"
:public-key "0xpk1"}
"User Number Two"
{:name "user2"
:alias "User Number Two"
:public-key "0xpk2"}
"User Number Three"
{:name "user3"
:alias "User Number Three"
:public-key "0xpk3"}})]
(test/testing "no mentions"
(let [text "foo bar @buzz kek @foo"
result (mentions/replace-mentions text users)]
(test/is (= result text) (pr-str text))))

(test/testing "starts with mention"
(let [text "@User Number One"
result (mentions/replace-mentions text users)]
(test/is (= result "@0xpk1") (pr-str text))))

(test/testing "starts with mention but no space after"
(let [text "@User Number Onefoo"
result (mentions/replace-mentions text users)]
(test/is (= result text) (pr-str text))))

(test/testing "starts with mention, some text after mention"
(let [text "@User Number One foo"
result (mentions/replace-mentions text users)]
(test/is (= result "@0xpk1 foo") (pr-str text))))

(test/testing "starts with some text, then mention"
(let [text "text @User Number One"
result (mentions/replace-mentions text users)]
(test/is (= result "text @0xpk1") (pr-str text))))

(test/testing "starts with some text, then mention, then more text"
(let [text "text @User Number One foo"
result (mentions/replace-mentions text users)]
(test/is (= result "text @0xpk1 foo") (pr-str text))))

(test/testing "no space before mention"
(let [text "text@User Number One"
result (mentions/replace-mentions text users)]
(test/is (= result "text@0xpk1") (pr-str text))))

(test/testing "two different mentions"
(let [text "@User Number One @User Number two"
result (mentions/replace-mentions text users)]
(test/is (= result "@0xpk1 @0xpk2") (pr-str text))))

(test/testing "two different mentions inside text"
(let [text "foo@User Number One bar @User Number two baz"
result (mentions/replace-mentions text users)]
(test/is (= result "foo@0xpk1 bar @0xpk2 baz") (pr-str text))))

(test/testing "ens mention"
(let [text "@user2"
result (mentions/replace-mentions text users)]
(test/is (= result "@0xpk2") (pr-str text))))

(test/testing "multiple mentions"
(let [text (string/join
" "
(repeat 1000 "@User Number One @User Number two"))
result (mentions/replace-mentions text users)
exprected-result (string/join
" "
(repeat 1000 "@0xpk1 @0xpk2"))]
(test/is (= exprected-result result))))))
13 changes: 8 additions & 5 deletions src/status_im/contact/db.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@

(defn get-all-contacts-in-group-chat
[members admins contacts {:keys [public-key] :as current-account}]
(let [current-contact (-> current-account
(select-keys [:name :preferred-name :public-key :photo-path])
(clojure.set/rename-keys {:name :alias
:preferred-name :name}))
all-contacts (assoc contacts public-key current-contact)]
(let [current-contact (some->
current-account
(select-keys [:name :preferred-name :public-key :photo-path])
(clojure.set/rename-keys {:name :alias
:preferred-name :name}))
all-contacts (cond-> contacts
current-contact
(assoc public-key current-contact))]
(->> members
(map #(or (get all-contacts %)
(public-key->new-contact %)))
Expand Down

0 comments on commit 2934de9

Please sign in to comment.