[#6094]: Persist extensions

Signed-off-by: Aleksandr Pantiukhov <alwxndr@gmail.com>
This commit is contained in:
Aleksandr Pantiukhov 2018-10-12 22:14:44 +02:00
parent 99162fa971
commit 23503dc996
No known key found for this signature in database
GPG Key ID: 153E4F21734042F5
18 changed files with 255 additions and 103 deletions

View File

@ -2,6 +2,7 @@
(:require status-im.utils.db
status-im.ui.screens.network-settings.db
status-im.ui.screens.bootnodes-settings.db
status-im.ui.screens.extensions.db
[clojure.string :as string]
[cljs.spec.alpha :as spec]
[status-im.constants :as const])
@ -39,6 +40,7 @@
(spec/def :account/network (spec/nilable string?))
(spec/def :account/networks (spec/nilable :networks/networks))
(spec/def :account/bootnodes (spec/nilable :bootnodes/bootnodes))
(spec/def :account/extensions (spec/nilable :extensions/extensions))
(spec/def :account/wnode (spec/nilable string?))
(spec/def :account/settings (spec/nilable (spec/map-of keyword? any?)))
(spec/def :account/signing-phrase :global/not-empty-string)
@ -60,7 +62,8 @@
:account/last-sign-in :account/sharing-usage-data? :account/dev-mode?
:account/seed-backed-up? :account/mnemonic
:account/wallet-set-up-passed? :account/last-request
:account/bootnodes :account/mainnet-warning-shown?]))
:account/bootnodes :account/extensions
:account/mainnet-warning-shown?]))
(spec/def :accounts/accounts (spec/nilable (spec/map-of :account/address :accounts/account)))

View File

@ -17,20 +17,52 @@
{}
bootnodes))
(defn- deserialize-networks [networks]
(reduce-kv
(fn [acc network-id props]
(assoc acc network-id (update props :config types/json->clj)))
{}
networks))
(defn- deserialize-extensions [extensions]
(reduce-kv
(fn [acc _ {:keys [id] :as extension}]
(assoc acc id extension))
{}
extensions))
(defn- deserialize-account [account]
(-> account
(update :settings core/deserialize)
(update :extensions deserialize-extensions)
(update :bootnodes deserialize-bootnodes)
(update :networks deserialize-networks)))
(defn- serialize-bootnodes [bootnodes]
(->> bootnodes
vals
(mapcat vals)))
(defn- deserialize-account [account]
(defn- serialize-networks [networks]
(map (fn [[_ props]]
(update props :config types/clj->json))
networks))
(defn- serialize-extensions [extensions]
(or (vals extensions) '()))
(defn- serialize-account [account]
(-> account
(update :settings core/deserialize)
(update :bootnodes deserialize-bootnodes)
(update :networks (partial reduce-kv
(fn [acc network-id props]
(assoc acc network-id
(update props :config types/json->clj)))
{}))))
(update :settings core/serialize)
(update :extensions serialize-extensions)
(update :bootnodes serialize-bootnodes)
(update :networks serialize-networks)))
(defn save-account-tx
"Returns tx function for saving account"
[account]
(fn [realm]
(core/create realm :account (serialize-account account) true)))
(re-frame/reg-cofx
:data-store/get-all-accounts
@ -40,16 +72,3 @@
(core/all-clj :account)
(as-> accounts
(map deserialize-account accounts))))))
(defn- serialize-account [account]
(-> account
(update :settings core/serialize)
(update :bootnodes serialize-bootnodes)
(update :networks (partial map (fn [[_ props]]
(update props :config types/clj->json))))))
(defn save-account-tx
"Returns tx function for saving account"
[account]
(fn [realm]
(core/create realm :account (serialize-account account) true)))

View File

@ -198,3 +198,8 @@
(def v11 (assoc-in v10
[:properties :installation-id]
{:type :string}))
(def v12 (assoc-in v11
[:properties :extensions]
{:type :list
:objectType :extension}))

View File

@ -2,6 +2,7 @@
(:require [status-im.data-store.realm.schemas.base.network :as network]
[status-im.data-store.realm.schemas.base.account :as account]
[status-im.data-store.realm.schemas.base.bootnode :as bootnode]
[status-im.data-store.realm.schemas.base.extension :as extension]
[status-im.data-store.realm.schemas.base.migrations :as migrations]))
(def v1 [network/v1
@ -41,6 +42,11 @@
bootnode/v4
account/v11])
(def v12 [network/v1
bootnode/v4
extension/v12
account/v12])
;; put schemas ordered by version
(def schemas [{:schema v1
:schemaVersion 1
@ -74,4 +80,7 @@
:migration migrations/v10}
{:schema v11
:schemaVersion 11
:migration migrations/v11}])
:migration migrations/v11}
{:schema v12
:schemaVersion 12
:migration migrations/v12}])

View File

@ -0,0 +1,11 @@
(ns status-im.data-store.realm.schemas.base.extension)
(def v12 {:name :extension
:primaryKey :id
:properties {:id :string
:name {:type :string}
:url {:type :string}
:active? {:type :bool
:default true}
:data {:type :string
:optional true}}})

View File

@ -87,3 +87,6 @@
installation-id (random/guid)]
(when (string/blank? old-installation-id)
(aset account "installation-id" installation-id))))))
(defn v12 [old-realm new-realm]
(log/debug "migrating base database v12: " old-realm new-realm))

View File

@ -416,6 +416,32 @@
(fn [cofx [_ _ url]]
(extensions/set-extension-url-from-qr cofx url)))
(handlers/register-handler-fx
:extensions.ui/add-extension-pressed
(fn [cofx [_ extension-id]]
(extensions/edit cofx extension-id)))
(handlers/register-handler-fx
:extensions.ui/input-changed
(fn [cofx [_ input-key value]]
(extensions/set-input cofx input-key value)))
(handlers/register-handler-fx
:extensions.ui/activation-checkbox-pressed
(fn [cofx [_ id state]]
(extensions/toggle-activation cofx id state)))
(handlers/register-handler-fx
:extensions.ui/show-button-pressed
(fn [_ [_ uri]]
{:extension/load [uri :extension/stage]}))
(handlers/register-handler-fx
:extensions.ui/install-button-pressed
[(re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ data]]
(extensions/install cofx data)))
;; log-level module
(handlers/register-handler-fx

View File

@ -1,14 +1,17 @@
(ns status-im.extensions.core
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[pluto.reader :as reader]
[pluto.registry :as registry]
[pluto.storages :as storages]
[re-frame.core :as re-frame]
[status-im.accounts.update.core :as accounts.update]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.impl.transactions :as transactions]
[status-im.ui.components.react :as react]
[status-im.ui.components.button.view :as button]
[status-im.utils.handlers :as handlers]
[status-im.i18n :as i18n]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.handlers :as handlers]
[status-im.utils.fx :as fx]))
(re-frame/reg-fx
@ -183,3 +186,63 @@
[cofx url]
(fx/merge (assoc-in cofx [:db :extension-url] url)
(navigation/navigate-back)))
(fx/defn set-input
[{:keys [db]} input-key value]
{:db (update db :extensions/manage assoc input-key {:value value})})
(fx/defn fetch [cofx id]
(get-in cofx [:db :account/account :extensions id]))
(fx/defn edit
[cofx id]
(let [{:keys [url]} (fetch cofx id)]
(fx/merge (set-input cofx :url (str url))
(navigation/navigate-to-cofx :edit-extension nil))))
(fx/defn add
[cofx extension-data active?]
(when-let [extension-key (get-in extension-data ['meta :name])]
(fx/merge cofx
#(registry/add extension-data %)
(when active?
#(registry/activate extension-key %)))))
(fx/defn install
[{{:extensions/keys [manage] :account/keys [account] :as db} :db
random-id-generator :random-id-generator :as cofx}
extension-data]
(let [extension-key (get-in extension-data ['meta :name])
{:keys [url id]} manage
extension {:id (-> (:value id)
(or (random-id-generator))
(string/replace "-" ""))
:name (str extension-key)
:url (:value url)
:active? true}
new-extensions (assoc (:extensions account) (:id extension) extension)]
(fx/merge cofx
{:ui/show-confirmation {:title (i18n/label :t/success)
:content (i18n/label :t/extension-installed)
:on-accept #(re-frame/dispatch [:navigate-to-clean :my-profile])
:on-cancel nil}}
(accounts.update/account-update {:extensions new-extensions} {})
(add extension-data true))))
(fx/defn toggle-activation
[cofx id state]
(let [toggle-fn (get {true registry/activate
false registry/deactivate}
state)
extensions (get-in cofx [:db :account/account :extensions])
new-extensions (assoc-in extensions [id :active?] state)
extension-key (get-in extensions [id :name])]
(fx/merge cofx
(accounts.update/account-update {:extensions new-extensions} {:success-event nil})
#(toggle-fn extension-key %))))
(defn load-active-extensions
[{:keys [db]}]
(let [extensions (vals (get-in db [:account/account :extensions]))]
(doseq [{:keys [url active?]} extensions]
(load-from url #(re-frame/dispatch [:extension/add (-> % read-extension parse :data) active?])))))

View File

@ -6,6 +6,7 @@
[status-im.constants :as constants]
[status-im.data-store.core :as data-store]
[status-im.data-store.realm.core :as realm]
[status-im.extensions.core :as extensions]
[status-im.i18n :as i18n]
[status-im.browser.core :as browser]
[status-im.contact.core :as contact]
@ -199,6 +200,7 @@
(chat-loading/initialize-pending-messages)
(browser/initialize-browsers)
(browser/initialize-dapp-permissions)
#(extensions/load-active-extensions %)
#(when-not platform/desktop?
(initialize-wallet %))
(accounts.update/update-sign-in-time)

View File

@ -217,6 +217,7 @@
:networks/manage
:mailservers/manage
:bootnodes/manage
:extensions/manage
:inbox/wnodes
:inbox/current-id
:node/status
@ -314,7 +315,6 @@
::device-UUID
::collectible
::collectibles
::extension-url
::staged-extension
::extensions-store
:registry/registry]))

View File

@ -1,9 +1,7 @@
(ns status-im.ui.screens.extensions.add.events
(:require [re-frame.core :as re-frame]
[pluto.registry :as registry]
[status-im.extensions.core :as extensions]
[status-im.ui.screens.navigation :as navigation]
[status-im.i18n :as i18n]
[status-im.utils.handlers :as handlers]
[status-im.utils.fx :as fx]))
@ -12,23 +10,6 @@
(fn [[url follow-up-event]]
(extensions/load-from url #(re-frame/dispatch [follow-up-event (-> % extensions/read-extension extensions/parse)]))))
(handlers/register-handler-fx
:extension/install
(fn [cofx [_ extension-data]]
(let [extension-key (get-in extension-data ['meta :name])]
(fx/merge cofx
{:ui/show-confirmation {:title (i18n/label :t/success)
:content (i18n/label :t/extension-installed)
:on-accept #(re-frame/dispatch [:navigate-to-clean :my-profile])
:on-cancel nil}}
#(registry/add extension-data %)
#(registry/activate extension-key %)))))
(handlers/register-handler-fx
:extension/edit-address
(fn [{:keys [db]} [_ address]]
{:db (assoc db :extension-url address)}))
(handlers/register-handler-fx
:extension/stage
(fn [{:keys [db] :as cofx} [_ extension-data]]
@ -37,14 +18,6 @@
(navigation/navigate-to-cofx :show-extension nil))))
(handlers/register-handler-fx
:extension/show
(fn [cofx [_ uri]]
{:extension/load [uri :extension/stage]}))
(handlers/register-handler-fx
:extension/toggle-activation
(fn [cofx [_ id state]]
(when-let [toggle-fn (get {true registry/activate
false registry/deactivate}
state)]
(toggle-fn id cofx))))
:extension/add
(fn [cofx [_ data active?]]
(extensions/add cofx data active?)))

View File

@ -2,9 +2,9 @@
(:require [re-frame.core :as re-frame]))
(re-frame/reg-sub
:get-extension-url
:get-manage-extension
(fn [db]
(:extension-url db)))
(:extensions/manage db)))
(re-frame/reg-sub
:get-staged-extension

View File

@ -6,12 +6,10 @@
[status-im.i18n :as i18n]
[status-im.ui.components.react :as react]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.text-input.view :as text-input]
[status-im.ui.screens.extensions.add.styles :as styles]))
@ -69,7 +67,7 @@
{:forward? true
:label (i18n/label :t/install)
:disabled? (not (empty? errors))
:on-press #(re-frame/dispatch [:extension/install data])}]]]]
:on-press #(re-frame/dispatch [:extensions.ui/install-button-pressed data])}]]]]
[react/view styles/screen
[status-bar/status-bar]
[react/view {:flex 1}
@ -85,26 +83,27 @@
[react/view
[vector-icons/icon :icons/qr {:color colors/blue}]]])
(views/defview add-extension []
(views/letsubs [extension-url [:get-extension-url]]
[react/view styles/screen
[status-bar/status-bar]
[react/keyboard-avoiding-view components.styles/flex
[toolbar/simple-toolbar (i18n/label :t/extension-find)]
[react/scroll-view {:keyboard-should-persist-taps :handled}
[react/view styles/wrapper
[text-input/text-input-with-label
{:label (i18n/label :t/extension-address)
:style styles/input
:container styles/input-container
:placeholder (i18n/label :t/extension-url)
:content qr-code
:default-value extension-url
:on-change-text #(re-frame/dispatch [:extension/edit-address %])}]]]
[react/view styles/bottom-container
[react/view components.styles/flex]
[components.common/bottom-button
{:forward? true
:label (i18n/label :t/find)
:disabled? (not (extensions/valid-uri? extension-url))
:on-press #(re-frame/dispatch [:extension/show (string/trim extension-url)])}]]]]))
(views/defview edit-extension []
(views/letsubs [manage-extension [:get-manage-extension]]
(let [url (get-in manage-extension [:url :value])]
[react/view styles/screen
[status-bar/status-bar]
[react/keyboard-avoiding-view components.styles/flex
[toolbar/simple-toolbar (i18n/label :t/extension-find)]
[react/scroll-view {:keyboard-should-persist-taps :handled}
[react/view styles/wrapper
[text-input/text-input-with-label
{:label (i18n/label :t/extension-address)
:style styles/input
:container styles/input-container
:content qr-code
:default-value url
:placeholder (i18n/label :t/extension-url)
:on-change-text #(re-frame/dispatch [:extensions.ui/input-changed :url %])}]]]
[react/view styles/bottom-container
[react/view components.styles/flex]
[components.common/bottom-button
{:forward? true
:label (i18n/label :t/find)
:disabled? (not (extensions/valid-uri? url))
:on-press #(re-frame/dispatch [:extensions.ui/show-button-pressed (string/trim url)])}]]]])))

View File

@ -0,0 +1,20 @@
(ns status-im.ui.screens.extensions.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require
[clojure.string :as string]
[cljs.spec.alpha :as spec]))
(spec/def ::not-blank-string (complement string/blank?))
(spec/def :extension/id ::not-blank-string)
(spec/def :extension/name ::not-blank-string)
(spec/def :extension/url ::not-blank-string)
(spec/def :extension/active? boolean?)
(spec/def :extension/data (spec/nilable string?))
(spec/def :extension/extension (allowed-keys :req-un [:extension/id
:extension/name
:extension/url
:extension/active?]
:opt-un [:extension/data]))
(spec/def :extensions/extensions (spec/nilable (spec/map-of :extension/id :extension/extension)))

View File

@ -1,10 +1,30 @@
(ns status-im.ui.screens.extensions.styles
(:require [status-im.ui.components.colors :as colors]))
(:require [status-im.ui.components.colors :as colors])
(:require-macros [status-im.utils.styles :refer [defstyle]]))
(def wrapper
{:flex 1
:background-color colors/white})
(defstyle extension-item
{:flex-direction :row
:background-color :white
:align-items :center
:padding-horizontal 16
:ios {:height 64}
:android {:height 56}})
(def extension-item-inner
{:flex 1
:padding-horizontal 16})
(defstyle extension-item-name-text
{:color colors/black
:ios {:font-size 17
:letter-spacing -0.2
:line-height 20}
:android {:font-size 16}})
(defn wnode-icon [connected?]
{:width 40
:height 40

View File

@ -3,6 +3,8 @@
status-im.ui.screens.extensions.add.subs))
(re-frame/reg-sub
:get-extensions
(fn [db]
(seq (:registry db))))
:extensions/all-extensions
:<- [:get :account/account]
(fn [account]
(get account :extensions)))

View File

@ -14,32 +14,29 @@
[react/view (styles/wnode-icon true)
[vector-icons/icon :icons/wnode {:color :white}]])
(defn navigate-to-add-extension [wnode-id]
(re-frame/dispatch [:navigate-to :add-extension wnode-id]))
(defn- render-extension [[id {:keys [state]}]]
(defn- render-extension [{:keys [id name url active?]}]
[list/list-item-with-checkbox
{:checked? (= :active state)
:on-value-change #(re-frame/dispatch [:extension/toggle-activation id %])}
{:checked? active?
:on-value-change #(re-frame/dispatch [:extensions.ui/activation-checkbox-pressed id %])}
[list/item
wnode-icon
[list/item-content
[list/item-primary id]
[list/item-secondary id]]]])
[list/item-primary name]
[list/item-secondary url]]]])
(views/defview extensions-settings []
(views/letsubs [extensions [:get-extensions]]
(views/letsubs [extensions [:extensions/all-extensions]]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (i18n/label :t/extensions)]
[toolbar/actions
[(toolbar.actions/add false (partial navigate-to-add-extension nil))]]]
[(toolbar.actions/add false #(re-frame/dispatch [:extensions.ui/add-extension-pressed]))]]]
[react/view styles/wrapper
[list/flat-list {:data extensions
[list/flat-list {:data (vals extensions)
:default-separator? false
:key-fn first
:key-fn :id
:render-fn render-extension
:content-container-style (merge (when (zero? (count extensions)) {:flex-grow 1}) {:justify-content :center})
:empty-component [react/text {:style styles/empty-list}

View File

@ -47,7 +47,7 @@
[status-im.ui.screens.fleet-settings.views :refer [fleet-settings]]
[status-im.ui.screens.offline-messaging-settings.views :refer [offline-messaging-settings]]
[status-im.ui.screens.offline-messaging-settings.edit-mailserver.views :refer [edit-mailserver]]
[status-im.ui.screens.extensions.add.views :refer [add-extension show-extension]]
[status-im.ui.screens.extensions.add.views :refer [edit-extension show-extension]]
[status-im.ui.screens.bootnodes-settings.views :refer [bootnodes-settings]]
[status-im.ui.screens.bootnodes-settings.edit-bootnode.views :refer [edit-bootnode]]
[status-im.ui.screens.currency-settings.views :refer [currency-settings]]
@ -273,7 +273,7 @@
:edit-mailserver edit-mailserver
:help-center help-center
:extensions-settings extensions-settings
:add-extension add-extension
:edit-extension edit-extension
:show-extension show-extension
:network-settings network-settings
:network-details network-details