Discovery fixes (#432, #434)

No more newline characters in statuses (#463)
Copyable address and key in profile (#462), several fixes
This commit is contained in:
Alexander Pantyuhov 2016-11-10 15:34:25 +03:00 committed by Roman Volosovskyi
parent b49c3e92d1
commit 2f7d75c90f
40 changed files with 630 additions and 442 deletions

View File

@ -14,6 +14,7 @@
"awesome-phonenumber": "^1.0.13", "awesome-phonenumber": "^1.0.13",
"babel-plugin-transform-es2015-block-scoping": "6.15.0", "babel-plugin-transform-es2015-block-scoping": "6.15.0",
"browserify-zlib": "^0.1.4", "browserify-zlib": "^0.1.4",
"buffer": "^3.6.0",
"chance": "1.0.4", "chance": "1.0.4",
"console-browserify": "^1.1.0", "console-browserify": "^1.1.0",
"constants-browserify": "0.0.1", "constants-browserify": "0.0.1",

View File

@ -65,7 +65,7 @@
[{:keys [current-account-id current-public-key web3 accounts]} _] [{:keys [current-account-id current-public-key web3 accounts]} _]
(let [{:keys [name photo-path status]} (get accounts current-account-id) (let [{:keys [name photo-path status]} (get accounts current-account-id)
{:keys [updates-public-key updates-private-key]} (accounts current-account-id)] {:keys [updates-public-key updates-private-key]} (accounts current-account-id)]
(protocol/broadcats-profile! (protocol/broadcast-profile!
{:web3 web3 {:web3 web3
:message {:from current-public-key :message {:from current-public-key
:message-id (random/id) :message-id (random/id)

View File

@ -15,7 +15,6 @@
[status-im.contacts.views.contact-list :refer [contact-list]] [status-im.contacts.views.contact-list :refer [contact-list]]
[status-im.contacts.views.new-contact :refer [new-contact]] [status-im.contacts.views.new-contact :refer [new-contact]]
[status-im.qr-scanner.screen :refer [qr-scanner]] [status-im.qr-scanner.screen :refer [qr-scanner]]
[status-im.discovery.tag :refer [discovery-tag]]
[status-im.discovery.search-results :refer [discovery-search-results]] [status-im.discovery.search-results :refer [discovery-search-results]]
[status-im.chat.screen :refer [chat]] [status-im.chat.screen :refer [chat]]
[status-im.accounts.login.screen :refer [login]] [status-im.accounts.login.screen :refer [login]]
@ -89,7 +88,6 @@
(let [current-view (validate-current-view @view-id @signed-up?)] (let [current-view (validate-current-view @view-id @signed-up?)]
(let [component (case current-view (let [component (case current-view
:discovery main-tabs :discovery main-tabs
:discovery-tag discovery-tag
:discovery-search-results discovery-search-results :discovery-search-results discovery-search-results
:add-participants new-participants :add-participants new-participants
:remove-participants remove-participants :remove-participants remove-participants

View File

@ -18,6 +18,22 @@
:additional-height 0} :additional-height 0}
:chat {:new-message {:border-top-color styles/color-transparent :chat {:new-message {:border-top-color styles/color-transparent
:border-top-width 0.5}} :border-top-width 0.5}}
:discovery {:subtitle {:color styles/color-gray2
:font-size 14}
:popular {:border-radius 1
:margin-top 2
:margin-bottom 4
:margin-right 2
:elevation 3}
:tag {:flex-direction "column"
:background-color "#7099e619"
:border-radius 5
:padding 4}
:item {:status-text {:color styles/color-black
:line-height 22
:font-size 14}}}
:contacts {:subtitle {:color styles/color-gray2
:font-size 14}}
:bottom-gradient {:height 3} :bottom-gradient {:height 3}
:input-label {:left 4} :input-label {:left 4}
:input-error-text {:margin-left 4} :input-error-text {:margin-left 4}
@ -58,4 +74,7 @@
:chats {:action-button? true :chats {:action-button? true
:new-chat-in-toolbar? false} :new-chat-in-toolbar? false}
:contacts {:action-button? true :contacts {:action-button? true
:new-contact-in-toolbar? false}}) :new-contact-in-toolbar? false
:uppercase-subtitles? false
:group-block-shadows? true}
:discovery {:uppercase-subtitles? false}})

View File

@ -393,10 +393,12 @@
(register-handler :start-chat (register-handler :start-chat
(u/side-effect! (u/side-effect!
(fn [{:keys [chats]} [_ contact-id options navigation-type]] (fn [{:keys [chats current-public-key]}
(if (chats contact-id) [_ contact-id options navigation-type]]
(dispatch [(or navigation-type :navigate-to) :chat contact-id]) (when-not (= current-public-key contact-id)
(dispatch [::start-chat! contact-id options navigation-type]))))) (if (chats contact-id)
(dispatch [(or navigation-type :navigate-to) :chat contact-id])
(dispatch [::start-chat! contact-id options navigation-type]))))))
(register-handler :add-chat (register-handler :add-chat
(u/side-effect! (u/side-effect!

View File

@ -39,6 +39,12 @@
:font :default} :font :default}
name]]) name]])
(defn clean-text [s]
(-> s
(str/replace #"\n" " ")
(str/replace #"\r" "")
(str/trim)))
(defn drawer-menu [] (defn drawer-menu []
(let (let
[account (subscribe [:get-current-account]) [account (subscribe [:get-current-account])
@ -65,7 +71,7 @@
:value name :value name
:on-change-text #(dispatch [:set-in [:profile-edit :name] %]) :on-change-text #(dispatch [:set-in [:profile-edit :name] %])
:on-end-editing #(when (and new-name (not (str/blank? new-name))) :on-end-editing #(when (and new-name (not (str/blank? new-name)))
(dispatch [:account-update {:name new-name}]))}]] (dispatch [:account-update {:name (clean-text new-name)}]))}]]
[view st/status-container [view st/status-container
[text-input {:style st/status-input [text-input {:style st/status-input
:editable true :editable true
@ -77,8 +83,8 @@
:on-change-text #(dispatch [:set-in [:profile-edit :status] %]) :on-change-text #(dispatch [:set-in [:profile-edit :status] %])
:on-blur (fn [] :on-blur (fn []
(when (and new-status (not (str/blank? new-status))) (when (and new-status (not (str/blank? new-status)))
(dispatch [:check-status-change new-status]) (dispatch [:check-status-change (clean-text new-status)])
(dispatch [:account-update {:status new-status}]))) (dispatch [:account-update {:status (clean-text new-status)}])))
:default-value status}]] :default-value status}]]
[view st/menu-items-container [view st/menu-items-container
[menu-item {:name (label :t/profile) [menu-item {:name (label :t/profile)

View File

@ -54,16 +54,18 @@
(defn text (defn text
([t] ([t]
(r/as-element [text-class t])) (r/as-element [text-class t]))
([{:keys [style font] :as opts ([{:keys [style font uppercase?] :as opts
:or {font :default}} t & other] :or {font :default}} t & ts]
(r/as-element (r/as-element
(let [font (get-in platform-specific [:fonts font])] (let [font (get-in platform-specific [:fonts font])
ts (cond->> (conj ts t)
uppercase? (map clojure.string/upper-case))]
(vec (concat (vec (concat
[text-class [text-class
(-> opts (-> opts
(dissoc :font) (dissoc :font)
(assoc :style (merge style font)))] (assoc :style (merge style font)))]
(conj other t))))))) ts))))))
(defn text-input [props text] (defn text-input [props text]
[text-input-class (merge [text-input-class (merge

View File

@ -5,6 +5,7 @@
[reagent.core :as r] [reagent.core :as r]
[status-im.components.selectable-field.styles :as st] [status-im.components.selectable-field.styles :as st]
[status-im.i18n :refer [label]] [status-im.i18n :refer [label]]
[status-im.utils.platform :as p]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(defn- on-press (defn- on-press
@ -41,7 +42,7 @@
[text-input {:style (st/sized-text full-height) [text-input {:style (st/sized-text full-height)
:multiline true :multiline true
:selectTextOnFocus true :selectTextOnFocus true
:editable editable? :editable (if p/android? true editable?)
:auto-focus true :auto-focus true
:on-selection-change #(on-selection-change % component) :on-selection-change #(on-selection-change % component)
:on-focus #(log/debug "Focused" %) :on-focus #(log/debug "Focused" %)

View File

@ -8,6 +8,7 @@
(def color-gray "#838c93de") (def color-gray "#838c93de")
(def color-gray2 "#8f838c93") (def color-gray2 "#8f838c93")
(def color-gray3 "#00000040") (def color-gray3 "#00000040")
(def color-steel "#838b91")
(def color-white "white") (def color-white "white")
(def color-light-blue-transparent "#bbc4cb32") (def color-light-blue-transparent "#bbc4cb32")
(def color-light-gray "#EEF2F5") (def color-light-gray "#EEF2F5")

View File

@ -22,7 +22,7 @@
:height toolbar-height}) :height toolbar-height})
(defn toolbar-nav-actions-container [actions] (defn toolbar-nav-actions-container [actions]
{:width (if (and actions (> (count actions) 0)) {:width (when (and actions (> (count actions) 0))
(-> (+ toolbar-icon-width toolbar-icon-spacing) (-> (+ toolbar-icon-width toolbar-icon-spacing)
(* (count actions)) (* (count actions))
(+ toolbar-icon-spacing))) (+ toolbar-icon-spacing)))
@ -45,9 +45,12 @@
:color text1-color :color text1-color
:font-size 16}) :font-size 16})
(def toolbar-actions-container (defn toolbar-actions-container [actions-count custom]
{:flex-direction "row" (merge {:flex-direction "row"
:margin-left 8}) :margin-left toolbar-icon-spacing}
(when (and (= actions-count 0)
(not custom))
{:width (+ toolbar-icon-width toolbar-icon-spacing)})))
(def toolbar-action (def toolbar-action
{:width toolbar-icon-width {:width toolbar-icon-width

View File

@ -11,14 +11,14 @@
[status-im.components.toolbar.styles :as st] [status-im.components.toolbar.styles :as st]
[status-im.utils.platform :refer [platform-specific]])) [status-im.utils.platform :refer [platform-specific]]))
(defn toolbar [{title :title (defn toolbar [{title :title
nav-action :nav-action nav-action :nav-action
hide-nav? :hide-nav? hide-nav? :hide-nav?
actions :actions actions :actions
custom-action :custom-action custom-action :custom-action
background-color :background-color background-color :background-color
custom-content :custom-content custom-content :custom-content
style :style}] style :style}]
(let [style (merge (st/toolbar-wrapper background-color) style)] (let [style (merge (st/toolbar-wrapper background-color) style)]
[view {:style style} [view {:style style}
[view st/toolbar [view st/toolbar
@ -38,7 +38,7 @@
[text {:style st/toolbar-title-text [text {:style st/toolbar-title-text
:font :toolbar-title} :font :toolbar-title}
title]]) title]])
[view st/toolbar-actions-container [view (st/toolbar-actions-container (count actions) (or custom-content custom-action))
(if actions (if actions
(for [{action-image :image (for [{action-image :image
action-handler :handler} actions] action-handler :handler} actions]

View File

@ -205,7 +205,8 @@
(dispatch [:update-chat! {:chat-id chat-id (dispatch [:update-chat! {:chat-id chat-id
:contact-info nil :contact-info nil
:pending-contact? false}]) :pending-contact? false}])
(dispatch [:watch-contact contact]))))) (dispatch [:watch-contact contact])
(dispatch [:discoveries-send-portions chat-id])))))
(defn set-contact-identity-from-qr (defn set-contact-identity-from-qr
[db [_ _ contact-identity]] [db [_ _ contact-identity]]
@ -215,21 +216,21 @@
(register-handler :contact-update-received (register-handler :contact-update-received
(u/side-effect! (u/side-effect!
(fn [{:keys [chats] :as db} [_ {:keys [from payload]}]] (fn [{:keys [chats current-public-key] :as db} [_ {:keys [from payload]}]]
(let [{:keys [content timestamp]} payload (when (not= current-public-key from)
{:keys [status name profile-image]} (:profile content) (let [{:keys [content timestamp]} payload
prev-last-updated (get-in db [:contacts from :last-updated])] {:keys [status name profile-image]} (:profile content)
(if (<= prev-last-updated timestamp) prev-last-updated (get-in db [:contacts from :last-updated])]
(let [contact {:whisper-identity from (if (<= prev-last-updated timestamp)
:name name (let [contact {:whisper-identity from
:photo-path profile-image :name name
:status status :photo-path profile-image
:last-updated timestamp}] :status status
(dispatch [:check-status! contact payload]) :last-updated timestamp}]
(dispatch [:update-contact! contact]) (dispatch [:update-contact! contact])
(when (chats from) (when (chats from)
(dispatch [:update-chat! {:chat-id from (dispatch [:update-chat! {:chat-id from
:name name}])))))))) :name name}])))))))))
(register-handler :contact-online-received (register-handler :contact-online-received
(u/side-effect! (u/side-effect!

View File

@ -27,7 +27,9 @@
[status-im.i18n :refer [label]] [status-im.i18n :refer [label]]
[status-im.utils.platform :refer [platform-specific]])) [status-im.utils.platform :refer [platform-specific]]))
(defn contact-list-toolbar [] (def contacts-limit 50)
(defn toolbar-view []
(let [new-contact? (get-in platform-specific [:contacts :new-contact-in-toolbar?]) (let [new-contact? (get-in platform-specific [:contacts :new-contact-in-toolbar?])
actions (cond->> [{:image {:source {:uri :icon_search} actions (cond->> [{:image {:source {:uri :icon_search}
:style icon-search} :style icon-search}
@ -44,35 +46,57 @@
:style {:elevation 0} :style {:elevation 0}
:actions actions}])) :actions actions}]))
(def contacts-limit 10) (defn subtitle-view [subtitle contacts-count]
[view st/contact-group-header-inner
[text {:style (merge st/contact-group-subtitle
(get-in platform-specific [:component-styles :contacts :subtitle]))
:uppercase? (get-in platform-specific [:contacts :uppercase-subtitles?])
:font :medium}
subtitle]
[text {:style (merge st/contact-group-count
(get-in platform-specific [:component-styles :contacts :subtitle]))
:uppercase? (get-in platform-specific [:contacts :uppercase-subtitles?])
:font :medium}
(str contacts-count)]])
(defn contact-group [contacts contacts-count title group top? click-handler] (defn group-top-view []
[view st/contact-group [linear-gradient {:style st/contact-group-header-gradient-bottom
[view st/contact-group-header :colors st/contact-group-header-gradient-bottom-colors}])
(when-not top?
[linear-gradient {:style st/contact-group-header-gradient-top (defn group-bottom-view []
:colors st/contact-group-header-gradient-top-colors}]) [linear-gradient {:style st/contact-group-header-gradient-top
[view st/contact-group-header-inner :colors st/contact-group-header-gradient-top-colors}])
[text {:style st/contact-group-text} title]
[text {:style st/contact-group-size-text} (str contacts-count)]] (defn line-view []
[linear-gradient {:style st/contact-group-header-gradient-bottom [view {:style {:background-color "#D7D7D7"
:colors st/contact-group-header-gradient-bottom-colors}]] :height 1}}])
;; todo what if there is no contacts, should we show some information
;; about this? (defn contact-group-view [contacts contacts-count subtitle group click-handler]
[view {:flexDirection :column} (let [shadows? (get-in platform-specific [:contacts :group-block-shadows?])]
(doall [view st/contact-group
;; TODO not imlemented: contact more button handler [view st/contact-group-header
(map (fn [contact] [subtitle-view subtitle contacts-count]]
(let [whisper-identity (:whisper-identity contact) (if shadows?
click-handler (or click-handler on-press)] [group-top-view]
^{:key contact} [line-view])
[contact-extended-view contact nil (click-handler whisper-identity) nil])) [view
contacts))] (doall
(when (<= contacts-limit (count contacts)) (map (fn [contact]
[view st/show-all (let [whisper-identity (:whisper-identity contact)
[touchable-highlight {:on-press #(dispatch [:navigate-to :group-contacts group])} click-handler (or click-handler on-press)]
[view ^{:key contact}
[text {:style st/show-all-text} (label :t/show-all)]]]])]) [contact-extended-view contact nil (click-handler whisper-identity) nil]))
contacts))]
(when (<= contacts-limit (count contacts))
[view st/show-all
[touchable-highlight {:on-press #(dispatch [:navigate-to :group-contacts group])}
[view
[text {:style st/show-all-text
:font :medium}
(label :t/show-all)]]]])
(if shadows?
[group-bottom-view]
[line-view])]))
(defn contacts-action-button [] (defn contacts-action-button []
[view st/buttons-container [view st/buttons-container
@ -97,7 +121,7 @@
show-toolbar-shadow? (r/atom false)] show-toolbar-shadow? (r/atom false)]
(fn [] (fn []
[view st/contacts-list-container [view st/contacts-list-container
[contact-list-toolbar] [toolbar-view]
[view {:style st/toolbar-shadow} [view {:style st/toolbar-shadow}
(when @show-toolbar-shadow? (when @show-toolbar-shadow?
[linear-gradient {:style st/contact-group-header-gradient-bottom [linear-gradient {:style st/contact-group-header-gradient-bottom
@ -108,18 +132,18 @@
(let [offset (.. e -nativeEvent -contentOffset -y)] (let [offset (.. e -nativeEvent -contentOffset -y)]
(reset! show-toolbar-shadow? (<= st/contact-group-header-height offset))))} (reset! show-toolbar-shadow? (<= st/contact-group-header-height offset))))}
(when (pos? @dapps-count) (when (pos? @dapps-count)
[contact-group [contact-group-view
@dapps @dapps
@dapps-count @dapps-count
(label :t/contacts-group-dapps) (label :t/contacts-group-dapps)
:dapps true :dapps
@click-handler]) @click-handler])
(when (pos? @people-count) (when (pos? @people-count)
[contact-group [contact-group-view
@peoples @peoples
@people-count @people-count
(label :t/contacts-group-people) (label :t/contacts-group-people)
:people false :people
@click-handler])] @click-handler])]
[view st/empty-contact-groups [view st/empty-contact-groups
[react/icon :group_big st/empty-contacts-icon] [react/icon :group_big st/empty-contacts-icon]

View File

@ -18,13 +18,13 @@
:backgroundColor toolbar-background2}) :backgroundColor toolbar-background2})
(def contact-groups (def contact-groups
{:flex 1 {:flex 1
:backgroundColor toolbar-background2}) :background-color toolbar-background2})
(def empty-contact-groups (def empty-contact-groups
(merge contact-groups (merge contact-groups
{:align-items :center {:align-items :center
:padding-top 150})) :justify-content :center}))
(def empty-contacts-icon (def empty-contacts-icon
{:height 62 {:height 62
@ -39,11 +39,11 @@
{:backgroundColor color-white}) {:backgroundColor color-white})
(def contact-group (def contact-group
{:flexDirection :column}) {:flex-direction :column})
(def contact-group-header (def contact-group-header
{:flexDirection :column {:flex-direction :column
:backgroundColor toolbar-background2}) :background-color toolbar-background2})
(def contact-group-header-inner (def contact-group-header-inner
{:flexDirection :row {:flexDirection :row
@ -51,16 +51,12 @@
:height 48 :height 48
:backgroundColor toolbar-background2}) :backgroundColor toolbar-background2})
(def contact-group-text (def contact-group-subtitle
{:flex 1 {:flex 1
:marginLeft 16 :margin-left 16})
:fontSize 14
:color text5-color})
(def contact-group-size-text (def contact-group-count
{:marginRight 14 {:margin-right 14})
:fontSize 12
:color text2-color})
(def contact-group-header-gradient-top (def contact-group-header-gradient-top
{:flexDirection :row {:flexDirection :row

View File

@ -1,7 +1,8 @@
(ns status-im.contacts.subs (ns status-im.contacts.subs
(:require-macros [reagent.ratom :refer [reaction]]) (:require-macros [reagent.ratom :refer [reaction]])
(:require [re-frame.core :refer [register-sub subscribe]] (:require [re-frame.core :refer [register-sub subscribe]]
[status-im.utils.identicon :refer [identicon]])) [status-im.utils.identicon :refer [identicon]]
[taoensso.timbre :as log]))
(register-sub :get-contacts (register-sub :get-contacts
(fn [db _] (fn [db _]
@ -19,7 +20,7 @@
(register-sub :all-added-contacts (register-sub :all-added-contacts
(fn [db _] (fn [db _]
(let [contacts (reaction (:contacts @db))] (let [contacts (reaction (:contacts @db))]
(->> (remove :pending @contacts) (->> (remove #(true? (:pending (second %))) @contacts)
(sort-contacts) (sort-contacts)
(reaction))))) (reaction)))))

View File

@ -16,7 +16,12 @@
(let [{pending-db :pending (let [{pending-db :pending
:as contact-db} (data-store/get-by-id whisper-identity) :as contact-db} (data-store/get-by-id whisper-identity)
contact (assoc contact :pending (boolean (if contact-db contact (assoc contact :pending (boolean (if contact-db
(and pending-db pending) ;; TODO:
;; this is temporary fix for pending users
;; we need to change this (if ...) to (and pending-db pending)
(if (nil? pending)
pending-db
(and pending-db pending))
pending)))] pending)))]
(data-store/save contact (if contact-db true false)))) (data-store/save contact (if contact-db true false))))

View File

@ -2,14 +2,18 @@
(:require [status-im.data-store.realm.discovery :as data-store])) (:require [status-im.data-store.realm.discovery :as data-store]))
(defn get-all (defn get-all
[] [ordering]
(->> (data-store/get-all-as-list) (->> (data-store/get-all-as-list ordering)
(mapv #(update % :tags vals)))) (mapv #(update % :tags vals))))
(defn save (defn save
[discovery] [discovery]
(data-store/save discovery)) (data-store/save discovery))
(defn exists?
[message-id]
(data-store/exists? message-id))
(defn save-all (defn save-all
[discoveries] [discoveries]
(data-store/save-all discoveries)) (data-store/save-all discoveries))

View File

@ -1,15 +1,16 @@
(ns status-im.data-store.realm.discovery (ns status-im.data-store.realm.discovery
(:require [status-im.data-store.realm.core :as realm] (:require [status-im.data-store.realm.core :as realm]
[taoensso.timbre :as log])) [taoensso.timbre :as log])
(:refer-clojure :exclude [exists?]))
(defn get-all (defn get-all
[] [ordering]
(-> (realm/get-all @realm/account-realm :discovery) (-> (realm/get-all @realm/account-realm :discovery)
(realm/sorted :priority :desc))) (realm/sorted :created-at ordering)))
(defn get-all-as-list (defn get-all-as-list
[] [ordering]
(-> (get-all) (-> (get-all ordering)
realm/realm-collection->list)) realm/realm-collection->list))
(defn get-tag-by-name [tag] (defn get-tag-by-name [tag]
@ -38,11 +39,15 @@
(defn- upsert-discovery [{:keys [message-id tags] :as discovery}] (defn- upsert-discovery [{:keys [message-id tags] :as discovery}]
(log/debug "Creating/updating discovery with tags: " tags) (log/debug "Creating/updating discovery with tags: " tags)
(let [prev-tags (get-tags message-id)] (let [prev-tags (get-tags message-id)]
(if prev-tags (when prev-tags
(update-tags-counter dec prev-tags)) (update-tags-counter dec prev-tags))
(realm/create @realm/account-realm :discovery discovery true) (realm/create @realm/account-realm :discovery discovery true)
(update-tags-counter inc tags))) (update-tags-counter inc tags)))
(defn exists?
[message-id]
(realm/exists? @realm/account-realm :discovery {:message-id message-id}))
(defn save (defn save
[discovery] [discovery]
(realm/write @realm/account-realm (realm/write @realm/account-realm

View File

@ -3,6 +3,7 @@
[status-im.data-store.realm.schemas.account.v2.core :as v2] [status-im.data-store.realm.schemas.account.v2.core :as v2]
[status-im.data-store.realm.schemas.account.v3.core :as v3] [status-im.data-store.realm.schemas.account.v3.core :as v3]
[status-im.data-store.realm.schemas.account.v4.core :as v4] [status-im.data-store.realm.schemas.account.v4.core :as v4]
[status-im.data-store.realm.schemas.account.v5.core :as v5]
)) ))
; put schemas ordered by version ; put schemas ordered by version
@ -17,4 +18,7 @@
:migration v3/migration} :migration v3/migration}
{:schema v4/schema {:schema v4/schema
:schemaVersion 4 :schemaVersion 4
:migration v4/migration}]) :migration v4/migration}
{:schema v5/schema
:schemaVersion 5
:migration v5/migration}])

View File

@ -25,5 +25,4 @@
user-status/schema]) user-status/schema])
(defn migration [old-realm new-realm] (defn migration [old-realm new-realm]
(log/debug "migrating v2 account database: " old-realm new-realm) (log/debug "migrating v2 account database: " old-realm new-realm))
(contact/migration old-realm new-realm))

View File

@ -25,5 +25,4 @@
user-status/schema]) user-status/schema])
(defn migration [old-realm new-realm] (defn migration [old-realm new-realm]
(log/debug "migrating v3 account database: " old-realm new-realm) (log/debug "migrating v3 account database: " old-realm new-realm))
(contact/migration old-realm new-realm))

View File

@ -0,0 +1,28 @@
(ns status-im.data-store.realm.schemas.account.v5.core
(:require [taoensso.timbre :as log]
[status-im.data-store.realm.schemas.account.v4.chat :as chat]
[status-im.data-store.realm.schemas.account.v3.message :as message]
[status-im.data-store.realm.schemas.account.v2.contact :as contact]
[status-im.data-store.realm.schemas.account.v1.chat-contact :as chat-contact]
[status-im.data-store.realm.schemas.account.v1.command :as command]
[status-im.data-store.realm.schemas.account.v5.discovery :as discovery]
[status-im.data-store.realm.schemas.account.v1.kv-store :as kv-store]
[status-im.data-store.realm.schemas.account.v1.pending-message :as pending-message]
[status-im.data-store.realm.schemas.account.v1.request :as request]
[status-im.data-store.realm.schemas.account.v1.tag :as tag]
[status-im.data-store.realm.schemas.account.v1.user-status :as user-status]))
(def schema [chat/schema
chat-contact/schema
command/schema
contact/schema
discovery/schema
kv-store/schema
message/schema
pending-message/schema
request/schema
tag/schema
user-status/schema])
(defn migration [old-realm new-realm]
(log/debug "migrating v5 account database: " old-realm new-realm))

View File

@ -0,0 +1,16 @@
(ns status-im.data-store.realm.schemas.account.v5.discovery
(:require [taoensso.timbre :as log]))
(def schema {:name :discovery
:primaryKey :message-id
:properties {:message-id "string"
:name {:type "string" :optional true}
:status "string"
:whisper-id "string"
:photo-path {:type "string" :optional true}
:tags {:type "list"
:objectType "tag"}
:created-at {:type "int" :default 0}}})
(defn migration [old-realm new-realm]
(log/debug "migrating discovery schema"))

View File

@ -19,7 +19,7 @@
:new-contact-identity "" :new-contact-identity ""
:contacts {} :contacts {}
:discoveries [] :discoveries {}
:discovery-search-tags [] :discovery-search-tags []
:tags {} :tags {}

View File

@ -5,103 +5,117 @@
[status-im.protocol.core :as protocol] [status-im.protocol.core :as protocol]
[status-im.navigation.handlers :as nav] [status-im.navigation.handlers :as nav]
[status-im.data-store.discovery :as discoveries] [status-im.data-store.discovery :as discoveries]
[status-im.data-store.contacts :as contacts]
[status-im.utils.handlers :as u] [status-im.utils.handlers :as u]
[status-im.utils.datetime :as time] [status-im.utils.datetime :as time]
[status-im.utils.random :as random] [status-im.utils.random :as random]))
[status-im.data-store.contacts :as contacts]))
(def request-discoveries-interval-s 600)
(register-handler :init-discoveries (register-handler :init-discoveries
(fn [db _] (fn [db _]
(-> db (-> db
(assoc :discoveries [])))) (assoc :tags [])
(assoc :discoveries {}))))
(defn calculate-priority [{:keys [chats contacts]} from payload] (defn identities [contacts]
(let [contact (get contacts from) (->> (map second contacts)
chat (get chats from) (remove (fn [{:keys [dapp? pending]}]
seen-online-recently? (< (- (time/now-ms) (get contact :last-online)) (or pending dapp?)))
time/hour)] (map :whisper-identity)))
(+ (time/now-ms) ;; message is newer => priority is higher
(if contact time/day 0) ;; user exists in contact list => increase priority
(if chat time/day 0) ;; chat with this user exists => increase priority
(if seen-online-recently? time/hour 0) ;; the user was online recently => increase priority
)))
(defmethod nav/preload-data! :discovery (defmethod nav/preload-data! :discovery
[{:keys [discoveries] :as db} _] [db _]
(-> db (-> db
(assoc :tags (discoveries/get-all-tags)) (assoc :tags (discoveries/get-all-tags))
;; todo add limit (assoc :discoveries (->> (discoveries/get-all :desc)
;; todo hash-map with whisper-id as key and sorted by last-update (map (fn [{:keys [message-id] :as discovery}]
;; may be more efficient here [message-id discovery]))
(assoc :discoveries (discoveries/get-all)))) (into {})))))
(register-handler :discovery-response-received
(u/side-effect!
(fn [db [_ {:keys [from payload]}]]
(let [{:keys [discovery-id profile hashtags]} payload
{:keys [name profile-image status]} profile
discovery {:message-id discovery-id
:name name
:photo-path profile-image
:status status
:whisper-id from
:tags (map #(hash-map :name %) hashtags)
:last-updated (js/Date.)
:priority (calculate-priority db from payload)}]
(dispatch [:add-discovery discovery])))))
(register-handler :check-status!
(u/side-effect!
(fn [db [_ {:keys [whisper-identity status]} payload]]
(let [{old-status :status} (contacts/get-by-id whisper-identity)]
(when (not= old-status status)
(let [hashtags (get-hashtags status)]
(when-not (empty? hashtags)
(let [{:keys [message-id content]} payload
{:keys [name profile-image status]} (content :profile)
discovery {:message-id message-id
:name name
:photo-path profile-image
:status status
:whisper-id whisper-identity
:tags (map #(hash-map :name %) hashtags)
:last-updated (js/Date.)
:priority (calculate-priority db whisper-identity payload)}]
(dispatch [:add-discovery discovery])))))))))
(register-handler :broadcast-status (register-handler :broadcast-status
(u/side-effect! (u/side-effect!
(fn [{:keys [current-public-key web3 current-account-id accounts]} (fn [{:keys [current-public-key web3 current-account-id accounts contacts]}
[_ status hashtags]] [_ status hashtags]]
(let [{:keys [name photo-path]} (get accounts current-account-id)] (let [{:keys [name photo-path]} (get accounts current-account-id)
(protocol/broadcats-discoveries! message-id (random/id)
{:web3 web3 message {:message-id message-id
:hashtags (set hashtags) :from current-public-key
:message {:from current-public-key :payload {:message-id message-id
:message-id (random/id) :status status
:payload {:status status :hashtags (vec hashtags)
:profile {:name name :profile {:name name
:status status :profile-image photo-path}}}]
:profile-image photo-path}}}}) (doseq [id (identities contacts)]
(protocol/watch-hashtags! {:web3 web3 (protocol/send-status!
:hashtags (set hashtags) {:web3 web3
:callback #(dispatch [:incoming-message %1 %2])}))))) :message (assoc message :to id)}))
(dispatch [:status-received message])))))
(register-handler :show-discovery-tag (register-handler :status-received
(fn [db [_ tag]] (u/side-effect!
(dispatch [:navigate-to :discovery-tag]) (fn [{:keys [discoveries] :as db} [_ {:keys [from payload]}]]
(assoc db :current-tag tag))) (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
discovery {: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-discovery discovery]))))))
(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)))))
(register-handler :request-discoveries
(u/side-effect!
(fn [{:keys [current-public-key web3 contacts]}]
(doseq [id (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)}}))))))
(register-handler :discoveries-send-portions
(u/side-effect!
(fn [{:keys [current-public-key contacts web3]} [_ to]]
(when (get contacts to)
(protocol/send-discoveries-response!
{:web3 web3
:discoveries (discoveries/get-all :asc)
:message {:from current-public-key
:to to}})))))
(register-handler :discoveries-request-received
(u/side-effect!
(fn [_ [_ {:keys [from]}]]
(dispatch [:discoveries-send-portions from]))))
(register-handler :discoveries-response-received
(u/side-effect!
(fn [{:keys [discoveries contacts]} [_ {:keys [payload from]}]]
(when (get contacts from)
(when-let [data (:data payload)]
(doseq [{:keys [message-id] :as discovery} data]
(when (and (not (discoveries/exists? message-id))
(not (get discoveries message-id)))
(let [discovery (assoc discovery :created-at (time/now-ms))]
(dispatch [:add-discovery discovery])))))))))
(defn add-discovery (defn add-discovery
[{db-discoveries :discoveries [db [_ discovery]]
:as db} [_ {:keys [message-id] :as discovery}]] (assoc db :new-discovery discovery))
(let [updated-discoveries (if-let [i (first-index #(= (:message-id %) message-id) db-discoveries)]
(assoc db-discoveries i discovery)
(conj db-discoveries discovery))]
(-> db
(assoc :new-discovery discovery)
(assoc :discoveries updated-discoveries))))
(defn save-discovery! (defn save-discovery!
[{:keys [new-discovery]} _] [{:keys [new-discovery]} _]
@ -109,7 +123,11 @@
(defn reload-tags! (defn reload-tags!
[db _] [db _]
(assoc db :tags (discoveries/get-all-tags))) (assoc db :tags (discoveries/get-all-tags)
:discoveries (->> (discoveries/get-all :desc)
(map (fn [{:keys [message-id] :as discovery}]
[message-id discovery]))
(into {}))))
(register-handler :add-discovery (register-handler :add-discovery
(-> add-discovery (-> add-discovery
@ -120,4 +138,4 @@
:remove-old-discoveries! :remove-old-discoveries!
(u/side-effect! (u/side-effect!
(fn [_ _] (fn [_ _]
(discoveries/delete :priority :asc 1000 200)))) (discoveries/delete :created-at :asc 1000 200))))

View File

@ -16,7 +16,8 @@
[status-im.components.carousel.carousel :refer [carousel]] [status-im.components.carousel.carousel :refer [carousel]]
[status-im.discovery.views.popular-list :refer [discovery-popular-list]] [status-im.discovery.views.popular-list :refer [discovery-popular-list]]
[status-im.discovery.views.discovery-list-item :refer [discovery-list-item]] [status-im.discovery.views.discovery-list-item :refer [discovery-list-item]]
[status-im.contacts.styles :as contacts-styles])) [status-im.contacts.styles :as contacts-styles]
[status-im.utils.platform :refer [platform-specific]]))
(defn get-hashtags [status] (defn get-hashtags [status]
(let [hashtags (map #(str/lower-case (str/replace % #"#" "")) (re-seq #"[^ !?,;:.]+" status))] (let [hashtags (map #(str/lower-case (str/replace % #"#" "")) (re-seq #"[^ !?,;:.]+" status))]
@ -35,7 +36,7 @@
(dispatch [:navigate-to :discovery-search-results])))}] (dispatch [:navigate-to :discovery-search-results])))}]
[view [view
[text {:style st/discovery-title [text {:style st/discovery-title
:font :default} :font :toolbar-title}
(label :t/discovery)]])]) (label :t/discovery)]])])
(defn toogle-search [current-value] (defn toogle-search [current-value]
@ -52,46 +53,51 @@
:style st/search-icon} :style st/search-icon}
:handler #(toogle-search show-search?)}]}]) :handler #(toogle-search show-search?)}]}])
(defn title [label-kw] (defn title [label-kw spacing?]
[view st/section-spacing [view st/section-spacing
[text {:style st/discovery-subtitle [text {:style (merge (get-in platform-specific [:component-styles :discovery :subtitle])
:font :medium} (when spacing? {:margin-top 16}))
:uppercase? (get-in platform-specific [:discovery :uppercase-subtitles?])
:font :medium}
(label label-kw)]]) (label label-kw)]])
(defview discovery-popular [{:keys [contacts]}] (defview discovery-popular [{:keys [contacts]}]
[popular-tags [:get-popular-tags 10]] [popular-tags [:get-popular-tags 10]]
(if (seq popular-tags) [view st/popular-container
[view [title :t/popular-tags false]
[title :t/popular-tags] (if (pos? (count popular-tags))
(if (pos? (count popular-tags)) [carousel {:pageStyle st/carousel-page-style
[carousel {:pageStyle st/carousel-page-style} :gap 0
(for [{:keys [name count]} popular-tags] :sneak (if (> (count popular-tags) 1) 16 8)}
[discovery-popular-list {:tag name (for [{:keys [name]} popular-tags]
:count count [discovery-popular-list {:tag name
:contacts contacts}])] :contacts contacts}])]
[text (label :t/none)])] [text (label :t/none)])])
[view contacts-styles/empty-contact-groups
;; todo change icon
[icon :group_big contacts-styles/empty-contacts-icon]
[text {:style contacts-styles/empty-contacts-text}
(label :t/no-statuses-discovered)]]))
(defview discovery-recent [{:keys [contacts]}] (defview discovery-recent [{:keys [contacts]}]
[discoveries [:get :discoveries]] [discoveries [:get-recent-discoveries]]
(when (seq discoveries) (when (seq discoveries)
[view [view st/recent-container
[title :t/recent] [title :t/recent true]
[view st/recent-list [view st/recent-list
(for [{:keys [message-id] :as discovery} discoveries] (let [discoveries (map-indexed vector discoveries)]
^{:key (str "message-" message-id)} (for [[i {:keys [message-id] :as discovery}] discoveries]
[discovery-list-item discovery])]])) ^{:key (str "message-" message-id)}
[discovery-list-item discovery (not= (inc i) (count discoveries))]))]]))
(defview discovery [] (defview discovery []
[show-search? [:get ::show-search?] [show-search? [:get ::show-search?]
contacts [:get :contacts]] contacts [:get :contacts]
discoveries [:get-recent-discoveries]]
[view st/discovery-container [view st/discovery-container
[discovery-toolbar show-search?] [discovery-toolbar show-search?]
[scroll-view st/scroll-view-container (if discoveries
[discovery-popular {:contacts contacts}] [scroll-view st/scroll-view-container
[discovery-recent {:contacts contacts}]] [discovery-popular {:contacts contacts}]
[discovery-recent {:contacts contacts}]]
[view contacts-styles/empty-contact-groups
;; todo change icon
[icon :group_big contacts-styles/empty-contacts-icon]
[text {:style contacts-styles/empty-contacts-text}
(label :t/no-statuses-discovered)]])
[bottom-gradient]]) [bottom-gradient]])

View File

@ -2,39 +2,59 @@
(:require-macros [status-im.utils.views :refer [defview]]) (:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]] (:require [re-frame.core :refer [subscribe dispatch]]
[status-im.utils.listview :refer [to-datasource]] [status-im.utils.listview :refer [to-datasource]]
[status-im.components.react :refer [view text list-view list-item]] [status-im.components.status-bar :refer [status-bar]]
[status-im.components.react :refer [view
text
icon
list-view
list-item
scroll-view]]
[status-im.i18n :refer [label]]
[status-im.components.toolbar.view :refer [toolbar]] [status-im.components.toolbar.view :refer [toolbar]]
[status-im.discovery.views.discovery-list-item :refer [discovery-list-item]] [status-im.discovery.views.discovery-list-item :refer [discovery-list-item]]
[status-im.discovery.styles :as st])) [status-im.discovery.styles :as st]
[status-im.utils.platform :refer [platform-specific]]
[status-im.contacts.styles :as contacts-styles]
[taoensso.timbre :as log]))
(defn render-separator [_ row-id _] (defn render-separator [_ row-id _]
(list-item [view {:style st/row-separator (list-item [view {:style st/row-separator
:key row-id}])) :key row-id}]))
(defn title-content [tags] (defn title-content [tags]
[view st/tag-title-container [scroll-view {:horizontal true
(for [tag (take 3 tags)] :bounces false
^{:key (str "tag-" tag)} :flex 1
[view {:style st/tag-container} :contentContainerStyle st/tag-title-scroll}
[text {:style st/tag-title [view st/tag-title-container
:font :default} (for [tag (take 3 tags)]
(str " #" tag)]])]) ^{:key (str "tag-" tag)}
[view (merge (get-in platform-specific [:component-styles :discovery :tag])
{:margin-left 2 :margin-right 2})
[text {:style st/tag-title
:font :default}
(str " #" tag)]])]])
(defview discovery-search-results [] (defview discovery-search-results []
[discoveries [:get-discovery-search-results] [discoveries [:get-popular-discoveries 250]
tags [:get :discovery-search-tags]] tags [:get :discovery-search-tags]]
(let [datasource (to-datasource discoveries)] (let [discoveries (:discoveries discoveries)
datasource (to-datasource discoveries)]
[view st/discovery-tag-container [view st/discovery-tag-container
[status-bar]
[toolbar {:nav-action {:image {:source {:uri :icon_back} [toolbar {:nav-action {:image {:source {:uri :icon_back}
:style st/icon-back} :style st/icon-back}
:handler #(dispatch [:navigate-back])} :handler #(dispatch [:navigate-back])}
:custom-content (title-content tags) :custom-content (title-content tags)
:actions [{:image {:source {:uri :icon_search} :style st/discovery-tag-toolbar}]
:style st/icon-search} (if (empty? discoveries)
:handler (fn [])}]}] [view st/empty-view
;; todo change icon
[list-view {:dataSource datasource [icon :group_big contacts-styles/empty-contacts-icon]
:renderRow (fn [row _ _] [text {:style contacts-styles/empty-contacts-text}
(list-item [discovery-list-item row])) (label :t/no-statuses-found)]]
:renderSeparator render-separator [list-view {:dataSource datasource
:style st/recent-list}]])) :renderRow (fn [row _ _]
(list-item [discovery-list-item row]))
:renderSeparator render-separator
:style st/recent-list}])]))

View File

@ -1,5 +1,7 @@
(ns status-im.discovery.styles (ns status-im.discovery.styles
(:require [status-im.components.styles :refer [color-gray2]])) (:require [status-im.components.styles :refer [color-gray2
color-white]]
[status-im.components.toolbar.styles :refer [toolbar-background2]]))
;; common ;; common
@ -8,11 +10,18 @@
:border-bottom-color "#eff2f3"}) :border-bottom-color "#eff2f3"})
(def row (def row
{:flex-direction :row}) {:flex-direction :row
:margin-bottom 10})
(def column (def column
{:flex-direction :column}) {:flex-direction :column})
(def empty-view
{:flex 1
:background-color color-white
:align-items :center
:justify-content :center})
;; Toolbar ;; Toolbar
(def discovery-toolbar-content (def discovery-toolbar-content
@ -21,7 +30,7 @@
:justify-content :center}) :justify-content :center})
(def discovery-toolbar (def discovery-toolbar
{:background-color "#eef2f5" {:background-color toolbar-background2
:elevation 0}) :elevation 0})
(def discovery-search-input (def discovery-search-input
@ -30,7 +39,7 @@
:margin-left 18 :margin-left 18
:line-height 42 :line-height 42
:font-size 14 :font-size 14
:color "#9CBFC0"}) :color "#7099e6"})
(def discovery-title (def discovery-title
{:color "#000000de" {:color "#000000de"
@ -38,25 +47,19 @@
:text-align :center :text-align :center
:font-size 16}) :font-size 16})
(def discovery-subtitle
{:color color-gray2
:font-size 14})
(def section-spacing (def section-spacing
{:padding 16}) {:padding 16})
(def scroll-view-container (def scroll-view-container
{}) {:bounces false})
;; Popular ;; Popular
(def popular-container
{:background-color toolbar-background2})
(def carousel-page-style (def carousel-page-style
{:borderRadius 1 {})
:shadowColor "black"
:shadowRadius 1
:shadowOpacity 0.8
:elevation 2
:marginBottom 10})
(def tag-name (def tag-name
{:color "#7099e6" {:color "#7099e6"
@ -66,16 +69,10 @@
:align-items :center :align-items :center
:justify-content :center}) :justify-content :center})
(def tag-name-container
{:flex-direction "column"
:background-color "#eef2f5"
:border-radius 5
:padding 4})
(def tag-count (def tag-count
{:color "#838c93" {:color "#838c93"
:font-size 12 :font-size 12
:padding-right 5 :padding-right 6
:padding-bottom 2 :padding-bottom 2
:align-items :center :align-items :center
:justify-content :center}) :justify-content :center})
@ -84,63 +81,67 @@
{:flex 0.2 {:flex 0.2
:flex-direction "column" :flex-direction "column"
:align-items "flex-end" :align-items "flex-end"
:padding-top 10 :padding-top 6
:padding-right 9}) :padding-right 9})
(def separator
{:background-color "rgb(200, 199, 204)"
:height 0.5})
;; Popular list item
(def popular-list-container (def popular-list-container
{:flex 1 {:flex 1
:background-color :white :background-color :white
:padding-left 10 :margin-left 16
:padding-top 16}) :padding-left 16
:padding-top 18})
(def popular-list
{:background-color :white
:padding-top 13})
;; Popular list item
(def popular-list-item (def popular-list-item
{:flex-direction :row {:flex-direction :row
:padding-top 10 :padding-bottom 16
:padding-bottom 10}) :top 1})
(def popular-list-item-status
{:color "black"
:line-height 22
:font-size 14})
(def popular-list-item-name (def popular-list-item-name
{:color "black" {:color "black"
:font-size 14 :font-size 15
:line-height 24}) :padding-bottom 4})
(def popular-list-item-name-container (def popular-list-item-name-container
{:flex 0.8 {:flex 0.8
:flex-direction "column"}) :flex-direction "column"
:padding-top 16})
(def popular-list-item-avatar-container (def popular-list-item-avatar-container
{:flex 0.2 {:flex 0.2
:flex-direction "column" :flex-direction "column"
:align-items :center :align-items :center
:padding-top 5}) :padding-top 16})
(def popular-list-item-avatar
{:border-radius 18
:width 36
:height 36})
;; discovery_recent ;; discovery_recent
(def recent-container
{:background-color toolbar-background2})
(def recent-list (def recent-list
{:background-color :white {:background-color :white
:padding-left 16}) :padding-left 16})
;; Discovery tag ;; Discovery tag
(def discovery-tag-toolbar
{:border-bottom-color "#D7D7D7"
:border-bottom-width 1})
(def discovery-tag-container (def discovery-tag-container
{:flex 1 {:flex 1
:backgroundColor "#eef2f5"}) :backgroundColor "#eef2f5"})
(def tag-title-scroll
{:flex 1
:alignItems "center"
:justifyContent "center"})
(def tag-title-container (def tag-title-container
{:flex 1 {:flex 1
:alignItems "center" :alignItems "center"
@ -153,14 +154,6 @@
:padding-right 5 :padding-right 5
:padding-bottom 2}) :padding-bottom 2})
(def tag-container
{:backgroundColor "#eef2f5"
:flexWrap :wrap
:borderRadius 5
:padding 4
:margin-left 2
:margin-right 2})
(def icon-back (def icon-back
{:width 8 {:width 8
:height 14}) :height 14})
@ -171,7 +164,7 @@
(def discovery-container (def discovery-container
{:flex 1 {:flex 1
:backgroundColor :#eef2f5}) :backgroundColor color-white})
(def hamburger-icon (def hamburger-icon
{:width 16 {:width 16

View File

@ -1,21 +1,44 @@
(ns status-im.discovery.subs (ns status-im.discovery.subs
(:require-macros [reagent.ratom :refer [reaction]]) (:require-macros [reagent.ratom :refer [reaction]])
(:require [re-frame.core :refer [register-sub]])) (:require [re-frame.core :refer [register-sub]]
[status-im.utils.datetime :as time]))
(defn- get-discoveries-by-tags [{:keys [discoveries current-tag]} tags limit] (defn- calculate-priority [{:keys [chats contacts current-public-key]}
(let [tags' (or tags [current-tag]) {:keys [whisper-id created-at]}]
filter-tag (filter #(every? (->> (map :name (:tags %)) (let [contact (get contacts whisper-id)
(into (hash-set))) chat (get chats whisper-id)
tags')) seen-online-recently? (< (- (time/now-ms) (get contact :last-online))
xform (if limit time/hour)
(comp filter-tag (take limit)) me? (= current-public-key whisper-id)]
filter-tag)] (+ created-at ;; message is newer => priority is higher
(into [] xform discoveries))) (if (or me? contact) time/day 0) ;; user exists in contact list => increase priority
(if (or me? chat) time/day 0) ;; chat with this user exists => increase priority
(if (or me? seen-online-recently?) time/hour 0) ;; the user was online recently => increase priority
)))
(register-sub :get-discoveries-by-tags (defn- get-discoveries-by-tags [discoveries current-tag tags]
(fn [db [_ tags limit]] (let [tags' (or tags [current-tag])]
(-> (get-discoveries-by-tags @db tags limit) (filter #(every? (->> (map :name (:tags %))
(reaction)))) (into (hash-set)))
tags')
(vals discoveries))))
(register-sub :get-popular-discoveries
(fn [db [_ limit tags]]
(let [discoveries (reaction (:discoveries @db))
current-tag (reaction (:current-tag @db))
search-tags (reaction (:discovery-search-tags @db))
discoveries (->> (get-discoveries-by-tags @discoveries @current-tag (or tags @search-tags))
(map #(assoc % :priority (calculate-priority db %)))
(sort-by :priority >))]
(reaction {:discoveries (take limit discoveries)
:total (count discoveries)}))))
(register-sub :get-recent-discoveries
(fn [db]
(->> (:discoveries @db)
(vals)
(reaction))))
(register-sub :get-popular-tags (register-sub :get-popular-tags
(fn [db [_ limit]] (fn [db [_ limit]]
@ -24,6 +47,8 @@
(register-sub :get-discovery-search-results (register-sub :get-discovery-search-results
(fn [db _] (fn [db _]
(let [tags (get-in @db [:discovery-search-tags])] (let [discoveries (reaction (:discoveries @db))
(-> (get-discoveries-by-tags @db tags nil) current-tag (reaction (:current-tag @db))
tags (reaction (:discovery-search-tags @db))]
(-> (get-discoveries-by-tags @discoveries @current-tag @tags)
(reaction))))) (reaction)))))

View File

@ -1,36 +0,0 @@
(ns status-im.discovery.tag
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[status-im.utils.listview :refer [to-datasource]]
[status-im.components.react :refer [view text list-view list-item]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.discovery.views.discovery-list-item :refer [discovery-list-item]]
[status-im.discovery.styles :as st]))
(defn render-separator [_ row-id _]
(list-item [view {:style st/row-separator
:key row-id}]))
(defn title-content [tag]
[view st/tag-title-container
[view {:style st/tag-container}
[text {:style st/tag-title} (str " #" tag)]]])
(defview discovery-tag []
[tag [:get :current-tag]
discoveries [:get-discoveries-by-tags]]
(let [datasource (to-datasource discoveries)]
[view st/discovery-tag-container
[toolbar {:nav-action {:image {:source {:uri :icon_back}
:style st/icon-back}
:handler #(dispatch [:navigate-back])}
:custom-content (title-content tag)
:actions [{:image {:source {:uri :icon_search}
:style st/icon-search}
:handler (fn [])}]}]
[list-view {:dataSource datasource
:renderRow (fn [row _ _]
(list-item [discovery-list-item row]))
:renderSeparator render-separator
:style st/recent-list}]]))

View File

@ -2,32 +2,54 @@
(:require-macros [status-im.utils.views :refer [defview]]) (:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]] (:require [re-frame.core :refer [subscribe dispatch]]
[clojure.string :as str] [clojure.string :as str]
[status-im.components.react :refer [view text image]] [status-im.components.react :refer [view text image touchable-highlight]]
[status-im.discovery.styles :as st] [status-im.discovery.styles :as st]
[status-im.utils.gfycat.core :refer [generate-gfy]] [status-im.utils.gfycat.core :refer [generate-gfy]]
[status-im.utils.identicon :refer [identicon]] [status-im.utils.identicon :refer [identicon]]
[status-im.i18n :refer [label]] [status-im.i18n :refer [label]]
[status-im.components.chat-icon.screen :as ci])) [status-im.components.chat-icon.screen :as ci]
[status-im.utils.platform :refer [platform-specific]]
[taoensso.timbre :as log]))
(defview discovery-list-item [{:keys [name photo-path status whisper-id]}] (defn tag-view [tag]
[text {:style {:color "#7099e6"}
:font :medium}
(str tag " ")])
(defn status-view [item-style {:keys [message-id status]}]
[text {:style (:status-text item-style)
:font :default}
(for [[i status] (map-indexed vector (str/split status #" "))]
(if (.startsWith status "#")
^{:key (str "item-" message-id "-" i)}
[tag-view status]
^{:key (str "item-" message-id "-" i)}
(str status " ")))])
(defview discovery-list-item [{:keys [name photo-path whisper-id] :as message}
show-separator?]
[{contact-name :name [{contact-name :name
contact-photo-path :photo-path} [:get-in [:contacts whisper-id]]] contact-photo-path :photo-path} [:get-in [:contacts whisper-id]]]
[view st/popular-list-item (let [item-style (get-in platform-specific [:component-styles :discovery :item])]
[view st/popular-list-item-name-container [view
[text {:style st/popular-list-item-name [view st/popular-list-item
:font :medium [view st/popular-list-item-name-container
:number-of-lines 1} [text {:style st/popular-list-item-name
(cond :font :medium
(not (str/blank? contact-name)) contact-name :number-of-lines 1}
(not (str/blank? name)) name (cond
:else (generate-gfy))] (not (str/blank? contact-name)) contact-name
[text {:style st/popular-list-item-status (not (str/blank? name)) name
:font :default :else (generate-gfy))]
:number-of-lines 2} [status-view item-style message]]
status]] [view (merge st/popular-list-item-avatar-container
[view st/popular-list-item-avatar-container (:icon item-style))
[ci/chat-icon (cond [touchable-highlight {:on-press #(dispatch [:start-chat whisper-id])}
(not (str/blank? contact-photo-path)) contact-photo-path [view
(not (str/blank? photo-path)) photo-path [ci/chat-icon (cond
:else (identicon whisper-id)) (not (str/blank? contact-photo-path)) contact-photo-path
{:size 36}]]]) (not (str/blank? photo-path)) photo-path
:else (identicon whisper-id))
{:size 36}]]]]]
(when show-separator?
[view st/separator])]))

View File

@ -3,20 +3,23 @@
(:require (:require
[re-frame.core :refer [subscribe dispatch]] [re-frame.core :refer [subscribe dispatch]]
[status-im.components.react :refer [view [status-im.components.react :refer [view
list-view list-view
list-item list-item
touchable-highlight touchable-highlight
text]] text]]
[status-im.discovery.styles :as st] [status-im.discovery.styles :as st]
[status-im.utils.listview :refer [to-datasource]] [status-im.utils.listview :refer [to-datasource]]
[status-im.discovery.views.discovery-list-item :refer [discovery-list-item]])) [status-im.discovery.views.discovery-list-item :refer [discovery-list-item]]
[status-im.utils.platform :refer [platform-specific]]))
(defview discovery-popular-list [{:keys [tag count contacts]}] (defview discovery-popular-list [{:keys [tag contacts]}]
[discoveries [:get-discoveries-by-tags [tag] 3]] [discoveries [:get-popular-discoveries 3 [tag]]]
[view st/popular-list-container [view (merge st/popular-list-container
(get-in platform-specific [:component-styles :discovery :popular]))
[view st/row [view st/row
[view st/tag-name-container [view (get-in platform-specific [:component-styles :discovery :tag])
[touchable-highlight {:onPress #(dispatch [:show-discovery-tag tag])} [touchable-highlight {:on-press #(do (dispatch [:set :discovery-search-tags [tag]])
(dispatch [:navigate-to :discovery-search-results]))}
[view [view
[text {:style st/tag-name [text {:style st/tag-name
:font :medium} :font :medium}
@ -24,7 +27,8 @@
[view st/tag-count-container [view st/tag-count-container
[text {:style st/tag-count [text {:style st/tag-count
:font :default} :font :default}
count]]] (:total discoveries)]]]
(for [{:keys [message-id] :as discovery} discoveries] (let [discoveries (map-indexed vector (:discoveries discoveries))]
^{:key (str "message-" message-id)} (for [[i {:keys [message-id] :as discovery}] discoveries]
[discovery-list-item discovery])]) ^{:key (str "message-" message-id)}
[discovery-list-item discovery (not= (inc i) (count discoveries))]))])

View File

@ -64,6 +64,7 @@
(dispatch [:init-chat]) (dispatch [:init-chat])
(dispatch [:init-discoveries]) (dispatch [:init-discoveries])
(dispatch [:send-account-update-if-needed]) (dispatch [:send-account-update-if-needed])
(dispatch [:start-requesting-discoveries])
(dispatch [:remove-old-discoveries!])))) (dispatch [:remove-old-discoveries!]))))
(register-handler :reset-app (register-handler :reset-app

View File

@ -13,7 +13,6 @@
[status-im.contacts.views.contact-list :refer [contact-list]] [status-im.contacts.views.contact-list :refer [contact-list]]
[status-im.contacts.views.new-contact :refer [new-contact]] [status-im.contacts.views.new-contact :refer [new-contact]]
[status-im.qr-scanner.screen :refer [qr-scanner]] [status-im.qr-scanner.screen :refer [qr-scanner]]
[status-im.discovery.tag :refer [discovery-tag]]
[status-im.discovery.search-results :refer [discovery-search-results]] [status-im.discovery.search-results :refer [discovery-search-results]]
[status-im.chat.screen :refer [chat]] [status-im.chat.screen :refer [chat]]
[status-im.accounts.login.screen :refer [login]] [status-im.accounts.login.screen :refer [login]]
@ -74,7 +73,6 @@
(let [current-view (validate-current-view @view-id @signed-up?)] (let [current-view (validate-current-view @view-id @signed-up?)]
(let [component (case current-view (let [component (case current-view
:discovery main-tabs :discovery main-tabs
:discovery-tag discovery-tag
:discovery-search-results discovery-search-results :discovery-search-results discovery-search-results
:add-participants new-participants :add-participants new-participants
:remove-participants remove-participants :remove-participants remove-participants

View File

@ -21,6 +21,27 @@
:border-bottom-width 0.5} :border-bottom-width 0.5}
:chat {:new-message {:border-top-color styles/color-gray3 :chat {:new-message {:border-top-color styles/color-gray3
:border-top-width 0.5}} :border-top-width 0.5}}
:discovery {:subtitle {:color styles/color-steel
:font-size 13
:letter-spacing 1}
:popular {:border-radius 3
:border-width 1
:border-color "#D7D7D7"}
:tag {:flex-direction "column"
:background-color "rgb(227, 235, 250)"
:border-radius 4
:border-width 1
:border-color "rgba(112, 153, 230, 0.31)"
:padding 6}
:item {:status-text {:color styles/color-steel
:font-size 14
:letter-spacing -0.1}
:icon {:padding-top 0
:bottom -4
:justify-content :flex-end}}}
:contacts {:subtitle {:color styles/color-steel
:font-size 13
:letter-spacing 1}}
:bottom-gradient {:height 1} :bottom-gradient {:height 1}
:input-label {:left 0} :input-label {:left 0}
:input-error-text {:margin-left 0} :input-error-text {:margin-left 0}
@ -60,5 +81,8 @@
:chats {:action-button? false :chats {:action-button? false
:new-chat-in-toolbar? true} :new-chat-in-toolbar? true}
:contacts {:action-button? false :contacts {:action-button? false
:new-contact-in-toolbar? true}}) :new-contact-in-toolbar? true
:uppercase-subtitles? true
:group-block-shadows? false}
:discovery {:uppercase-subtitles? true}})

View File

@ -36,9 +36,12 @@
;; discoveries ;; discoveries
(def watch-user! discoveries/watch-user!) (def watch-user! discoveries/watch-user!)
(def contact-request! discoveries/contact-request!) (def contact-request! discoveries/contact-request!)
(def watch-hashtags! discoveries/watch-hashtags!) (def broadcast-profile! discoveries/broadcast-profile!)
(def broadcats-profile! discoveries/broadcats-profile!) (def send-status! discoveries/send-status!)
(def broadcats-discoveries! discoveries/broadcats-discoveries!) (def send-discoveries-request! discoveries/send-discoveries-request!)
(def send-discoveries-response! discoveries/send-discoveries-response!)
(def message-pending? d/message-pending?)
;; initialization ;; initialization
(s/def ::rpc-url string?) (s/def ::rpc-url string?)
@ -82,10 +85,6 @@
{:topics [chat-id]} {:topics [chat-id]}
(l/message-listener (assoc listener-options :callback callback (l/message-listener (assoc listener-options :callback callback
:keypair keypair)))) :keypair keypair))))
;; start listening to discoveries
(watch-hashtags! {:web3 web3
:hashtags hashtags
:callback callback})
;; start listening to profiles ;; start listening to profiles
(doseq [{:keys [identity keypair]} contacts] (doseq [{:keys [identity keypair]} contacts]
(watch-user! {:web3 web3 (watch-user! {:web3 web3

View File

@ -9,13 +9,9 @@
[status-im.protocol.validation :refer-macros [valid?]] [status-im.protocol.validation :refer-macros [valid?]]
[status-im.utils.random :as random])) [status-im.utils.random :as random]))
(def discovery-topic "status-discovery")
(def discovery-topic-prefix "status-discovery-") (def discovery-topic-prefix "status-discovery-")
(def discovery-hashtag-prefix "status-hashtag-") (def discovery-hashtag-prefix "status-hashtag-")
(defn- add-hashtag-prefix [hashtag]
(str discovery-hashtag-prefix hashtag))
(defn- make-discovery-topic [identity] (defn- make-discovery-topic [identity]
(str discovery-topic-prefix identity)) (str discovery-topic-prefix identity))
@ -72,35 +68,12 @@
(defonce watched-hashtag-topics (atom nil)) (defonce watched-hashtag-topics (atom nil))
(defn- hashtags->topics
"Create topics from hashtags."
[hashtags]
(->> hashtags
(map (fn [tag]
[tag [(add-hashtag-prefix tag) discovery-topic]]))
(into {})))
(s/def :discoveries/hashtags (s/every string? :kind-of set?)) (s/def :discoveries/hashtags (s/every string? :kind-of set?))
(defn stop-watching-hashtags!
[web3]
(doseq [topics @watched-hashtag-topics]
(f/remove-filter! web3 topics)))
(s/def ::callback fn?) (s/def ::callback fn?)
(s/def :watch-hashtags/options (s/def :watch-hashtags/options
(s/keys :req-un [:options/web3 :discoveries/hashtags ::callback])) (s/keys :req-un [:options/web3 :discoveries/hashtags ::callback]))
(defn watch-hashtags!
[{:keys [web3 hashtags] :as options}]
{:pre [(valid? :watch-hashtags/options options)]}
(debug :watch-hashtags hashtags)
(stop-watching-hashtags! web3)
(let [hashtag-topics (vals (hashtags->topics hashtags))]
(reset! watched-hashtag-topics hashtag-topics)
(doseq [topics hashtag-topics]
(f/add-filter! web3 {:topics topics} (l/message-listener options)))))
(s/def ::status (s/nilable string?)) (s/def ::status (s/nilable string?))
(s/def ::profile (s/keys :req-un [::status])) (s/def ::profile (s/keys :req-un [::status]))
(s/def :profile/payload (s/def :profile/payload
@ -111,7 +84,7 @@
(s/def :broadcast-profile/options (s/def :broadcast-profile/options
(s/keys :req-un [:profile/message :options/web3])) (s/keys :req-un [:profile/message :options/web3]))
(defn broadcats-profile! (defn broadcast-profile!
[{:keys [web3 message] :as options}] [{:keys [web3 message] :as options}]
{:pre [(valid? :broadcast-profile/options options)]} {:pre [(valid? :broadcast-profile/options options)]}
(debug :broadcasting-status) (debug :broadcasting-status)
@ -132,18 +105,32 @@
(s/def :broadcast-hasthags/options (s/def :broadcast-hasthags/options
(s/keys :req-un [:discoveries/hashtags :status/message :options/web3])) (s/keys :req-un [:discoveries/hashtags :status/message :options/web3]))
(defn broadcats-discoveries! (defn send-status!
[{:keys [web3 hashtags message] :as options}] [{:keys [web3 message]}]
{:pre [(valid? :broadcast-hasthags/options options)]}
(debug :broadcasting-status) (debug :broadcasting-status)
(let [discovery-id (random/id)] (let [message (-> message
(doseq [[tag hashtag-topics] (hashtags->topics hashtags)] (assoc :type :discovery
(d/add-pending-message! :topics [(make-discovery-topic (:from message))]))]
web3 (d/add-pending-message! web3 message)))
(-> message
(assoc :type :discovery (defn send-discoveries-request!
:topics hashtag-topics) [{:keys [web3 message]}]
(assoc-in [:payload :tag] tag) (debug :sending-discoveries-request)
(assoc-in [:payload :hashtags] (vec hashtags)) (d/add-pending-message!
(assoc-in [:payload :discovery-id] discovery-id) web3
(update :message-id str tag)))))) (-> message
(assoc :type :discoveries-request
:topics [(make-discovery-topic (:from message))]))))
(defn send-discoveries-response!
[{:keys [web3 discoveries message]}]
(debug :sending-discoveries-response)
(doseq [portion (->> (take 100 discoveries)
(partition 10 10 nil))]
(d/add-pending-message!
web3
(-> message
(assoc :type :discoveries-response
:topics [(make-discovery-topic (:from message))]
:message-id (random/id)
:payload {:data (into [] portion)})))))

View File

@ -85,7 +85,9 @@
:add-group-identity (dispatch [:participant-invited-to-group message]) :add-group-identity (dispatch [:participant-invited-to-group message])
:leave-group (dispatch [:participant-left-group message]) :leave-group (dispatch [:participant-left-group message])
:contact-request (dispatch [:contact-request-received message]) :contact-request (dispatch [:contact-request-received message])
:discovery (dispatch [:discovery-response-received message]) :discovery (dispatch [:status-received message])
:discoveries-request (dispatch [:discoveries-request-received message])
:discoveries-response (dispatch [:discoveries-response-received message])
:profile (dispatch [:contact-update-received message]) :profile (dispatch [:contact-update-received message])
:online (dispatch [:contact-online-received message]) :online (dispatch [:contact-online-received message])
:pending (dispatch [:pending-message-upsert message]) :pending (dispatch [:pending-message-upsert message])

View File

@ -129,7 +129,7 @@
(defn delivery-callback (defn delivery-callback
[web3 {:keys [id requires-ack? to]}] [web3 {:keys [id requires-ack? to]}]
(fn [error _] (fn [error _]
(when error (log/error :shh-post-error error)) (when error (log/warn :shh-post-error error))
(when-not error (when-not error
(debug :delivery-callback) (debug :delivery-callback)
(message-was-sent! web3 id to) (message-was-sent! web3 id to)
@ -159,7 +159,7 @@
;; todo add some notification about network issues ;; todo add some notification about network issues
(<= attempts (* 5 max-attempts-number)) (<= attempts (* 5 max-attempts-number))
(and (and
;; if message was not send lees then max-attempts-number times ;; if message was not send less then max-attempts-number times
;; continue attempts ;; continue attempts
(<= attempts max-attempts-number) (<= attempts max-attempts-number)
;; check retransmition interval ;; check retransmition interval
@ -169,6 +169,15 @@
[message message-type ttl-config default-ttl] [message message-type ttl-config default-ttl]
(update message :ttl #(or % ((keyword message-type) ttl-config) default-ttl))) (update message :ttl #(or % ((keyword message-type) ttl-config) default-ttl)))
(defn message-pending?
[web3 required-type required-to]
(some (fn [[_ messages]]
(some (fn [[_ {:keys [type to]}]]
(and (= type required-type)
(= to required-to)))
messages))
(@messages web3)))
(defn run-delivery-loop! (defn run-delivery-loop!
[web3 {:keys [delivery-loop-ms-interval default-ttl ttl-config [web3 {:keys [delivery-loop-ms-interval default-ttl ttl-config
send-online-s-interval online-message] send-online-s-interval online-message]

View File

@ -107,6 +107,7 @@
:popular-tags "Popular tags" :popular-tags "Popular tags"
:recent "Recent" :recent "Recent"
:no-statuses-discovered "No statuses discovered" :no-statuses-discovered "No statuses discovered"
:no-statuses-found "No statuses found"
;settings ;settings
:settings "Settings" :settings "Settings"