diff --git a/project.clj b/project.clj index 6c28586918..d38df4f8e7 100644 --- a/project.clj +++ b/project.clj @@ -9,8 +9,8 @@ [re-frame "0.7.0"] [prismatic/schema "1.0.4"] ^{:voom {:repo "git@github.com:status-im/status-lib.git" - :branch "discover-rework"}} - [status-im/protocol "0.1.1-20160706_085008-ge61756a"] + :branch "discover-rework"}} + [status-im/protocol "0.1.3-20160818_085900-gda79e8e"] [natal-shell "0.3.0"] [com.andrewmcveigh/cljs-time "0.4.0"]] :plugins [[lein-cljsbuild "1.1.1"] diff --git a/src/status_im/accounts/handlers.cljs b/src/status_im/accounts/handlers.cljs index 99a4e0308f..42d8f1803e 100644 --- a/src/status_im/accounts/handlers.cljs +++ b/src/status_im/accounts/handlers.cljs @@ -2,6 +2,7 @@ (:require [status-im.models.accounts :as accounts] [re-frame.core :refer [register-handler after dispatch dispatch-sync debug]] [status-im.utils.logging :as log] + [status-im.protocol.api :as api] [status-im.components.geth :as geth] [status-im.utils.types :refer [json->clj]] [status-im.persistence.simple-kv-store :as kv] @@ -13,11 +14,13 @@ [status-im.i18n :refer [label]] [status-im.constants :refer [content-type-command-request]] 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]] - (accounts/save-accounts [account] false)) + (accounts/save-accounts [account] true)) (register-handler :add-account @@ -28,14 +31,14 @@ (defn save-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) public-key (:pubkey data) address (:address data) account {:public-key public-key :address address :name address - :photo-path (identicon address)}] + :photo-path (identicon public-key)}] (log/debug "account-created: " account) (when (not (str/blank? public-key)) (do @@ -45,17 +48,37 @@ (register-handler :create-account (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)) +(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 :account-update - (fn [db [_ data]] - (let [current-account-id (get db :current-account-id) - account (-> (get-in db [:accounts current-account-id]) - (merge data))] - (accounts/save-accounts [account] true) - (assoc-in db [:accounts current-account-id] account)))) + (-> (fn [{:keys [current-account-id accounts] :as db} [_ data]] + (let [data (assoc data :last-updated (time/now-ms)) + account (-> (get accounts current-account-id) + (merge data))] + (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] (let [is-login-screen? (= (:view-id db) :login)] diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs index 76ecddabc4..0342c7cb8e 100644 --- a/src/status_im/android/core.cljs +++ b/src/status_im/android/core.cljs @@ -14,7 +14,6 @@ [status-im.contacts.views.contact-list :refer [contact-list]] [status-im.contacts.views.new-contact :refer [new-contact]] [status-im.qr-scanner.screen :refer [qr-scanner]] - [status-im.discovery.screen :refer [discovery]] [status-im.discovery.tag :refer [discovery-tag]] [status-im.chat.screen :refer [chat]] [status-im.accounts.login.screen :refer [login]] diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index db2d0a76d1..dfc396238b 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -479,13 +479,13 @@ [{:keys [current-chat-id]} _] (r/write :account (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! [{:keys [current-chat-id]} _] (r/write :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/delete :account))))) @@ -550,3 +550,9 @@ j/adjust-resize))))) (fn [db [_ 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))) diff --git a/src/status_im/chat/handlers/requests.cljs b/src/status_im/chat/handlers/requests.cljs index a536ac946e..000ad07b8a 100644 --- a/src/status_im/chat/handlers/requests.cljs +++ b/src/status_im/chat/handlers/requests.cljs @@ -24,7 +24,7 @@ [{:keys [current-chat-id] :as db} [_ chat-id]] (let [chat-id' (or chat-id current-chat-id) requests (-> ;; todo maybe limit is needed - (realm/get-by-fields :account :requests + (realm/get-by-fields :account :request {:chat-id chat-id' :status "open"}) (realm/sorted :added :desc) @@ -36,7 +36,7 @@ [_ [_ chat-id message-id]] (realm/write :account (fn [] - (-> (realm/get-by-fields :account :requests + (-> (realm/get-by-fields :account :request {:chat-id chat-id :message-id message-id}) (realm/single) diff --git a/src/status_im/chat/handlers/unviewed_messages.cljs b/src/status_im/chat/handlers/unviewed_messages.cljs index 539c8cc197..4b0cffe093 100644 --- a/src/status_im/chat/handlers/unviewed_messages.cljs +++ b/src/status_im/chat/handlers/unviewed_messages.cljs @@ -5,7 +5,7 @@ (defn delivered-messages [] (-> (realm/get-by-fields - :account :msgs + :account :message {:delivery-status :delivered :outgoing false}) (realm/collection->map))) diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index af1aa1993e..e2fc084f96 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -15,6 +15,7 @@ [status-im.chat.styles.screen :as st] [status-im.utils.listview :refer [to-datasource-inverted]] [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.toolbar :refer [toolbar]] [status-im.chat.views.message :refer [chat-message]] @@ -23,7 +24,8 @@ [status-im.chat.views.new-message :refer [chat-message-new]] [status-im.i18n :refer [label label-pluralize]] [status-im.components.animation :as anim] - [reagent.core :as r])) + [reagent.core :as r] + [clojure.string :as str])) (defn contacts-by-identity [contacts] @@ -191,16 +193,27 @@ [overlay {:on-click-outside #(dispatch [:set-show-actions false])} [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] - (let [{:keys [group-chat name contacts]} - (subscribe [:chat-properties [:group-chat :name :contacts]]) + (let [{:keys [group-chat chat-id name contacts]} (subscribe [:chat-properties [:group-chat :chat-id :name :contacts]]) + contact (subscribe [:get-in [:contacts @chat-id]]) show-actions (subscribe [:show-actions])] (fn [] [view (st/chat-name-view @show-actions) [text {:style st/chat-name-text :platform-specific platform-specific :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 [view {:flexDirection :row} [icon :group st/group-icon] @@ -209,11 +222,10 @@ :font :medium} (let [cnt (inc (count @contacts))] (label-pluralize cnt :t/members))]] - ;; TODO stub data: last activity [text {:style st/last-activity :platform-specific platform-specific :font :default} - (label :t/last-active)])]))) + (online-text @contact @chat-id)])]))) (defn toolbar-action [] (let [show-actions (subscribe [:show-actions])] diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index b181b3b2a9..076442358c 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -57,7 +57,7 @@ :renderRow (fn [row _ _] (list-item [chat-list-item row])) :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 :onLayout (fn [event] (when-not @chats-scrolled? diff --git a/src/status_im/chats_list/views/chat_list_item.cljs b/src/status_im/chats_list/views/chat_list_item.cljs index a7877e5e82..97d4ad5ded 100644 --- a/src/status_im/chats_list/views/chat_list_item.cljs +++ b/src/status_im/chats_list/views/chat_list_item.cljs @@ -5,8 +5,7 @@ image touchable-highlight]] [status-im.components.styles :refer [font]] - [status-im.chats-list.views.inner-item :refer - [chat-list-item-inner-view]])) + [status-im.chats-list.views.inner-item :refer [chat-list-item-inner-view]])) (defn chat-list-item [[chat-id chat]] [touchable-highlight diff --git a/src/status_im/chats_list/views/inner_item.cljs b/src/status_im/chats_list/views/inner_item.cljs index b8d94902f4..64462c8a49 100644 --- a/src/status_im/chats_list/views/inner_item.cljs +++ b/src/status_im/chats_list/views/inner_item.cljs @@ -4,7 +4,9 @@ [status-im.components.chat-icon.screen :refer [chat-icon-view-chat-list]] [status-im.chats-list.styles :as st] [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 [{:keys [chat-id name color new-messages-count @@ -16,7 +18,9 @@ [chat-icon-view-chat-list chat-id group-chat name color online]] [view st/item-container [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 [icon :group st/group-icon]) (when group-chat diff --git a/src/status_im/commands/handlers/loading.cljs b/src/status_im/commands/handlers/loading.cljs index feeb9be155..569ed20866 100644 --- a/src/status_im/commands/handlers/loading.cljs +++ b/src/status_im/commands/handlers/loading.cljs @@ -15,7 +15,7 @@ [_ [identity]] (dispatch [::fetch-commands! identity]) ;; 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)] (dispatch [::parse-commands! identity file]) (dispatch [::fetch-commands! identity]))) @@ -73,7 +73,7 @@ (defn save-commands-js! [_ [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! [db [id reason details]] diff --git a/src/status_im/components/carousel/carousel.cljs b/src/status_im/components/carousel/carousel.cljs index 5f1bbc85dc..86eeb23ddf 100644 --- a/src/status_im/components/carousel/carousel.cljs +++ b/src/status_im/components/carousel/carousel.cljs @@ -12,8 +12,8 @@ (defn window-page-width [] (.-width (.get (.. r/react-native -Dimensions) "window"))) -(def defaults {:gap 10 - :sneak 10 +(def defaults {:gap 8 + :sneak 8 :pageStyle {} :scrollThreshold 20}) @@ -175,5 +175,4 @@ :component-did-update component-did-update :display-name "carousel" :reagent-render reagent-render}] - (log/debug "Creating carousel component: " data) (reagent.core/create-class component-data))) diff --git a/src/status_im/components/main_tabs.cljs b/src/status_im/components/main_tabs.cljs index a6bbd2a054..9c7f4d41c4 100644 --- a/src/status_im/components/main_tabs.cljs +++ b/src/status_im/components/main_tabs.cljs @@ -95,10 +95,10 @@ [animated-view {:style (st/tab-view-container anim-value)} content])}))) -(defn tab-view [{:keys [view-id screen]}] +(defn tab-view [platform-specific {:keys [view-id screen]}] ^{:key view-id} [tab-view-container view-id - [screen]]) + [screen {:platform-specific platform-specific}]]) (defview main-tabs [{platform-specific :platform-specific}] [view-id [:get :view-id] @@ -109,6 +109,6 @@ [drawer-view {:platform-specific platform-specific} [view {:style common-st/flex :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 :tab-list tab-list}]]]]]) diff --git a/src/status_im/components/tabs/styles.cljs b/src/status_im/components/tabs/styles.cljs index 30b6ab03d1..166345d2b4 100644 --- a/src/status_im/components/tabs/styles.cljs +++ b/src/status_im/components/tabs/styles.cljs @@ -10,7 +10,7 @@ text2-color toolbar-background1]])) -(def tabs-height 59) +(def tabs-height 60) (def tab-height 56) (defn tabs-container [hidden? animation? offset-y] @@ -67,5 +67,5 @@ :left 0 :right 0 :bottom 0 - :padding-bottom 59 + :padding-bottom 60 :transform [{:translateX offset-x}]}) diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index f61bed64d5..912d16e4d9 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -4,18 +4,26 @@ [status-im.models.contacts :as contacts] [status-im.utils.crypt :refer [encrypt]] [clojure.string :as s] + [status-im.protocol.api :as api] [status-im.utils.utils :refer [http-post]] [status-im.utils.phone-number :refer [format-phone-number]] [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 [_ [_ 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}]] - (update db :contacts assoc whisper-identity contact)) + (update-in db [:contacts whisper-identity] merge contact)) ((after save-contact)))) (defn load-contacts! [db _] @@ -23,6 +31,8 @@ (map (fn [{:keys [whisper-identity] :as contact}] [whisper-identity contact])) (into {}))] + (doseq [[_ contact] contacts] + (dispatch [:watch-contact contact])) (assoc db :contacts contacts))) (register-handler :load-contacts load-contacts!) @@ -108,17 +118,34 @@ (defn add-new-contact [db [_ {:keys [whisper-identity] :as contact}]] (-> db (update :contacts assoc whisper-identity contact) - (assoc :new-contact {:name "" - :address "" - :whisper-identity "" - :phone-number ""}))) + (assoc :new-contact-identity ""))) (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 - [{:keys [new-contact] :as db} [_ _ qr-contact]] - (assoc db :new-contact (merge new-contact qr-contact))) +(defn set-contact-identity-from-qr + [db [_ _ contact-identity]] + (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}])))))) diff --git a/src/status_im/contacts/validations.cljs b/src/status_im/contacts/validations.cljs index b10e27ccb6..ea78ecdaab 100644 --- a/src/status_im/contacts/validations.cljs +++ b/src/status_im/contacts/validations.cljs @@ -7,7 +7,7 @@ (.isAddress js/Web3.prototype s)) (defn unique-identity? [identity] - (not (realm/exists? :account :contacts :whisper-identity identity))) + (not (realm/exists? :account :contact :whisper-identity identity))) (defn valid-length? [identity] (let [length (count identity)] diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs index 4c27d5e1a1..5740fdcceb 100644 --- a/src/status_im/contacts/views/contact_inner.cljs +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -20,7 +20,7 @@ (if (pos? (count (:name contact))) name ;; todo is this correct behaviour? - (label :t/no-name))] + (label :t/user-anonymous))] (when info [text {:style st/info-text} info])]])) diff --git a/src/status_im/contacts/views/new_contact.cljs b/src/status_im/contacts/views/new_contact.cljs index 850d9c3995..2a58ed3c38 100644 --- a/src/status_im/contacts/views/new_contact.cljs +++ b/src/status_im/contacts/views/new_contact.cljs @@ -37,14 +37,32 @@ [text {:style toolbar-title-text} (label :t/add-new-contact)]]) -(defview contact-name-input [name] - [] - [text-field - {:error (if (str/blank? name) "" nil) - :errorColor "#7099e6" - :value name - :label (label :t/name) - :onChangeText #(dispatch [:set-in [:new-contact :name] %])}]) +(defn on-add-contact [id] + (if (v/is-address? id) + (http-post "get-contacts-by-address" {:addresses [id]} + (fn [{:keys [contacts]}] + (if (> (count contacts) 0) + (let [{:keys [whisper-identity]} (first contacts) + 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] [] @@ -53,48 +71,20 @@ error (label :t/enter-valid-address))] [view button-input-container - [text-field - {:error error - :errorColor "#7099e6" - :value whisper-identity - :wrapperStyle (merge button-input) - :label (label :t/address) - :onChangeText #(do - (dispatch [:set-in [:new-contact :whisper-identity] %]) - (dispatch [:set :new-contact-address-error nil]))}] - [scan-button {:showLabel (zero? (count whisper-identity)) - :handler #(dispatch [:scan-qr-code {:toolbar-title (label :t/new-contact)} :set-new-contact-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)))})) + [text-field + {:error error + :errorColor "#7099e6" + :value whisper-identity + :wrapperStyle (merge button-input) + :label (label :t/address) + :onChangeText #(do + (dispatch [:set-in [:new-contact-identity] %]) + (dispatch [:set :new-contact-address-error nil]))}] + [scan-button {:showLabel (zero? (count whisper-identity)) + :handler #(dispatch [:scan-qr-code {:toolbar-title (label :t/new-contact)} :set-contact-identity-from-qr])}]])) (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]] [view st/contact-form-container [view @@ -104,9 +94,11 @@ :style icon-back} :handler #(dispatch [:navigate-back])} :custom-content toolbar-title - :action (toolbar-action whisper-identity new-contact error)}]] + :action (toolbar-action new-contact-identity error)}]] [view st/form-container - [contact-name-input name] - [contact-whisper-id-input whisper-identity error]] + [contact-whisper-id-input new-contact-identity error]] [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)]]]) diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index 65b5eda641..c2bff7494e 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -21,7 +21,9 @@ :status nil :photo-path nil} - :contacts [] + :new-contact-identity "" + :contacts {} + :contacts-ids #{} :selected-contacts #{} :current-chat-id "console" @@ -36,10 +38,6 @@ :navigation-stack (list default-view) :current-tag nil :qr-codes {} - :new-contact {:name "" - :address "" - :whisper-identity "" - :phone-number ""} :keyboard-height 0 :disable-group-creation false :animations {;; todo clear this diff --git a/src/status_im/discovery/handlers.cljs b/src/status_im/discovery/handlers.cljs index 2f062260bc..cd5265b5a4 100644 --- a/src/status_im/discovery/handlers.cljs +++ b/src/status_im/discovery/handlers.cljs @@ -1,67 +1,72 @@ (ns status-im.discovery.handlers (:require [re-frame.core :refer [after dispatch enrich]] + [status-im.utils.utils :refer [first-index]] [status-im.utils.handlers :refer [register-handler]] [status-im.protocol.api :as api] [status-im.navigation.handlers :as nav] [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 [{:keys [discoveries] :as db} _] - (if-not (seq discoveries) - (-> db - (assoc :tags (discoveries/all-tags)) - ;; todo add limit - ;; todo hash-map with whisper-id as key and sorted by last-update - ;; may be more efficient here - (assoc :discoveries (discoveries/discovery-list))) - db)) + (-> db + (assoc :tags (discoveries/all-tags)) + ;; todo add limit + ;; todo hash-map with whisper-id as key and sorted by last-update + ;; may be more efficient here + (assoc :discoveries (discoveries/discovery-list)))) (register-handler :discovery-response-received (u/side-effect! - (fn [_ [_ from payload]] - (let [{:keys [name status hashtags location]} payload - location (or location "") - discovery [{:name name - :status status - :whisper-id from - :photo "" - :location location - :tags (map #(hash-map :name %) hashtags) - :last-updated (js/Date.)}]] + (fn [db [_ from payload]] + (let [{:keys [msg-id name photo-path status hashtags]} payload + discovery {:msg-id msg-id + :name name + :photo-path photo-path + :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 :broadcast-status (u/side-effect! - (fn [{:keys [name]} [_ status hashtags]] - (api/broadcast-discover-status name status hashtags)))) + (fn [{:keys [current-account-id accounts]} [_ status hashtags]] + (let [account (get accounts current-account-id)] + (api/broadcast-discover-status account status hashtags))))) (register-handler :show-discovery-tag (fn [db [_ tag]] (dispatch [:navigate-to :discovery-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 - [db [_ discovery]] - (-> db - (assoc :new-discovery discovery) - (update :discoveries conj discovery))) + [{db-discoveries :discoveries + :as db} [_ {:keys [msg-id] :as discovery}]] + (let [updated-discoveries (if-let [i (first-index #(= (:msg-id %) msg-id) db-discoveries)] + (assoc db-discoveries i discovery) + (conj db-discoveries discovery))] + (-> db + (assoc :new-discovery discovery) + (assoc :discoveries updated-discoveries)))) (defn save-discovery! [{:keys [new-discovery]} _] @@ -75,3 +80,9 @@ (-> add-discovery ((after save-discovery!)) ((enrich reload-tags!)))) + +(register-handler + :remove-old-discoveries! + (u/side-effect! + (fn [_ _] + (discoveries/remove-discoveries! :priority :asc 1000 200)))) diff --git a/src/status_im/discovery/model.cljs b/src/status_im/discovery/model.cljs index 7b8a72b3a8..f1c4e11600 100644 --- a/src/status_im/discovery/model.cljs +++ b/src/status_im/discovery/model.cljs @@ -5,87 +5,72 @@ (defn get-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))) -(defn decrease-tag-counter [tag] +(defn update-tag-counter [func tag] (let [tag (:name tag) tag-object (get-tag tag)] (if tag-object - (let [counter (dec (:count tag-object))] - (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 + (r/create :account :tag {:name tag - :count (inc (:count tag-object))} + :count (func (:count tag-object))} true)))) -(defn decrease-tags-counter [tags] - (doseq [tag tags] - (decrease-tag-counter tag))) +(defn update-tags-counter [func tags] + (doseq [tag (distinct tags)] + (update-tag-counter func tag))) -(defn increase-tags-counter [tags] - (doseq [tag tags] - (increase-tag-counter tag))) +(defn get-tags [msg-id] + (-> (r/get-by-field :account :discovery :msg-id msg-id) + (r/single-cljs) + (:tags) + (vals))) -(defn get-tags [whisper-id] - (:tags (-> (r/get-by-field :base :discoveries :whisper-id whisper-id) - (r/single-cljs)))) - -(defn- create-discovery [{:keys [tags] :as discovery}] - (log/debug "Creating discovery: " discovery tags) - (r/create :base :discoveries discovery true) - (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- upsert-discovery [{:keys [msg-id tags] :as discovery}] + (log/debug "Creating/updating discovery with tags: " tags) + (let [prev-tags (get-tags msg-id)] + (if prev-tags + (update-tags-counter dec prev-tags)) + (r/create :account :discovery discovery true) + (update-tags-counter inc tags))) (defn discovery-list [] - (->> (-> (r/get-all :base :discoveries) - (r/sorted :last-updated :desc) - r/collection->map) - (map #(update % :tags vals)))) + (->> (-> (r/get-all :account :discovery) + (r/sorted :priority :desc) + (r/collection->map)) + (mapv #(update % :tags vals)))) (defn- add-discoveries [discoveries] - (r/write :base + (r/write :account (fn [] - (let [db-discoveries (discovery-list)] - (mapv (fn [discovery] - (if-not (discovery-exist? db-discoveries - discovery) - (create-discovery discovery) - (update-discovery discovery))) - discoveries))))) + (doseq [discovery discoveries] + (upsert-discovery discovery))))) (defn save-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] - (let [discoveries (-> (r/get-by-filter :base :discoveries (str "tags.name = '" tag "'")) - (r/sorted :last-updated :desc))] + (let [discoveries (-> (r/get-by-filter :account :discovery (str "tags.name = '" tag "'")) + (r/sorted :priority :desc))] (log/debug "Discoveries by tag: " tag) (if (pos? limit) (r/page discoveries 0 limit) discoveries))) (defn all-tags [] - (-> (r/get-all :base :tag) + (-> (r/get-all :account :tag) (r/sorted :count :desc) r/collection->map)) diff --git a/src/status_im/discovery/screen.cljs b/src/status_im/discovery/screen.cljs index 78f995dd50..660bb1fb0b 100644 --- a/src/status_im/discovery/screen.cljs +++ b/src/status_im/discovery/screen.cljs @@ -8,6 +8,7 @@ text-input]] [status-im.components.status-bar :refer [status-bar]] [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.recent :refer [discovery-recent]] [status-im.discovery.styles :as st] @@ -19,18 +20,21 @@ (let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))] (or hashtags []))) -(defn title-content [show-search] +(defn title-content [platform-specific show-search] [view st/discovery-toolbar-content (if show-search [text-input {:style st/discovery-search-input :autoFocus true :placeholder (label :t/search-tags) :onSubmitEditing (fn [e] - (let [search (aget e "nativeEvent" "text") + (let [search (aget e "nativeEvent" "text") hashtags (get-hashtags search)] (dispatch [:broadcast-status search hashtags])))}] [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] (dispatch [:set ::show-search (not current-value)])) @@ -42,21 +46,33 @@ {:style st/discovery-toolbar :nav-action {:image {:source {:uri :icon_hamburger} :style st/hamburger-icon} - :handler #(dispatch [:create-fake-discovery!])} - :custom-content [title-content show-search] + :handler open-drawer} + :custom-content [title-content platform-specific show-search] :action {:image {:source {:uri :icon_search} :style st/search-icon} :handler #(toogle-search show-search)}}]]) (defview discovery [{platform-specific :platform-specific}] - [show-search [:get ::show-search]] + [show-search [:get ::show-search] + contacts [:get :contacts]] [view st/discovery-container [discovery-toolbar show-search platform-specific] [scroll-view st/scroll-view-container + [view st/section-spacing - [text {:style st/discovery-subtitle} (label :t/popular-tags)]] - [popular] + [text {:style st/discovery-subtitle + :platform-specific platform-specific + :font :medium} + (label :t/popular-tags)]] + [popular {:contacts contacts + :platform-specific platform-specific}] + [view st/section-spacing - [text {:style st/discovery-subtitle} (label :t/recent)]] - [discovery-recent]] + [text {:style st/discovery-subtitle + :platform-specific platform-specific + :font :medium} + (label :t/recent)]] + [discovery-recent {:contacts contacts + :platform-specific platform-specific}]] + [bottom-gradient]]) diff --git a/src/status_im/discovery/styles.cljs b/src/status_im/discovery/styles.cljs index ee9782f800..80fb34691e 100644 --- a/src/status_im/discovery/styles.cljs +++ b/src/status_im/discovery/styles.cljs @@ -1,7 +1,5 @@ (ns status-im.discovery.styles - (:require [status-im.components.styles :refer [font - title-font - color-white + (:require [status-im.components.styles :refer [color-white color-gray2 chat-background online-color @@ -14,55 +12,51 @@ ;; common (def row-separator - {:borderBottomWidth 1 - :borderBottomColor "#eff2f3"}) + {:border-bottom-width 1 + :border-bottom-color "#eff2f3"}) (def row - {:flexDirection :row}) + {:flex-direction :row}) (def column - {:flexDirection "column"}) + {:flex-direction :column}) -;; discovery.cljs +;; Toolbar (def discovery-toolbar-content - {:flex 1 - :alignItems :center - :justifyContent :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}) + {:flex 1 + :align-items :center + :justify-content :center}) (def discovery-toolbar - {:backgroundColor "#eef2f5" - :elevation 0}) + {:background-color "#eef2f5" + :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 - {:color color-gray2 - :fontFamily "sans-serif-medium" - :fontSize 14}) + {:color color-gray2 + :font-size 14}) (def section-spacing - {:paddingLeft 30 - :paddingTop 15 - :paddingBottom 15}) + {:padding 16}) (def scroll-view-container {}) -;; discovery_popular.cljs +;; Popular (def carousel-page-style {:borderRadius 1 @@ -72,91 +66,84 @@ :elevation 2 :marginBottom 10}) -;; discovery_populat_list.cljs - (def tag-name - {:color "#7099e6" - :fontFamily "sans-serif-medium" - :fontSize 14 - :paddingRight 5 - :paddingBottom 2 - :alignItems :center - :justifyContent :center}) + {:color "#7099e6" + :font-size 14 + :padding-right 5 + :padding-bottom 2 + :align-items :center + :justify-content :center}) (def tag-name-container - {:flexDirection "column" - :backgroundColor "#eef2f5" - :borderRadius 5 - :padding 4}) + {:flex-direction "column" + :background-color "#eef2f5" + :border-radius 5 + :padding 4}) (def tag-count - {:color "#838c93" - :fontFamily "sans-serif" - :fontSize 12 - :paddingRight 5 - :paddingBottom 2 - :alignItems :center - :justifyContent :center}) + {:color "#838c93" + :font-size 12 + :padding-right 5 + :padding-bottom 2 + :align-items :center + :justify-content :center}) (def tag-count-container - {:flex 0.2 - :flexDirection "column" - :alignItems "flex-end" - :paddingTop 10 - :paddingRight 9}) + {:flex 0.2 + :flex-direction "column" + :align-items "flex-end" + :padding-top 10 + :padding-right 9}) (def popular-list-container - {:flex 1 - :backgroundColor :white - :paddingLeft 10 - :paddingTop 16}) + {:flex 1 + :background-color :white + :padding-left 10 + :padding-top 16}) (def popular-list - {:backgroundColor :white - :paddingTop 13}) + {:background-color :white + :padding-top 13}) -;; discover_popular_list_item.cjls +;; Popular list item (def popular-list-item - {:flexDirection :row - :paddingTop 10 - :paddingBottom 10}) + {:flex-direction :row + :padding-top 10 + :padding-bottom 10}) (def popular-list-item-status - {:color "black" - :fontFamily "sans-serif" - :lineHeight 22 - :fontSize 14}) + {:color "black" + :line-height 22 + :font-size 14}) (def popular-list-item-name - {:color "black" - :fontFamily "sans-serif-medium" - :fontSize 14 - :lineHeight 24}) + {:color "black" + :font-size 14 + :line-height 24}) (def popular-list-item-name-container - {:flex 0.8 - :flexDirection "column"}) + {:flex 0.8 + :flex-direction "column"}) (def popular-list-item-avatar-container - {:flex 0.2 - :flexDirection "column" - :alignItems :center - :paddingTop 5}) + {:flex 0.2 + :flex-direction "column" + :align-items :center + :padding-top 5}) (def popular-list-item-avatar - {:resizeMode "contain" - :borderRadius 20 - :width 40 - :height 40}) + {:border-radius 18 + :width 36 + :height 36}) ;; discovery_recent (def recent-list - {:backgroundColor :white - :paddingLeft 15}) + {:background-color :white + :padding-left 16}) -;; discovery_tag +;; Discovery tag (def discovery-tag-container {:flex 1 diff --git a/src/status_im/discovery/tag.cljs b/src/status_im/discovery/tag.cljs index aeda1f4d3c..df138105f9 100644 --- a/src/status_im/discovery/tag.cljs +++ b/src/status_im/discovery/tag.cljs @@ -1,14 +1,11 @@ (ns status-im.discovery.tag - (: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 :refer [toolbar]] - [status-im.discovery.views.popular-list-item :refer [popular-list-item]] - [status-im.discovery.styles :as st])) - -(defn render-row [row _ _] - (list-item [popular-list-item row])) + (: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 :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 @@ -19,22 +16,21 @@ [view {:style st/tag-container} [text {:style st/tag-title} (str " #" tag)]]]) -(defn discovery-tag [] - (let [tag (subscribe [:get :current-tag]) - discoveries (subscribe [:get-discoveries-by-tag])] - (fn [] - (let [items @discoveries - datasource (to-datasource items)] - [view st/discovery-tag-container - [toolbar {:nav-action {:image {:source {:uri :icon_back} +(defview discovery-tag [{platform-specific :platform-specific}] + [tag [:get :current-tag] + discoveries [:get-discoveries-by-tag]] + (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) - :action {:image {:source {:uri :icon_search} + :custom-content (title-content tag) + :action {:image {:source {:uri :icon_search} :style st/icon-search} :handler (fn [])}}] - [list-view {:dataSource datasource - :renderRow render-row - :renderSeparator render-separator - :style st/recent-list}]])))) + [list-view {:dataSource datasource + :renderRow (fn [row _ _] + (list-item [discovery-list-item row platform-specific])) + :renderSeparator render-separator + :style st/recent-list}]])) diff --git a/src/status_im/discovery/views/discovery_list_item.cljs b/src/status_im/discovery/views/discovery_list_item.cljs new file mode 100644 index 0000000000..2e9f428d31 --- /dev/null +++ b/src/status_im/discovery/views/discovery_list_item.cljs @@ -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))}}]]]) diff --git a/src/status_im/discovery/views/popular.cljs b/src/status_im/discovery/views/popular.cljs index 28b1d8c0c3..3b9a021405 100644 --- a/src/status_im/discovery/views/popular.cljs +++ b/src/status_im/discovery/views/popular.cljs @@ -13,11 +13,13 @@ (defn page-width [] (.-width (.get (.. r/react-native -Dimensions) "window"))) -(defview popular [] - [popular-tags [:get-popular-tags 3]] +(defview popular [{:keys [contacts platform-specific]}] + [popular-tags [:get-popular-tags 10]] (if (pos? (count popular-tags)) - [carousel {:pageStyle st/carousel-page-style - :sneak 20} + [carousel {:pageStyle st/carousel-page-style} (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)])) diff --git a/src/status_im/discovery/views/popular_list.cljs b/src/status_im/discovery/views/popular_list.cljs index 8aae75b569..f5d6b32bb1 100644 --- a/src/status_im/discovery/views/popular_list.cljs +++ b/src/status_im/discovery/views/popular_list.cljs @@ -9,27 +9,28 @@ text]] [status-im.discovery.styles :as st] [status-im.utils.listview :refer [to-datasource]] - [status-im.discovery.views.popular-list-item :refer [popular-list-item]])) - -(defn render-row [row _ _] - (list-item [popular-list-item row])) + [status-im.discovery.views.discovery-list-item :refer [discovery-list-item]])) (defn render-separator [_ row-id _] (list-item [view {:style st/row-separator :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]] [view st/popular-list-container [view st/row [view st/tag-name-container [touchable-highlight {:onPress #(dispatch [:show-discovery-tag tag])} [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 - [text {:style st/tag-count} count]]] - [list-view {:dataSource (to-datasource discoveries) - :enableEmptySections true - :renderRow render-row - :renderSeparator render-separator - :style st/popular-list}]]) + [text {:style st/tag-count + :platform-specific platform-specific + :font :default} + count]]] + (for [{:keys [msg-id] :as discovery} discoveries] + ^{:key (str "message-" msg-id)} + [discovery-list-item discovery platform-specific])]) diff --git a/src/status_im/discovery/views/popular_list_item.cljs b/src/status_im/discovery/views/popular_list_item.cljs deleted file mode 100644 index adc1334e96..0000000000 --- a/src/status_im/discovery/views/popular_list_item.cljs +++ /dev/null @@ -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}}]]]) diff --git a/src/status_im/discovery/views/recent.cljs b/src/status_im/discovery/views/recent.cljs index bb4849bcae..4a1c0ca125 100644 --- a/src/status_im/discovery/views/recent.cljs +++ b/src/status_im/discovery/views/recent.cljs @@ -5,19 +5,15 @@ [status-im.components.react :refer [view list-view list-item]] [status-im.utils.listview :refer [to-datasource]] [status-im.discovery.styles :as st] - [status-im.discovery.views.popular-list-item - :refer [popular-list-item]])) - -(defn render-row [row _ _] - (list-item [popular-list-item row])) + [status-im.discovery.views.discovery-list-item :refer [discovery-list-item]])) (defn render-separator [_ row-id _] (list-item [view {:style st/row-separator :key row-id}])) -(defview discovery-recent [] +(defview discovery-recent [{platform-specific :platform-specific}] [discoveries [:get :discoveries]] - [list-view {:dataSource (to-datasource discoveries) - :renderRow render-row - :renderSeparator render-separator - :style st/recent-list}]) + [view st/recent-list + (for [{:keys [msg-id] :as discovery} discoveries] + ^{:key (str "message-" msg-id)} + [discovery-list-item discovery platform-specific])]) diff --git a/src/status_im/group_settings/handlers.cljs b/src/status_im/group_settings/handlers.cljs index 0c0521bcfd..a1cf3d9013 100644 --- a/src/status_im/group_settings/handlers.cljs +++ b/src/status_im/group_settings/handlers.cljs @@ -22,7 +22,7 @@ (let [property (db-name db)] (r/write :account (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) (aset (name property-name) property))))))) @@ -80,7 +80,7 @@ (r/write :account (fn [] (r/create :account - :chats + :chat (update chat :contacts remove-identities selected-participants) true))))) diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index cf28e0c160..a3d02f06d5 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -22,7 +22,8 @@ status-im.commands.handlers.jail status-im.qr-scanner.handlers status-im.accounts.handlers - status-im.protocol.handlers)) + status-im.protocol.handlers + [status-im.utils.datetime :as time])) ;; -- Middleware ------------------------------------------------------------ ;; @@ -72,7 +73,10 @@ (dispatch [:initialize-account-db]) (dispatch [:initialize-chats]) (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 (u/side-effect! diff --git a/src/status_im/ios/core.cljs b/src/status_im/ios/core.cljs index 0c4473e8fd..af2f9af54c 100644 --- a/src/status_im/ios/core.cljs +++ b/src/status_im/ios/core.cljs @@ -12,7 +12,6 @@ [status-im.contacts.views.contact-list :refer [contact-list]] [status-im.contacts.views.new-contact :refer [new-contact]] [status-im.qr-scanner.screen :refer [qr-scanner]] - [status-im.discovery.screen :refer [discovery]] [status-im.discovery.tag :refer [discovery-tag]] [status-im.chat.screen :refer [chat]] [status-im.accounts.login.screen :refer [login]] diff --git a/src/status_im/models/accounts.cljs b/src/status_im/models/accounts.cljs index a19a00cf6c..bba9684d65 100644 --- a/src/status_im/models/accounts.cljs +++ b/src/status_im/models/accounts.cljs @@ -2,11 +2,11 @@ (:require [status-im.persistence.realm.core :as r])) (defn get-accounts [] - (-> (r/get-all :base :accounts) + (-> (r/get-all :base :account) r/collection->map)) (defn save-account [update?] - #(r/create :base :accounts % update?)) + #(r/create :base :account % update?)) (defn save-accounts [accounts update?] (r/write :base #(mapv (save-account update?) accounts))) @@ -15,7 +15,7 @@ ;;;;;;;;;;;;;;;;;;;;---------------------------------------------- (defn accounts-list [] - (r/get-all :base :accounts)) + (r/get-all :base :account)) (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))) diff --git a/src/status_im/models/chats.cljs b/src/status_im/models/chats.cljs index 854e7bcc57..ce41b09df4 100644 --- a/src/status_im/models/chats.cljs +++ b/src/status_im/models/chats.cljs @@ -12,7 +12,7 @@ (defn chat-name-from-contacts [identities] (let [chat-name (->> identities (map (fn [identity] - (-> (r/get-by-field :account :contacts :whisper-identity identity) + (-> (r/get-by-field :account :contact :whisper-identity identity) (r/single-cljs) :name))) (filter identity) @@ -25,7 +25,7 @@ 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] ;; TODO Get real status @@ -42,7 +42,7 @@ (defn create-chat ([{:keys [last-msg-id] :as chat}] (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] (when-not (chat-exists? chat-id) (let [chat-name (or chat-name @@ -52,7 +52,7 @@ (fn [] (let [contacts (mapv (fn [ident] {:identity ident}) identities)] - (r/create :account :chats + (r/create :account :chat {:chat-id chat-id :is-active true :name chat-name @@ -63,7 +63,7 @@ (add-status-message 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) (aget "contacts"))) @@ -79,7 +79,7 @@ (mapv (fn [ident] {:identity ident})) (concat only-old-contacts))] - (r/create :account :chats + (r/create :account :chat {:chat-id group-id :is-active true :name group-name @@ -91,18 +91,18 @@ (map #(update % :contacts vals) chats)) (defn chats-list [] - (-> (r/get-all :account :chats) + (-> (r/get-all :account :chat) (r/sorted :timestamp :desc) r/collection->map normalize-contacts)) (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/list-to-array :contacts))) (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 first)) @@ -123,14 +123,14 @@ (r/write :account (fn [] (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") (r/filtered query) (.forEach (fn [object _ _] (aset object "is-in-chat" false)))))))) (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")] (js->clj (.map results (fn [object _ _] (aget object "chat-id")))))) @@ -138,6 +138,6 @@ (defn set-chat-active [chat-id active?] (r/write :account (fn [] - (-> (r/get-by-field :account :chats :chat-id chat-id) + (-> (r/get-by-field :account :chat :chat-id chat-id) (r/single) (aset "is-active" active?))))) diff --git a/src/status_im/models/contacts.cljs b/src/status_im/models/contacts.cljs index e565e46546..70f67ac34c 100644 --- a/src/status_im/models/contacts.cljs +++ b/src/status_im/models/contacts.cljs @@ -5,18 +5,14 @@ exclude-query]])) (defn get-contacts [] - (-> (r/get-all :account :contacts) + (-> (r/get-all :account :contact) (r/sorted :name :asc) r/collection->map)) -(defn create-contact [{:keys [name photo-path whisper-identity] :as contact}] - (let [contact-from-db (r/get-one-by-field :account :contacts +(defn create-contact [{:keys [whisper-identity] :as contact}] + (let [contact-from-db (r/get-one-by-field :account :contact :whisper-identity whisper-identity)] - (when-not contact-from-db - (->> {:name (or name "") - :photo-path (or photo-path (identicon whisper-identity))} - (merge contact) - (r/create :account :contacts))))) + (r/create :account :contact contact (if contact-from-db true false)))) (defn save-contacts [contacts] (r/write :account #(mapv create-contact contacts))) @@ -25,13 +21,13 @@ ;;;;;;;;;;;;;;;;;;;;---------------------------------------------- (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] (if (empty? exclude-idents) (contacts-list) (let [query (exclude-query :whisper-identity exclude-idents)] - (-> (r/get-all :account :contacts) + (-> (r/get-all :account :contact) (r/filtered query) (r/sorted :name :asc))))) @@ -39,9 +35,9 @@ (if (empty? include-indents) () (let [query (include-query :whisper-identity include-indents)] - (-> (r/get-all :account :contacts) + (-> (r/get-all :account :contact) (r/filtered query) (r/sorted :name :asc))))) (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))) diff --git a/src/status_im/models/messages.cljs b/src/status_im/models/messages.cljs index 4d45a64456..e2b1f2c5e8 100644 --- a/src/status_im/models/messages.cljs +++ b/src/status_im/models/messages.cljs @@ -29,7 +29,7 @@ [chat-id {:keys [delivery-status msg-id content] :or {delivery-status :pending} :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 (fn [] (let [content' (if (string? content) @@ -41,7 +41,7 @@ :content content' :delivery-status delivery-status :timestamp (timestamp)})] - (r/create :account :msgs message' true)))))) + (r/create :account :message message' true)))))) (defn command-type? [type] (contains? @@ -51,7 +51,7 @@ (defn get-messages ([chat-id] (get-messages chat-id 0)) ([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/page from (+ from c/default-number-of-messages)) (r/collection->map)) @@ -66,5 +66,5 @@ (log/debug "update-message!" msg) (r/write :account (fn [] - (when (r/exists? :account :msgs :msg-id msg-id) - (r/create :account :msgs msg true))))) + (when (r/exists? :account :message :msg-id msg-id) + (r/create :account :message msg true))))) diff --git a/src/status_im/models/requests.cljs b/src/status_im/models/requests.cljs index 62a2dfb6fb..8dde634ecd 100644 --- a/src/status_im/models/requests.cljs +++ b/src/status_im/models/requests.cljs @@ -2,11 +2,11 @@ (:require [status-im.persistence.realm.core :as r])) (defn get-requests [] - (-> (r/get-all :account :requests) + (-> (r/get-all :account :request) r/collection->map)) (defn create-request [request] - (r/create :account :requests request true)) + (r/create :account :request request true)) (defn save-request [request] (r/write :account @@ -17,5 +17,5 @@ (r/write :account #(mapv create-request requests))) (defn requests-list [] - (r/get-all :account :requests)) + (r/get-all :account :request)) diff --git a/src/status_im/persistence/realm/schemas.cljs b/src/status_im/persistence/realm/schemas.cljs index 34a5fd94fc..e5a096792f 100644 --- a/src/status_im/persistence/realm/schemas.cljs +++ b/src/status_im/persistence/realm/schemas.cljs @@ -1,99 +1,98 @@ (ns status-im.persistence.realm.schemas (:require [status-im.components.styles :refer [default-chat-color]])) -(def base {:schema [{:name :accounts +(def base {:schema [{:name :account :primaryKey :address - :properties {:address "string" - :public-key "string" - :name "string" - :phone {:type "string" :optional true} - :email {:type "string" :optional true} - :status {:type "string" :optional true} - :photo-path "string"}} - {:name :tag - :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"}} + :properties {:address "string" + :public-key "string" + :name {:type "string" :optional true} + :phone {:type "string" :optional true} + :email {:type "string" :optional true} + :status {:type "string" :optional true} + :photo-path "string" + :last-updated {:type "int" :default 0}}} {:name :kv-store :primaryKey :key :properties {:key "string" :value "string"}}] :schemaVersion 0}) -(def account {:schema [{:name :contacts - :primaryKey :whisper-identity - :properties {:phone-number {:type "string" - :optional true} - :whisper-identity "string" - :name {:type "string" - :optional true} - :photo-path {:type "string" - :optinal true}}} - {:name :requests - :properties {:message-id :string - :chat-id :string - :type :string - :status {:type :string - :default "open"} - :added :date}} - {:name :kv-store - :primaryKey :key - :properties {:key "string" - :value "string"}} - {:name :msgs - :primaryKey :msg-id - :properties {:msg-id "string" - :from "string" - :to {:type "string" - :optional true} - :content "string" ;; TODO make it ArrayBuffer - :content-type "string" - :timestamp "int" - :chat-id {:type "string" - :indexed true} - :outgoing "bool" - :delivery-status {:type "string" - :optional true} - :same-author "bool" - :same-direction "bool" - :preview {:type :string - :optional true}}} - {:name :chat-contact - :properties {:identity "string" - :is-in-chat {:type "bool" - :default true}}} - {:name :chats - :primaryKey :chat-id - :properties {:chat-id "string" - :name "string" - :color {:type "string" - :default default-chat-color} - :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 :commands - :primaryKey :chat-id - :properties {:chat-id "string" - :file "string"}}] +(def account {:schema [{:name :contact + :primaryKey :whisper-identity + :properties {:address {:type "string" :optional true} + :whisper-identity "string" + :name {:type "string" :optional true} + :photo-path {:type "string" :optional true} + :last-updated {:type "int" :default 0} + :last-online {:type "int" :default 0}}} + {:name :request + :properties {:message-id :string + :chat-id :string + :type :string + :status {:type :string + :default "open"} + :added :date}} + {:name :tag + :primaryKey :name + :properties {:name "string" + :count {:type "int" :optional true :default 0}}} + {:name :discovery + :primaryKey :msg-id + :properties {:msg-id "string" + :name {:type "string" :optional true} + :status "string" + :whisper-id "string" + :photo-path {:type "string" :optional true} + :tags {:type "list" + :objectType "tag"} + :priority {:type "int" :default 0} + :last-updated "date"}} + {:name :kv-store + :primaryKey :key + :properties {:key "string" + :value "string"}} + {:name :message + :primaryKey :msg-id + :properties {:msg-id "string" + :from "string" + :to {:type "string" + :optional true} + :content "string" ;; TODO make it ArrayBuffer + :content-type "string" + :timestamp "int" + :chat-id {:type "string" + :indexed true} + :outgoing "bool" + :delivery-status {:type "string" + :optional true} + :same-author "bool" + :same-direction "bool" + :preview {:type :string + :optional true}}} + {:name :chat-contact + :properties {:identity "string" + :is-in-chat {:type "bool" + :default true}}} + {:name :chat + :primaryKey :chat-id + :properties {:chat-id "string" + :name "string" + :color {:type "string" + :default default-chat-color} + :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}) diff --git a/src/status_im/profile/handlers.cljs b/src/status_im/profile/handlers.cljs index ef3186000d..7cbbe251ed 100644 --- a/src/status_im/profile/handlers.cljs +++ b/src/status_im/profile/handlers.cljs @@ -4,7 +4,36 @@ [status-im.components.react :refer [show-image-picker]] [status-im.utils.image-processing :refer [img->base64]] [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 (u/side-effect! diff --git a/src/status_im/profile/screen.cljs b/src/status_im/profile/screen.cljs index fb5637b5b4..55b04187c3 100644 --- a/src/status_im/profile/screen.cljs +++ b/src/status_im/profile/screen.cljs @@ -16,6 +16,9 @@ my-profile-icon]] [status-im.components.status-bar :refer [status-bar]] [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.utils.phone-number :refer [format-phone-number valid-mobile-number?]] @@ -25,32 +28,6 @@ [status-im.i18n :refer [label]] [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?]}] [view [touchable-highlight {:style st/back-btn-touchable @@ -74,7 +51,8 @@ :style st/ok-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 username :name} :account photo-path :photo-path @@ -122,19 +100,35 @@ :on-change-text on-change-text} (or value (when-not edit-mode? empty-value))]]]) -(defview profile [] - [{:keys [name whisper-identity phone-number]} [:contact]] +(defview profile [{platform-specific :platform-specific}] + [{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} - [touchable-highlight {:style st/back-btn-touchable - :on-press #(dispatch [:navigate-back])} - [view st/back-btn-container - [icon :back st/back-btn-icon]]] + [status-bar {:platform-specific platform-specific}] + [view + [touchable-highlight {:style st/back-btn-touchable + :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/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 [touchable-highlight {:onPress #(message-user whisper-identity)} [view st/message-btn @@ -144,14 +138,23 @@ )} [view st/more-btn [icon :more_vertical_blue st/more-btn-image]]]]] - [view st/profile-properties-container - [profile-property-view {:name (label :t/username) - :value name}] - [profile-property-view {:name (label :t/phone-number) - :value phone-number}] - ;; TODO stub data - [profile-property-view {:name (label :t/email) - :value (label :t/not-implemented)}] + + [scroll-view st/profile-properties-container + [profile-property-view {:name (label :t/username) + :value (if (not= username address) + username) + :empty-value (label :t/not-specified) + :platform-specific platform-specific}] + [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 [touchable-highlight {:on-press (fn [] ;; TODO not implemented @@ -180,7 +183,7 @@ [status-image-view {:platform-specific platform-specific :account account :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?}] [scroll-view st/profile-properties-container @@ -205,6 +208,6 @@ :profile-data profile-edit-data :platform-specific platform-specific}] [view st/qr-code-container - [qr-code {:value (clj->js {:name username - :whisper-identity public-key}) - :size 150}]]]]) + ;; TODO: this public key should be replaced by address + [qr-code {:value (str "ethereum:" public-key) + :size 220}]]]]) diff --git a/src/status_im/protocol/protocol_handler.cljs b/src/status_im/protocol/protocol_handler.cljs index 8f79ed7939..4bde5d223b 100644 --- a/src/status_im/protocol/protocol_handler.cljs +++ b/src/status_im/protocol/protocol_handler.cljs @@ -45,4 +45,8 @@ (dispatch [:participant-left-group from group-id msg-id])) :discover-response (let [{:keys [from payload]} event] (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)))}) diff --git a/src/status_im/qr_scanner/screen.cljs b/src/status_im/qr_scanner/screen.cljs index 173b3ac6cb..733799a6fb 100644 --- a/src/status_im/qr_scanner/screen.cljs +++ b/src/status_im/qr_scanner/screen.cljs @@ -10,7 +10,8 @@ [status-im.components.toolbar :refer [toolbar]] [status-im.qr-scanner.styles :as st] [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] [view @@ -25,9 +26,10 @@ [identifier [:get :current-qr-context]] [view st/barcode-scanner-container [qr-scanner-toolbar (:toolbar-title identifier) platform-specific] - [camera {;:on-bar-code-read #(js/alert "ok") - :onBarCodeRead #(let [data (json->clj (.-data %))] - (dispatch [:set-qr-code identifier data])) + [camera {:onBarCodeRead (fn [code] + (let [data (-> (.-data code) + (str/replace #"ethereum:" ""))] + (dispatch [:set-qr-code identifier data]))) :style st/barcode-scanner}] [view st/rectangle-container [view st/rectangle diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 5236b1ef0e..a21e6cd797 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -20,7 +20,21 @@ :members {:one "1 member, 1 active" :other "{{count}} members, {{count}} active" :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" @@ -80,7 +94,6 @@ ;contacts :contacts "Contacts" - :no-name "Noname" :new-contact "New Contact" :show-all "SHOW ALL" :contacs-group-dapps "Dapps" diff --git a/src/status_im/utils/datetime.cljs b/src/status_im/utils/datetime.cljs index 03695b427a..9dd4468820 100644 --- a/src/status_im/utils/datetime.cljs +++ b/src/status_im/utils/datetime.cljs @@ -1,9 +1,18 @@ (ns status-im.utils.datetime (:require [cljs-time.core :as t :refer [date-time now plus days hours before?]] [cljs-time.coerce :refer [from-long to-long]] - [cljs-time.format :as format :refer [formatters - formatter - unparse]])) + [cljs-time.format :refer [formatters + formatter + 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)))) @@ -17,8 +26,23 @@ yesterday (plus today (days -1))] (cond (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)))) +(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 [] (to-long (now))) diff --git a/src/status_im/utils/utils.cljs b/src/status_im/utils/utils.cljs index ed3e76b3cd..91b19d43cd 100644 --- a/src/status_im/utils/utils.cljs +++ b/src/status_im/utils/utils.cljs @@ -55,3 +55,13 @@ (if (and (< max (count s)) s) (str (subs s 0 (- max 3)) "...") 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))))))