implemented spec for app-db

This commit is contained in:
Andrey Shovkoplyas 2017-07-10 18:27:26 +03:00 committed by Roman Volosovskyi
parent 5051d1ce9e
commit 21626549d1
27 changed files with 309 additions and 112 deletions

View File

@ -0,0 +1,9 @@
(ns status-im.accounts.specs
(:require [cljs.spec.alpha :as s]))
(s/def :accounts/accounts map?) ;;{id (string) account (map)} all created accounts
(s/def :accounts/account-creation? (s/nilable boolean?)) ;;true during creating new account
(s/def :accounts/creating-account? boolean?) ;;what is the difference ? ^
(s/def :accounts/current-account-id (s/nilable string?)) ;;id of logged in account
(s/def :accounts/recover map?) ;;used during recovering account
(s/def :accounts/login map?) ;;used during logging

View File

@ -3,6 +3,7 @@
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.handlers]
[status-im.subs]
[status-im.specs]
[status-im.components.react :refer [app-registry
app-state
keyboard
@ -176,5 +177,4 @@
(dispatch [:initialize-crypt])
(dispatch [:initialize-geth])
(status/set-soft-input-mode status/adjust-resize)
(dispatch [:load-user-phone-number])
(init-back-button-handler!))

View File

@ -100,7 +100,6 @@
(register-handler :cancel-command
(fn [{:keys [current-chat-id] :as db} _]
(-> db
(dissoc :canceled-command)
(assoc-in [:chats current-chat-id :command-input] {})
(update-in [:chats current-chat-id :input-text] safe-trim))))
@ -154,11 +153,10 @@
(fn [db [_ phone-number message-id]]
(sign-up-service/start-listening-confirmation-code-sms)
(let [formatted (format-phone-number phone-number)]
(-> db
(assoc :user-phone-number formatted)
(server/sign-up formatted
(server/sign-up db
formatted
message-id
sign-up-service/on-sign-up-response)))))
sign-up-service/on-sign-up-response))))
(register-handler :start-listening-confirmation-code-sms
(fn [db [_ listener]]
@ -261,7 +259,6 @@
[{:keys [current-chat-id current-account-id] :as db} [_ _ id]]
(let [chat-id (or id current-chat-id)
messages (get-in db [:chats chat-id :messages])
command? (= :command (get-in db [:edit-mode chat-id]))
db' (-> db
(assoc :current-chat-id chat-id)
(assoc-in [:chats chat-id :was-opened?] true))
@ -291,18 +288,18 @@
(register-handler :add-chat-loaded-callback
(fn [db [_ chat-id callback]]
(log/debug "Add chat loaded callback: " chat-id callback)
(update-in db [::chat-loaded-callbacks chat-id] conj callback)))
(update-in db [:chat-loaded-callbacks chat-id] conj callback)))
(register-handler ::clear-chat-loaded-callbacks
(fn [db [_ chat-id]]
(log/debug "Clear chat loaded callback: " chat-id)
(assoc-in db [::chat-loaded-callbacks chat-id] nil)))
(assoc-in db [:chat-loaded-callbacks chat-id] nil)))
(register-handler :invoke-chat-loaded-callbacks
(u/side-effect!
(fn [db [_ chat-id]]
(log/debug "Invoking chat loaded callbacks: " chat-id)
(let [callbacks (get-in db [::chat-loaded-callbacks chat-id])]
(let [callbacks (get-in db [:chat-loaded-callbacks chat-id])]
(log/debug "Invoking chat loaded callbacks: " callbacks)
(doseq [callback callbacks]
(callback))

View File

@ -4,7 +4,7 @@
[status-im.data-store.messages :as messages]))
(defn set-unviewed-messages [db]
(let [messages (->> (::raw-unviewed-messages db)
(let [messages (->> (:raw-unviewed-messages db)
(group-by :chat-id)
(map (fn [[id messages]]
[id {:messages-ids (map :message-id messages)
@ -12,11 +12,11 @@
(into {}))]
(-> db
(assoc :unviewed-messages messages)
(dissoc ::raw-unviewed-messages))))
(dissoc :raw-unviewed-messages))))
(defn load-messages! [db]
(let [messages (messages/get-unviewed)]
(assoc db ::raw-unviewed-messages messages)))
(assoc db :raw-unviewed-messages messages)))
(register-handler ::set-unviewed-messages set-unviewed-messages)

View File

@ -0,0 +1,30 @@
(ns status-im.chat.specs
(:require [cljs.spec.alpha :as s]))
(s/def :chat/chats map?) ;; {id (string) chat (map)} active chats on chat's tab
(s/def :chat/current-chat-id string?) ;;current or last opened chat-id
(s/def :chat/chat-id string?) ;;what is the difference ? ^
(s/def :chat/new-chat map?) ;;used during adding new chat
(s/def :chat/new-chat-name string?) ;;we have name in the new-chat why do we need this field
(s/def :chat/chat-animations map?) ;;{id (string) props (map)}
(s/def :chat/chat-ui-props map?) ;;{id (string) props (map)}
(s/def :chat/chat-list-ui-props map?)
(s/def :chat/layout-height number?) ;;height of chat's view layout
(s/def :chat/expandable-view-height-to-value number?)
(s/def :chat/global-commands map?) ; {key (keyword) command (map)} atm used for browse command
(s/def :chat/loading-allowed boolean?) ;;allow to load more messages
(s/def :chat/message-data map?)
(s/def :chat/message-id->transaction-id map?)
(s/def :chat/message-status map?)
(s/def :chat/unviewed-messages (s/nilable map?))
(s/def :chat/selected-participants set?)
(s/def :chat/chat-loaded-callbacks map?)
(s/def :chat/commands-callbacks map?)
(s/def :chat/command-hash-valid? boolean?)
(s/def :chat/public-group-topic string?)
(s/def :chat/confirmation-code-sms-listener any?) ; .addListener result object
(s/def :chat/messages seq?)
(s/def :chat/loaded-chats seq?)
(s/def :chat/bot-subscriptions map?)
(s/def :chat/new-request map?)
(s/def :chat/raw-unviewed-messages vector?)

View File

@ -61,7 +61,7 @@
(defn dispatch-loaded!
[db [{{:keys [whisper-identity]} :contact
:as params} file]]
(if (::valid-hash db)
(if (:command-hash-valid? db)
(dispatch [::parse-commands! params file])
(dispatch [::loading-failed! whisper-identity ::wrong-hash])))
@ -97,7 +97,7 @@
;; todo check
#_(= (get-hash-by-identity db identity)
(get-hash-by-file file))]
(assoc db ::valid-hash valid?)))
(assoc db :command-hash-valid? valid?)))
(defn each-merge [coll with]
(->> coll
@ -160,7 +160,7 @@
:type :command))
(= id bots-constants/mailman-bot)
(update db :contacts (fn [contacts]
(update :contacts (fn [contacts]
(reduce (fn [contacts [k _]]
(update-in contacts [k :commands]
(fn [c]
@ -229,16 +229,16 @@
(reg-handler :add-commands-loading-callback
(fn [db [chat-id callback]]
(update-in db [::commands-callbacks chat-id] conj callback)))
(update-in db [:commands-callbacks chat-id] conj callback)))
(reg-handler :invoke-commands-loading-callbacks
(u/side-effect!
(fn [db [chat-id]]
(let [callbacks (get-in db [::commands-callbacks chat-id])]
(let [callbacks (get-in db [:commands-callbacks chat-id])]
(doseq [callback callbacks]
(callback))
(dispatch [::clear-commands-callbacks chat-id])))))
(reg-handler ::clear-commands-callbacks
(fn [db [chat-id]]
(assoc-in db [::commands-callbacks chat-id] nil)))
(assoc-in db [:commands-callbacks chat-id] nil)))

View File

@ -401,8 +401,7 @@
(after #(dispatch [:navigate-to :contact-toggle-list]))
(fn [db [_ group-type]]
(->
(assoc db :contact-group nil
:group-type group-type
(assoc db :group-type group-type
:selected-contacts #{}
:new-chat-name "")
(assoc-in [:toolbar-search :show] nil)

View File

@ -0,0 +1,15 @@
(ns status-im.contacts.specs
(:require [cljs.spec.alpha :as s]))
(s/def :contacts/contacts map?) ;; {id (string) contact (map)}
(s/def :contacts/new-contacts seq?)
(s/def :contacts/new-contact-identity string?) ;;public key of new contact during adding this new contact
(s/def :contacts/new-contact-public-key-error string?)
(s/def :contacts/contact-identity string?) ;;on showing this contact profile
(s/def :contacts/contacts-ui-props map?)
(s/def :contacts/contact-list-ui-props map?)
(s/def :contacts/contacts-click-handler (s/nilable fn?)) ;;used in modal list (for example for wallet)
(s/def :contacts/contacts-click-action (s/nilable keyword?)) ;;used in modal list (for example for wallet)
(s/def :contacts/contacts-click-params map?) ;;used in modal list (for example for wallet)

View File

@ -1,57 +1,28 @@
(ns status-im.db
(:require [status-im.components.react :refer [animated]]
[status-im.components.animation :as anim]
[status-im.constants :refer [console-chat-id]]
(:require [status-im.constants :refer [console-chat-id]]
[status-im.utils.platform :as p]))
;; initial state of app-db
(def app-db {:identity-password "replace-me-with-user-entered-password"
:identity "me"
:current-public-key "me"
(def app-db {:current-public-key ""
:status-module-initialized? (or p/ios? js/goog.DEBUG)
:keyboard-height 0
:accounts {}
:current-account-id nil
:navigation-stack '()
:contacts {}
:qr-codes {}
:contact-groups {}
:selected-contacts #{}
:chats {}
:current-chat-id console-chat-id
:loading-allowed true
:selected-participants #{}
:profile-edit {:edit? false
:name nil
:email nil
:status nil
:photo-path nil}
:new-contact-identity ""
:contacts {}
:contact-groups {}
:discoveries {}
:discover-search-tags []
:tags {}
:chats {}
:chat {:command nil
:last-message nil}
:current-chat-id console-chat-id
:contacts-ids #{}
:selected-contacts #{}
:chats-updated-signal 0
:selected-participants #{}
:view-id nil
:navigation-stack '()
:current-tag nil
:qr-codes {}
:keyboard-height 0
:loading-allowed true
:discover-search-tags '()
:tags []
:sync-state :done
:sync-listening-started nil
:status-module-initialized? (or p/ios? js/goog.DEBUG)
:edit-mode {}
:network :testnet})
(defn chat-command-path [chat-id]
[:chats chat-id :command-input :command])
(defn chat-command-to-message-id-path [chat-id]
[:chats chat-id :command-input :to-message-id])
(defn chat-command-content-path [chat-id]
[:chats chat-id :command-input :content])
(defn chat-command-request-path [chat-id message-id]
[:chats chat-id :command-requests message-id])

View File

@ -0,0 +1,8 @@
(ns status-im.discover.specs
(:require [cljs.spec.alpha :as s]))
(s/def :discoveries/discoveries map?) ;; {id (string) descovery (map)}
(s/def :discoveries/discover-search-tags seq?)
(s/def :discoveries/tags vector?)
(s/def :discoveries/current-tag map?)
(s/def :discoveries/request-discoveries-timer int?)

View File

@ -13,8 +13,8 @@
(+ created-at ;; message is newer => priority is higher
(if (or me? contact) time/day 0) ;; user exists in contact list => increase priority
(if (or me? chat) time/day 0) ;; chat with this user exists => increase priority
(if (or me? seen-online-recently?) time/hour 0) ;; the user was online recently => increase priority
)))
(if (or me? seen-online-recently?) time/hour 0)))) ;; the user was online recently => increase priority
(defn- get-discoveries-by-tags [discoveries current-tag tags]
(let [tags' (or tags [current-tag])]

View File

@ -10,10 +10,6 @@
[status-im.constants :refer [text-content-type]]
[status-im.navigation.handlers :as nav]))
(defmethod nav/preload-data! :group-settings
[db _]
(assoc db :selected-participants #{}))
(defn save-property!
[current-chat-id property-name value]
(chats/save-property current-chat-id property-name value))
@ -32,13 +28,11 @@
(defn prepare-chat-settings
[{:keys [current-chat-id] :as db}]
(let [{:keys [name color]} (-> db
(let [{:keys [name]} (-> db
(get-in [:chats current-chat-id])
(select-keys [:name :color]))]
(assoc db :new-chat-name name
:new-chat-color color
:group-type :chat-group
:group-settings {})))
:group-type :chat-group)))
(register-handler :show-group-settings
(after (fn [_ _] (dispatch [:navigate-to :chat-group-settings])))
@ -64,10 +58,6 @@
(after delete-messages!)
clear-messages)
(register-handler :group-settings
(fn [db [_ k v]]
(assoc-in db [:group-settings k] v)))
(defn remove-identities [collection identities]
(remove #(identities (:identity %)) collection))

View File

@ -9,10 +9,6 @@
(let [identity (first (:selected-participants @db))]
(get-in @db [:contacts identity])))))
(register-sub :group-settings
(fn [db [_ k]]
(reaction (get-in @db [:group-settings k]))))
(defn get-chat-name-validation-messages [chat-name]
(filter some?
(list (when (zero? (count chat-name))

View File

@ -60,8 +60,7 @@
(fn [db _]
(-> db
(assoc :current-chat-id console-chat-id)
(dissoc :edit-mode
:transactions
(dissoc :transactions
:transactions-queue
:new-contact-identity))))
@ -224,9 +223,3 @@
(register-handler :update-geolocation
(fn [db [_ geolocation]]
(assoc db :geolocation geolocation)))
;; -- User data --------------------------------------------------------------
(register-handler :load-user-phone-number
(fn [db [_]]
;; todo fetch phone number from db
(assoc db :user-phone-number "123")))

View File

@ -3,6 +3,7 @@
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.handlers]
[status-im.subs]
[status-im.specs]
[status-im.components.react :refer [view
modal
app-registry
@ -145,5 +146,4 @@
(dispatch [:listen-to-network-status!])
(dispatch [:initialize-crypt])
(dispatch [:initialize-geth])
(dispatch [:load-user-phone-number])
(.registerComponent app-registry "StatusIm" #(r/reactify-component app-root)))

View File

@ -26,7 +26,7 @@
(defn -preload-data! [{:keys [was-modal?] :as db} & args]
(if was-modal?
(dissoc db :was-modal)
(dissoc db :was-modal?) ;;TODO check how it worked with this bug
(apply preload-data! db args)))
(register-handler :navigate-forget

View File

@ -0,0 +1,8 @@
(ns status-im.navigation.specs
(:require [cljs.spec.alpha :as s]))
(s/def :navigation/view-id keyword?) ;;current view
(s/def :navigation/modal (s/nilable keyword?)) ;;modal view id
(s/def :navigation/navigation-stack seq?) ;;stack of view's ids (keywords)
(s/def :navigation/prev-tab-view-id keyword?)
(s/def :navigation/prev-view-id keyword?)

View File

@ -246,7 +246,7 @@
(into {}))]
(-> db
(update :contact-groups merge new-groups')
(assoc :new-groups (vals new-groups')))))
(assoc :new-groups (into [] (vals new-groups'))))))
(register-handler :add-groups
(after save-groups!)
@ -263,7 +263,7 @@
(defmethod nav/preload-data! :new-public-group
[db]
(dissoc db :public-group/topic))
(dissoc db :public-group-topic))
(defn move-item [v from to]
(if (< from to)

View File

@ -21,7 +21,7 @@
[cljs.spec.alpha :as s]))
(defview new-group-toolbar []
[topic [:get :public-group/topic]]
[topic [:get :public-group-topic]]
(let [create-btn-enabled? (s/valid? ::v/topic topic)]
[view
[status-bar]
@ -33,7 +33,7 @@
#(dispatch [:create-new-public-group topic]))}]}]]))
(defview group-name-input []
[topic [:get :public-group/topic]]
[topic [:get :public-group-topic]]
[view
[text-field
{:error (cond
@ -48,7 +48,7 @@
:label-hidden? true
:input-style st/group-chat-topic-input
:auto-focus true
:on-change-text #(dispatch [:set :public-group/topic %])
:on-change-text #(dispatch [:set :public-group-topic %])
:value topic
:validator #(re-matches #"[a-z\-]*" %)
:auto-capitalize :none}]

View File

@ -0,0 +1,11 @@
(ns status-im.new-group.specs
(:require [cljs.spec.alpha :as s]))
(s/def :group/contact-groups map?) ;; {id (string) group (map)}
(s/def :group/contact-group-id string?) ;;used during editing contact group
(s/def :group/group-type keyword?) ;;contact group or chat group
(s/def :group/new-group map?) ;;used during creating or edeting contact group
(s/def :group/new-groups (s/nilable vector?)) ;;used during creating or edeting contact groups
(s/def :group/contacts-group (s/nilable map?))
(s/def :group/selected-contacts set?)
(s/def :group/groups-order seq?) ;;list of group ids

View File

@ -0,0 +1,5 @@
(ns status-im.profile.specs
(:require [cljs.spec.alpha :as s]))
;EDIT PROFILE
(s/def :profile/profile-edit map?)

View File

@ -0,0 +1,6 @@
(ns status-im.qr-scanner.specs
(:require [cljs.spec.alpha :as s]))
(s/def :qr/qr-codes map?) ;;on scan qr
(s/def :qr/qr-modal map?) ;;used in qr modal screen
(s/def :qr/current-qr-context map?)

124
src/status_im/specs.cljs Normal file
View File

@ -0,0 +1,124 @@
(ns status-im.specs
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as s]
[status-im.accounts.specs]
[status-im.navigation.specs]
[status-im.contacts.specs]
[status-im.qr-scanner.specs]
[status-im.new-group.specs]
[status-im.chat.specs]
[status-im.profile.specs]
[status-im.transactions.specs]
[status-im.discover.specs]))
;GLOBAL
(s/def ::current-public-key string?) ;;public key of current logged in account
(s/def ::first-run boolean?) ;;true when application running at first time
(s/def ::was-modal? boolean?)
(s/def ::rpc-url string?) ;;"http://localhost:8545"
(s/def ::web3 any?) ;;object? doesn't work
(s/def ::webview-bridge any?) ;;object?
(s/def ::status-module-initialized? boolean?)
(s/def ::status-node-started? (s/nilable boolean?))
(s/def ::toolbar-search map?)
(s/def ::keyboard-height number?) ;;height of native keyboard if shown
(s/def ::keyboard-max-height number?)
(s/def ::orientation keyword?) ;;:unknown - not used
(s/def ::network-status (s/nilable keyword?)) ;;:online - presence of internet connection in the phone
;NODE
(s/def ::sync-listening-started boolean?)
(s/def ::sync-state keyword?)
;NETWORK
(s/def ::network keyword?) ;;network name :testnet
(s/def ::db (allowed-keys :opt-un
[::current-public-key
::first-run
::modal
::was-modal?
::rpc-url
::web3
::webview-bridge
::status-module-initialized?
::status-node-started?
::toolbar-search
::keyboard-height
::keyboard-max-height
::orientation
::network-status
::sync-listening-started
::sync-state
::network
:accounts/accounts
:accounts/account-creation?
:accounts/creating-account?
:accounts/current-account-id
:accounts/recover
:accounts/login
:navigation/view-id
:navigation/navigation-stack
:navigation/prev-tab-view-id
:navigation/prev-view-id
:contacts/contacts
:contacts/new-contacts
:contacts/new-contact-identity
:contacts/new-contact-public-key-error
:contacts/contact-identity
:contacts/contacts-ui-props
:contacts/contact-list-ui-props
:contacts/contacts-click-handler
:contacts/contacts-click-action
:contacts/contacts-click-params
:qr/qr-codes
:qr/qr-modal
:qr/current-qr-context
:group/contact-groups
:group/contact-group-id
:group/group-type
:group/new-group
:group/new-groups
:group/contacts-group
:group/selected-contacts
:group/groups-order
:chats/chats
:chats/current-chat-id
:chats/chat-id
:chats/new-chat
:chats/new-chat-name
:chats/chat-animations
:chats/chat-ui-props
:chats/chat-list-ui-props
:chats/layout-height
:chats/expandable-view-height-to-value
:chats/global-commands
:chats/loading-allowed
:chats/message-data
:chats/message-id->transaction-id
:chats/message-status
:chats/unviewed-messages
:chats/selected-participants
:chats/chat-loaded-callbacks
:chats/commands-callbacks
:chats/command-hash-valid?
:chats/public-group-topic
:chats/confirmation-code-sms-listener
:chats/messages
:chats/loaded-chats
:chats/bot-subscriptions
:chats/new-request
:chats/raw-unviewed-messages
:profile/profile-edit
:transactions/transactions
:transactions/transactions-queue
:transactions/selected-transaction
:transactions/confirm-transactions
:transactions/confirmed-transactions-count
:transactions/transactions-list-ui-props
:transactions/transaction-details-ui-props
:transactions/wrong-password-counter
:transactions/wrong-password?
:discoveries/discoveries
:discoveries/discover-search-tags
:discoveries/tags
:discoveries/current-tag
:discoveries/request-discoveries-timer]))

View File

@ -0,0 +1,12 @@
(ns status-im.transactions.specs
(:require [cljs.spec.alpha :as s]))
(s/def :transactions/transactions map?) ;; {id (string) transaction (map)}
(s/def :transactions/transactions-queue map?) ;; {id (string) transaction (map)}
(s/def :transactions/selected-transaction map?)
(s/def :transactions/confirm-transactions map?)
(s/def :transactions/confirmed-transactions-count int?)
(s/def :transactions/transactions-list-ui-props map?)
(s/def :transactions/transaction-details-ui-props map?)
(s/def :transactions/wrong-password-counter int?)
(s/def :transactions/wrong-password? boolean?)

View File

@ -0,0 +1,12 @@
(ns status-im.utils.db
(:require [cljs.spec.alpha :as s]))
(defmacro allowed-keys
[& {:keys [req req-un opt opt-un] :as args}]
`(s/merge (s/keys ~@(apply concat (vec args)))
(s/map-of ~(set (concat req
(map (comp keyword name) req-un)
opt
(map (comp keyword name) opt-un)))
any?)))

View File

@ -1,7 +1,8 @@
(ns status-im.utils.handlers
(:require [re-frame.core :refer [after dispatch debug] :as re-core]
[clojure.string :as str]
[taoensso.timbre :as log]))
[taoensso.timbre :as log]
[cljs.spec.alpha :as s]))
(defn side-effect!
"Middleware for handlers that will not affect db."
@ -21,10 +22,20 @@
(let [new-db (handler db v)]
new-db)))
(defn check-spec
"throw an exception if db doesn't match the spec"
[handler]
(fn check-handler
[db v]
(let [new-db (handler db v)]
(when-not (s/valid? :status-im.specs/db new-db)
(throw (ex-info (str "spec check failed on: " (first v) "\n " (s/explain-str :status-im.specs/db new-db)) {})))
new-db)))
(defn register-handler
([name handler] (register-handler name nil handler))
([name middleware handler]
(re-core/register-handler name [debug-handlers-names middleware] handler)))
(re-core/register-handler name [debug-handlers-names (when js/goog.DEBUG check-spec) middleware] handler)))
(defn get-hashtags [status]
(if status