introduce priority map
- in future PRs we want to reduce the expensive sort operations on list of messages while keeping the possibility to get a message by its ID - priority map allow to keep a map sorted by it's value which is what we want to do here. we want to keep the messages ordered by clock-value Signed-off-by: yenda <eric@status.im>
This commit is contained in:
parent
77e9aea755
commit
73ccb44663
|
@ -10,14 +10,14 @@
|
|||
[status-im.transport.message.public-chat :as public-chat]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.desktop.events :as desktop.events]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.navigation :as navigation]
|
||||
[status-im.utils.clocks :as utils.clocks]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.platform :as platform]))
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.priority-map :refer [empty-message-map]]
|
||||
[status-im.utils.utils :as utils]))
|
||||
|
||||
(defn multi-user-chat? [cofx chat-id]
|
||||
(get-in cofx [:db :chats chat-id :group-chat]))
|
||||
|
@ -49,7 +49,8 @@
|
|||
:is-active true
|
||||
:timestamp now
|
||||
:contacts #{chat-id}
|
||||
:last-clock-value 0}))
|
||||
:last-clock-value 0
|
||||
:messages empty-message-map}))
|
||||
|
||||
(fx/defn upsert-chat
|
||||
"Upsert chat when not deleted"
|
||||
|
@ -97,7 +98,7 @@
|
|||
deleted-at-clock-value
|
||||
(utils.clocks/send 0))]
|
||||
{:db (update-in db [:chats chat-id] merge
|
||||
{:messages {}
|
||||
{:messages empty-message-map
|
||||
:message-groups {}
|
||||
:unviewed-messages #{}
|
||||
:not-loaded-message-ids #{}
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
[status-im.chat.commands.core :as commands]
|
||||
[status-im.chat.models :as chat-model]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.contacts :as contacts-store]
|
||||
[status-im.data-store.user-statuses :as user-statuses-store]
|
||||
[status-im.utils.contacts :as utils.contacts]
|
||||
[status-im.utils.datetime :as time]
|
||||
[status-im.utils.fx :as fx]))
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.priority-map :refer [empty-message-map]]))
|
||||
|
||||
(def index-messages (partial into {} (map (juxt :message-id identity))))
|
||||
(def index-messages (partial into empty-message-map
|
||||
(map (juxt :message-id identity))))
|
||||
|
||||
(defn- sort-references
|
||||
"Sorts message-references sequence primary by clock value,
|
||||
|
@ -70,10 +70,11 @@
|
|||
:deduplication-ids (get stored-deduplication-ids chat-id)
|
||||
:not-loaded-message-ids (set/difference (get stored-message-ids chat-id)
|
||||
(set message-ids))
|
||||
:referenced-messages (index-messages
|
||||
(get-referenced-messages
|
||||
chat-id
|
||||
(get-referenced-ids chat-messages)))))))
|
||||
:referenced-messages (into {}
|
||||
(map (juxt :message-id identity)
|
||||
(get-referenced-messages
|
||||
chat-id
|
||||
(get-referenced-ids chat-messages))))))))
|
||||
{}
|
||||
all-stored-chats)]
|
||||
(fx/merge cofx
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
(ns status-im.utils.priority-map
|
||||
(:require [cljs.core :as core])
|
||||
(:use [cljs.reader :only [register-tag-parser!]])
|
||||
(:require-macros [cljs.core :as coreclj]))
|
||||
|
||||
;; from https://github.com/tailrecursion/cljs-priority-map/blob/master/src/cljs/tailrecursion/priority_map.cljs
|
||||
;; fixing `vals` and `keys` function
|
||||
|
||||
(deftype PersistentPriorityMap [priority->set-of-items item->priority meta keyfn ^:mutable __hash]
|
||||
IPrintWithWriter
|
||||
(-pr-writer [coll writer opts]
|
||||
(let [pr-pair (fn [keyval] (pr-sequential-writer writer pr-writer "" " " "" opts keyval))]
|
||||
(pr-sequential-writer writer pr-pair "#status-im.utils.priority-map {" ", " "}" opts coll)))
|
||||
|
||||
IWithMeta
|
||||
(-with-meta [this meta]
|
||||
(PersistentPriorityMap. priority->set-of-items item->priority meta keyfn __hash))
|
||||
|
||||
IMeta
|
||||
(-meta [this] meta)
|
||||
|
||||
ICollection
|
||||
(-conj [this entry]
|
||||
(if (vector? entry)
|
||||
(-assoc this (-nth entry 0) (-nth entry 1))
|
||||
(reduce -conj this entry)))
|
||||
|
||||
IEmptyableCollection
|
||||
(-empty [this] (with-meta
|
||||
status-im.utils.priority-map.PersistentPriorityMap.EMPTY
|
||||
meta))
|
||||
|
||||
IEquiv
|
||||
(-equiv [this other]
|
||||
(-equiv item->priority other))
|
||||
|
||||
IHash
|
||||
(-hash [this]
|
||||
(coreclj/caching-hash this core/hash-unordered-coll __hash))
|
||||
|
||||
ISeqable
|
||||
(-seq [this]
|
||||
(if keyfn
|
||||
(seq (for [[priority item-set] priority->set-of-items, item item-set]
|
||||
(MapEntry. item (item->priority item) nil)))
|
||||
(seq (for [[priority item-set] priority->set-of-items, item item-set]
|
||||
(MapEntry. item priority nil)))))
|
||||
|
||||
IReversible
|
||||
(-rseq [coll]
|
||||
(if keyfn
|
||||
(seq (for [[priority item-set] (rseq priority->set-of-items), item item-set]
|
||||
(MapEntry. item (item->priority item) nil)))
|
||||
(seq (for [[priority item-set] (rseq priority->set-of-items), item item-set]
|
||||
(MapEntry. item priority nil)))))
|
||||
|
||||
ICounted
|
||||
(-count [this]
|
||||
(count item->priority))
|
||||
|
||||
ILookup
|
||||
(-lookup [this item]
|
||||
(get item->priority item))
|
||||
(-lookup [coll item not-found]
|
||||
(get item->priority item not-found))
|
||||
|
||||
IStack
|
||||
(-peek [this]
|
||||
(when-not (zero? (count item->priority))
|
||||
(let [f (first priority->set-of-items)
|
||||
item (first (val f))]
|
||||
(if keyfn
|
||||
[item (item->priority item)]
|
||||
[item (key f)]))))
|
||||
(-pop [this]
|
||||
(if (zero? (count item->priority))
|
||||
(throw (js/Error. "Can't pop empty priority map"))
|
||||
(let [f (first priority->set-of-items)
|
||||
item-set (val f)
|
||||
item (first item-set)
|
||||
priority-key (key f)]
|
||||
(if (= (count item-set) 1)
|
||||
(PersistentPriorityMap.
|
||||
(dissoc priority->set-of-items priority-key)
|
||||
(dissoc item->priority item)
|
||||
meta
|
||||
keyfn
|
||||
nil)
|
||||
(PersistentPriorityMap.
|
||||
(assoc priority->set-of-items priority-key (disj item-set item)),
|
||||
(dissoc item->priority item)
|
||||
meta
|
||||
keyfn
|
||||
nil)))))
|
||||
|
||||
IAssociative
|
||||
(-assoc [this item priority]
|
||||
(if-let [current-priority (get item->priority item nil)]
|
||||
(if (= current-priority priority)
|
||||
this
|
||||
(let [priority-key (keyfn priority)
|
||||
current-priority-key (keyfn current-priority)
|
||||
item-set (get priority->set-of-items current-priority-key)]
|
||||
(if (= (count item-set) 1)
|
||||
(PersistentPriorityMap.
|
||||
(assoc (dissoc priority->set-of-items current-priority-key)
|
||||
priority-key (conj (get priority->set-of-items priority-key #{}) item))
|
||||
(assoc item->priority item priority)
|
||||
meta
|
||||
keyfn
|
||||
nil)
|
||||
(PersistentPriorityMap.
|
||||
(assoc priority->set-of-items
|
||||
current-priority-key (disj (get priority->set-of-items current-priority-key) item)
|
||||
priority-key (conj (get priority->set-of-items priority-key #{}) item))
|
||||
(assoc item->priority item priority)
|
||||
meta
|
||||
keyfn
|
||||
nil))))
|
||||
(let [priority-key (keyfn priority)]
|
||||
(PersistentPriorityMap.
|
||||
(assoc priority->set-of-items
|
||||
priority-key (conj (get priority->set-of-items priority-key #{}) item))
|
||||
(assoc item->priority item priority)
|
||||
meta
|
||||
keyfn
|
||||
nil))))
|
||||
|
||||
(-contains-key? [this item]
|
||||
(contains? item->priority item))
|
||||
|
||||
IMap
|
||||
(-dissoc [this item]
|
||||
(let [priority (item->priority item ::not-found)]
|
||||
(if (= priority ::not-found)
|
||||
this
|
||||
(let [priority-key (keyfn priority)
|
||||
item-set (priority->set-of-items priority-key)]
|
||||
(if (= (count item-set) 1)
|
||||
(PersistentPriorityMap.
|
||||
(dissoc priority->set-of-items priority-key)
|
||||
(dissoc item->priority item)
|
||||
meta
|
||||
keyfn
|
||||
nil)
|
||||
(PersistentPriorityMap.
|
||||
(assoc priority->set-of-items priority-key (disj item-set item)),
|
||||
(dissoc item->priority item)
|
||||
meta
|
||||
keyfn
|
||||
nil))))))
|
||||
|
||||
ISorted
|
||||
(-sorted-seq [this ascending?]
|
||||
((if ascending? seq rseq) this))
|
||||
(-sorted-seq-from [this k ascending?]
|
||||
(let [sets (if ascending?
|
||||
(subseq priority->set-of-items >= k)
|
||||
(rsubseq priority->set-of-items <= k))]
|
||||
(if keyfn
|
||||
(seq (for [[priority item-set] sets, item item-set]
|
||||
[item (item->priority item)]))
|
||||
(seq (for [[priority item-set] sets, item item-set]
|
||||
[item priority])))))
|
||||
(-entry-key [this entry]
|
||||
(keyfn (val entry)))
|
||||
(-comparator [this] compare)
|
||||
|
||||
IFn
|
||||
(-invoke [this item]
|
||||
(-lookup this item))
|
||||
(-invoke [this item not-found]
|
||||
(-lookup this item not-found)))
|
||||
|
||||
(set! status-im.utils.priority-map.PersistentPriorityMap.EMPTY
|
||||
(PersistentPriorityMap. (sorted-map) {} {} identity nil))
|
||||
|
||||
(defn- pm-empty-by [comparator]
|
||||
(PersistentPriorityMap. (sorted-map-by comparator) {} {} identity nil))
|
||||
|
||||
(defn- pm-empty-keyfn
|
||||
([keyfn] (PersistentPriorityMap. (sorted-map) {} {} keyfn nil))
|
||||
([keyfn comparator] (PersistentPriorityMap. (sorted-map-by comparator) {} {} keyfn nil)))
|
||||
|
||||
(defn- read-priority-map [elems]
|
||||
(if (map? elems)
|
||||
(into status-im.utils-map.PersistentPriorityMap.EMPTY elems)
|
||||
(throw (js/Error "Priority map literal expects a map for its elements."))))
|
||||
|
||||
(register-tag-parser! "status-im.utils.priority-map" read-priority-map)
|
||||
|
||||
(defn priority-map
|
||||
"keyval => key val
|
||||
Returns a new priority map with supplied mappings."
|
||||
([& keyvals]
|
||||
(loop [in (seq keyvals) out status-im.utils.priority-map.PersistentPriorityMap.EMPTY]
|
||||
(if in
|
||||
(recur (nnext in) (assoc out (first in) (second in)))
|
||||
out))))
|
||||
|
||||
(defn priority-map-by
|
||||
"keyval => key val
|
||||
Returns a new priority map with supplied
|
||||
mappings, using the supplied comparator."
|
||||
([comparator & keyvals]
|
||||
(loop [in (seq keyvals) out (pm-empty-by comparator)]
|
||||
(if in
|
||||
(recur (nnext in) (assoc out (first in) (second in)))
|
||||
out))))
|
||||
|
||||
(defn priority-map-keyfn
|
||||
"keyval => key val
|
||||
Returns a new priority map with supplied
|
||||
mappings, using the supplied keyfn."
|
||||
([keyfn & keyvals]
|
||||
(loop [in (seq keyvals) out (pm-empty-keyfn keyfn)]
|
||||
(if in
|
||||
(recur (nnext in) (assoc out (first in) (second in)))
|
||||
out))))
|
||||
|
||||
(defn priority-map-keyfn-by
|
||||
"keyval => key val
|
||||
Returns a new priority map with supplied
|
||||
mappings, using the supplied keyfn and comparator."
|
||||
([keyfn comparator & keyvals]
|
||||
(loop [in (seq keyvals) out (pm-empty-keyfn keyfn comparator)]
|
||||
(if in
|
||||
(recur (nnext in) (assoc out (first in) (second in)))
|
||||
out))))
|
||||
|
||||
(def empty-message-map
|
||||
(priority-map-keyfn-by :clock-value >))
|
Loading…
Reference in New Issue