Update mention suggestions on selection change

This commit is contained in:
Roman Volosovskyi 2020-09-22 14:39:56 +03:00
parent 99755d08e9
commit 55278828e6
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
4 changed files with 86 additions and 38 deletions

View File

@ -158,5 +158,5 @@
(fx/merge cofx (fx/merge cofx
(send-image) (send-image)
(send-plain-text-message input-text-with-mentions current-chat-id) (send-plain-text-message input-text-with-mentions current-chat-id)
(mentions/clear-suggestions) (mentions/clear-mentions)
(mentions/clear-cursor)))) (mentions/clear-cursor))))

View File

@ -151,26 +151,6 @@
(defn check-mentions [cofx text] (defn check-mentions [cofx text]
(replace-mentions text #(get-mentionable-users cofx))) (replace-mentions text #(get-mentionable-users cofx)))
(defn check-for-at-sign
([text]
(check-for-at-sign text 0 0))
([text from cnt]
(if-let [idx (string/index-of text at-sign from)]
(recur text (inc idx) (inc cnt))
cnt)))
(defn at-sign-change [previous-text new-text]
(cond
(= "" previous-text)
(check-for-at-sign new-text)
(= "" new-text)
(- (check-for-at-sign previous-text))
:else
(- (check-for-at-sign new-text)
(check-for-at-sign previous-text))))
(defn get-at-sign-idxs (defn get-at-sign-idxs
([text start] ([text start]
(get-at-sign-idxs text start 0 [])) (get-at-sign-idxs text start 0 []))
@ -296,11 +276,9 @@
previous-text previous-text
(subs previous-text start end)) (subs previous-text start end))
chat-id (:current-chat-id db) chat-id (:current-chat-id db)
change (at-sign-change normalized-previous-text new-text)
previous-state (get-in db [:chats chat-id :mentions]) previous-state (get-in db [:chats chat-id :mentions])
text (get-in db [:chat/inputs chat-id :input-text]) text (get-in db [:chat/inputs chat-id :input-text])
new-state (-> previous-state new-state (-> previous-state
(update :at-sign-counter + change)
(merge args) (merge args)
(assoc :previous-text normalized-previous-text)) (assoc :previous-text normalized-previous-text))
old-at-idxs (:at-idxs new-state) old-at-idxs (:at-idxs new-state)
@ -337,7 +315,7 @@
[{:keys [db]} mentionable-users] [{:keys [db]} mentionable-users]
(let [chat-id (:current-chat-id db) (let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text]) text (get-in db [:chat/inputs chat-id :input-text])
{:keys [new-text at-sign-counter start end] :as state} {:keys [new-text start end] :as state}
(get-in db [:chats chat-id :mentions]) (get-in db [:chats chat-id :mentions])
new-at-idxs (check-idx-for-mentions new-at-idxs (check-idx-for-mentions
text text
@ -357,12 +335,12 @@
[{:keys [db] :as cofx} mentionable-users] [{:keys [db] :as cofx} mentionable-users]
(let [chat-id (:current-chat-id db) (let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text]) text (get-in db [:chat/inputs chat-id :input-text])
{:keys [new-text at-sign-counter start end] :as state} {:keys [new-text at-idxs start end] :as state}
(get-in db [:chats chat-id :mentions]) (get-in db [:chats chat-id :mentions])
new-text (or new-text text)] new-text (or new-text text)]
(log/debug "[mentions] calculate suggestions" (log/debug "[mentions] calculate suggestions"
"state" state) "state" state)
(if-not (pos? at-sign-counter) (if-not (seq at-idxs)
{:db (-> db {:db (-> db
(assoc-in [:chats/mention-suggestions chat-id] nil) (assoc-in [:chats/mention-suggestions chat-id] nil)
(assoc-in [:chats chat-id :mentions :at-idxs] nil) (assoc-in [:chats chat-id :mentions :at-idxs] nil)
@ -382,8 +360,10 @@
(when (and (not (> at-sign-idx start)) (when (and (not (> at-sign-idx start))
(not (> (- end at-sign-idx) 100))) (not (> (- end at-sign-idx) 100)))
(get-suggestions mentionable-users searched-text))] (get-suggestions mentionable-users searched-text))]
(log/debug "[mentions] mention detected" (log/debug "[mentions] mention check"
"addition" addition? "addition" addition?
"at-sign-idx" at-sign-idx
"start" start
"end" end "end" end
"searched-text" (pr-str searched-text) "searched-text" (pr-str searched-text)
"mentions" (count mentions)) "mentions" (count mentions))
@ -419,10 +399,18 @@
[{:keys [db]}] [{:keys [db]}]
(log/debug "[mentions] clear suggestions") (log/debug "[mentions] clear suggestions")
(let [chat-id (:current-chat-id db)] (let [chat-id (:current-chat-id db)]
{:db (-> db {:db (update db :chats/mention-suggestions dissoc chat-id)}))
(update-in [:chats chat-id] dissoc :mentions)
(update :chats/input-with-mentions dissoc chat-id) (fx/defn clear-mentions
(update :chats/mention-suggestions dissoc chat-id))})) [{:keys [db] :as cofx}]
(log/debug "[mentions] clear mentions")
(let [chat-id (:current-chat-id db)]
(fx/merge
cofx
{:db (-> db
(update-in [:chats chat-id] dissoc :mentions)
(update :chats/input-with-mentions dissoc chat-id))}
(clear-suggestions))))
(fx/defn clear-cursor (fx/defn clear-cursor
{:events [::clear-cursor]} {:events [::clear-cursor]}
@ -430,3 +418,29 @@
(log/debug "[mentions] clear cursor") (log/debug "[mentions] clear cursor")
{:db {:db
(update db :chats/cursor dissoc (:current-chat-id db))}) (update db :chats/cursor dissoc (:current-chat-id db))})
(fx/defn check-selection
{:events [::on-selection-change]}
[{:keys [db] :as cofx}
{:keys [start end] :as selection}
mentionable-users]
(let [chat-id (:current-chat-id db)
{:keys [mention-end at-idxs]}
(get-in db [:chats chat-id :mentions])]
(when (seq at-idxs)
(if (some
(fn [{:keys [from to] :as idx}]
(when (and (not (< start from))
(<= (dec end) to))
idx))
at-idxs)
(fx/merge
cofx
{:db (update-in db [:chats chat-id :mentions]
assoc
:start end
:end end
:new-text "")}
(calculate-suggestion mentionable-users))
(clear-suggestions cofx)))))

View File

@ -885,7 +885,6 @@
(fn [contacts] (fn [contacts]
(reduce (reduce
(fn [acc [key {:keys [alias name identicon public-key] :as contact}]] (fn [acc [key {:keys [alias name identicon public-key] :as contact}]]
(println :foo alias (contact.db/blocked? contact))
(if (and alias (if (and alias
(not= alias "") (not= alias "")
(not (contact.db/blocked? contact))) (not (contact.db/blocked? contact)))

View File

@ -81,7 +81,9 @@
(defn text-input (defn text-input
[{:keys [cooldown-enabled? input-with-mentions on-text-change set-active-panel text-input-ref]}] [{:keys [cooldown-enabled? input-with-mentions on-text-change set-active-panel text-input-ref]}]
(let [cursor @(re-frame/subscribe [:chat/cursor]) (let [cursor @(re-frame/subscribe [:chat/cursor])
mentionable-users @(re-frame/subscribe [:chats/mentionable-users])] mentionable-users @(re-frame/subscribe [:chats/mentionable-users])
timeout-id (atom nil)
last-text-change (atom nil)]
[rn/view {:style (styles/text-input-wrapper)} [rn/view {:style (styles/text-input-wrapper)}
[rn/text-input [rn/text-input
{:style (styles/text-input) {:style (styles/text-input)
@ -112,15 +114,48 @@
:end cursor})) :end cursor}))
:on-selection-change :on-selection-change
(fn [_] (fn [args]
;; NOTE(rasom): we have to reset `cursor` value when user starts using (let [selection (.-selection ^js (.-nativeEvent ^js args))
;; text-input because otherwise cursor will stay in the same position start (.-start selection)
(when (and cursor platform/android?) end (.-end selection)]
(re-frame/dispatch [::mentions/clear-cursor]))) ;; 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 :on-change
(fn [args] (fn [args]
(let [text (.-text ^js (.-nativeEvent ^js 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) (on-text-change text)
;; NOTE(rasom): on iOS `on-change` is dispatched after `on-text-input`, ;; NOTE(rasom): on iOS `on-change` is dispatched after `on-text-input`,
;; that's why mention suggestions are calculated on `on-change` ;; that's why mention suggestions are calculated on `on-change`