Moved text-input props to their own functions, renamed get-suggestions to get-user-suggestions

Signed-off-by: shivekkhurana <shivek@status.im>
This commit is contained in:
shivekkhurana 2020-11-19 21:41:53 +05:30
parent 4cb058f1ca
commit ecc41bebe0
No known key found for this signature in database
GPG Key ID: 9BEB56E6E62968C7
3 changed files with 155 additions and 154 deletions

View File

@ -231,7 +231,7 @@
lcase-text (string/lower-case text)]
(re-find regex lcase-text)))
(defn get-suggestions [users searched-text]
(defn get-user-suggestions [users searched-text]
(reduce
(fn [acc [k {:keys [alias name nickname searchable-phrases] :as user}]]
(if-let [match
@ -272,26 +272,26 @@
(match-mention text users mention-key-idx (inc mention-key-idx) []))
([text users mention-key-idx next-word-idx words]
(when-let [word (re-find word-regex (subs text next-word-idx))]
(let [new-words (conj words word)
searched-text (let [text (-> new-words
string/join
string/lower-case
string/trim)
last-char (dec (count text))]
(if (re-matches ending-chars-regex (str (nth text last-char nil)))
(subs text 0 last-char)
text))
suggestions (get-suggestions users searched-text)
suggestions-cnt (count suggestions)]
(cond (zero? suggestions-cnt)
(let [new-words (conj words word)
searched-text (let [text (-> new-words
string/join
string/lower-case
string/trim)
last-char (dec (count text))]
(if (re-matches ending-chars-regex (str (nth text last-char nil)))
(subs text 0 last-char)
text))
user-suggestions (get-user-suggestions users searched-text)
user-suggestions-cnt (count user-suggestions)]
(cond (zero? user-suggestions-cnt)
nil
(and (= 1 suggestions-cnt)
(mentioned? (second (first suggestions))
(and (= 1 user-suggestions-cnt)
(mentioned? (second (first user-suggestions))
(subs text (inc mention-key-idx))))
(second (first suggestions))
(second (first user-suggestions))
(> suggestions-cnt 1)
(> user-suggestions-cnt 1)
(let [word-len (count word)
text-len (count text)
next-word-start (+ next-word-idx word-len)]
@ -300,33 +300,32 @@
next-word-start new-words))))))))
(defn replace-mentions
([text users-fn]
([text users]
(let [idxs (get-at-signs text)]
(replace-mentions text users-fn idxs 0)))
([text users-fn idxs diff]
(replace-mentions text users idxs 0)))
([text users idxs diff]
(if (or (string/blank? text)
(empty? idxs))
text
(let [mention-key-idx (- (first idxs) diff)]
(if-not mention-key-idx
text
(let [users (users-fn)]
(if-not (seq users)
text
(let [{:keys [public-key match]}
(match-mention text users mention-key-idx)]
(if-not match
(recur text (fn [] users) (rest idxs) diff)
(let [new-text (string/join
[(subs text 0 (inc mention-key-idx))
public-key
(subs text (+ (inc mention-key-idx)
(count match)))])]
(recur new-text (fn [] users) (rest idxs)
(+ diff (- (count text) (count new-text))))))))))))))
(if-not (seq users)
text
(let [{:keys [public-key match]}
(match-mention text users mention-key-idx)]
(if-not match
(recur text users (rest idxs) diff)
(let [new-text (string/join
[(subs text 0 (inc mention-key-idx))
public-key
(subs text (+ (inc mention-key-idx)
(count match)))])]
(recur new-text users (rest idxs)
(+ diff (- (count text) (count new-text)))))))))))))
(defn check-mentions [cofx text]
(replace-mentions text #(get-mentionable-users cofx)))
(replace-mentions text (get-mentionable-users cofx)))
(defn get-at-sign-idxs
([text start]
@ -401,14 +400,14 @@
new-idxs)))))))
(defn check-entry
[text {:keys [from checked?] :as entry} users-fn]
[text {:keys [from checked?] :as entry} mentionable-users]
(if checked?
entry
(let [{:keys [match]}
(match-mention (str text "@") (users-fn) from)]
(if match
(let [{user-match :match}
(match-mention (str text "@") mentionable-users from)]
(if user-match
{:from from
:to (+ from (count match))
:to (+ from (count user-match))
:checked? true
:mention? true}
{:from from
@ -416,12 +415,13 @@
:checked? true
:mention false}))))
(defn check-idx-for-mentions [text idxs users-fn]
(defn check-idx-for-mentions
[text idxs mentionable-users]
(let [idxs
(reduce
(fn [acc {:keys [from] :as entry}]
(let [previous-entry-idx (dec (count acc))
new-entry (check-entry text entry users-fn)]
new-entry (check-entry text entry mentionable-users)]
(cond-> acc
(and (>= previous-entry-idx 0)
(not (get-in acc [previous-entry-idx :mention?])))
@ -497,7 +497,7 @@
new-at-idxs (check-idx-for-mentions
text
(:at-idxs state)
(fn [] mentionable-users))
mentionable-users)
calculated-input (calculate-input text new-at-idxs)]
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
{:db (-> db
@ -524,8 +524,8 @@
(assoc-in [:chats/input-with-mentions chat-id] [[:text text]]))}
(let [new-at-idxs (check-idx-for-mentions
text
(:at-idxs state)
(fn [] mentionable-users))
at-idxs
mentionable-users)
calculated-input (calculate-input text new-at-idxs)
addition? (<= start end)
end (if addition?
@ -536,7 +536,7 @@
mentions
(when (and (not (> at-sign-idx start))
(not (> (- end at-sign-idx) 100)))
(get-suggestions mentionable-users searched-text))]
(get-user-suggestions mentionable-users searched-text))]
(log/debug "[mentions] mention check"
"addition" addition?
"at-sign-idx" at-sign-idx

View File

@ -4,19 +4,18 @@
[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"}})]
(let [users {"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 "empty string"
(let [text ""
result (mentions/replace-mentions text users)]

View File

@ -79,6 +79,80 @@
:accessibility-label :send-message-button
:color (styles/send-icon-color)}]]])
(defn selection [cursor]
;; NOTE(rasom): In case if mention is added on pressing suggestion and
;; it is placed inside some text we have to specify `:selection` on
;; Android to ensure that cursor is added after the mention, not after
;; the last char in input. On iOS it works that way without this code
(when (and cursor platform/android?)
(clj->js {:start cursor :end cursor})))
(defn on-selection-change [cursor timeout-id last-text-change mentionable-users args]
(let [selection (.-selection ^js (.-nativeEvent ^js args))
start (.-start selection)
end (.-end selection)]
;; NOTE(rasom): on iOS we do not dispatch this event immediately
;; because it is needed only in case if selection is changed without
;; typing. Timeout might be canceled on `on-change`.
(when platform/ios?
(reset!
timeout-id
(utils.utils/set-timeout
#(re-frame/dispatch [::mentions/on-selection-change
{:start start
:end end}
mentionable-users])
50)))
;; NOTE(rasom): on Android we dispatch event only in case if there
;; was no text changes during last 50ms. `on-selection-change` is
;; dispatched after `on-change`, that's why there is no another way
;; to know whether selection was changed without typing.
(when (and platform/android?
(or (not @last-text-change)
(< 50 (- (js/Date.now) @last-text-change))))
(re-frame/dispatch [::mentions/on-selection-change
{:start start
:end end}
mentionable-users]))
;; NOTE(rasom): we have to reset `cursor` value when user starts using
;; text-input because otherwise cursor will stay in the same position
(when (and cursor platform/android?)
(re-frame/dispatch [::mentions/clear-cursor]))))
(defn on-change [on-text-change last-text-change timeout-id mentionable-users args]
(let [text (.-text ^js (.-nativeEvent ^js args))]
;; NOTE(rasom): on iOS `on-selection-change` is canceled in case if it
;; happens during typing because it is not needed for mention
;; suggestions calculation
(when (and platform/ios? @timeout-id)
(utils.utils/clear-timeout @timeout-id))
(when platform/android?
(reset! last-text-change (js/Date.now)))
(on-text-change text)
;; NOTE(rasom): on iOS `on-change` is dispatched after `on-text-input`,
;; that's why mention suggestions are calculated on `on-change`
(when platform/ios?
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))
(defn on-text-input [mentionable-users args]
(let [native-event (.-nativeEvent ^js args)
text (.-text ^js native-event)
previous-text (.-previousText ^js native-event)
range (.-range ^js native-event)
start (.-start ^js range)
end (.-end ^js range)]
(re-frame/dispatch
[::mentions/on-text-input
{:new-text text
:previous-text previous-text
:start start
:end end}])
;; NOTE(rasom): on Android `on-text-input` is dispatched after
;; `on-change`, that's why mention suggestions are calculated
;; on `on-change`
(when platform/android?
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))
(defn text-input
[{:keys [cooldown-enabled? input-with-mentions on-text-change set-active-panel text-input-ref]}]
(let [cursor @(re-frame/subscribe [:chat/cursor])
@ -87,101 +161,29 @@
last-text-change (atom nil)]
[rn/view {:style (styles/text-input-wrapper)}
[rn/text-input
{:style (styles/text-input)
:ref text-input-ref
:maxFontSizeMultiplier 1
:accessibility-label :chat-message-input
:text-align-vertical :center
:multiline true
:editable (not cooldown-enabled?)
:blur-on-submit false
:auto-focus false
:on-focus #(set-active-panel nil)
:max-length chat.constants/max-text-size
:placeholder-text-color (:text-02 @colors/theme)
:placeholder (if cooldown-enabled?
(i18n/label :cooldown/text-input-disabled)
(i18n/label :t/type-a-message))
:underlineColorAndroid :transparent
:auto-capitalize :sentences
:selection
;; NOTE(rasom): In case if mention is added on pressing suggestion and
;; it is placed inside some text we have to specify `:selection` on
;; Android to ensure that cursor is added after the mention, not after
;; the last char in input. On iOS it works that way without this code
(when (and cursor platform/android?)
(clj->js
{:start cursor
:end cursor}))
:on-selection-change
(fn [args]
(let [selection (.-selection ^js (.-nativeEvent ^js args))
start (.-start selection)
end (.-end selection)]
;; NOTE(rasom): on iOS we do not dispatch this event immediately
;; because it is needed only in case if selection is changed without
;; typing. Timeout might be canceled on `on-change`.
(when platform/ios?
(reset!
timeout-id
(utils.utils/set-timeout
#(re-frame/dispatch [::mentions/on-selection-change
{:start start
:end end}
mentionable-users])
50)))
;; NOTE(rasom): on Android we dispatch event only in case if there
;; was no text changes during last 50ms. `on-selection-change` is
;; dispatched after `on-change`, that's why there is no another way
;; to know whether selection was changed without typing.
(when (and platform/android?
(or (not @last-text-change)
(< 50 (- (js/Date.now) @last-text-change))))
(re-frame/dispatch [::mentions/on-selection-change
{:start start
:end end}
mentionable-users]))
;; NOTE(rasom): we have to reset `cursor` value when user starts using
;; text-input because otherwise cursor will stay in the same position
(when (and cursor platform/android?)
(re-frame/dispatch [::mentions/clear-cursor]))))
:on-change
(fn [args]
(let [text (.-text ^js (.-nativeEvent ^js args))]
;; NOTE(rasom): on iOS `on-selection-change` is canceled in case if it
;; happens during typing because it is not needed for mention
;; suggestions calculation
(when (and platform/ios? @timeout-id)
(utils.utils/clear-timeout @timeout-id))
(when platform/android?
(reset! last-text-change (js/Date.now)))
(on-text-change text)
;; NOTE(rasom): on iOS `on-change` is dispatched after `on-text-input`,
;; that's why mention suggestions are calculated on `on-change`
(when platform/ios?
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))
:on-text-input
(fn [args]
(let [native-event (.-nativeEvent ^js args)
text (.-text ^js native-event)
previous-text (.-previousText ^js native-event)
range (.-range ^js native-event)
start (.-start ^js range)
end (.-end ^js range)]
(re-frame/dispatch
[::mentions/on-text-input
{:new-text text
:previous-text previous-text
:start start
:end end}])
;; NOTE(rasom): on Android `on-text-input` is dispatched after
;; `on-change`, that's why mention suggestions are calculated
;; on `on-change`
(when platform/android?
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))}
{:style (styles/text-input)
:ref text-input-ref
:max-font-size-multiplier 1
:accessibility-label :chat-message-input
:text-align-vertical :center
:multiline true
:editable (not cooldown-enabled?)
:blur-on-submit false
:auto-focus false
:on-focus #(set-active-panel nil)
:max-length chat.constants/max-text-size
:placeholder-text-color (:text-02 @colors/theme)
:placeholder (if cooldown-enabled?
(i18n/label :cooldown/text-input-disabled)
(i18n/label :t/type-a-message))
:underline-color-android :transparent
:auto-capitalize :sentences
:selection (selection cursor)
:on-selection-change (partial on-selection-change
cursor timeout-id last-text-change mentionable-users)
:on-change (partial on-change
on-text-change last-text-change timeout-id mentionable-users)
:on-text-input (partial on-text-input mentionable-users)}
;; NOTE(rasom): reduce was used instead of for here because although
;; each text component was given a unique id it still would mess with
;; colors on Android. In case if entire component is built without lists