discovery rework

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

View File

@ -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"]

View File

@ -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)]

View File

@ -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]]

View File

@ -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)))

View File

@ -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)

View File

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

View File

@ -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])]

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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]]

View File

@ -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)))

View File

@ -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}]]]]])

View File

@ -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}]})

View File

@ -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}]))))))

View File

@ -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)]

View File

@ -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])]]))

View File

@ -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)]]])

View File

@ -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

View File

@ -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))))

View File

@ -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))

View File

@ -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]])

View File

@ -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

View File

@ -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}]]))

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 []
(.-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)]))

View File

@ -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])])

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.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])])

View File

@ -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)))))

View File

@ -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!

View File

@ -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]]

View File

@ -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)))

View File

@ -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?)))))

View File

@ -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)))

View File

@ -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)))))

View File

@ -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))

View File

@ -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})

View File

@ -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!

View File

@ -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}]]]])

View File

@ -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)))})

View File

@ -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

View File

@ -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"

View File

@ -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)))

View File

@ -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))))))