Highlighted mentions in input
This commit is contained in:
parent
44e3076022
commit
0448edb509
|
@ -30,7 +30,7 @@
|
|||
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))})
|
||||
|
||||
(fx/defn select-mention
|
||||
[{:keys [db] :as cofx} {:keys [name] :as user}]
|
||||
[{:keys [db] :as cofx} {:keys [alias name searched-text match] :as user}]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
new-text (mentions/new-input-text-with-mention cofx user)
|
||||
at-sign-idx (get-in db [:chats chat-id :mentions :at-sign-idx])]
|
||||
|
@ -39,7 +39,17 @@
|
|||
{:db (-> db
|
||||
(assoc-in [:chats/cursor chat-id] (+ at-sign-idx (count name) 2))
|
||||
(assoc-in [:chats/mention-suggestions chat-id] nil))}
|
||||
(set-chat-input-text new-text))))
|
||||
(set-chat-input-text new-text)
|
||||
;; NOTE(roman): on-text-input event is not dispatched when we change input
|
||||
;; programmatically, so we have to call `on-text-input` manually
|
||||
(mentions/on-text-input
|
||||
(let [match-len (count match)
|
||||
searched-text-len (count searched-text)]
|
||||
{:new-text (subs match searched-text-len)
|
||||
:previous-text ""
|
||||
:start (+ at-sign-idx searched-text-len 1)
|
||||
:end (+ at-sign-idx match-len 1)}))
|
||||
(mentions/recheck-at-idxs {alias user}))))
|
||||
|
||||
(defn- start-cooldown [{:keys [db]} cooldowns]
|
||||
{:dispatch-later [{:dispatch [:chat/disable-cooldown]
|
||||
|
|
|
@ -85,7 +85,9 @@
|
|||
(string/lower-case name)
|
||||
searched-text)
|
||||
name)]
|
||||
(assoc acc k (assoc user :match match))
|
||||
(assoc acc k (assoc user
|
||||
:match match
|
||||
:searched-text searched-text))
|
||||
acc))
|
||||
{}
|
||||
users))
|
||||
|
@ -169,6 +171,120 @@
|
|||
(- (check-for-at-sign new-text)
|
||||
(check-for-at-sign previous-text))))
|
||||
|
||||
(defn get-at-sign-idxs
|
||||
([text start]
|
||||
(get-at-sign-idxs text start 0 []))
|
||||
([text start from idxs]
|
||||
(if-let [idx (string/index-of text at-sign from)]
|
||||
(recur text start (inc idx) (conj idxs (+ start idx)))
|
||||
idxs)))
|
||||
|
||||
(defn calc-at-idxs
|
||||
[{:keys [at-idxs new-text previous-text start]}]
|
||||
(let [new-idxs (get-at-sign-idxs new-text start)
|
||||
new-idx-cnt (count new-idxs)
|
||||
last-new-idx (when (pos? new-idx-cnt)
|
||||
(nth new-idxs (dec new-idx-cnt)))
|
||||
new-text-len (count new-text)
|
||||
old-text-len (count previous-text)
|
||||
old-end (+ start old-text-len)]
|
||||
(if-not (seq at-idxs)
|
||||
(map (fn [idx]
|
||||
{:from idx
|
||||
:checked? false})
|
||||
new-idxs)
|
||||
(let [diff (- new-text-len old-text-len)
|
||||
{:keys [state added?]}
|
||||
(->> at-idxs
|
||||
(keep (fn [{:keys [from to] :as entry}]
|
||||
(let [to+1 (inc to)]
|
||||
(cond
|
||||
;; starts after change
|
||||
(>= from old-end)
|
||||
(assoc entry
|
||||
:from (+ from diff)
|
||||
:to (+ to diff))
|
||||
|
||||
;; starts and end before change
|
||||
(and
|
||||
(< from start)
|
||||
(or
|
||||
;; is not checked yet
|
||||
(not to+1)
|
||||
(< to+1 start)))
|
||||
entry
|
||||
|
||||
;; starts before change intersects with it
|
||||
(and (< from start)
|
||||
(>= to+1 start))
|
||||
{:from from
|
||||
:checked? false}
|
||||
|
||||
;; starts in changed part of text
|
||||
:else nil))))
|
||||
(reduce
|
||||
(fn [{:keys [state added?] :as acc} {:keys [from] :as entry}]
|
||||
(if (and last-new-idx
|
||||
(> from last-new-idx)
|
||||
(not added?))
|
||||
{:state (conj
|
||||
(into state (map (fn [idx]
|
||||
{:from idx
|
||||
:checked? false})
|
||||
new-idxs))
|
||||
entry)
|
||||
:added? true}
|
||||
(update acc :state conj entry)))
|
||||
{:state []}))]
|
||||
(if added?
|
||||
state
|
||||
(into state (map (fn [idx]
|
||||
{:from idx
|
||||
:checked? false})
|
||||
new-idxs)))))))
|
||||
|
||||
(defn check-entry
|
||||
[text {:keys [from checked?] :as entry} users-fn]
|
||||
(if checked?
|
||||
entry
|
||||
(let [{:keys [match]}
|
||||
(match-mention (str text "@") (users-fn) from)]
|
||||
(if match
|
||||
{:from from
|
||||
:to (+ from (count match))
|
||||
:checked? true
|
||||
:mention? true}
|
||||
{:from from
|
||||
:to (count text)
|
||||
:checked? true
|
||||
:mention false}))))
|
||||
|
||||
(defn check-idx-for-mentions [text idxs users-fn]
|
||||
(let [idxs
|
||||
(reduce
|
||||
(fn [acc {:keys [from] :as entry}]
|
||||
(let [previous-entry-idx (dec (count acc))
|
||||
new-entry (check-entry text entry users-fn)]
|
||||
(cond-> acc
|
||||
(and (>= previous-entry-idx 0)
|
||||
(not (get-in acc [previous-entry-idx :mention?])))
|
||||
(assoc-in [previous-entry-idx :to] (dec from))
|
||||
|
||||
(>= previous-entry-idx 0)
|
||||
(assoc-in [previous-entry-idx :next-at-idx] from)
|
||||
|
||||
:always
|
||||
(conj (dissoc new-entry :next-at-idx)))))
|
||||
[]
|
||||
idxs)]
|
||||
(when (seq idxs)
|
||||
(let [last-idx (dec (count idxs))]
|
||||
(if (get-in idxs [last-idx :mention?])
|
||||
idxs
|
||||
(-> idxs
|
||||
(assoc-in [last-idx :to] (dec (count text)))
|
||||
(assoc-in [last-idx :checked?] false)))))))
|
||||
|
||||
(fx/defn on-text-input
|
||||
{:events [::on-text-input]}
|
||||
[{:keys [db] :as cofx} {:keys [new-text previous-text start end] :as args}]
|
||||
|
@ -182,13 +298,60 @@
|
|||
chat-id (:current-chat-id db)
|
||||
change (at-sign-change normalized-previous-text new-text)
|
||||
previous-state (get-in db [:chats chat-id :mentions])
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
new-state (-> previous-state
|
||||
(update :at-sign-counter + change)
|
||||
(merge args)
|
||||
(assoc :previous-text normalized-previous-text))]
|
||||
(assoc :previous-text normalized-previous-text))
|
||||
old-at-idxs (:at-idxs new-state)
|
||||
new-at-idxs (calc-at-idxs new-state)
|
||||
new-state (assoc new-state :at-idxs new-at-idxs)]
|
||||
(log/debug "[mentions] on-text-input state" new-state)
|
||||
{:db (assoc-in db [:chats chat-id :mentions] new-state)}))
|
||||
|
||||
(defn calculate-input [text [first-idx :as idxs]]
|
||||
(if-not first-idx
|
||||
[[:text text]]
|
||||
(let [idx-cnt (count idxs)
|
||||
last-from (get-in idxs [(dec idx-cnt) :from])]
|
||||
(reduce
|
||||
(fn [acc {:keys [from to next-at-idx mention?]}]
|
||||
(cond
|
||||
(and mention? next-at-idx)
|
||||
(into acc [[:mention (subs text from (inc to))]
|
||||
[:text (subs text (inc to) next-at-idx)]])
|
||||
|
||||
(and mention? (= last-from from))
|
||||
(into acc [[:mention (subs text from (inc to))]
|
||||
[:text (subs text (inc to))]])
|
||||
|
||||
:else
|
||||
(conj acc [:text (subs text from (inc to))])))
|
||||
(let [first-from (:from first-idx)]
|
||||
(if (zero? first-from)
|
||||
[]
|
||||
[[:text (subs text 0 first-from)]]))
|
||||
idxs))))
|
||||
|
||||
(fx/defn recheck-at-idxs
|
||||
[{:keys [db]} mentionable-users]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
{:keys [new-text at-sign-counter start end] :as state}
|
||||
(get-in db [:chats chat-id :mentions])
|
||||
new-at-idxs (check-idx-for-mentions
|
||||
text
|
||||
(:at-idxs state)
|
||||
(fn [] mentionable-users))
|
||||
calculated-input (calculate-input text new-at-idxs)]
|
||||
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
|
||||
{:db (-> db
|
||||
(update-in
|
||||
[:chats chat-id :mentions]
|
||||
assoc
|
||||
:at-idxs new-at-idxs)
|
||||
(assoc-in [:chats/input-with-mentions chat-id] calculated-input))}))
|
||||
|
||||
(fx/defn calculate-suggestion
|
||||
{:events [::calculate-suggestions]}
|
||||
[{:keys [db] :as cofx} mentionable-users]
|
||||
|
@ -200,8 +363,16 @@
|
|||
(log/debug "[mentions] calculate suggestions"
|
||||
"state" state)
|
||||
(if-not (pos? at-sign-counter)
|
||||
{:db (assoc-in db [:chats/mention-suggestions chat-id] nil)}
|
||||
(let [addition? (<= start end)
|
||||
{:db (-> db
|
||||
(assoc-in [:chats/mention-suggestions chat-id] nil)
|
||||
(assoc-in [:chats chat-id :mentions :at-idxs] nil)
|
||||
(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))
|
||||
calculated-input (calculate-input text new-at-idxs)
|
||||
addition? (<= start end)
|
||||
end (if addition?
|
||||
(+ start (count new-text))
|
||||
start)
|
||||
|
@ -216,21 +387,24 @@
|
|||
"end" end
|
||||
"searched-text" (pr-str searched-text)
|
||||
"mentions" (count mentions))
|
||||
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
|
||||
{:db (-> db
|
||||
(update-in [:chats chat-id :mentions]
|
||||
assoc
|
||||
:at-sign-idx at-sign-idx
|
||||
:at-idxs new-at-idxs
|
||||
:mention-end end)
|
||||
(assoc-in [:chats/input-with-mentions chat-id] calculated-input)
|
||||
(assoc-in [:chats/mention-suggestions chat-id] mentions))}))))
|
||||
|
||||
(defn new-input-text-with-mention
|
||||
[{:keys [db]} {:keys [name]}]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
{:keys [mention-end at-sign-idx] :as state}
|
||||
{:keys [mention-end at-sign-idx]}
|
||||
(get-in db [:chats chat-id :mentions])]
|
||||
(log/debug "[mentions] clear suggestions"
|
||||
"state" state)
|
||||
"state" new-input-text-with-mention)
|
||||
(string/join
|
||||
[(subs text 0 (inc at-sign-idx))
|
||||
name
|
||||
|
@ -247,6 +421,7 @@
|
|||
(let [chat-id (:current-chat-id db)]
|
||||
{:db (-> db
|
||||
(update-in [:chats chat-id] dissoc :mentions)
|
||||
(update :chats/input-with-mentions dissoc chat-id)
|
||||
(update :chats/mention-suggestions dissoc chat-id))}))
|
||||
|
||||
(fx/defn clear-cursor
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
(reg-root-key-sub :group-chat/invitations :group-chat/invitations)
|
||||
(reg-root-key-sub :chats/mention-suggestions :chats/mention-suggestions)
|
||||
(reg-root-key-sub :chats/cursor :chats/cursor)
|
||||
|
||||
(reg-root-key-sub :chats/input-with-mentions :chats/input-with-mentions)
|
||||
;;browser
|
||||
(reg-root-key-sub :browsers :browser/browsers)
|
||||
(reg-root-key-sub :browser/options :browser/options)
|
||||
|
@ -904,6 +904,13 @@
|
|||
(fn [[chat-id cursor]]
|
||||
(get cursor chat-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chat/input-with-mentions
|
||||
:<- [:chats/current-chat-id]
|
||||
:<- [:chats/input-with-mentions]
|
||||
(fn [[chat-id cursor]]
|
||||
(get cursor chat-id)))
|
||||
|
||||
;;BOOTNODES ============================================================================================================
|
||||
|
||||
(re-frame/reg-sub
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
:color (styles/send-icon-color)}]]])
|
||||
|
||||
(defn text-input
|
||||
[{:keys [cooldown-enabled? text-value 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])
|
||||
mentionable-users @(re-frame/subscribe [:chats/mentionable-users])]
|
||||
[rn/view {:style (styles/text-input-wrapper)}
|
||||
|
@ -90,7 +90,6 @@
|
|||
:accessibility-label :chat-message-input
|
||||
:text-align-vertical :center
|
||||
:multiline true
|
||||
:default-value text-value
|
||||
:editable (not cooldown-enabled?)
|
||||
:blur-on-submit false
|
||||
:auto-focus false
|
||||
|
@ -146,7 +145,20 @@
|
|||
;; `on-change`, that's why mention suggestions are calculated
|
||||
;; on `on-change`
|
||||
(when platform/android?
|
||||
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))}]]))
|
||||
(re-frame/dispatch [::mentions/calculate-suggestions 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
|
||||
;; inside it works just fine on both platforms.
|
||||
(reduce
|
||||
(fn [acc [type text]]
|
||||
(conj
|
||||
acc
|
||||
[rn/text (when (= type :mention)
|
||||
{:style {:color "#0DA4C9"}})
|
||||
text]))
|
||||
[:<>]
|
||||
input-with-mentions)]]))
|
||||
|
||||
(defn mention-item
|
||||
[[_ {:keys [identicon alias name] :as user}]]
|
||||
|
@ -248,7 +260,10 @@
|
|||
(let [disconnected? @(re-frame/subscribe [:disconnected?])
|
||||
{:keys [processing]} @(re-frame/subscribe [:multiaccounts/login])
|
||||
mainnet? @(re-frame/subscribe [:mainnet?])
|
||||
input-text @(re-frame/subscribe [:chats/current-chat-input-text])
|
||||
input-text
|
||||
@(re-frame/subscribe [:chats/current-chat-input-text])
|
||||
input-with-mentions
|
||||
@(re-frame/subscribe [:chat/input-with-mentions])
|
||||
cooldown-enabled? @(re-frame/subscribe [:chats/cooldown-enabled?])
|
||||
one-to-one-chat? @(re-frame/subscribe [:current-chat/one-to-one-chat?])
|
||||
{:keys [public?
|
||||
|
@ -295,6 +310,7 @@
|
|||
:on-send-press #(do (re-frame/dispatch [:chat.ui/send-current-message])
|
||||
(clear-input))
|
||||
:text-value input-text
|
||||
:input-with-mentions input-with-mentions
|
||||
:on-text-change on-text-change
|
||||
:cooldown-enabled? cooldown-enabled?
|
||||
:show-send show-send
|
||||
|
|
Loading…
Reference in New Issue