discovery rework

Former-commit-id: 92073f3d67
This commit is contained in:
alwxndr 2016-08-04 18:36:13 +03:00
parent 8f39f113fa
commit a598200ace
45 changed files with 718 additions and 564 deletions

View File

@ -9,8 +9,8 @@
[re-frame "0.7.0"] [re-frame "0.7.0"]
[prismatic/schema "1.0.4"] [prismatic/schema "1.0.4"]
^{:voom {:repo "git@github.com:status-im/status-lib.git" ^{:voom {:repo "git@github.com:status-im/status-lib.git"
:branch "discover-rework"}} :branch "discover-rework"}}
[status-im/protocol "0.1.1-20160706_085008-ge61756a"] [status-im/protocol "0.1.3-20160818_085900-gda79e8e"]
[natal-shell "0.3.0"] [natal-shell "0.3.0"]
[com.andrewmcveigh/cljs-time "0.4.0"]] [com.andrewmcveigh/cljs-time "0.4.0"]]
:plugins [[lein-cljsbuild "1.1.1"] :plugins [[lein-cljsbuild "1.1.1"]

View File

@ -2,6 +2,7 @@
(:require [status-im.models.accounts :as accounts] (:require [status-im.models.accounts :as accounts]
[re-frame.core :refer [register-handler after dispatch dispatch-sync debug]] [re-frame.core :refer [register-handler after dispatch dispatch-sync debug]]
[status-im.utils.logging :as log] [status-im.utils.logging :as log]
[status-im.protocol.api :as api]
[status-im.components.geth :as geth] [status-im.components.geth :as geth]
[status-im.utils.types :refer [json->clj]] [status-im.utils.types :refer [json->clj]]
[status-im.persistence.simple-kv-store :as kv] [status-im.persistence.simple-kv-store :as kv]
@ -13,11 +14,13 @@
[status-im.i18n :refer [label]] [status-im.i18n :refer [label]]
[status-im.constants :refer [content-type-command-request]] [status-im.constants :refer [content-type-command-request]]
status-im.accounts.login.handlers status-im.accounts.login.handlers
[clojure.string :as str])) [clojure.string :as str]
[status-im.utils.datetime :as time]
[status-im.utils.handlers :as u]))
(defn save-account [_ [_ account]] (defn save-account [_ [_ account]]
(accounts/save-accounts [account] false)) (accounts/save-accounts [account] true))
(register-handler (register-handler
:add-account :add-account
@ -28,14 +31,14 @@
(defn save-password [password] (defn save-password [password]
(storage/put kv/kv-store :password password)) (storage/put kv/kv-store :password password))
(defn account-created [db result password] (defn account-created [result password]
(let [data (json->clj result) (let [data (json->clj result)
public-key (:pubkey data) public-key (:pubkey data)
address (:address data) address (:address data)
account {:public-key public-key account {:public-key public-key
:address address :address address
:name address :name address
:photo-path (identicon address)}] :photo-path (identicon public-key)}]
(log/debug "account-created: " account) (log/debug "account-created: " account)
(when (not (str/blank? public-key)) (when (not (str/blank? public-key))
(do (do
@ -45,17 +48,37 @@
(register-handler (register-handler
:create-account :create-account
(fn [db [_ password]] (fn [db [_ password]]
(geth/create-account password (fn [result] (account-created db result password))) (geth/create-account password (fn [result] (account-created result password)))
db)) db))
(defn save-account-to-realm!
[{:keys [current-account-id accounts]} _]
(accounts/save-accounts [(get accounts current-account-id)] true))
(defn send-account-update
[{:keys [current-account-id accounts]} _]
(api/send-account-update (get accounts current-account-id)))
(register-handler (register-handler
:account-update :account-update
(fn [db [_ data]] (-> (fn [{:keys [current-account-id accounts] :as db} [_ data]]
(let [current-account-id (get db :current-account-id) (let [data (assoc data :last-updated (time/now-ms))
account (-> (get-in db [:accounts current-account-id]) account (-> (get accounts current-account-id)
(merge data))] (merge data))]
(accounts/save-accounts [account] true) (assoc-in db [:accounts current-account-id] account)))
(assoc-in db [:accounts current-account-id] account)))) ((after save-account-to-realm!))
((after send-account-update))))
(register-handler
:send-account-update-if-needed
(u/side-effect!
(fn [{:keys [current-account-id accounts]} _]
(let [{:keys [last-updated]} (get accounts current-account-id)
now (time/now-ms)
needs-update? (> (- now last-updated) time/week)]
(log/info "Need to send account-update: " needs-update?)
(when needs-update?
(dispatch [:account-update]))))))
(defn initialize-account [db address] (defn initialize-account [db address]
(let [is-login-screen? (= (:view-id db) :login)] (let [is-login-screen? (= (:view-id db) :login)]

View File

@ -14,7 +14,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.screen :refer [discovery]]
[status-im.discovery.tag :refer [discovery-tag]] [status-im.discovery.tag :refer [discovery-tag]]
[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]]

View File

@ -479,13 +479,13 @@
[{:keys [current-chat-id]} _] [{:keys [current-chat-id]} _]
(r/write :account (r/write :account
(fn [] (fn []
(r/delete :account (r/get-by-field :account :msgs :chat-id current-chat-id))))) (r/delete :account (r/get-by-field :account :message :chat-id current-chat-id)))))
(defn delete-chat! (defn delete-chat!
[{:keys [current-chat-id]} _] [{:keys [current-chat-id]} _]
(r/write :account (r/write :account
(fn [] :account (fn [] :account
(->> (r/get-by-field :account :chats :chat-id current-chat-id) (->> (r/get-by-field :account :chat :chat-id current-chat-id)
(r/single) (r/single)
(r/delete :account))))) (r/delete :account)))))
@ -550,3 +550,9 @@
j/adjust-resize))))) j/adjust-resize)))))
(fn [db [_ chat-id mode]] (fn [db [_ chat-id mode]]
(assoc-in db [:kb-mode chat-id] mode))) (assoc-in db [:kb-mode chat-id] mode)))
(register-handler :update-chat!
(fn [db [_ chat-id new-chat-data]]
(if (get-in db [:chats chat-id])
(update-in db [:chats chat-id] merge new-chat-data)
db)))

View File

@ -24,7 +24,7 @@
[{:keys [current-chat-id] :as db} [_ chat-id]] [{:keys [current-chat-id] :as db} [_ chat-id]]
(let [chat-id' (or chat-id current-chat-id) (let [chat-id' (or chat-id current-chat-id)
requests (-> ;; todo maybe limit is needed requests (-> ;; todo maybe limit is needed
(realm/get-by-fields :account :requests (realm/get-by-fields :account :request
{:chat-id chat-id' {:chat-id chat-id'
:status "open"}) :status "open"})
(realm/sorted :added :desc) (realm/sorted :added :desc)
@ -36,7 +36,7 @@
[_ [_ chat-id message-id]] [_ [_ chat-id message-id]]
(realm/write :account (realm/write :account
(fn [] (fn []
(-> (realm/get-by-fields :account :requests (-> (realm/get-by-fields :account :request
{:chat-id chat-id {:chat-id chat-id
:message-id message-id}) :message-id message-id})
(realm/single) (realm/single)

View File

@ -5,7 +5,7 @@
(defn delivered-messages [] (defn delivered-messages []
(-> (realm/get-by-fields (-> (realm/get-by-fields
:account :msgs :account :message
{:delivery-status :delivered {:delivery-status :delivered
:outgoing false}) :outgoing false})
(realm/collection->map))) (realm/collection->map)))

View File

@ -15,6 +15,7 @@
[status-im.chat.styles.screen :as st] [status-im.chat.styles.screen :as st]
[status-im.utils.listview :refer [to-datasource-inverted]] [status-im.utils.listview :refer [to-datasource-inverted]]
[status-im.utils.utils :refer [truncate-str]] [status-im.utils.utils :refer [truncate-str]]
[status-im.utils.datetime :as time]
[status-im.components.invertible-scroll-view :refer [invertible-scroll-view]] [status-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
[status-im.components.toolbar :refer [toolbar]] [status-im.components.toolbar :refer [toolbar]]
[status-im.chat.views.message :refer [chat-message]] [status-im.chat.views.message :refer [chat-message]]
@ -23,7 +24,8 @@
[status-im.chat.views.new-message :refer [chat-message-new]] [status-im.chat.views.new-message :refer [chat-message-new]]
[status-im.i18n :refer [label label-pluralize]] [status-im.i18n :refer [label label-pluralize]]
[status-im.components.animation :as anim] [status-im.components.animation :as anim]
[reagent.core :as r])) [reagent.core :as r]
[clojure.string :as str]))
(defn contacts-by-identity [contacts] (defn contacts-by-identity [contacts]
@ -191,16 +193,27 @@
[overlay {:on-click-outside #(dispatch [:set-show-actions false])} [overlay {:on-click-outside #(dispatch [:set-show-actions false])}
[actions-list-view platform-specific]]) [actions-list-view platform-specific]])
(defn online-text [contact chat-id]
(if contact
(if (> (get contact :last-online) 0)
(time/time-ago (time/to-date (get contact :last-online)))
(label :t/active-unknown))
(if (= chat-id "console")
(label :t/active-online)
(label :t/active-unknown))))
(defn toolbar-content [platform-specific] (defn toolbar-content [platform-specific]
(let [{:keys [group-chat name contacts]} (let [{:keys [group-chat chat-id name contacts]} (subscribe [:chat-properties [:group-chat :chat-id :name :contacts]])
(subscribe [:chat-properties [:group-chat :name :contacts]]) contact (subscribe [:get-in [:contacts @chat-id]])
show-actions (subscribe [:show-actions])] show-actions (subscribe [:show-actions])]
(fn [] (fn []
[view (st/chat-name-view @show-actions) [view (st/chat-name-view @show-actions)
[text {:style st/chat-name-text [text {:style st/chat-name-text
:platform-specific platform-specific :platform-specific platform-specific
:font :medium} :font :medium}
(truncate-str (or @name (label :t/chat-name)) 30)] (if (str/blank? @name)
(label :t/user-anonymous)
(truncate-str (or @name (label :t/chat-name)) 30))]
(if @group-chat (if @group-chat
[view {:flexDirection :row} [view {:flexDirection :row}
[icon :group st/group-icon] [icon :group st/group-icon]
@ -209,11 +222,10 @@
:font :medium} :font :medium}
(let [cnt (inc (count @contacts))] (let [cnt (inc (count @contacts))]
(label-pluralize cnt :t/members))]] (label-pluralize cnt :t/members))]]
;; TODO stub data: last activity
[text {:style st/last-activity [text {:style st/last-activity
:platform-specific platform-specific :platform-specific platform-specific
:font :default} :font :default}
(label :t/last-active)])]))) (online-text @contact @chat-id)])])))
(defn toolbar-action [] (defn toolbar-action []
(let [show-actions (subscribe [:show-actions])] (let [show-actions (subscribe [:show-actions])]

View File

@ -57,7 +57,7 @@
:renderRow (fn [row _ _] :renderRow (fn [row _ _]
(list-item [chat-list-item row])) (list-item [chat-list-item row]))
:style st/list-container :style st/list-container
;;; if "maximazing" chat list will make scroll to 0, ;;; if "maximizing" chat list will make scroll to 0,
;;; then disable maximazing ;;; then disable maximazing
:onLayout (fn [event] :onLayout (fn [event]
(when-not @chats-scrolled? (when-not @chats-scrolled?

View File

@ -5,8 +5,7 @@
image image
touchable-highlight]] touchable-highlight]]
[status-im.components.styles :refer [font]] [status-im.components.styles :refer [font]]
[status-im.chats-list.views.inner-item :refer [status-im.chats-list.views.inner-item :refer [chat-list-item-inner-view]]))
[chat-list-item-inner-view]]))
(defn chat-list-item [[chat-id chat]] (defn chat-list-item [[chat-id chat]]
[touchable-highlight [touchable-highlight

View File

@ -4,7 +4,9 @@
[status-im.components.chat-icon.screen :refer [chat-icon-view-chat-list]] [status-im.components.chat-icon.screen :refer [chat-icon-view-chat-list]]
[status-im.chats-list.styles :as st] [status-im.chats-list.styles :as st]
[status-im.utils.utils :refer [truncate-str]] [status-im.utils.utils :refer [truncate-str]]
[status-im.utils.datetime :as time])) [status-im.i18n :refer [label]]
[status-im.utils.datetime :as time]
[clojure.string :as str]))
(defview chat-list-item-inner-view (defview chat-list-item-inner-view
[{:keys [chat-id name color new-messages-count [{:keys [chat-id name color new-messages-count
@ -16,7 +18,9 @@
[chat-icon-view-chat-list chat-id group-chat name color online]] [chat-icon-view-chat-list chat-id group-chat name color online]]
[view st/item-container [view st/item-container
[view st/name-view [view st/name-view
[text {:style st/name-text} (truncate-str name 20)] [text {:style st/name-text} (if (str/blank? name)
(label :t/user-anonymous)
(truncate-str name 20))]
(when group-chat (when group-chat
[icon :group st/group-icon]) [icon :group st/group-icon])
(when group-chat (when group-chat

View File

@ -15,7 +15,7 @@
[_ [identity]] [_ [identity]]
(dispatch [::fetch-commands! identity]) (dispatch [::fetch-commands! identity])
;; todo uncomment ;; todo uncomment
#_(if-let [{:keys [file]} (realm/get-one-by-field :account :commands #_(if-let [{:keys [file]} (realm/get-one-by-field :account :command
:chat-id identity)] :chat-id identity)]
(dispatch [::parse-commands! identity file]) (dispatch [::parse-commands! identity file])
(dispatch [::fetch-commands! identity]))) (dispatch [::fetch-commands! identity])))
@ -73,7 +73,7 @@
(defn save-commands-js! (defn save-commands-js!
[_ [id file]] [_ [id file]]
(realm/create-object :account :commands {:chat-id id :file file})) (realm/create-object :account :command {:chat-id id :file file}))
(defn loading-failed! (defn loading-failed!
[db [id reason details]] [db [id reason details]]

View File

@ -12,8 +12,8 @@
(defn window-page-width [] (defn window-page-width []
(.-width (.get (.. r/react-native -Dimensions) "window"))) (.-width (.get (.. r/react-native -Dimensions) "window")))
(def defaults {:gap 10 (def defaults {:gap 8
:sneak 10 :sneak 8
:pageStyle {} :pageStyle {}
:scrollThreshold 20}) :scrollThreshold 20})
@ -175,5 +175,4 @@
:component-did-update component-did-update :component-did-update component-did-update
:display-name "carousel" :display-name "carousel"
:reagent-render reagent-render}] :reagent-render reagent-render}]
(log/debug "Creating carousel component: " data)
(reagent.core/create-class component-data))) (reagent.core/create-class component-data)))

View File

@ -95,10 +95,10 @@
[animated-view {:style (st/tab-view-container anim-value)} [animated-view {:style (st/tab-view-container anim-value)}
content])}))) content])})))
(defn tab-view [{:keys [view-id screen]}] (defn tab-view [platform-specific {:keys [view-id screen]}]
^{:key view-id} ^{:key view-id}
[tab-view-container view-id [tab-view-container view-id
[screen]]) [screen {:platform-specific platform-specific}]])
(defview main-tabs [{platform-specific :platform-specific}] (defview main-tabs [{platform-specific :platform-specific}]
[view-id [:get :view-id] [view-id [:get :view-id]
@ -109,6 +109,6 @@
[drawer-view {:platform-specific platform-specific} [drawer-view {:platform-specific platform-specific}
[view {:style common-st/flex [view {:style common-st/flex
:pointerEvents (if tab-animation? :none :auto)} :pointerEvents (if tab-animation? :none :auto)}
(doall (map #(tab-view %) tab-list)) (doall (map #(tab-view platform-specific %) tab-list))
[tabs {:selected-view-id view-id [tabs {:selected-view-id view-id
:tab-list tab-list}]]]]]) :tab-list tab-list}]]]]])

View File

@ -10,7 +10,7 @@
text2-color text2-color
toolbar-background1]])) toolbar-background1]]))
(def tabs-height 59) (def tabs-height 60)
(def tab-height 56) (def tab-height 56)
(defn tabs-container [hidden? animation? offset-y] (defn tabs-container [hidden? animation? offset-y]
@ -67,5 +67,5 @@
:left 0 :left 0
:right 0 :right 0
:bottom 0 :bottom 0
:padding-bottom 59 :padding-bottom 60
:transform [{:translateX offset-x}]}) :transform [{:translateX offset-x}]})

View File

@ -4,18 +4,26 @@
[status-im.models.contacts :as contacts] [status-im.models.contacts :as contacts]
[status-im.utils.crypt :refer [encrypt]] [status-im.utils.crypt :refer [encrypt]]
[clojure.string :as s] [clojure.string :as s]
[status-im.protocol.api :as api]
[status-im.utils.utils :refer [http-post]] [status-im.utils.utils :refer [http-post]]
[status-im.utils.phone-number :refer [format-phone-number]] [status-im.utils.phone-number :refer [format-phone-number]]
[status-im.utils.handlers :as u] [status-im.utils.handlers :as u]
[status-im.utils.utils :refer [require]])) [status-im.utils.utils :refer [require]]
[status-im.utils.logging :as log]))
(defn save-contact (defn save-contact
[_ [_ contact]] [_ [_ contact]]
(contacts/save-contacts [contact])) (contacts/save-contacts [contact]))
(register-handler :add-contact (defn watch-contact
[_ [_ contact]]
(api/watch-user contact))
(register-handler :watch-contact (u/side-effect! watch-contact))
(register-handler :update-contact
(-> (fn [db [_ {:keys [whisper-identity] :as contact}]] (-> (fn [db [_ {:keys [whisper-identity] :as contact}]]
(update db :contacts assoc whisper-identity contact)) (update-in db [:contacts whisper-identity] merge contact))
((after save-contact)))) ((after save-contact))))
(defn load-contacts! [db _] (defn load-contacts! [db _]
@ -23,6 +31,8 @@
(map (fn [{:keys [whisper-identity] :as contact}] (map (fn [{:keys [whisper-identity] :as contact}]
[whisper-identity contact])) [whisper-identity contact]))
(into {}))] (into {}))]
(doseq [[_ contact] contacts]
(dispatch [:watch-contact contact]))
(assoc db :contacts contacts))) (assoc db :contacts contacts)))
(register-handler :load-contacts load-contacts!) (register-handler :load-contacts load-contacts!)
@ -108,17 +118,34 @@
(defn add-new-contact [db [_ {:keys [whisper-identity] :as contact}]] (defn add-new-contact [db [_ {:keys [whisper-identity] :as contact}]]
(-> db (-> db
(update :contacts assoc whisper-identity contact) (update :contacts assoc whisper-identity contact)
(assoc :new-contact {:name "" (assoc :new-contact-identity "")))
:address ""
:whisper-identity ""
:phone-number ""})))
(register-handler :add-new-contact (register-handler :add-new-contact
(after save-contact) (-> add-new-contact
add-new-contact) ((after save-contact))
((after watch-contact))))
(defn set-new-contact-from-qr (defn set-contact-identity-from-qr
[{:keys [new-contact] :as db} [_ _ qr-contact]] [db [_ _ contact-identity]]
(assoc db :new-contact (merge new-contact qr-contact))) (assoc db :new-contact-identity contact-identity))
(register-handler :set-new-contact-from-qr set-new-contact-from-qr) (register-handler :set-contact-identity-from-qr set-contact-identity-from-qr)
(register-handler :contact-update-received
(u/side-effect!
;; TODO: security issue: we should use `from` instead of `public-key` here, but for testing it is much easier to use `public-key`
(fn [db [_ from {{:keys [public-key last-updated name] :as account} :account}]]
(let [prev-last-updated (get-in db [:contacts public-key :last-updated])]
(if (< prev-last-updated last-updated)
(let [contact (-> (assoc account :whisper-identity public-key)
(dissoc :public-key))]
(dispatch [:update-contact contact])
(dispatch [:update-chat! public-key {:name name}])))))))
(register-handler :contact-online-received
(u/side-effect!
(fn [db [_ from {last-online :at :as payload}]]
(let [prev-last-online (get-in db [:contacts from :last-online])]
(if (< prev-last-online last-online)
(dispatch [:update-contact {:whisper-identity from
:last-online last-online}]))))))

View File

@ -7,7 +7,7 @@
(.isAddress js/Web3.prototype s)) (.isAddress js/Web3.prototype s))
(defn unique-identity? [identity] (defn unique-identity? [identity]
(not (realm/exists? :account :contacts :whisper-identity identity))) (not (realm/exists? :account :contact :whisper-identity identity)))
(defn valid-length? [identity] (defn valid-length? [identity]
(let [length (count identity)] (let [length (count identity)]

View File

@ -20,7 +20,7 @@
(if (pos? (count (:name contact))) (if (pos? (count (:name contact)))
name name
;; todo is this correct behaviour? ;; todo is this correct behaviour?
(label :t/no-name))] (label :t/user-anonymous))]
(when info (when info
[text {:style st/info-text} [text {:style st/info-text}
info])]])) info])]]))

View File

@ -37,14 +37,32 @@
[text {:style toolbar-title-text} [text {:style toolbar-title-text}
(label :t/add-new-contact)]]) (label :t/add-new-contact)]])
(defview contact-name-input [name] (defn on-add-contact [id]
[] (if (v/is-address? id)
[text-field (http-post "get-contacts-by-address" {:addresses [id]}
{:error (if (str/blank? name) "" nil) (fn [{:keys [contacts]}]
:errorColor "#7099e6" (if (> (count contacts) 0)
:value name (let [{:keys [whisper-identity]} (first contacts)
:label (label :t/name) contact {:name ""
:onChangeText #(dispatch [:set-in [:new-contact :name] %])}]) :address id
:photo-path (identicon whisper-identity)
:whisper-identity whisper-identity}]
(dispatch [:add-new-contact contact]))
(dispatch [:set :new-contact-address-error (label :t/unknown-address)]))))
(dispatch [:add-new-contact {:name ""
:photo-path (identicon id)
:whisper-identity id}])))
(defn toolbar-action [new-contact-identity error]
(let [valid-contact? (and
(s/valid? ::v/whisper-identity new-contact-identity)
(nil? error))]
{:image {:source {:uri (if valid-contact?
:icon_ok_blue
:icon_ok_disabled)}
:style icon-search}
:handler #(when valid-contact?
(on-add-contact new-contact-identity))}))
(defview contact-whisper-id-input [whisper-identity error] (defview contact-whisper-id-input [whisper-identity error]
[] []
@ -53,48 +71,20 @@
error error
(label :t/enter-valid-address))] (label :t/enter-valid-address))]
[view button-input-container [view button-input-container
[text-field [text-field
{:error error {:error error
:errorColor "#7099e6" :errorColor "#7099e6"
:value whisper-identity :value whisper-identity
:wrapperStyle (merge button-input) :wrapperStyle (merge button-input)
:label (label :t/address) :label (label :t/address)
:onChangeText #(do :onChangeText #(do
(dispatch [:set-in [:new-contact :whisper-identity] %]) (dispatch [:set-in [:new-contact-identity] %])
(dispatch [:set :new-contact-address-error nil]))}] (dispatch [:set :new-contact-address-error nil]))}]
[scan-button {:showLabel (zero? (count whisper-identity)) [scan-button {:showLabel (zero? (count whisper-identity))
:handler #(dispatch [:scan-qr-code {:toolbar-title (label :t/new-contact)} :set-new-contact-from-qr])}]])) :handler #(dispatch [:scan-qr-code {:toolbar-title (label :t/new-contact)} :set-contact-identity-from-qr])}]]))
(defn on-add-contact [whisper-identity new-contact]
(if (v/is-address? whisper-identity)
(http-post "get-contacts-by-address" {:addresses [whisper-identity]}
(fn [{:keys [contacts]}]
(if (> (count contacts) 0)
(let [contact (first contacts)
new-contact (merge
new-contact
{:address whisper-identity
:whisper-identity (:whisper-identity contact)})]
(dispatch [:add-new-contact new-contact]))
(dispatch [:set :new-contact-address-error (label :t/unknown-address)]))))
(dispatch [:add-new-contact new-contact])))
(defn toolbar-action [whisper-identity new-contact error]
(let [valid-contact? (and
(s/valid? ::v/contact new-contact)
(nil? error))]
{:image {:source {:uri (if valid-contact?
:icon_ok_blue
:icon_ok_disabled)}
:style icon-search}
:handler #(when valid-contact?
(let [contact (merge
{:photo-path (identicon whisper-identity)}
new-contact)]
(on-add-contact whisper-identity contact)))}))
(defview new-contact [{platform-specific :platform-specific}] (defview new-contact [{platform-specific :platform-specific}]
[{:keys [name whisper-identity phone-number] :as new-contact} [:get :new-contact] [new-contact-identity [:get :new-contact-identity]
error [:get :new-contact-address-error]] error [:get :new-contact-address-error]]
[view st/contact-form-container [view st/contact-form-container
[view [view
@ -104,9 +94,11 @@
:style icon-back} :style icon-back}
:handler #(dispatch [:navigate-back])} :handler #(dispatch [:navigate-back])}
:custom-content toolbar-title :custom-content toolbar-title
:action (toolbar-action whisper-identity new-contact error)}]] :action (toolbar-action new-contact-identity error)}]]
[view st/form-container [view st/form-container
[contact-name-input name] [contact-whisper-id-input new-contact-identity error]]
[contact-whisper-id-input whisper-identity error]]
[view st/address-explication-container [view st/address-explication-container
[text {:style st/address-explication} (label :t/address-explication)]]]) [text {:style st/address-explication
:platform-specific platform-specific
:font :default}
(label :t/address-explication)]]])

View File

@ -21,7 +21,9 @@
:status nil :status nil
:photo-path nil} :photo-path nil}
:contacts [] :new-contact-identity ""
:contacts {}
:contacts-ids #{} :contacts-ids #{}
:selected-contacts #{} :selected-contacts #{}
:current-chat-id "console" :current-chat-id "console"
@ -36,10 +38,6 @@
:navigation-stack (list default-view) :navigation-stack (list default-view)
:current-tag nil :current-tag nil
:qr-codes {} :qr-codes {}
:new-contact {:name ""
:address ""
:whisper-identity ""
:phone-number ""}
:keyboard-height 0 :keyboard-height 0
:disable-group-creation false :disable-group-creation false
:animations {;; todo clear this :animations {;; todo clear this

View File

@ -1,67 +1,72 @@
(ns status-im.discovery.handlers (ns status-im.discovery.handlers
(:require [re-frame.core :refer [after dispatch enrich]] (:require [re-frame.core :refer [after dispatch enrich]]
[status-im.utils.utils :refer [first-index]]
[status-im.utils.handlers :refer [register-handler]] [status-im.utils.handlers :refer [register-handler]]
[status-im.protocol.api :as api] [status-im.protocol.api :as api]
[status-im.navigation.handlers :as nav] [status-im.navigation.handlers :as nav]
[status-im.discovery.model :as discoveries] [status-im.discovery.model :as discoveries]
[status-im.utils.handlers :as u])) [status-im.utils.handlers :as u]
[status-im.utils.datetime :as time]))
(register-handler :init-discoveries
(fn [db _]
(-> db
(assoc :discoveries []))))
(defn calculate-priority [{:keys [chats contacts]} from payload]
(let [contact (get contacts from)
chat (get chats from)
seen-online-recently? (< (- (time/now-ms) (get contact :last-online))
time/hour)]
(+ (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} _] [{:keys [discoveries] :as db} _]
(if-not (seq discoveries) (-> db
(-> db (assoc :tags (discoveries/all-tags))
(assoc :tags (discoveries/all-tags)) ;; todo add limit
;; todo add limit ;; todo hash-map with whisper-id as key and sorted by last-update
;; todo hash-map with whisper-id as key and sorted by last-update ;; may be more efficient here
;; may be more efficient here (assoc :discoveries (discoveries/discovery-list))))
(assoc :discoveries (discoveries/discovery-list)))
db))
(register-handler :discovery-response-received (register-handler :discovery-response-received
(u/side-effect! (u/side-effect!
(fn [_ [_ from payload]] (fn [db [_ from payload]]
(let [{:keys [name status hashtags location]} payload (let [{:keys [msg-id name photo-path status hashtags]} payload
location (or location "") discovery {:msg-id msg-id
discovery [{:name name :name name
:status status :photo-path photo-path
:whisper-id from :status status
:photo "" :whisper-id from
:location location :tags (map #(hash-map :name %) hashtags)
:tags (map #(hash-map :name %) hashtags) :last-updated (js/Date.)
:last-updated (js/Date.)}]] :priority (calculate-priority db from payload)}]
(dispatch [:add-discovery discovery]))))) (dispatch [:add-discovery discovery])))))
(register-handler :broadcast-status (register-handler :broadcast-status
(u/side-effect! (u/side-effect!
(fn [{:keys [name]} [_ status hashtags]] (fn [{:keys [current-account-id accounts]} [_ status hashtags]]
(api/broadcast-discover-status name status hashtags)))) (let [account (get accounts current-account-id)]
(api/broadcast-discover-status account status hashtags)))))
(register-handler :show-discovery-tag (register-handler :show-discovery-tag
(fn [db [_ tag]] (fn [db [_ tag]]
(dispatch [:navigate-to :discovery-tag]) (dispatch [:navigate-to :discovery-tag])
(assoc db :current-tag tag))) (assoc db :current-tag tag)))
;; todo remove this
(register-handler :create-fake-discovery!
(u/side-effect!
(fn [_ _]
(let [number (rand-int 999)
discovery {:name (str "Name " number)
:status (str "Status This is some longer status to get the second line " number)
:whisper-id (str number)
:photo ""
:location ""
:tags [{:name "tag1"}
{:name "tag2"}
{:name "tag3"}]
:last-updated (new js/Date)}]
(dispatch [:add-discovery discovery])))))
(defn add-discovery (defn add-discovery
[db [_ discovery]] [{db-discoveries :discoveries
(-> db :as db} [_ {:keys [msg-id] :as discovery}]]
(assoc :new-discovery discovery) (let [updated-discoveries (if-let [i (first-index #(= (:msg-id %) msg-id) db-discoveries)]
(update :discoveries conj discovery))) (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]} _]
@ -75,3 +80,9 @@
(-> add-discovery (-> add-discovery
((after save-discovery!)) ((after save-discovery!))
((enrich reload-tags!)))) ((enrich reload-tags!))))
(register-handler
:remove-old-discoveries!
(u/side-effect!
(fn [_ _]
(discoveries/remove-discoveries! :priority :asc 1000 200))))

View File

@ -5,87 +5,72 @@
(defn get-tag [tag] (defn get-tag [tag]
(log/debug "Getting tag: " tag) (log/debug "Getting tag: " tag)
(-> (r/get-by-field :base :tag :name tag) (-> (r/get-by-field :account :tag :name tag)
(r/single-cljs))) (r/single-cljs)))
(defn decrease-tag-counter [tag] (defn update-tag-counter [func tag]
(let [tag (:name tag) (let [tag (:name tag)
tag-object (get-tag tag)] tag-object (get-tag tag)]
(if tag-object (if tag-object
(let [counter (dec (:count tag-object))] (r/create :account :tag
(if (zero? counter)
(r/delete :base tag-object)
(r/create :base :tag
{:name tag
:count counter}
true))))))
(defn increase-tag-counter [tag]
(let [tag (:name tag)
tag-object (get-tag tag)]
(if tag-object
(r/create :base :tag
{:name tag {:name tag
:count (inc (:count tag-object))} :count (func (:count tag-object))}
true)))) true))))
(defn decrease-tags-counter [tags] (defn update-tags-counter [func tags]
(doseq [tag tags] (doseq [tag (distinct tags)]
(decrease-tag-counter tag))) (update-tag-counter func tag)))
(defn increase-tags-counter [tags] (defn get-tags [msg-id]
(doseq [tag tags] (-> (r/get-by-field :account :discovery :msg-id msg-id)
(increase-tag-counter tag))) (r/single-cljs)
(:tags)
(vals)))
(defn get-tags [whisper-id] (defn- upsert-discovery [{:keys [msg-id tags] :as discovery}]
(:tags (-> (r/get-by-field :base :discoveries :whisper-id whisper-id) (log/debug "Creating/updating discovery with tags: " tags)
(r/single-cljs)))) (let [prev-tags (get-tags msg-id)]
(if prev-tags
(defn- create-discovery [{:keys [tags] :as discovery}] (update-tags-counter dec prev-tags))
(log/debug "Creating discovery: " discovery tags) (r/create :account :discovery discovery true)
(r/create :base :discoveries discovery true) (update-tags-counter inc tags)))
(increase-tags-counter tags))
(defn- update-discovery [{:keys [whisper-id tags] :as discovery}]
(let [old-tags (get-tags whisper-id)
tags (map :name tags)]
(decrease-tags-counter old-tags)
(r/create :base :discoveries discovery true)
(increase-tags-counter tags)))
(defn- discovery-exist? [discoveries discovery]
(some #(= (:whisper-id discovery) (:whisper-id %)) discoveries))
(defn discovery-list [] (defn discovery-list []
(->> (-> (r/get-all :base :discoveries) (->> (-> (r/get-all :account :discovery)
(r/sorted :last-updated :desc) (r/sorted :priority :desc)
r/collection->map) (r/collection->map))
(map #(update % :tags vals)))) (mapv #(update % :tags vals))))
(defn- add-discoveries [discoveries] (defn- add-discoveries [discoveries]
(r/write :base (r/write :account
(fn [] (fn []
(let [db-discoveries (discovery-list)] (doseq [discovery discoveries]
(mapv (fn [discovery] (upsert-discovery discovery)))))
(if-not (discovery-exist? db-discoveries
discovery)
(create-discovery discovery)
(update-discovery discovery)))
discoveries)))))
(defn save-discoveries [discoveries] (defn save-discoveries [discoveries]
(add-discoveries discoveries)) (add-discoveries discoveries))
(defn remove-discoveries! [by ordering critical-count to-delete-count]
(let [objs (r/get-all :account :discovery)
count (r/get-count objs)]
(if (> count critical-count)
(let [to-delete (-> (r/sorted objs by ordering)
(r/page 0 to-delete-count))]
(r/write :account
(fn []
(log/debug (str "Deleting " (r/get-count to-delete) " discoveries"))
(r/delete :account to-delete)))))))
(defn discoveries-by-tag [tag limit] (defn discoveries-by-tag [tag limit]
(let [discoveries (-> (r/get-by-filter :base :discoveries (str "tags.name = '" tag "'")) (let [discoveries (-> (r/get-by-filter :account :discovery (str "tags.name = '" tag "'"))
(r/sorted :last-updated :desc))] (r/sorted :priority :desc))]
(log/debug "Discoveries by tag: " tag) (log/debug "Discoveries by tag: " tag)
(if (pos? limit) (if (pos? limit)
(r/page discoveries 0 limit) (r/page discoveries 0 limit)
discoveries))) discoveries)))
(defn all-tags [] (defn all-tags []
(-> (r/get-all :base :tag) (-> (r/get-all :account :tag)
(r/sorted :count :desc) (r/sorted :count :desc)
r/collection->map)) r/collection->map))

View File

@ -8,6 +8,7 @@
text-input]] text-input]]
[status-im.components.status-bar :refer [status-bar]] [status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar :refer [toolbar]] [status-im.components.toolbar :refer [toolbar]]
[status-im.components.drawer.view :refer [open-drawer]]
[status-im.discovery.views.popular :refer [popular]] [status-im.discovery.views.popular :refer [popular]]
[status-im.discovery.views.recent :refer [discovery-recent]] [status-im.discovery.views.recent :refer [discovery-recent]]
[status-im.discovery.styles :as st] [status-im.discovery.styles :as st]
@ -19,18 +20,21 @@
(let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))] (let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))]
(or hashtags []))) (or hashtags [])))
(defn title-content [show-search] (defn title-content [platform-specific show-search]
[view st/discovery-toolbar-content [view st/discovery-toolbar-content
(if show-search (if show-search
[text-input {:style st/discovery-search-input [text-input {:style st/discovery-search-input
:autoFocus true :autoFocus true
:placeholder (label :t/search-tags) :placeholder (label :t/search-tags)
:onSubmitEditing (fn [e] :onSubmitEditing (fn [e]
(let [search (aget e "nativeEvent" "text") (let [search (aget e "nativeEvent" "text")
hashtags (get-hashtags search)] hashtags (get-hashtags search)]
(dispatch [:broadcast-status search hashtags])))}] (dispatch [:broadcast-status search hashtags])))}]
[view [view
[text {:style st/discovery-title} (label :t/discovery)]])]) [text {:style st/discovery-title
:platform-specific platform-specific
:font :default}
(label :t/discovery)]])])
(defn toogle-search [current-value] (defn toogle-search [current-value]
(dispatch [:set ::show-search (not current-value)])) (dispatch [:set ::show-search (not current-value)]))
@ -42,21 +46,33 @@
{:style st/discovery-toolbar {:style st/discovery-toolbar
:nav-action {:image {:source {:uri :icon_hamburger} :nav-action {:image {:source {:uri :icon_hamburger}
:style st/hamburger-icon} :style st/hamburger-icon}
:handler #(dispatch [:create-fake-discovery!])} :handler open-drawer}
:custom-content [title-content show-search] :custom-content [title-content platform-specific show-search]
:action {:image {:source {:uri :icon_search} :action {:image {:source {:uri :icon_search}
:style st/search-icon} :style st/search-icon}
:handler #(toogle-search show-search)}}]]) :handler #(toogle-search show-search)}}]])
(defview discovery [{platform-specific :platform-specific}] (defview discovery [{platform-specific :platform-specific}]
[show-search [:get ::show-search]] [show-search [:get ::show-search]
contacts [:get :contacts]]
[view st/discovery-container [view st/discovery-container
[discovery-toolbar show-search platform-specific] [discovery-toolbar show-search platform-specific]
[scroll-view st/scroll-view-container [scroll-view st/scroll-view-container
[view st/section-spacing [view st/section-spacing
[text {:style st/discovery-subtitle} (label :t/popular-tags)]] [text {:style st/discovery-subtitle
[popular] :platform-specific platform-specific
:font :medium}
(label :t/popular-tags)]]
[popular {:contacts contacts
:platform-specific platform-specific}]
[view st/section-spacing [view st/section-spacing
[text {:style st/discovery-subtitle} (label :t/recent)]] [text {:style st/discovery-subtitle
[discovery-recent]] :platform-specific platform-specific
:font :medium}
(label :t/recent)]]
[discovery-recent {:contacts contacts
:platform-specific platform-specific}]]
[bottom-gradient]]) [bottom-gradient]])

View File

@ -1,7 +1,5 @@
(ns status-im.discovery.styles (ns status-im.discovery.styles
(:require [status-im.components.styles :refer [font (:require [status-im.components.styles :refer [color-white
title-font
color-white
color-gray2 color-gray2
chat-background chat-background
online-color online-color
@ -14,55 +12,51 @@
;; common ;; common
(def row-separator (def row-separator
{:borderBottomWidth 1 {:border-bottom-width 1
:borderBottomColor "#eff2f3"}) :border-bottom-color "#eff2f3"})
(def row (def row
{:flexDirection :row}) {:flex-direction :row})
(def column (def column
{:flexDirection "column"}) {:flex-direction :column})
;; discovery.cljs ;; Toolbar
(def discovery-toolbar-content (def discovery-toolbar-content
{:flex 1 {:flex 1
:alignItems :center :align-items :center
:justifyContent :center}) :justify-content :center})
(def discovery-search-input
{:flex 1
:marginLeft 18
:lineHeight 42
:fontSize 14
:fontFamily "Avenir-Roman"
:color "#9CBFC0"})
(def discovery-title
{:color "#000000de"
:alignSelf :center
:textAlign :center
:fontFamily "sans-serif"
:fontSize 16})
(def discovery-toolbar (def discovery-toolbar
{:backgroundColor "#eef2f5" {:background-color "#eef2f5"
:elevation 0}) :elevation 0})
(def discovery-search-input
{:flex 1
:align-self "stretch"
:margin-left 18
:line-height 42
:font-size 14
:color "#9CBFC0"})
(def discovery-title
{:color "#000000de"
:align-self :center
:text-align :center
:font-size 16})
(def discovery-subtitle (def discovery-subtitle
{:color color-gray2 {:color color-gray2
:fontFamily "sans-serif-medium" :font-size 14})
:fontSize 14})
(def section-spacing (def section-spacing
{:paddingLeft 30 {:padding 16})
:paddingTop 15
:paddingBottom 15})
(def scroll-view-container (def scroll-view-container
{}) {})
;; discovery_popular.cljs ;; Popular
(def carousel-page-style (def carousel-page-style
{:borderRadius 1 {:borderRadius 1
@ -72,91 +66,84 @@
:elevation 2 :elevation 2
:marginBottom 10}) :marginBottom 10})
;; discovery_populat_list.cljs
(def tag-name (def tag-name
{:color "#7099e6" {:color "#7099e6"
:fontFamily "sans-serif-medium" :font-size 14
:fontSize 14 :padding-right 5
:paddingRight 5 :padding-bottom 2
:paddingBottom 2 :align-items :center
:alignItems :center :justify-content :center})
:justifyContent :center})
(def tag-name-container (def tag-name-container
{:flexDirection "column" {:flex-direction "column"
:backgroundColor "#eef2f5" :background-color "#eef2f5"
:borderRadius 5 :border-radius 5
:padding 4}) :padding 4})
(def tag-count (def tag-count
{:color "#838c93" {:color "#838c93"
:fontFamily "sans-serif" :font-size 12
:fontSize 12 :padding-right 5
:paddingRight 5 :padding-bottom 2
:paddingBottom 2 :align-items :center
:alignItems :center :justify-content :center})
:justifyContent :center})
(def tag-count-container (def tag-count-container
{:flex 0.2 {:flex 0.2
:flexDirection "column" :flex-direction "column"
:alignItems "flex-end" :align-items "flex-end"
:paddingTop 10 :padding-top 10
:paddingRight 9}) :padding-right 9})
(def popular-list-container (def popular-list-container
{:flex 1 {:flex 1
:backgroundColor :white :background-color :white
:paddingLeft 10 :padding-left 10
:paddingTop 16}) :padding-top 16})
(def popular-list (def popular-list
{:backgroundColor :white {:background-color :white
:paddingTop 13}) :padding-top 13})
;; discover_popular_list_item.cjls ;; Popular list item
(def popular-list-item (def popular-list-item
{:flexDirection :row {:flex-direction :row
:paddingTop 10 :padding-top 10
:paddingBottom 10}) :padding-bottom 10})
(def popular-list-item-status (def popular-list-item-status
{:color "black" {:color "black"
:fontFamily "sans-serif" :line-height 22
:lineHeight 22 :font-size 14})
:fontSize 14})
(def popular-list-item-name (def popular-list-item-name
{:color "black" {:color "black"
:fontFamily "sans-serif-medium" :font-size 14
:fontSize 14 :line-height 24})
:lineHeight 24})
(def popular-list-item-name-container (def popular-list-item-name-container
{:flex 0.8 {:flex 0.8
:flexDirection "column"}) :flex-direction "column"})
(def popular-list-item-avatar-container (def popular-list-item-avatar-container
{:flex 0.2 {:flex 0.2
:flexDirection "column" :flex-direction "column"
:alignItems :center :align-items :center
:paddingTop 5}) :padding-top 5})
(def popular-list-item-avatar (def popular-list-item-avatar
{:resizeMode "contain" {:border-radius 18
:borderRadius 20 :width 36
:width 40 :height 36})
:height 40})
;; discovery_recent ;; discovery_recent
(def recent-list (def recent-list
{:backgroundColor :white {:background-color :white
:paddingLeft 15}) :padding-left 16})
;; discovery_tag ;; Discovery tag
(def discovery-tag-container (def discovery-tag-container
{:flex 1 {:flex 1

View File

@ -1,14 +1,11 @@
(ns status-im.discovery.tag (ns status-im.discovery.tag
(:require (:require-macros [status-im.utils.views :refer [defview]])
[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.react :refer [view text list-view list-item]]
[status-im.components.toolbar :refer [toolbar]] [status-im.components.toolbar :refer [toolbar]]
[status-im.discovery.views.popular-list-item :refer [popular-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]))
(defn render-row [row _ _]
(list-item [popular-list-item row]))
(defn render-separator [_ row-id _] (defn render-separator [_ row-id _]
(list-item [view {:style st/row-separator (list-item [view {:style st/row-separator
@ -19,22 +16,21 @@
[view {:style st/tag-container} [view {:style st/tag-container}
[text {:style st/tag-title} (str " #" tag)]]]) [text {:style st/tag-title} (str " #" tag)]]])
(defn discovery-tag [] (defview discovery-tag [{platform-specific :platform-specific}]
(let [tag (subscribe [:get :current-tag]) [tag [:get :current-tag]
discoveries (subscribe [:get-discoveries-by-tag])] discoveries [:get-discoveries-by-tag]]
(fn [] (let [datasource (to-datasource discoveries)]
(let [items @discoveries [view st/discovery-tag-container
datasource (to-datasource items)] [toolbar {:nav-action {:image {:source {:uri :icon_back}
[view st/discovery-tag-container
[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 @tag) :custom-content (title-content tag)
:action {:image {:source {:uri :icon_search} :action {:image {:source {:uri :icon_search}
:style st/icon-search} :style st/icon-search}
:handler (fn [])}}] :handler (fn [])}}]
[list-view {:dataSource datasource [list-view {:dataSource datasource
:renderRow render-row :renderRow (fn [row _ _]
:renderSeparator render-separator (list-item [discovery-list-item row platform-specific]))
:style st/recent-list}]])))) :renderSeparator render-separator
:style st/recent-list}]]))

View File

@ -0,0 +1,33 @@
(ns status-im.discovery.views.discovery-list-item
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[clojure.string :as str]
[status-im.components.react :refer [view text image]]
[status-im.discovery.styles :as st]
[status-im.utils.identicon :refer [identicon]]
[status-im.i18n :refer [label]]))
(defview discovery-list-item [{:keys [name photo-path status whisper-id]} platform-specific]
[{contact-name :name
contact-photo-path :photo-path} [:get-in [:contacts whisper-id]]]
[view st/popular-list-item
[view st/popular-list-item-name-container
[text {:style st/popular-list-item-name
:platform-specific platform-specific
:font :medium
:number-of-lines 1}
(cond
(not (str/blank? contact-name)) contact-name
(not (str/blank? name)) name
:else (label :t/user-anonymous))]
[text {:style st/popular-list-item-status
:platform-specific platform-specific
:font :default
:number-of-lines 2}
status]]
[view st/popular-list-item-avatar-container
[image {:style st/popular-list-item-avatar
:source {:uri (cond
(not (str/blank? contact-photo-path)) contact-photo-path
(not (str/blank? photo-path)) photo-path
:else (identicon whisper-id))}}]]])

View File

@ -13,11 +13,13 @@
(defn page-width [] (defn page-width []
(.-width (.get (.. r/react-native -Dimensions) "window"))) (.-width (.get (.. r/react-native -Dimensions) "window")))
(defview popular [] (defview popular [{:keys [contacts platform-specific]}]
[popular-tags [:get-popular-tags 3]] [popular-tags [:get-popular-tags 10]]
(if (pos? (count popular-tags)) (if (pos? (count popular-tags))
[carousel {:pageStyle st/carousel-page-style [carousel {:pageStyle st/carousel-page-style}
:sneak 20}
(for [{:keys [name count]} popular-tags] (for [{:keys [name count]} popular-tags]
[discovery-popular-list name count])] [discovery-popular-list {:tag name
:count count
:contacts contacts
:platform-specific platform-specific}])]
[text (label :t/none)])) [text (label :t/none)]))

View File

@ -9,27 +9,28 @@
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.popular-list-item :refer [popular-list-item]])) [status-im.discovery.views.discovery-list-item :refer [discovery-list-item]]))
(defn render-row [row _ _]
(list-item [popular-list-item row]))
(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}]))
(defview discovery-popular-list [tag count] (defview discovery-popular-list [{:keys [tag count contacts platform-specific]}]
[discoveries [:get-discoveries-by-tag tag 3]] [discoveries [:get-discoveries-by-tag tag 3]]
[view st/popular-list-container [view st/popular-list-container
[view st/row [view st/row
[view st/tag-name-container [view st/tag-name-container
[touchable-highlight {:onPress #(dispatch [:show-discovery-tag tag])} [touchable-highlight {:onPress #(dispatch [:show-discovery-tag tag])}
[view [view
[text {:style st/tag-name} (str " #" (name tag))]]]] [text {:style st/tag-name
:platform-specific platform-specific
:font :medium}
(str " #" (name tag))]]]]
[view st/tag-count-container [view st/tag-count-container
[text {:style st/tag-count} count]]] [text {:style st/tag-count
[list-view {:dataSource (to-datasource discoveries) :platform-specific platform-specific
:enableEmptySections true :font :default}
:renderRow render-row count]]]
:renderSeparator render-separator (for [{:keys [msg-id] :as discovery} discoveries]
:style st/popular-list}]]) ^{:key (str "message-" msg-id)}
[discovery-list-item discovery platform-specific])])

View File

@ -1,15 +0,0 @@
(ns status-im.discovery.views.popular-list-item
(:require [status-im.components.react :refer [view text image]]
[status-im.discovery.styles :as st]
[reagent.core :as r]))
(defn popular-list-item
[{:keys [name status]}]
[view st/popular-list-item
[view st/popular-list-item-name-container
[text {:style st/popular-list-item-name} name]
[text {:style st/popular-list-item-status
:numberOfLines 2} status]]
[view st/popular-list-item-avatar-container
[image {:style st/popular-list-item-avatar
:source {:uri :icon_avatar}}]]])

View File

@ -5,19 +5,15 @@
[status-im.components.react :refer [view list-view list-item]] [status-im.components.react :refer [view list-view list-item]]
[status-im.utils.listview :refer [to-datasource]] [status-im.utils.listview :refer [to-datasource]]
[status-im.discovery.styles :as st] [status-im.discovery.styles :as st]
[status-im.discovery.views.popular-list-item [status-im.discovery.views.discovery-list-item :refer [discovery-list-item]]))
:refer [popular-list-item]]))
(defn render-row [row _ _]
(list-item [popular-list-item row]))
(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}]))
(defview discovery-recent [] (defview discovery-recent [{platform-specific :platform-specific}]
[discoveries [:get :discoveries]] [discoveries [:get :discoveries]]
[list-view {:dataSource (to-datasource discoveries) [view st/recent-list
:renderRow render-row (for [{:keys [msg-id] :as discovery} discoveries]
:renderSeparator render-separator ^{:key (str "message-" msg-id)}
:style st/recent-list}]) [discovery-list-item discovery platform-specific])])

View File

@ -22,7 +22,7 @@
(let [property (db-name db)] (let [property (db-name db)]
(r/write :account (r/write :account
(fn [] (fn []
(-> (r/get-by-field :account :chats :chat-id current-chat-id) (-> (r/get-by-field :account :chat :chat-id current-chat-id)
(r/single) (r/single)
(aset (name property-name) property))))))) (aset (name property-name) property)))))))
@ -80,7 +80,7 @@
(r/write :account (r/write :account
(fn [] (fn []
(r/create :account (r/create :account
:chats :chat
(update chat :contacts remove-identities selected-participants) (update chat :contacts remove-identities selected-participants)
true))))) true)))))

View File

@ -22,7 +22,8 @@
status-im.commands.handlers.jail status-im.commands.handlers.jail
status-im.qr-scanner.handlers status-im.qr-scanner.handlers
status-im.accounts.handlers status-im.accounts.handlers
status-im.protocol.handlers)) status-im.protocol.handlers
[status-im.utils.datetime :as time]))
;; -- Middleware ------------------------------------------------------------ ;; -- Middleware ------------------------------------------------------------
;; ;;
@ -72,7 +73,10 @@
(dispatch [:initialize-account-db]) (dispatch [:initialize-account-db])
(dispatch [:initialize-chats]) (dispatch [:initialize-chats])
(dispatch [:load-contacts]) (dispatch [:load-contacts])
(dispatch [:init-chat])))) (dispatch [:init-chat])
(dispatch [:init-discoveries])
(dispatch [:send-account-update-if-needed])
(dispatch [:remove-old-discoveries!]))))
(register-handler :reset-app (register-handler :reset-app
(u/side-effect! (u/side-effect!

View File

@ -12,7 +12,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.screen :refer [discovery]]
[status-im.discovery.tag :refer [discovery-tag]] [status-im.discovery.tag :refer [discovery-tag]]
[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]]

View File

@ -2,11 +2,11 @@
(:require [status-im.persistence.realm.core :as r])) (:require [status-im.persistence.realm.core :as r]))
(defn get-accounts [] (defn get-accounts []
(-> (r/get-all :base :accounts) (-> (r/get-all :base :account)
r/collection->map)) r/collection->map))
(defn save-account [update?] (defn save-account [update?]
#(r/create :base :accounts % update?)) #(r/create :base :account % update?))
(defn save-accounts [accounts update?] (defn save-accounts [accounts update?]
(r/write :base #(mapv (save-account update?) accounts))) (r/write :base #(mapv (save-account update?) accounts)))
@ -15,7 +15,7 @@
;;;;;;;;;;;;;;;;;;;;---------------------------------------------- ;;;;;;;;;;;;;;;;;;;;----------------------------------------------
(defn accounts-list [] (defn accounts-list []
(r/get-all :base :accounts)) (r/get-all :base :account))
(defn account-by-address [address] (defn account-by-address [address]
(r/single-cljs (r/get-by-field :base :accounts :address address))) (r/single-cljs (r/get-by-field :base :account :address address)))

View File

@ -12,7 +12,7 @@
(defn chat-name-from-contacts [identities] (defn chat-name-from-contacts [identities]
(let [chat-name (->> identities (let [chat-name (->> identities
(map (fn [identity] (map (fn [identity]
(-> (r/get-by-field :account :contacts :whisper-identity identity) (-> (r/get-by-field :account :contact :whisper-identity identity)
(r/single-cljs) (r/single-cljs)
:name))) :name)))
(filter identity) (filter identity)
@ -25,7 +25,7 @@
chat-id)) chat-id))
(defn chat-exists? [chat-id] (defn chat-exists? [chat-id]
(r/exists? :account :chats :chat-id chat-id)) (r/exists? :account :chat :chat-id chat-id))
(defn add-status-message [chat-id] (defn add-status-message [chat-id]
;; TODO Get real status ;; TODO Get real status
@ -42,7 +42,7 @@
(defn create-chat (defn create-chat
([{:keys [last-msg-id] :as chat}] ([{:keys [last-msg-id] :as chat}]
(let [chat (assoc chat :last-msg-id (or last-msg-id ""))] (let [chat (assoc chat :last-msg-id (or last-msg-id ""))]
(r/write :account #(r/create :account :chats chat)))) (r/write :account #(r/create :account :chat chat))))
([db chat-id identities group-chat? chat-name] ([db chat-id identities group-chat? chat-name]
(when-not (chat-exists? chat-id) (when-not (chat-exists? chat-id)
(let [chat-name (or chat-name (let [chat-name (or chat-name
@ -52,7 +52,7 @@
(fn [] (fn []
(let [contacts (mapv (fn [ident] (let [contacts (mapv (fn [ident]
{:identity ident}) identities)] {:identity ident}) identities)]
(r/create :account :chats (r/create :account :chat
{:chat-id chat-id {:chat-id chat-id
:is-active true :is-active true
:name chat-name :name chat-name
@ -63,7 +63,7 @@
(add-status-message chat-id))))) (add-status-message chat-id)))))
(defn chat-contacts [chat-id] (defn chat-contacts [chat-id]
(-> (r/get-by-field :account :chats :chat-id chat-id) (-> (r/get-by-field :account :chat :chat-id chat-id)
(r/single) (r/single)
(aget "contacts"))) (aget "contacts")))
@ -79,7 +79,7 @@
(mapv (fn [ident] (mapv (fn [ident]
{:identity ident})) {:identity ident}))
(concat only-old-contacts))] (concat only-old-contacts))]
(r/create :account :chats (r/create :account :chat
{:chat-id group-id {:chat-id group-id
:is-active true :is-active true
:name group-name :name group-name
@ -91,18 +91,18 @@
(map #(update % :contacts vals) chats)) (map #(update % :contacts vals) chats))
(defn chats-list [] (defn chats-list []
(-> (r/get-all :account :chats) (-> (r/get-all :account :chat)
(r/sorted :timestamp :desc) (r/sorted :timestamp :desc)
r/collection->map r/collection->map
normalize-contacts)) normalize-contacts))
(defn chat-by-id [chat-id] (defn chat-by-id [chat-id]
(-> (r/get-by-field :account :chats :chat-id chat-id) (-> (r/get-by-field :account :chat :chat-id chat-id)
(r/single-cljs) (r/single-cljs)
(r/list-to-array :contacts))) (r/list-to-array :contacts)))
(defn chat-by-id2 [chat-id] (defn chat-by-id2 [chat-id]
(-> (r/get-by-field :account :chats :chat-id chat-id) (-> (r/get-by-field :account :chat :chat-id chat-id)
r/collection->map r/collection->map
first)) first))
@ -123,14 +123,14 @@
(r/write :account (r/write :account
(fn [] (fn []
(let [query (include-query :identity identities) (let [query (include-query :identity identities)
chat (r/single (r/get-by-field :account :chats :chat-id chat-id))] chat (r/single (r/get-by-field :account :chat :chat-id chat-id))]
(-> (aget chat "contacts") (-> (aget chat "contacts")
(r/filtered query) (r/filtered query)
(.forEach (fn [object _ _] (.forEach (fn [object _ _]
(aset object "is-in-chat" false)))))))) (aset object "is-in-chat" false))))))))
(defn active-group-chats [] (defn active-group-chats []
(let [results (r/filtered (r/get-all :account :chats) (let [results (r/filtered (r/get-all :account :chat)
"group-chat = true && is-active = true")] "group-chat = true && is-active = true")]
(js->clj (.map results (fn [object _ _] (js->clj (.map results (fn [object _ _]
(aget object "chat-id")))))) (aget object "chat-id"))))))
@ -138,6 +138,6 @@
(defn set-chat-active [chat-id active?] (defn set-chat-active [chat-id active?]
(r/write :account (r/write :account
(fn [] (fn []
(-> (r/get-by-field :account :chats :chat-id chat-id) (-> (r/get-by-field :account :chat :chat-id chat-id)
(r/single) (r/single)
(aset "is-active" active?))))) (aset "is-active" active?)))))

View File

@ -5,18 +5,14 @@
exclude-query]])) exclude-query]]))
(defn get-contacts [] (defn get-contacts []
(-> (r/get-all :account :contacts) (-> (r/get-all :account :contact)
(r/sorted :name :asc) (r/sorted :name :asc)
r/collection->map)) r/collection->map))
(defn create-contact [{:keys [name photo-path whisper-identity] :as contact}] (defn create-contact [{:keys [whisper-identity] :as contact}]
(let [contact-from-db (r/get-one-by-field :account :contacts (let [contact-from-db (r/get-one-by-field :account :contact
:whisper-identity whisper-identity)] :whisper-identity whisper-identity)]
(when-not contact-from-db (r/create :account :contact contact (if contact-from-db true false))))
(->> {:name (or name "")
:photo-path (or photo-path (identicon whisper-identity))}
(merge contact)
(r/create :account :contacts)))))
(defn save-contacts [contacts] (defn save-contacts [contacts]
(r/write :account #(mapv create-contact contacts))) (r/write :account #(mapv create-contact contacts)))
@ -25,13 +21,13 @@
;;;;;;;;;;;;;;;;;;;;---------------------------------------------- ;;;;;;;;;;;;;;;;;;;;----------------------------------------------
(defn contacts-list [] (defn contacts-list []
(r/sorted (r/get-all :account :contacts) :name :asc)) (r/sorted (r/get-all :account :contact) :name :asc))
(defn contacts-list-exclude [exclude-idents] (defn contacts-list-exclude [exclude-idents]
(if (empty? exclude-idents) (if (empty? exclude-idents)
(contacts-list) (contacts-list)
(let [query (exclude-query :whisper-identity exclude-idents)] (let [query (exclude-query :whisper-identity exclude-idents)]
(-> (r/get-all :account :contacts) (-> (r/get-all :account :contact)
(r/filtered query) (r/filtered query)
(r/sorted :name :asc))))) (r/sorted :name :asc)))))
@ -39,9 +35,9 @@
(if (empty? include-indents) (if (empty? include-indents)
() ()
(let [query (include-query :whisper-identity include-indents)] (let [query (include-query :whisper-identity include-indents)]
(-> (r/get-all :account :contacts) (-> (r/get-all :account :contact)
(r/filtered query) (r/filtered query)
(r/sorted :name :asc))))) (r/sorted :name :asc)))))
(defn contact-by-identity [identity] (defn contact-by-identity [identity]
(r/single-cljs (r/get-by-field :account :contacts :whisper-identity identity))) (r/single-cljs (r/get-by-field :account :contact :whisper-identity identity)))

View File

@ -29,7 +29,7 @@
[chat-id {:keys [delivery-status msg-id content] [chat-id {:keys [delivery-status msg-id content]
:or {delivery-status :pending} :or {delivery-status :pending}
:as message}] :as message}]
(when-not (r/exists? :account :msgs :msg-id msg-id) (when-not (r/exists? :account :message :msg-id msg-id)
(r/write :account (r/write :account
(fn [] (fn []
(let [content' (if (string? content) (let [content' (if (string? content)
@ -41,7 +41,7 @@
:content content' :content content'
:delivery-status delivery-status :delivery-status delivery-status
:timestamp (timestamp)})] :timestamp (timestamp)})]
(r/create :account :msgs message' true)))))) (r/create :account :message message' true))))))
(defn command-type? [type] (defn command-type? [type]
(contains? (contains?
@ -51,7 +51,7 @@
(defn get-messages (defn get-messages
([chat-id] (get-messages chat-id 0)) ([chat-id] (get-messages chat-id 0))
([chat-id from] ([chat-id from]
(->> (-> (r/get-by-field :account :msgs :chat-id chat-id) (->> (-> (r/get-by-field :account :message :chat-id chat-id)
(r/sorted :timestamp :desc) (r/sorted :timestamp :desc)
(r/page from (+ from c/default-number-of-messages)) (r/page from (+ from c/default-number-of-messages))
(r/collection->map)) (r/collection->map))
@ -66,5 +66,5 @@
(log/debug "update-message!" msg) (log/debug "update-message!" msg)
(r/write :account (r/write :account
(fn [] (fn []
(when (r/exists? :account :msgs :msg-id msg-id) (when (r/exists? :account :message :msg-id msg-id)
(r/create :account :msgs msg true))))) (r/create :account :message msg true)))))

View File

@ -2,11 +2,11 @@
(:require [status-im.persistence.realm.core :as r])) (:require [status-im.persistence.realm.core :as r]))
(defn get-requests [] (defn get-requests []
(-> (r/get-all :account :requests) (-> (r/get-all :account :request)
r/collection->map)) r/collection->map))
(defn create-request [request] (defn create-request [request]
(r/create :account :requests request true)) (r/create :account :request request true))
(defn save-request [request] (defn save-request [request]
(r/write :account (r/write :account
@ -17,5 +17,5 @@
(r/write :account #(mapv create-request requests))) (r/write :account #(mapv create-request requests)))
(defn requests-list [] (defn requests-list []
(r/get-all :account :requests)) (r/get-all :account :request))

View File

@ -1,99 +1,98 @@
(ns status-im.persistence.realm.schemas (ns status-im.persistence.realm.schemas
(:require [status-im.components.styles :refer [default-chat-color]])) (:require [status-im.components.styles :refer [default-chat-color]]))
(def base {:schema [{:name :accounts (def base {:schema [{:name :account
:primaryKey :address :primaryKey :address
:properties {:address "string" :properties {:address "string"
:public-key "string" :public-key "string"
:name "string" :name {:type "string" :optional true}
:phone {:type "string" :optional true} :phone {:type "string" :optional true}
:email {:type "string" :optional true} :email {:type "string" :optional true}
:status {:type "string" :optional true} :status {:type "string" :optional true}
:photo-path "string"}} :photo-path "string"
{:name :tag :last-updated {:type "int" :default 0}}}
:primaryKey :name
:properties {:name "string"
:count {:type "int"
:optional true
:default 0}}}
{:name :discoveries
:primaryKey :whisper-id
:properties {:name "string"
:status "string"
:whisper-id "string"
:photo "string"
:location "string"
:tags {:type "list"
:objectType "tag"}
:last-updated "date"}}
{:name :kv-store {:name :kv-store
:primaryKey :key :primaryKey :key
:properties {:key "string" :properties {:key "string"
:value "string"}}] :value "string"}}]
:schemaVersion 0}) :schemaVersion 0})
(def account {:schema [{:name :contacts (def account {:schema [{:name :contact
:primaryKey :whisper-identity :primaryKey :whisper-identity
:properties {:phone-number {:type "string" :properties {:address {:type "string" :optional true}
:optional true} :whisper-identity "string"
:whisper-identity "string" :name {:type "string" :optional true}
:name {:type "string" :photo-path {:type "string" :optional true}
:optional true} :last-updated {:type "int" :default 0}
:photo-path {:type "string" :last-online {:type "int" :default 0}}}
:optinal true}}} {:name :request
{:name :requests :properties {:message-id :string
:properties {:message-id :string :chat-id :string
:chat-id :string :type :string
:type :string :status {:type :string
:status {:type :string :default "open"}
:default "open"} :added :date}}
:added :date}} {:name :tag
{:name :kv-store :primaryKey :name
:primaryKey :key :properties {:name "string"
:properties {:key "string" :count {:type "int" :optional true :default 0}}}
:value "string"}} {:name :discovery
{:name :msgs :primaryKey :msg-id
:primaryKey :msg-id :properties {:msg-id "string"
:properties {:msg-id "string" :name {:type "string" :optional true}
:from "string" :status "string"
:to {:type "string" :whisper-id "string"
:optional true} :photo-path {:type "string" :optional true}
:content "string" ;; TODO make it ArrayBuffer :tags {:type "list"
:content-type "string" :objectType "tag"}
:timestamp "int" :priority {:type "int" :default 0}
:chat-id {:type "string" :last-updated "date"}}
:indexed true} {:name :kv-store
:outgoing "bool" :primaryKey :key
:delivery-status {:type "string" :properties {:key "string"
:optional true} :value "string"}}
:same-author "bool" {:name :message
:same-direction "bool" :primaryKey :msg-id
:preview {:type :string :properties {:msg-id "string"
:optional true}}} :from "string"
{:name :chat-contact :to {:type "string"
:properties {:identity "string" :optional true}
:is-in-chat {:type "bool" :content "string" ;; TODO make it ArrayBuffer
:default true}}} :content-type "string"
{:name :chats :timestamp "int"
:primaryKey :chat-id :chat-id {:type "string"
:properties {:chat-id "string" :indexed true}
:name "string" :outgoing "bool"
:color {:type "string" :delivery-status {:type "string"
:default default-chat-color} :optional true}
:group-chat {:type "bool" :same-author "bool"
:indexed true} :same-direction "bool"
:is-active "bool" :preview {:type :string
:timestamp "int" :optional true}}}
:contacts {:type "list" {:name :chat-contact
:objectType "chat-contact"} :properties {:identity "string"
:dapp-url {:type :string :is-in-chat {:type "bool"
:optional true} :default true}}}
:dapp-hash {:type :int {:name :chat
:optional true} :primaryKey :chat-id
:last-msg-id "string"}} :properties {:chat-id "string"
{:name :commands :name "string"
:primaryKey :chat-id :color {:type "string"
:properties {:chat-id "string" :default default-chat-color}
:file "string"}}] :group-chat {:type "bool"
:indexed true}
:is-active "bool"
:timestamp "int"
:contacts {:type "list"
:objectType "chat-contact"}
:dapp-url {:type :string
:optional true}
:dapp-hash {:type :int
:optional true}
:last-msg-id "string"}}
{:name :command
:primaryKey :chat-id
:properties {:chat-id "string"
:file "string"}}]
:schemaVersion 0}) :schemaVersion 0})

View File

@ -4,7 +4,36 @@
[status-im.components.react :refer [show-image-picker]] [status-im.components.react :refer [show-image-picker]]
[status-im.utils.image-processing :refer [img->base64]] [status-im.utils.image-processing :refer [img->base64]]
[status-im.i18n :refer [label]] [status-im.i18n :refer [label]]
[status-im.utils.handlers :as u])) [status-im.utils.handlers :as u]
[clojure.string :as str]))
(defn get-hashtags [status]
(let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))]
(or hashtags [])))
(defn message-user [identity]
(when identity
(dispatch [:navigate-to :chat identity])))
(defn update-profile [{name :name
email :email
photo-path :photo-path
status :status}
{new-name :name
new-email :email
new-status :status
new-photo-path :photo-path}]
(let [new-name (if (or (not new-name) (str/blank? new-name)) name new-name)
status-updated? (and (not= new-status nil)
(not= status new-status))]
(when status-updated?
(let [hashtags (get-hashtags new-status)]
(when-not (empty? hashtags)
(dispatch [:broadcast-status new-status hashtags]))))
(dispatch [:account-update {:name new-name
:email (or new-email email)
:status (or new-status status)
:photo-path (or new-photo-path photo-path)}])))
(register-handler :open-image-picker (register-handler :open-image-picker
(u/side-effect! (u/side-effect!

View File

@ -16,6 +16,9 @@
my-profile-icon]] my-profile-icon]]
[status-im.components.status-bar :refer [status-bar]] [status-im.components.status-bar :refer [status-bar]]
[status-im.profile.styles :as st] [status-im.profile.styles :as st]
[status-im.profile.handlers :refer [get-hashtags
message-user
update-profile]]
[status-im.components.qr-code :refer [qr-code]] [status-im.components.qr-code :refer [qr-code]]
[status-im.utils.phone-number :refer [format-phone-number [status-im.utils.phone-number :refer [format-phone-number
valid-mobile-number?]] valid-mobile-number?]]
@ -25,32 +28,6 @@
[status-im.i18n :refer [label]] [status-im.i18n :refer [label]]
[clojure.string :as str])) [clojure.string :as str]))
(defn- get-hashtags [status]
(let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))]
(or hashtags [])))
(defn- message-user [identity]
(when identity
(dispatch [:navigate-to :chat identity])))
(defn- update-profile [{name :name
email :email
photo-path :photo-path
status :status}
{new-name :name
new-email :email
new-status :status
new-photo-path :photo-path}]
(let [new-name (if (or (not new-name) (str/blank? new-name)) name new-name)
status-updated? (and (not= new-status nil)
(not= status new-status))]
(when status-updated?
(dispatch [:broadcast-status new-status (get-hashtags new-status)]))
(dispatch [:account-update {:name new-name
:email (or new-email email)
:status (or new-status status)
:photo-path (or new-photo-path photo-path)}])))
(defview toolbar [{:keys [account profile-edit-data edit?]}] (defview toolbar [{:keys [account profile-edit-data edit?]}]
[view [view
[touchable-highlight {:style st/back-btn-touchable [touchable-highlight {:style st/back-btn-touchable
@ -74,7 +51,8 @@
:style st/ok-btn-icon}] :style st/ok-btn-icon}]
[icon :dots st/edit-btn-icon])]]]) [icon :dots st/edit-btn-icon])]]])
(defview status-image-view [{{:keys [list-selection-fn]} :platform-specific (defview status-image-view [{{:keys [list-selection-fn]
:as platform-specific} :platform-specific
{address :address {address :address
username :name} :account username :name} :account
photo-path :photo-path photo-path :photo-path
@ -122,19 +100,35 @@
:on-change-text on-change-text} :on-change-text on-change-text}
(or value (when-not edit-mode? empty-value))]]]) (or value (when-not edit-mode? empty-value))]]])
(defview profile [] (defview profile [{platform-specific :platform-specific}]
[{:keys [name whisper-identity phone-number]} [:contact]] [{whisper-identity :whisper-identity
address :address
username :name
email :email
photo-path :photo-path
phone :phone
status :status
:as contact} [:contact]]
[scroll-view {:style st/profile} [scroll-view {:style st/profile}
[touchable-highlight {:style st/back-btn-touchable [status-bar {:platform-specific platform-specific}]
:on-press #(dispatch [:navigate-back])} [view
[view st/back-btn-container [touchable-highlight {:style st/back-btn-touchable
[icon :back st/back-btn-icon]]] :on-press (fn []
(dispatch [:navigate-back]))}
[view st/back-btn-container
[icon :back st/back-btn-icon]]]
[touchable-highlight {:style st/actions-btn-touchable
:on-press (fn []
(.log js/console "Dots pressed!"))}
[view st/actions-btn-container
[icon :dots st/edit-btn-icon]]]]
[status-image-view {:platform-specific platform-specific
:account contact
:photo-path photo-path
:edit? false}]
[view st/status-block [view st/status-block
[view st/user-photo-container
[profile-icon]]
[text {:style st/username} name]
;; TODO stub data
[text {:style st/status-input} (label :t/not-implemented)]
[view st/btns-container [view st/btns-container
[touchable-highlight {:onPress #(message-user whisper-identity)} [touchable-highlight {:onPress #(message-user whisper-identity)}
[view st/message-btn [view st/message-btn
@ -144,14 +138,23 @@
)} )}
[view st/more-btn [view st/more-btn
[icon :more_vertical_blue st/more-btn-image]]]]] [icon :more_vertical_blue st/more-btn-image]]]]]
[view st/profile-properties-container
[profile-property-view {:name (label :t/username) [scroll-view st/profile-properties-container
:value name}] [profile-property-view {:name (label :t/username)
[profile-property-view {:name (label :t/phone-number) :value (if (not= username address)
:value phone-number}] username)
;; TODO stub data :empty-value (label :t/not-specified)
[profile-property-view {:name (label :t/email) :platform-specific platform-specific}]
:value (label :t/not-implemented)}] [profile-property-view {:name (label :t/phone-number)
:value (if-not (or (not phone) (str/blank? phone))
(format-phone-number phone))
:empty-value (label :t/not-specified)
:platform-specific platform-specific}]
[profile-property-view {:name (label :t/email)
:value (if-not (or (not email) (str/blank? email))
email)
:empty-value (label :t/not-specified)
:platform-specific platform-specific}]
[view st/report-user-container [view st/report-user-container
[touchable-highlight {:on-press (fn [] [touchable-highlight {:on-press (fn []
;; TODO not implemented ;; TODO not implemented
@ -180,7 +183,7 @@
[status-image-view {:platform-specific platform-specific [status-image-view {:platform-specific platform-specific
:account account :account account
:photo-path (or new-photo-path photo-path) :photo-path (or new-photo-path photo-path)
:status (if (and new-status (not (str/blank? new-status))) new-status status) :status (or new-status status)
:edit? edit?}] :edit? edit?}]
[scroll-view st/profile-properties-container [scroll-view st/profile-properties-container
@ -205,6 +208,6 @@
:profile-data profile-edit-data :profile-data profile-edit-data
:platform-specific platform-specific}] :platform-specific platform-specific}]
[view st/qr-code-container [view st/qr-code-container
[qr-code {:value (clj->js {:name username ;; TODO: this public key should be replaced by address
:whisper-identity public-key}) [qr-code {:value (str "ethereum:" public-key)
:size 150}]]]]) :size 220}]]]])

View File

@ -45,4 +45,8 @@
(dispatch [:participant-left-group from group-id msg-id])) (dispatch [:participant-left-group from group-id msg-id]))
:discover-response (let [{:keys [from payload]} event] :discover-response (let [{:keys [from payload]} event]
(dispatch [:discovery-response-received from payload])) (dispatch [:discovery-response-received from payload]))
:contact-update (let [{:keys [from payload]} event]
(dispatch [:contact-update-received from payload]))
:contact-online (let [{:keys [from payload]} event]
(dispatch [:contact-online-received from payload]))
(log/info "Don't know how to handle" event-type)))}) (log/info "Don't know how to handle" event-type)))})

View File

@ -10,7 +10,8 @@
[status-im.components.toolbar :refer [toolbar]] [status-im.components.toolbar :refer [toolbar]]
[status-im.qr-scanner.styles :as st] [status-im.qr-scanner.styles :as st]
[status-im.utils.types :refer [json->clj]] [status-im.utils.types :refer [json->clj]]
[status-im.components.styles :as cst])) [status-im.components.styles :as cst]
[clojure.string :as str]))
(defn qr-scanner-toolbar [title platform-specific] (defn qr-scanner-toolbar [title platform-specific]
[view [view
@ -25,9 +26,10 @@
[identifier [:get :current-qr-context]] [identifier [:get :current-qr-context]]
[view st/barcode-scanner-container [view st/barcode-scanner-container
[qr-scanner-toolbar (:toolbar-title identifier) platform-specific] [qr-scanner-toolbar (:toolbar-title identifier) platform-specific]
[camera {;:on-bar-code-read #(js/alert "ok") [camera {:onBarCodeRead (fn [code]
:onBarCodeRead #(let [data (json->clj (.-data %))] (let [data (-> (.-data code)
(dispatch [:set-qr-code identifier data])) (str/replace #"ethereum:" ""))]
(dispatch [:set-qr-code identifier data])))
:style st/barcode-scanner}] :style st/barcode-scanner}]
[view st/rectangle-container [view st/rectangle-container
[view st/rectangle [view st/rectangle

View File

@ -20,7 +20,21 @@
:members {:one "1 member, 1 active" :members {:one "1 member, 1 active"
:other "{{count}} members, {{count}} active" :other "{{count}} members, {{count}} active"
:zero "no members"} :zero "no members"}
:last-active "Active a minute ago" :active-online "online"
:active-unknown "unknown"
;datetime
:datetime-second {:one "second"
:other "seconds"}
:datetime-minute {:one "minute"
:other "minutes"}
:datetime-hour {:one "hour"
:other "hours"}
:datetime-day {:one "day"
:other "days"}
:datetime-multiple "s"
:datetime-ago "ago"
:datetime-yesterday "yesterday"
;profile ;profile
:profile "Profile" :profile "Profile"
@ -80,7 +94,6 @@
;contacts ;contacts
:contacts "Contacts" :contacts "Contacts"
:no-name "Noname"
:new-contact "New Contact" :new-contact "New Contact"
:show-all "SHOW ALL" :show-all "SHOW ALL"
:contacs-group-dapps "Dapps" :contacs-group-dapps "Dapps"

View File

@ -1,9 +1,18 @@
(ns status-im.utils.datetime (ns status-im.utils.datetime
(:require [cljs-time.core :as t :refer [date-time now plus days hours before?]] (:require [cljs-time.core :as t :refer [date-time now plus days hours before?]]
[cljs-time.coerce :refer [from-long to-long]] [cljs-time.coerce :refer [from-long to-long]]
[cljs-time.format :as format :refer [formatters [cljs-time.format :refer [formatters
formatter formatter
unparse]])) unparse]]
[status-im.i18n :refer [label label-pluralize]]))
(def hour (* 1000 60 60))
(def day (* hour 24))
(def week (* 7 day))
(def units [{:name (label :t/datetime-second) :limit 60 :in-second 1}
{:name (label :t/datetime-minute) :limit 3600 :in-second 60}
{:name (label :t/datetime-hour) :limit 86400 :in-second 3600}
{:name (label :t/datetime-day) :limit nil :in-second 86400}])
(def time-zone-offset (hours (- (/ (.getTimezoneOffset (js/Date.)) 60)))) (def time-zone-offset (hours (- (/ (.getTimezoneOffset (js/Date.)) 60))))
@ -17,8 +26,23 @@
yesterday (plus today (days -1))] yesterday (plus today (days -1))]
(cond (cond
(before? local yesterday) (unparse (formatter "dd MMM") local) (before? local yesterday) (unparse (formatter "dd MMM") local)
(before? local today) "Yesterday" (before? local today) (label :t/datetime-yesterday)
:else (unparse (formatters :hour-minute) local)))) :else (unparse (formatters :hour-minute) local))))
(defn time-ago [time]
(let [diff (t/in-seconds (t/interval time (t/now)))]
(if (< diff 60)
(label :t/active-online)
(let [unit (first (drop-while #(or (>= diff (:limit %))
(not (:limit %)))
units))]
(-> (/ diff (:in-second unit))
Math/floor
int
(#(str % " " (label-pluralize % (:name unit)) " " (label :t/datetime-ago))))))))
(defn to-date [ms]
(from-long ms))
(defn now-ms [] (defn now-ms []
(to-long (now))) (to-long (now)))

View File

@ -55,3 +55,13 @@
(if (and (< max (count s)) s) (if (and (< max (count s)) s)
(str (subs s 0 (- max 3)) "...") (str (subs s 0 (- max 3)) "...")
s)) s))
(defn first-index
[cond coll]
(loop [index 0
cond cond
coll coll]
(when (seq coll)
(if (cond (first coll))
index
(recur (inc index) cond (next coll))))))