[feature] refactor discover events

use re-frame idiomatic register-handler-fx, reg-fx and coeffect.
the events are still not free of side-effects as there is some
db calls involved which will be taken care of in a future PR

it also reduces the events fired at app init from 3 to 1
discover-related event
This commit is contained in:
Eric Dvorsak 2017-11-20 23:24:07 +01:00
parent 1b563a59cc
commit 5c359886e5
No known key found for this signature in database
GPG Key ID: 932AC1CE5F05DE0C
21 changed files with 507 additions and 454 deletions

View File

@ -1,5 +1,6 @@
(ns status-im.data-store.core
(:require [status-im.data-store.realm.core :as data-source]))
(:require [status-im.data-store.realm.core :as data-source]
[status-im.utils.handlers :as handlers]))
(defn init []

View File

@ -1,28 +1,33 @@
(ns status-im.data-store.discover
(:require [status-im.data-store.realm.discover :as data-store])
(:refer-clojure :exclude [exists?]))
(:require [re-frame.core :as re-frame]
[status-im.data-store.realm.discover :as data-store]
[status-im.utils.handlers :as handlers]))
;; stores a collection of discover messages
;; removes the tags from the discovers because there is no
;; need to store them and realm doesn't support lists
;; of string
;; also deletes the oldest queries if the number of discovers stored is
;; above maximum-number-of-discoveries
(re-frame/reg-fx
:data-store.discover/save-all
(fn [[discovers maximum-number-of-discoveries]]
(data-store/save-all (mapv #(dissoc % :tags) discovers))
(data-store/delete :created-at :asc maximum-number-of-discoveries)))
(defn get-all
[ordering]
(mapv #(update % :tags vals)
(data-store/get-all-as-list ordering)))
(defn save
[discover]
(data-store/save discover))
(defn exists?
[message-id]
(data-store/exists? message-id))
(defn save-all
[discoveries]
(data-store/save-all discoveries))
(defn delete
[by ordering critical-count to-delete-count]
(data-store/delete by ordering critical-count to-delete-count))
(defn get-all-tags
;; extracts the hashtags from the status and put them into a set
;; for each discover
;; returns a map of discovers that can be used as is in the app-db
[]
(data-store/get-all-tags))
(reduce (fn [acc {:keys [message-id status] :as discover}]
(let [tags (handlers/get-hashtags status)
discover (assoc discover :tags tags)]
(assoc acc message-id discover)))
{}
(data-store/get-all-as-list :asc)))
(re-frame/reg-cofx
:data-store/discoveries
(fn [cofx _]
(assoc cofx :data-store/discoveries (get-all))))

View File

@ -13,67 +13,27 @@
[ordering]
(realm/js-object->clj (get-all ordering)))
(defn get-tag-by-name [tag]
(log/debug "Getting tag: " tag)
(realm/get-one-by-field-clj @realm/account-realm :tag :name tag))
(defn- update-tag-counter [func tag]
(let [tag (:name tag)
tag-object (get-tag-by-name tag)]
(if tag-object
(realm/create @realm/account-realm :tag
{:name tag
:count (func (:count tag-object))}
true))))
(defn- update-tags-counter [func tags]
(doseq [tag (distinct tags)]
(update-tag-counter func tag)))
(defn- get-tags
[message-id]
(-> (realm/get-one-by-field-clj @realm/account-realm :discover :message-id message-id)
:tags
vals))
(defn- upsert-discover [{:keys [message-id tags] :as discover}]
(log/debug "Creating/updating discover with tags: " tags)
(let [prev-tags (get-tags message-id)]
(when prev-tags
(update-tags-counter dec prev-tags))
(realm/create @realm/account-realm :discover discover true)
(update-tags-counter inc tags)))
(defn exists?
[message-id]
(realm/exists? @realm/account-realm :discover {:message-id message-id}))
(defn save
[discover]
(realm/write @realm/account-realm
#(upsert-discover discover)))
#(realm/create @realm/account-realm :discover discover true)))
(defn save-all
[discoveries]
(realm/write @realm/account-realm
(fn []
(doseq [discover discoveries]
(upsert-discover discover)))))
(realm/create @realm/account-realm :discover discover true)))))
(defn delete
[by ordering critical-count to-delete-count]
[by ordering max-count]
(let [discoveries (realm/get-all @realm/account-realm :discover)
count (realm/get-count discoveries)]
(if (> count critical-count)
(if (> count max-count)
(let [to-delete (-> discoveries
(realm/sorted by ordering)
(realm/page 0 to-delete-count))]
(realm/page 0 (- max-count count)))]
(realm/write @realm/account-realm
(fn []
(log/debug (str "Deleting " (realm/get-count to-delete) " discoveries"))
(realm/delete @realm/account-realm to-delete)))))))
(defn get-all-tags []
(-> (realm/get-all @realm/account-realm :tag)
(realm/sorted :count :desc)
realm/js-object->clj))

View File

@ -110,7 +110,7 @@
from (aget msg "from")
msg-status (aget msg "message-status")
statuses (aget msg "user-statuses")]
(when statuses
(when statuses
(.map statuses (fn [status _ _]
(aset status "status-id" (str message-id "-" from))
(aset status "message-id" message-id)

View File

@ -0,0 +1,31 @@
(ns status-im.data-store.realm.schemas.account.v20.core
(:require [status-im.data-store.realm.schemas.account.v19.chat :as chat]
[status-im.data-store.realm.schemas.account.v1.chat-contact :as chat-contact]
[status-im.data-store.realm.schemas.account.v19.contact :as contact]
[status-im.data-store.realm.schemas.account.v20.discover :as discover]
[status-im.data-store.realm.schemas.account.v19.message :as message]
[status-im.data-store.realm.schemas.account.v12.pending-message :as pending-message]
[status-im.data-store.realm.schemas.account.v1.processed-message :as processed-message]
[status-im.data-store.realm.schemas.account.v19.request :as request]
[status-im.data-store.realm.schemas.account.v19.user-status :as user-status]
[status-im.data-store.realm.schemas.account.v5.contact-group :as contact-group]
[status-im.data-store.realm.schemas.account.v5.group-contact :as group-contact]
[status-im.data-store.realm.schemas.account.v8.local-storage :as local-storage]
[taoensso.timbre :as log]
[cljs.reader :as reader]))
(def schema [chat/schema
chat-contact/schema
contact/schema
discover/schema
message/schema
pending-message/schema
processed-message/schema
request/schema
user-status/schema
contact-group/schema
group-contact/schema
local-storage/schema])
(defn migration [old-realm new-realm]
(log/debug "migrating v20 account database: " old-realm new-realm))

View File

@ -0,0 +1,14 @@
(ns status-im.data-store.realm.schemas.account.v20.discover
(:require [taoensso.timbre :as log]))
(def schema {:name :discover
:primaryKey :message-id
:properties {:message-id "string"
:name {:type "string" :optional true}
:status "string"
:whisper-id "string"
:photo-path {:type "string" :optional true}
:created-at {:type "int" :default 0}}})
(defn migration [_ _]
(log/debug "migrating discover schema"))

View File

@ -84,12 +84,11 @@
{:pre [(valid? :contact-request/message message)]}
(debug :send-command-request!)
(d/add-pending-message!
web3
(assoc message :type :contact-request
:requires-ack? true
:topics [f/status-topic])))
(defonce watched-hashtag-topics (atom nil))
web3
(assoc message
:type :contact-request
:requires-ack? true
:topics [f/status-topic])))
(s/def :discoveries/hashtags (s/every string? :kind-of set?))

View File

@ -80,38 +80,38 @@
::init-whisper
(fn [{:keys [web3 public-key groups updates-public-key updates-private-key status contacts pending-messages]}]
(protocol/init-whisper!
{:web3 web3
:identity public-key
:groups groups
:callback #(re-frame/dispatch [:incoming-message %1 %2])
:ack-not-received-s-interval 125
:default-ttl 120
:send-online-s-interval 180
:ttl-config {:public-group-message 2400}
:max-attempts-number 3
:delivery-loop-ms-interval 500
:profile-keypair {:public updates-public-key
:private updates-private-key}
:hashtags (handlers/get-hashtags status)
:pending-messages pending-messages
:contacts (keep (fn [{:keys [whisper-identity
public-key
private-key]}]
(when (and public-key private-key)
{:identity whisper-identity
:keypair {:public public-key
:private private-key}}))
contacts)
:post-error-callback #(re-frame/dispatch [::post-error %])})))
{:web3 web3
:identity public-key
:groups groups
:callback #(re-frame/dispatch [:incoming-message %1 %2])
:ack-not-received-s-interval 125
:default-ttl 120
:send-online-s-interval 180
:ttl-config {:public-group-message 2400}
:max-attempts-number 3
:delivery-loop-ms-interval 500
:profile-keypair {:public updates-public-key
:private updates-private-key}
:hashtags (mapv name (handlers/get-hashtags status))
:pending-messages pending-messages
:contacts (keep (fn [{:keys [whisper-identity
public-key
private-key]}]
(when (and public-key private-key)
{:identity whisper-identity
:keypair {:public public-key
:private private-key}}))
contacts)
:post-error-callback #(re-frame/dispatch [::post-error %])})))
(re-frame/reg-fx
::web3-get-syncing
(fn [web3]
(when web3
(.getSyncing
(.-eth web3)
(fn [error sync]
(re-frame/dispatch [:update-sync-state error sync]))))))
(.-eth web3)
(fn [error sync]
(re-frame/dispatch [:update-sync-state error sync]))))))
(re-frame/reg-fx
::save-processed-messages

View File

@ -32,10 +32,10 @@
:group/contact-groups {}
:group/selected-contacts #{}
:chats {}
:current-chat-id constants/console-chat-id
:current-chat-id constants/console-chat-id
:selected-participants #{}
:discoveries {}
:discover-search-tags '()
:discover-search-tags #{}
:discover-current-dapp {}
:tags []
:sync-state :done
@ -164,9 +164,9 @@
:chat/chat-ui-props
:chat/chat-list-ui-props
:chat/layout-height
:chat/expandable-view-height-to-value
:chat/expandable-view-height-to-value
:chat/message-data
:chat/message-status
:chat/message-status
:chat/selected-participants
:chat/chat-loaded-callbacks
:chat/public-group-topic
@ -182,7 +182,6 @@
:discoveries/tags
:discoveries/current-tag
:discoveries/request-discoveries-timer
:discoveries/new-discover
:wallet/wallet
:wallet/wallet.transactions
:wallet/wallet-selected-asset

View File

@ -57,7 +57,7 @@
(repeat (- columns extras) {:name ""})))))
(defview main []
(letsubs [all-dapps [:get-all-dapps]
(letsubs [all-dapps [:discover/all-dapps]
tabs-hidden? [:tabs-hidden?]]
(let [columns 3]
(when (seq all-dapps)

View File

@ -3,8 +3,7 @@
;; {id (string) descovery (map)}
(s/def :discoveries/discoveries (s/nilable map?))
(s/def :discoveries/discover-search-tags (s/nilable sequential?))
(s/def :discoveries/discover-search-tags (s/nilable set?))
(s/def :discoveries/tags (s/nilable vector?))
(s/def :discoveries/current-tag (s/nilable map?))
(s/def :discoveries/request-discoveries-timer (s/nilable int?))
(s/def :discoveries/new-discover (s/nilable map?))

View File

@ -1,158 +1,186 @@
(ns status-im.ui.screens.discover.events
(:require [re-frame.core :refer [after dispatch enrich]]
[status-im.utils.utils :refer [first-index]]
[status-im.utils.handlers :refer [register-handler get-hashtags]]
(:require [re-frame.core :as re-frame]
[status-im.protocol.core :as protocol]
[status-im.ui.screens.navigation :as nav]
[status-im.data-store.discover :as discoveries]
[status-im.utils.handlers :as u]
[status-im.utils.datetime :as time]
[status-im.utils.random :as random]
[taoensso.timbre :as log]
[status-im.utils.handlers :as handlers]))
[status-im.ui.screens.discover.navigation]
[status-im.utils.handlers :as handlers]
[clojure.string :as string]))
(def request-discoveries-interval-s 600)
(def maximum-number-of-discoveries 1000)
(register-handler :init-discoveries
(fn [db _]
(-> db
(assoc :tags [])
(assoc :discoveries {}))))
;; EFFECTS
(defmethod nav/preload-data! :discover
[db _]
(-> db
(assoc-in [:toolbar-search :show] nil)
(assoc :tags (discoveries/get-all-tags))
(assoc :discoveries (->> (discoveries/get-all :desc)
(map (fn [{:keys [message-id] :as discover}]
[message-id discover]))
(into {})))))
(re-frame/reg-fx
::send-portions
(fn [{:keys [current-public-key web3 contacts to discoveries]}]
(protocol/send-discoveries-response!
{:web3 web3
:discoveries discoveries
:message {:from current-public-key
:to to}})))
;; todo(goranjovic): at the moment we do nothing when a status without hashtags is posted
;; but we probably should post a special "delete" status that removes any previous
;; hashtag statuses in that scenario. In any case, that's the reason why this event
;; gets even the statuses without a hashtag - it may need to do stuff with them as well.
(register-handler :broadcast-status
(u/side-effect!
(fn [{:keys [current-public-key web3]
:accounts/keys [accounts current-account-id]
:contacts/keys [contacts]}
[_ status]]
(if-let [hashtags (seq (handlers/get-hashtags status))]
(let [{:keys [name photo-path]} (get accounts current-account-id)
message-id (random/id)
message {:message-id message-id
:from current-public-key
:payload {:message-id message-id
:status status
:hashtags (vec hashtags)
:profile {:name name
:profile-image photo-path}}}]
(doseq [id (u/identities contacts)]
(protocol/send-status!
{:web3 web3
:message (assoc message :to id)}))
(dispatch [:status-received message]))))))
(re-frame/reg-fx
::request-discoveries
(fn [{:keys [identities web3 current-public-key message-id]}]
(doseq [id identities]
(when-not (protocol/message-pending? web3 :discoveries-request id)
(protocol/send-discoveries-request!
{:web3 web3
:message {:from current-public-key
:to id
:message-id message-id}})))))
(register-handler :status-received
(u/side-effect!
(fn [{:keys [discoveries]} [_ {:keys [from payload]}]]
(when (and (not (discoveries/exists? (:message-id payload)))
(not (get discoveries (:message-id payload))))
(let [{:keys [message-id status hashtags profile]} payload
{:keys [name profile-image]} profile
discover {:message-id message-id
:name name
:photo-path profile-image
:status status
:whisper-id from
:tags (map #(hash-map :name %) hashtags)
:created-at (time/now-ms)}]
(dispatch [:add-discover discover]))))))
(re-frame/reg-fx
::broadcast-status
(fn [{:keys [identities web3 message]}]
(doseq [id identities]
(protocol/send-status!
{:web3 web3
:message (assoc message :to id)}))))
(register-handler :start-requesting-discoveries
(fn [{:keys [request-discoveries-timer] :as db}]
(when request-discoveries-timer
(js/clearInterval request-discoveries-timer))
(dispatch [:request-discoveries])
(assoc db :request-discoveries-timer
(js/setInterval #(dispatch [:request-discoveries])
(* request-discoveries-interval-s 1000)))))
;; HELPER-FN
(register-handler :request-discoveries
(u/side-effect!
(fn [{:keys [current-public-key web3]
:contacts/keys [contacts]}]
(doseq [id (u/identities contacts)]
(when-not (protocol/message-pending? web3 :discoveries-request id)
(protocol/send-discoveries-request!
{:web3 web3
:message {:from current-public-key
:to id
:message-id (random/id)}}))))))
(defn send-portions-when-contact-exists
[{:keys [current-public-key web3 discoveries]
:contacts/keys [contacts]}
to]
(when (get contacts to)
{::send-portions {:current-public-key current-public-key
:web3 web3
:contacts contacts
:to to
:discoveries (mapv #(dissoc % :tags) (vals discoveries))}}))
(register-handler :discoveries-send-portions
(u/side-effect!
(fn [{:keys [current-public-key web3]
:contacts/keys [contacts]} [_ to]]
(when (get contacts to)
(protocol/send-discoveries-response!
{:web3 web3
:discoveries (discoveries/get-all :asc)
:message {:from current-public-key
:to to}})))))
(defn add-discover [db {:keys [message-id] :as discover}]
(assoc-in db [:discoveries message-id] discover))
(register-handler :discoveries-request-received
(u/side-effect!
(fn [_ [_ {:keys [from]}]]
(dispatch [:discoveries-send-portions from]))))
(defn add-discovers [db discovers]
(reduce add-discover db discovers))
(register-handler :discoveries-response-received
(u/side-effect!
(fn [{:keys [discoveries]
:contacts/keys [contacts]} [_ {:keys [payload from]}]]
(when (get contacts from)
(when-let [data (:data payload)]
(doseq [{:keys [message-id] :as discover} data]
(when (and (not (discoveries/exists? message-id))
(not (get discoveries message-id)))
(let [discover (assoc discover :created-at (time/now-ms))]
(dispatch [:add-discover discover])))))))))
(defn new-discover? [discoveries {:keys [message-id]}]
(not (get discoveries message-id)))
(defn add-discover
[db [_ discover]]
(assoc db :new-discover discover))
(defn save-discover!
[{:keys [new-discover]} _]
(discoveries/save new-discover))
;; EVENTS
(defn reload-tags!
[db _]
(assoc db :tags (discoveries/get-all-tags)
:discoveries (->> (discoveries/get-all :desc)
(map (fn [{:keys [message-id] :as discover}]
[message-id discover]))
(into {}))))
(defn navigate-to-discover-search-results [db search-tags]
{:db (assoc db :discover-search-tags search-tags)
:dispatch [:navigate-to :discover-search-results]})
(register-handler :add-discover
(u/handlers->
add-discover
save-discover!
reload-tags!))
(handlers/register-handler-fx
:discover/search-tags-results-view
(fn [{:keys [db]} [_ search-text]]
(navigate-to-discover-search-results db (reduce (fn [acc tag]
(conj acc (-> tag
(string/replace #"#" "")
string/lower-case
keyword)))
#{}
(re-seq #"[^ !?,;:.]+" search-text)))))
(register-handler :remove-old-discoveries!
(u/side-effect!
(fn [_ _]
(discoveries/delete :created-at :asc 1000 200))))
(handlers/register-handler-fx
:discover/search-tag-results-view
(fn [{:keys [db]} [_ tag]]
(navigate-to-discover-search-results db #{(keyword tag)})))
(handlers/register-handler-db
:show-discovery
(fn [db [_ tags view-id]]
(-> db
(assoc :discover-search-tags tags)
(nav/navigate-to view-id))))
(handlers/register-handler-fx
:discover/popular-tags-view
(fn [{:keys [db]} [_ popular-tags]]
{:db (assoc db :discover-search-tags (into #{} popular-tags))
:dispatch [:navigate-to :discover-all-hashtags]}))
(handlers/register-handler-fx
:broadcast-status
[(re-frame/inject-cofx :random-id)]
(fn [{{:keys [current-public-key web3]
:accounts/keys [accounts current-account-id]
:contacts/keys [contacts]} :db
random-id :random-id}
[_ status]]
(when-let [hashtags (seq (handlers/get-hashtags status))]
(let [{:keys [name photo-path]} (get accounts current-account-id)
message {:message-id random-id
:from current-public-key
:payload {:message-id random-id
:status status
:hashtags (vec hashtags)
:profile {:name name
:profile-image photo-path}}}]
{::broadcast-status {:web3 web3
:message message
:identities (handlers/identities contacts)}
:dispatch [:status-received message]}))))
(handlers/register-handler-fx
:init-discoveries
[(re-frame/inject-cofx :data-store/discoveries)]
(fn [{:keys [data-store/discoveries db]} _]
{:db (assoc db :discoveries discoveries)
:dispatch [:request-discoveries]}))
(handlers/register-handler-fx
:request-discoveries
[(re-frame/inject-cofx :random-id)]
(fn [{{:keys [current-public-key web3]
:contacts/keys [contacts]} :db
random-id :random-id} [this-event]]
;; this event calls itself at regular intervals
;; TODO (yenda): this was previously using setInterval explicitly, with
;; dispatch-later it is using it implicitly. setInterval is
;; problematic for such long period of time and will cause a warning
;; for Android in latest versions of react-nativexb
{::request-discoveries {:current-public-key current-public-key
:web3 web3
:identities (handlers/identities contacts)
:message-id random-id}
:dispatch-later [{:ms (* request-discoveries-interval-s 1000)
:dispatch [this-event]}]}))
(handlers/register-handler-fx
:discoveries-send-portions
(fn [{:keys [db]} [_ to]]
(send-portions-when-contact-exists db to)))
(handlers/register-handler-fx
:discoveries-request-received
(fn [{:keys [db]} [_ {:keys [from]}]]
(send-portions-when-contact-exists db from)))
(handlers/register-handler-fx
:discoveries-response-received
[(re-frame/inject-cofx :now)]
(fn [{{:keys [discoveries]
:contacts/keys [contacts] :as db} :db
now :now}
[_ {:keys [payload from]}]]
(when (get contacts from)
(when-let [discovers (some->> (:data payload)
(filter #(new-discover? discoveries %))
(map #(assoc %
:created-at now
:tags (handlers/get-hashtags (:status %)))))]
{:db (add-discovers db discovers)
:data-store.discover/save-all [discovers maximum-number-of-discoveries]}))))
(handlers/register-handler-fx
:status-received
[(re-frame/inject-cofx :now)]
(fn [{{:keys [discoveries] :as db} :db
now :now}
[_ {{:keys [message-id status profile] :as payload} :payload
from :from}]]
(when (new-discover? discoveries payload)
(let [{:keys [name profile-image]} profile
discover {:message-id message-id
:name name
:photo-path profile-image
:status status
:tags (handlers/get-hashtags status)
:whisper-id from
:created-at now}]
{:db (add-discover db discover)
:data-store.discover/save-all [[discover] maximum-number-of-discoveries]}))))
(handlers/register-handler-fx
:show-status-author-profile

View File

@ -0,0 +1,7 @@
(ns status-im.ui.screens.discover.navigation
(:require [status-im.ui.screens.navigation :as navigation]
[status-im.data-store.discover :as discoveries]))
(defmethod navigation/preload-data! :discover
[db _]
(assoc-in db [:toolbar-search :show] nil))

View File

@ -10,7 +10,7 @@
(defn render-tag [tag]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:show-discovery [tag] :discover-search-results])}
{:on-press #(re-frame/dispatch [:discover/search-tag-results-view tag])}
[react/view styles/tag-view
[react/text {:style styles/tag-title
:font :default}
@ -24,16 +24,15 @@
:shows-horizontal-scroll-indicator false
:default-separator? false}]])
(defview discover-all-hashtags []
(letsubs [current-account [:get-current-account]
popular-tags [:get-popular-tags 10]
contacts [:get-contacts]
{:keys [discoveries]} [:get-popular-discoveries 10]] ;uses the tags passed via :discover-search-tags state
(defview discover-all-popular-hashtags []
(letsubs [current-account [:get-current-account]
contacts [:get-contacts]
{:keys [discoveries tags]} [:discover/all-popular-hashtags]]
[react/view styles/all-recent-container
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (i18n/label :t/popular-tags)]]
[tags-menu (map :name popular-tags)]
[tags-menu (map name tags)]
[react/scroll-view styles/list-container
[react/view styles/status-list-outer
[react/view styles/status-list-inner

View File

@ -7,7 +7,7 @@
[status-im.i18n :as i18n]))
(defview discover-all-recent []
(letsubs [discoveries [:get-recent-discoveries]
(letsubs [discoveries [:discover/recent-discoveries]
tabs-hidden? [:tabs-hidden?]
current-account [:get-current-account]
contacts [:get-contacts]]
@ -22,8 +22,8 @@
(let [discoveries (map-indexed vector discoveries)]
(for [[i {:keys [message-id] :as message}] discoveries]
^{:key (str "message-recent-" message-id)}
[components/discover-list-item-full
{:message message
:show-separator? (not= (inc i) (count discoveries))
:contacts contacts
:current-account current-account}]))]]])]))
[components/discover-list-item-full
{:message message
:show-separator? (not= (inc i) (count discoveries))
:contacts contacts
:current-account current-account}]))]]])]))

View File

@ -10,27 +10,26 @@
;; TOOD(oskarth): Refactor this, very similar to discover-all-hashtags view
(defview discover-search-results []
(letsubs [{:keys [discoveries total]} [:get-popular-discoveries 250]
tags [:get :discover-search-tags]
contacts [:get-contacts]
current-account [:get-current-account]]
[react/view styles/discover-tag-container
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (str "#" (first tags) " " total)]]
(if (empty? discoveries)
[react/view styles/empty-view
[vi/icon :icons/group-big {:style contacts-styles/empty-contacts-icon}]
[react/text {:style contacts-styles/empty-contacts-text}
(i18n/label :t/no-statuses-found)]]
[react/scroll-view styles/list-container
[react/view styles/status-list-outer
[react/view styles/status-list-inner
(let [discoveries (map-indexed vector discoveries)]
(for [[i {:keys [message-id] :as message}] discoveries]
^{:key (str "message-hashtag-" message-id)}
[components/discover-list-item-full
{:message message
:show-separator? (not= (inc i) (count discoveries))
:contacts contacts
:current-account current-account}]))]]])]))
(letsubs [{:keys [discoveries tags total]} [:discover/search-results 250]
contacts [:get-contacts]
current-account [:get-current-account]]
[react/view styles/discover-tag-container
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (str "#" (name (first tags)) " " total)]]
(if (empty? discoveries)
[react/view styles/empty-view
[vi/icon :icons/group-big {:style contacts-styles/empty-contacts-icon}]
[react/text {:style contacts-styles/empty-contacts-text}
(i18n/label :t/no-statuses-found)]]
[react/scroll-view styles/list-container
[react/view styles/status-list-outer
[react/view styles/status-list-inner
(let [discoveries (map-indexed vector discoveries)]
(for [[i {:keys [message-id] :as message}] discoveries]
^{:key (str "message-hashtag-" message-id)}
[components/discover-list-item-full
{:message message
:show-separator? (not= (inc i) (count discoveries))
:contacts contacts
:current-account current-account}]))]]])]))

View File

@ -2,19 +2,11 @@
(:require [re-frame.core :refer [reg-sub]]
[status-im.utils.datetime :as time]))
(reg-sub :get-discoveries :discoveries)
(reg-sub :get-current-tag :current-tag)
(reg-sub :get-discover-search-tags :discover-search-tags)
(reg-sub :get-tags :tags)
(defn- calculate-priority [chats current-public-key contacts
(defn- calculate-priority [now-ms chats current-public-key contacts
{:keys [whisper-id created-at]}]
(let [contact (get contacts whisper-id)
chat (get chats whisper-id)
seen-online-recently? (< (- (time/now-ms) (get contact :last-online))
seen-online-recently? (< (- now-ms (get contact :last-online))
time/hour)
me? (= current-public-key whisper-id)]
(+ created-at ; message is newer => priority is higher
@ -23,67 +15,94 @@
(if (or me? seen-online-recently?) time/hour 0)))) ; the user was online recently => increase priority
(defn- get-discoveries-by-tags [discoveries current-tag tags]
(let [tags' (or tags [current-tag])]
(filter #(some (->> (:tags %)
(map :name)
(into (hash-set)))
tags')
(vals discoveries))))
(defn- get-discoveries-by-tags [discoveries-by-tags search-tags]
(reduce (fn [acc search-tag]
(concat acc (get discoveries-by-tags search-tag [])))
[]
search-tags))
(reg-sub
:get-popular-discoveries
:<- [:get-discoveries]
:<- [:get-current-tag]
:<- [:get-discover-search-tags]
(reg-sub :discover/discoveries :discoveries)
(reg-sub :discover/discoveries-with-priority
:<- [:discover/discoveries]
:<- [:chats]
:<- [:get-contacts]
:<- [:get-current-public-key]
(fn [[discoveries current-tag discover-search-tags chats contacts public-key]
[_ limit tags]]
(let [discoveries (->> (get-discoveries-by-tags discoveries
current-tag
(or tags discover-search-tags))
(map #(assoc % :priority (calculate-priority chats public-key contacts %)))
(sort-by :priority >))]
{:discoveries (take limit discoveries)
:total (count discoveries)})))
:<- [:get :current-public-key]
(fn [[discoveries chats contacts current-public-key]]
(let [now-ms (time/now-ms)]
(map #(assoc % :priority (calculate-priority now-ms chats current-public-key contacts %)) (vals discoveries)))))
(reg-sub
:get-top-discovery-per-tag
:<- [:get-discoveries]
:<- [:get-tags]
(fn [[discoveries tags] [_ limit]]
(let [tag-names (map :name (take limit tags))]
(for [tag tag-names]
(let [results (get-discoveries-by-tags discoveries tag nil)]
[tag {:discovery (first results)
:total (count results)}])))))
(reg-sub :discover/search-tags :discover-search-tags)
(reg-sub
:get-recent-discoveries
:<- [:get-discoveries]
(reg-sub :discover/tags
:<- [:discover/discoveries]
(fn [discoveries]
(reduce (fn [acc {:keys [tags]}]
(into acc tags))
#{}
(vals discoveries))))
;; TODO(yenda) this is not really the most recent discoveries
;; it's just all off them
(reg-sub :discover/recent-discoveries
:<- [:discover/discoveries]
(fn [discoveries]
(sort-by :created-at > (vals discoveries))))
(reg-sub
:get-popular-tags
:<- [:get-tags]
(fn [tags [_ limit]]
(take limit tags)))
(reg-sub :discover/discoveries-by-tags
:<- [:discover/discoveries-with-priority]
(fn [discoveries]
(reduce (fn [discoveries-by-tags {:keys [tags] :as discovery}]
(reduce (fn [discoveries-by-tags tag]
(update discoveries-by-tags tag conj discovery))
discoveries-by-tags
tags))
{}
discoveries)))
(reg-sub
:get-discover-search-results
:<- [:get-discoveries]
:<- [:get-current-tag]
:<- [:get-discover-search-tags]
(fn [[discoveries current-tag discover-search-tags]]
(get-discoveries-by-tags discoveries current-tag discover-search-tags)))
(reg-sub :discover/most-popular-hashtags
:<- [:discover/discoveries-by-tags]
(fn [discoveries]
(->> discoveries
(sort-by (comp count val) >)
(take 10))))
(reg-sub
:get-all-dapps
:<- [:get-contact-groups]
(reg-sub :discover/popular-hashtags-preview
:<- [:discover/most-popular-hashtags]
(fn [most-popular-hashtags]
(->> most-popular-hashtags
(map (fn [[tag discoveries]] {:tag tag
:total (count discoveries)
:discovery (first (sort-by :priority > discoveries))})))))
(reg-sub :discover/all-popular-hashtags
:<- [:discover/most-popular-hashtags]
(fn [most-popular-hashtags]
(let [tags (map first most-popular-hashtags)
discoveries (apply concat (map second most-popular-hashtags))]
{:tags tags
:discoveries (sort-by :priority > (distinct discoveries))})))
(reg-sub :discover/search-results
:<- [:discover/discoveries-by-tags]
:<- [:discover/search-tags]
:<- [:chats]
:<- [:get-contacts]
(fn [[groups contacts]]
(let [dapp-ids (into #{} (map :identity) (get-in groups ["dapps" :contacts]))]
(select-keys contacts dapp-ids))))
:<- [:get :current-public-key]
(fn [[discoveries search-tags chats contacts current-public-key] [_ limit]]
(let [discoveries (->> (get-discoveries-by-tags discoveries search-tags)
(sort-by :priority >))]
{:discoveries (take limit discoveries)
:tags search-tags
:total (count discoveries)})))
(reg-sub :discover/all-dapps
(fn [db]
(let [dapp? (->> (get-in db [:group/contact-groups "dapps" :contacts])
(map :identity)
set)]
(->> (:contacts/contacts db)
(filter #(-> % key dapp?))
(into {})))))

View File

@ -39,16 +39,15 @@
:search-placeholder (i18n/label :t/search-tags)
:on-search-submit (fn [text]
(when-not (string/blank? text)
(let [tags (get-hashtags text)]
(re-frame/dispatch [:show-discovery tags :discover-search-results]))))}])
(re-frame/dispatch [:discover/search-tags-results-view text])))}])
(defn top-status-for-popular-hashtag [{:keys [tag item current-account contacts]}]
(let [{:keys [discovery total]} item]
(defn top-status-for-popular-hashtag [{:keys [popular-hashtag current-account contacts]}]
(let [{:keys [tag discovery total]} popular-hashtag]
[react/view styles/popular-list-container
[react/view styles/row
[react/view {}
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:show-discovery [tag] :discover-search-results])}
{:on-press #(re-frame/dispatch [:discover/search-tag-results-view tag])}
[react/view {}
[react/text {:style styles/tag-name
:font :medium}
@ -62,21 +61,19 @@
:current-account current-account
:contacts contacts}]]))
(defn popular-hashtags-preview [{:keys [popular-discoveries popular-tags contacts current-account]}]
(let [has-content? (seq popular-tags)
tags (map :name popular-tags)]
(defn popular-hashtags-preview [{:keys [popular-hashtags contacts current-account]}]
(let [has-content? (seq popular-hashtags)]
[react/view styles/popular-container
[components/title :t/popular-tags :t/all #(re-frame/dispatch [:show-discovery tags :discover-all-hashtags]) has-content?]
[components/title :t/popular-tags :t/all #(re-frame/dispatch [:navigate-to :discover-all-popular-hashtags]) has-content?]
(if has-content?
[carousel/carousel {:pageStyle styles/carousel-page-style
:gap 8
:sneak 16
:count (count popular-tags)}
(for [[tag item] popular-discoveries]
[top-status-for-popular-hashtag {:tag tag
:item item
:contacts contacts
:current-account current-account}])]
:count (count popular-hashtags)}
(for [popular-hashtag popular-hashtags]
[top-status-for-popular-hashtag {:popular-hashtag popular-hashtag
:contacts contacts
:current-account current-account}])]
[empty-section :empty-hashtags :t/no-hashtags-discovered-title :t/no-hashtags-discovered-body])]))
(defn recent-statuses-preview [{:keys [current-account contacts discoveries]}]
@ -147,20 +144,18 @@
search-text [:get-in [:toolbar-search :text]]
contacts [:get-contacts]
current-account [:get-current-account]
discoveries [:get-recent-discoveries]
all-dapps [:get-all-dapps]
popular-tags [:get-popular-tags 10]
popular-discoveries [:get-top-discovery-per-tag 10]]
discoveries [:discover/recent-discoveries]
all-dapps [:discover/all-dapps]
popular-hashtags [:discover/popular-hashtags-preview]]
[react/view styles/discover-container
[toolbar-view (and current-view?
(= show-search :discover)) search-text]
[react/scroll-view styles/list-container
[recent-statuses-preview {:contacts contacts
:current-account current-account
:discoveries discoveries}]
[popular-hashtags-preview {:popular-tags popular-tags
:popular-discoveries popular-discoveries
:contacts contacts
:current-account current-account}]
:current-account current-account
:discoveries discoveries}]
[popular-hashtags-preview {:popular-hashtags popular-hashtags
:contacts contacts
:current-account current-account}]
[all-dapps/preview all-dapps]
[public-chats-teaser]]]))

View File

@ -22,7 +22,7 @@
status-im.ui.screens.wallet.settings.events
status-im.ui.screens.wallet.transactions.events
status-im.ui.screens.wallet.choose-recipient.events
[re-frame.core :refer [dispatch reg-fx reg-cofx] :as re-frame]
[re-frame.core :as re-frame]
[status-im.native-module.core :as status]
[status-im.ui.components.react :as react]
[status-im.ui.components.permissions :as permissions]
@ -36,7 +36,7 @@
[status-im.utils.config :as config]
[status-im.utils.crypt :as crypt]
[status-im.utils.notifications :as notifications]
[status-im.utils.handlers :refer [register-handler-db register-handler-fx]]
[status-im.utils.handlers :as handlers]
[status-im.utils.instabug :as inst]
[status-im.utils.platform :as platform]
[status-im.utils.types :as types]
@ -60,21 +60,21 @@
{:chat-id chat-id}
jail-response]])
:when event]
(dispatch event)))})))
(re-frame/dispatch event)))})))
;;;; COFX
(reg-cofx
(re-frame/reg-cofx
:now
(fn [coeffects _]
(assoc coeffects :now (time/now-ms))))
(reg-cofx
(re-frame/reg-cofx
:random-id
(fn [coeffects _]
(assoc coeffects :random-id (random/id))))
(reg-cofx
(re-frame/reg-cofx
:random-id-seq
(fn [coeffects _]
(assoc coeffects :random-id-seq
@ -83,7 +83,7 @@
;;;; FX
(reg-fx
(re-frame/reg-fx
:call-jail
(fn [{:keys [callback-events-creator] :as opts}]
(status/call-jail
@ -92,103 +92,103 @@
(assoc :callback
(fn [jail-response]
(doseq [event (callback-events-creator jail-response)]
(dispatch event))))))))
(re-frame/dispatch event))))))))
(reg-fx
(re-frame/reg-fx
:call-jail-function
call-jail-function)
(reg-fx
(re-frame/reg-fx
:call-jail-function-n
(fn [opts-seq]
(doseq [opts opts-seq]
(call-jail-function opts))))
(reg-fx
(re-frame/reg-fx
:http-post
(fn [{:keys [action data success-event-creator failure-event-creator]}]
(utils/http-post action
data
#(dispatch (success-event-creator %))
#(dispatch (failure-event-creator %)))))
#(re-frame/dispatch (success-event-creator %))
#(re-frame/dispatch (failure-event-creator %)))))
(defn- http-get [{:keys [url response-validator success-event-creator failure-event-creator]}]
(if response-validator
(utils/http-get url
response-validator
#(dispatch (success-event-creator %))
#(dispatch (failure-event-creator %)))
#(re-frame/dispatch (success-event-creator %))
#(re-frame/dispatch (failure-event-creator %)))
(utils/http-get url
#(dispatch (success-event-creator %))
#(dispatch (failure-event-creator %)))))
#(re-frame/dispatch (success-event-creator %))
#(re-frame/dispatch (failure-event-creator %)))))
(reg-fx
(re-frame/reg-fx
:http-get
http-get)
(reg-fx
(re-frame/reg-fx
:http-get-n
(fn [calls]
(doseq [call calls]
(http-get call))))
(reg-fx
(re-frame/reg-fx
::init-store
(fn []
(data-store/init)))
(reg-fx
(re-frame/reg-fx
::initialize-crypt-fx
(fn []
(crypt/gen-random-bytes
1024
(fn [{:keys [error buffer]}]
(if error
(log/error "Failed to generate random bytes to initialize sjcl crypto")
(->> (.toString buffer "hex")
(.toBits (.. dependencies/eccjs -sjcl -codec -hex))
(.addEntropy (.. dependencies/eccjs -sjcl -random))))))))
1024
(fn [{:keys [error buffer]}]
(if error
(log/error "Failed to generate random bytes to initialize sjcl crypto")
(->> (.toString buffer "hex")
(.toBits (.. dependencies/eccjs -sjcl -codec -hex))
(.addEntropy (.. dependencies/eccjs -sjcl -random))))))))
(defn move-to-internal-storage [config]
(status/move-to-internal-storage
#(status/start-node config)))
#(status/start-node config)))
(reg-fx
(re-frame/reg-fx
:initialize-geth-fx
(fn [config]
(status/should-move-to-internal-storage?
(fn [should-move?]
(if should-move?
(dispatch [:request-permissions
[:read-external-storage]
#(move-to-internal-storage config)
#(dispatch [:move-to-internal-failure-message])])
(status/start-node config))))))
(fn [should-move?]
(if should-move?
(re-frame/dispatch [:request-permissions
[:read-external-storage]
#(move-to-internal-storage config)
#(re-frame/dispatch [:move-to-internal-failure-message])])
(status/start-node config))))))
(reg-fx
(re-frame/reg-fx
::status-module-initialized-fx
(fn []
(status/module-initialized!)))
(reg-fx
(re-frame/reg-fx
::request-permissions-fx
(fn [[permissions then else]]
(permissions/request-permissions permissions then else)))
(reg-fx
(re-frame/reg-fx
::testfairy-alert
(fn []
(when config/testfairy-enabled?
(utils/show-popup
(i18n/label :testfairy-title)
(i18n/label :testfairy-message)))))
(i18n/label :testfairy-title)
(i18n/label :testfairy-message)))))
(reg-fx
(re-frame/reg-fx
::get-fcm-token-fx
(fn []
(notifications/get-fcm-token)))
(reg-fx
(re-frame/reg-fx
:show-error
(fn [content]
(utils/show-popup "Error" content)))
@ -206,17 +206,17 @@
;;;; Handlers
(register-handler-db
(handlers/register-handler-db
:set
(fn [db [_ k v]]
(assoc db k v)))
(register-handler-db
(handlers/register-handler-db
:set-in
(fn [db [_ path v]]
(assoc-in db path v)))
(register-handler-fx
(handlers/register-handler-fx
:initialize-app
(fn [_ _]
{::testfairy-alert nil
@ -227,7 +227,7 @@
[:initialize-crypt]
[:initialize-geth]]}))
(register-handler-fx
(handlers/register-handler-fx
:initialize-db
(fn [{{:keys [status-module-initialized? status-node-started?
network-status network]
@ -242,7 +242,7 @@
:status-node-started? status-node-started?
:network network)}))
(register-handler-db
(handlers/register-handler-db
:initialize-account-db
(fn [{:keys [accounts/accounts contacts/contacts networks/networks
network network-status view-id navigation-stack chats
@ -271,7 +271,7 @@
console-contact
(assoc :contacts/contacts {console-chat-id console-contact})))))
(register-handler-fx
(handlers/register-handler-fx
:initialize-account
(fn [_ [_ address events-after]]
{:dispatch-n (cond-> [[:initialize-account-db address]
@ -281,18 +281,16 @@
[:initialize-chats]
[:load-contacts]
[:load-contact-groups]
[:init-discoveries]
[:initialize-debugging {:address address}]
[:send-account-update-if-needed]
[:start-requesting-discoveries]
[:remove-old-discoveries!]
[:init-discoveries]
[:update-wallet]
[:update-transactions]
[:get-fcm-token]]
(seq events-after)
(into events-after))}))
(register-handler-fx
(handlers/register-handler-fx
:check-console-chat
(fn [{{:accounts/keys [accounts] :as db} :db} [_ open-console?]]
(let [view (if (empty? accounts)
@ -307,12 +305,12 @@
(when open-console?
[[:navigate-to-chat console-chat-id]]))})))))
(register-handler-fx
(handlers/register-handler-fx
:initialize-crypt
(fn [_ _]
{::initialize-crypt-fx nil}))
(register-handler-fx
(handlers/register-handler-fx
:initialize-geth
(fn [{db :db} _]
(let [{:accounts/keys [current-account-id accounts]} db
@ -323,12 +321,12 @@
(get-in default-networks [default-network :config]))]
{:initialize-geth-fx network-config})))
(register-handler-fx
(handlers/register-handler-fx
:webview-geo-permissions-granted
(fn [{{:keys [webview-bridge]} :db} _]
(.geoPermissionsGranted webview-bridge)))
(register-handler-fx
(handlers/register-handler-fx
:get-fcm-token
(fn [_ _]
{::get-fcm-token-fx nil}))
@ -345,66 +343,66 @@
(defn handle-jail-signal [{:keys [chat_id data]}]
(let [{:keys [event data]} (types/json->clj data)]
(case event
"local-storage" (dispatch [:set-local-storage {:chat-id chat_id
"local-storage" (re-frame/dispatch [:set-local-storage {:chat-id chat_id
:data data}])
"show-suggestions" (dispatch [:show-suggestions-from-jail {:chat-id chat_id
"show-suggestions" (re-frame/dispatch [:show-suggestions-from-jail {:chat-id chat_id
:markup data}])
"send-message" (dispatch [:send-message-from-jail {:chat-id chat_id
"send-message" (re-frame/dispatch [:send-message-from-jail {:chat-id chat_id
:message data}])
"handler-result" (let [orig-params (:origParams data)]
;; TODO(janherich): figure out and fix chat_id from event
(dispatch [:command-handler! (:chat-id orig-params)
(re-frame/dispatch [:command-handler! (:chat-id orig-params)
(restore-command-ref-keyword orig-params)
{:result {:returned (dissoc data :origParams)}}]))
(log/debug "Unknown jail signal " event))))
(register-handler-fx
(handlers/register-handler-fx
:signal-event
(fn [_ [_ event-str]]
(log/debug :event-str event-str)
(inst/log (str "Signal event: " event-str))
(let [{:keys [type event]} (types/json->clj event-str)]
(case type
"transaction.queued" (dispatch [:transaction-queued event])
"transaction.failed" (dispatch [:transaction-failed event])
"node.started" (dispatch [:status-node-started])
"node.stopped" (dispatch [:status-node-stopped])
"module.initialized" (dispatch [:status-module-initialized])
"request_geo_permissions" (dispatch [:request-permissions [:geolocation]
#(dispatch [:webview-geo-permissions-granted])])
"transaction.queued" (re-frame/dispatch [:transaction-queued event])
"transaction.failed" (re-frame/dispatch [:transaction-failed event])
"node.started" (re-frame/dispatch [:status-node-started])
"node.stopped" (re-frame/dispatch [:status-node-stopped])
"module.initialized" (re-frame/dispatch [:status-module-initialized])
"request_geo_permissions" (re-frame/dispatch [:request-permissions [:geolocation]
#(re-frame/dispatch [:webview-geo-permissions-granted])])
"jail.signal" (handle-jail-signal event)
(log/debug "Event " type " not handled")))))
(register-handler-fx
(handlers/register-handler-fx
:status-module-initialized
(fn [{:keys [db]} _]
{:db (assoc db :status-module-initialized? true)
::status-module-initialized-fx nil}))
(register-handler-fx
(handlers/register-handler-fx
:status-node-started
(fn [{{:node/keys [after-start] :as db} :db}]
(merge {:db (assoc db :status-node-started? true)}
(when after-start {:dispatch-n [after-start]}))))
(register-handler-fx
(handlers/register-handler-fx
:status-node-stopped
(fn [{{:node/keys [after-stop]} :db}]
(when after-stop {:dispatch-n [after-stop]})))
(register-handler-fx
(handlers/register-handler-fx
:app-state-change
(fn [_ [_ state]]))
;; TODO(rasom): let's not remove this handler, it will be used for
;; pausing node on entering background on android
(register-handler-fx
(handlers/register-handler-fx
:request-permissions
(fn [_ [_ permissions then else]]
{::request-permissions-fx [permissions then else]}))
(register-handler-fx
(handlers/register-handler-fx
:request-geolocation-update
(fn [_ _]
{:dispatch [:request-permissions [:geolocation]
@ -412,8 +410,8 @@
(let [watch-id (atom nil)]
(.getCurrentPosition
react/geolocation
#(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])
#(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])
#(re-frame/dispatch [:update-geolocation (js->clj % :keywordize-keys true)])
#(re-frame/dispatch [:update-geolocation (js->clj % :keywordize-keys true)])
(clj->js {:enableHighAccuracy true :timeout 20000 :maximumAge 1000}))
(when platform/android?
(reset! watch-id
@ -423,9 +421,9 @@
(.clearWatch
react/geolocation
@watch-id)
(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])))))))]}))
(re-frame/dispatch [:update-geolocation (js->clj % :keywordize-keys true)])))))))]}))
(register-handler-db
(handlers/register-handler-db
:update-geolocation
(fn [db [_ geolocation]]
(assoc db :geolocation geolocation)))

View File

@ -101,7 +101,7 @@
:profile profile
:edit-my-profile edit-my-profile
:discover-all-recent discover-recent/discover-all-recent
:discover-all-hashtags discover-popular/discover-all-hashtags
:discover-all-popular-hashtags discover-popular/discover-all-popular-hashtags
:discover-search-results discover-search/discover-search-results
:discover-dapp-details discover-dapp-details/dapp-details
:discover-all-dapps discover-all-dapps/main

View File

@ -97,7 +97,7 @@
(defn get-hashtags [status]
(if status
(let [hashtags (map #(string/lower-case (subs % 1))
(let [hashtags (map #(keyword (string/lower-case (subs % 1)))
(re-seq #"#[^ !?,;:.]+" status))]
(set (or hashtags [])))
#{}))