Merge pull request #330 from status-im/feature/sync-indicator-#312
Sync indicator (#312)
Former-commit-id: 2f99eddc27
This commit is contained in:
commit
61b007c561
|
@ -25,12 +25,12 @@
|
|||
[status-im.chat.views.new-message :refer [chat-message-new]]
|
||||
[status-im.chat.views.actions :refer [actions-view]]
|
||||
[status-im.chat.views.bottom-info :refer [bottom-info-view]]
|
||||
[status-im.chat.views.toolbar-content :refer [toolbar-content-view]]
|
||||
[status-im.i18n :refer [label label-pluralize]]
|
||||
[status-im.components.animation :as anim]
|
||||
[status-im.constants :refer [console-chat-id
|
||||
content-type-status]]
|
||||
[status-im.components.sync-state.offline :refer [offline-view]]
|
||||
[status-im.constants :refer [content-type-status]]
|
||||
[reagent.core :as r]
|
||||
[clojure.string :as str]
|
||||
[cljs-time.core :as t]))
|
||||
|
||||
(defn contacts-by-identity [contacts]
|
||||
|
@ -82,44 +82,6 @@
|
|||
(assoc :last-message (= (js/parseInt index) (dec messages-count))))]
|
||||
(list-item [chat-message message])))
|
||||
|
||||
(defn online-text [contact chat-id]
|
||||
(cond
|
||||
(= chat-id console-chat-id)
|
||||
(label :t/available)
|
||||
|
||||
contact
|
||||
(let [last-online (get contact :last-online)
|
||||
last-online-date (time/to-date last-online)
|
||||
now-date (t/now)]
|
||||
(if (and (> last-online 0)
|
||||
(<= last-online-date now-date))
|
||||
(time/time-ago last-online-date)
|
||||
(label :t/active-unknown)))
|
||||
|
||||
:else (label :t/active-unknown)))
|
||||
|
||||
(defn toolbar-content []
|
||||
(let [{:keys [group-chat name contacts chat-id]} (subscribe [:chat-properties [:group-chat :name :contacts :chat-id]])
|
||||
show-actions (subscribe [:chat-ui-props :show-actions?])
|
||||
contact (subscribe [:get-in [:contacts @chat-id]])]
|
||||
(fn []
|
||||
[view (st/chat-name-view @show-actions)
|
||||
[text {:style st/chat-name-text
|
||||
:number-of-lines 1}
|
||||
(if (str/blank? @name)
|
||||
(label :t/user-anonymous)
|
||||
(or @name (label :t/chat-name)))]
|
||||
(if @group-chat
|
||||
[view {:flexDirection :row}
|
||||
[icon :group st/group-icon]
|
||||
[text {:style st/members
|
||||
:font :medium}
|
||||
(let [cnt (inc (count @contacts))]
|
||||
(label-pluralize cnt :t/members-active))]]
|
||||
[text {:style st/last-activity
|
||||
:font :default}
|
||||
(online-text @contact @chat-id)])])))
|
||||
|
||||
(defn toolbar-action []
|
||||
(let [show-actions (subscribe [:chat-ui-props :show-actions?])]
|
||||
(fn []
|
||||
|
@ -138,7 +100,7 @@
|
|||
[view
|
||||
[status-bar]
|
||||
[toolbar {:hide-nav? show-actions
|
||||
:custom-content [toolbar-content]
|
||||
:custom-content [toolbar-content-view]
|
||||
:custom-action [toolbar-action]
|
||||
:style (get-in platform-specific [:component-styles :toolbar])}]])
|
||||
|
||||
|
@ -222,4 +184,5 @@
|
|||
(when @show-actions?
|
||||
[actions-view])
|
||||
(when @show-bottom-info?
|
||||
[bottom-info-view])])})))
|
||||
[bottom-info-view])
|
||||
[offline-view {:top (get-in platform-specific [:component-styles :status-bar :default :height])}]])})))
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
|
||||
(def chat-view
|
||||
{:flex 1
|
||||
:backgroundColor chat-background})
|
||||
:background-color chat-background})
|
||||
|
||||
(def toolbar-container
|
||||
{})
|
||||
|
||||
(defn messages-container [bottom]
|
||||
{:flex 1
|
||||
|
@ -26,6 +29,7 @@
|
|||
(def action
|
||||
{:width 56
|
||||
:height 56
|
||||
:margin-top -2
|
||||
:alignItems :center
|
||||
:justifyContent :center})
|
||||
|
||||
|
@ -41,13 +45,12 @@
|
|||
|
||||
(defn chat-name-view [show-actions]
|
||||
{:flex 1
|
||||
:marginLeft (if show-actions 16 0)
|
||||
:alignItems :flex-start
|
||||
:justifyContent :center})
|
||||
:margin-left (if show-actions 16 0)
|
||||
:align-items :flex-start
|
||||
:justify-content :center})
|
||||
|
||||
(def chat-name-text
|
||||
{:marginTop -2.5
|
||||
:color text1-color
|
||||
{:color text1-color
|
||||
:fontSize 16})
|
||||
|
||||
(def group-icon
|
||||
|
@ -66,9 +69,13 @@
|
|||
:color text2-color})
|
||||
|
||||
(def last-activity
|
||||
{:marginTop 1
|
||||
:color text2-color
|
||||
:fontSize 12})
|
||||
{:margin-top 3
|
||||
:height 18})
|
||||
|
||||
(def last-activity-text
|
||||
{:color text2-color
|
||||
:background-color :transparent
|
||||
:font-size 12})
|
||||
|
||||
(defn actions-wrapper [status-bar-height]
|
||||
{:backgroundColor toolbar-background1
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
(ns status-im.chat.views.toolbar-content
|
||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||
[clojure.string :as str]
|
||||
[cljs-time.core :as t]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
icon]]
|
||||
[status-im.i18n :refer [label label-pluralize]]
|
||||
[status-im.chat.styles.screen :as st]
|
||||
[status-im.components.refreshable-text.view :refer [refreshable-text]]
|
||||
[status-im.utils.datetime :as time]
|
||||
[status-im.constants :refer [console-chat-id]]))
|
||||
|
||||
(defn online-text [contact chat-id]
|
||||
(cond
|
||||
(= chat-id console-chat-id) (label :t/available)
|
||||
contact (let [last-online (get contact :last-online)
|
||||
last-online-date (time/to-date last-online)
|
||||
now-date (t/now)]
|
||||
(if (and (> last-online 0)
|
||||
(<= last-online-date now-date))
|
||||
(time/time-ago last-online-date)
|
||||
(label :t/active-unknown)))
|
||||
:else (label :t/active-unknown)))
|
||||
|
||||
(defn last-activity [{:keys [online-text sync-state]}]
|
||||
[refreshable-text {:style st/last-activity
|
||||
:text-style st/last-activity-text
|
||||
:font :default
|
||||
:value (case sync-state
|
||||
:in-progress (label :t/sync-in-progress)
|
||||
:synced (label :t/sync-synced)
|
||||
online-text)}])
|
||||
|
||||
(defn group-last-activity [{:keys [contacts sync-state]}]
|
||||
(if (or (= sync-state :in-progress)
|
||||
(= sync-state :synced))
|
||||
[last-activity {:sync-state sync-state}]
|
||||
[view {:flex-direction :row}
|
||||
[icon :group st/group-icon]
|
||||
[text {:style st/members
|
||||
:font :medium}
|
||||
(let [cnt (inc (count contacts))]
|
||||
(label-pluralize cnt :t/members-active))]]))
|
||||
|
||||
(defn toolbar-content-view []
|
||||
(let [{:keys [group-chat
|
||||
name
|
||||
contacts
|
||||
chat-id]} (subscribe [:chat-properties [:group-chat :name :contacts :chat-id]])
|
||||
show-actions (subscribe [:chat-ui-props :show-actions?])
|
||||
contact (subscribe [:get-in [:contacts @chat-id]])
|
||||
sync-state (subscribe [:get :sync-state])]
|
||||
(fn []
|
||||
[view (st/chat-name-view @show-actions)
|
||||
[text {:style st/chat-name-text
|
||||
:number-of-lines 1}
|
||||
(if (str/blank? @name)
|
||||
(label :t/user-anonymous)
|
||||
(or @name (label :t/chat-name)))]
|
||||
(if @group-chat
|
||||
[group-last-activity {:contacts @contacts
|
||||
:sync-state @sync-state}]
|
||||
[last-activity {:online-text (online-text @contact @chat-id)
|
||||
:sync-state @sync-state}])])))
|
|
@ -24,6 +24,7 @@
|
|||
[status-im.chats-list.styles :as st]
|
||||
[status-im.utils.platform :refer [platform-specific]]
|
||||
[status-im.components.tabs.bottom-gradient :refer [bottom-gradient]]
|
||||
[status-im.components.sync-state.offline :refer [offline-view]]
|
||||
[status-im.components.tabs.styles :refer [tabs-height]]))
|
||||
|
||||
(defview chats-list-toolbar []
|
||||
|
@ -86,4 +87,5 @@
|
|||
:style st/list-container}]
|
||||
(when (get-in platform-specific [:chats :action-button?])
|
||||
[chats-action-button])
|
||||
[bottom-gradient]])
|
||||
[bottom-gradient]
|
||||
[offline-view]])
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
(ns status-im.components.refreshable-text.view
|
||||
(:require [reagent.core :as r]
|
||||
[reagent.impl.util :as ru]
|
||||
[status-im.components.react :refer [view
|
||||
animated-view
|
||||
text]]
|
||||
[status-im.components.animation :as anim]))
|
||||
|
||||
(defn start-animation [{:keys [old-value-top
|
||||
new-value-top
|
||||
old-value-opacity
|
||||
new-value-opacity]}]
|
||||
(anim/start
|
||||
(anim/timing old-value-top {:toValue 10
|
||||
:duration 300}))
|
||||
(anim/start
|
||||
(anim/timing new-value-top {:toValue 0
|
||||
:duration 300}))
|
||||
(anim/start
|
||||
(anim/timing old-value-opacity {:toValue 0
|
||||
:duration 300}))
|
||||
(anim/start
|
||||
(anim/timing new-value-opacity {:toValue 1.0
|
||||
:duration 300})))
|
||||
|
||||
(defn refreshable-text [{:keys [value]}]
|
||||
(let [old-value-top (anim/create-value 0)
|
||||
new-value-top (anim/create-value 0)
|
||||
old-value-opacity (anim/create-value 0)
|
||||
new-value-opacity (anim/create-value 1.0)
|
||||
context {:old-value-top old-value-top
|
||||
:new-value-top new-value-top
|
||||
:old-value-opacity old-value-opacity
|
||||
:new-value-opacity new-value-opacity}]
|
||||
(r/create-class
|
||||
{:get-initial-state
|
||||
(fn []
|
||||
{:old-value nil
|
||||
:value value})
|
||||
:component-will-update
|
||||
(fn [component props]
|
||||
(let [{new-value :value} (ru/extract-props props)
|
||||
{old-value :value} (r/props component)]
|
||||
(r/set-state component {:old-value old-value
|
||||
:value new-value})
|
||||
(anim/set-value old-value-top 0)
|
||||
(anim/set-value new-value-top -10)
|
||||
(anim/set-value old-value-opacity 1.0)
|
||||
(anim/set-value new-value-opacity 0.0)
|
||||
(start-animation context)))
|
||||
:reagent-render
|
||||
(fn [{:keys [style text-style font]}]
|
||||
(let [component (r/current-component)
|
||||
{:keys [old-value value]} (r/state component)]
|
||||
[view style
|
||||
[animated-view {:style {:position :absolute
|
||||
:margin-top old-value-top
|
||||
:opacity old-value-opacity}}
|
||||
[text {:style text-style
|
||||
:font font}
|
||||
old-value]]
|
||||
[animated-view {:style {:position :absolute
|
||||
:margin-top new-value-top
|
||||
:opacity new-value-opacity}}
|
||||
[text {:style text-style
|
||||
:font font}
|
||||
value]]]))})))
|
|
@ -0,0 +1,119 @@
|
|||
(ns status-im.components.sync-state.gradient
|
||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||
[reagent.core :as r]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
animated-view
|
||||
linear-gradient
|
||||
get-dimensions]]
|
||||
[status-im.components.sync-state.styles :as st]
|
||||
[status-im.components.animation :as anim]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(def gradient-animation-duration 700)
|
||||
(def synced-disappear-delay 2500)
|
||||
(def gradient-width 250)
|
||||
(def in-progress-animation-delay 1500)
|
||||
|
||||
(def window-width (:width (get-dimensions "window")))
|
||||
|
||||
(declare start-gradient-reverse-animation)
|
||||
|
||||
(defn- start-gradient-animation [{:keys [gradient-position sync-state] :as context}]
|
||||
(when (= @sync-state :in-progress)
|
||||
(anim/start
|
||||
(anim/timing gradient-position
|
||||
{:toValue (- window-width (/ gradient-width 3))
|
||||
:duration gradient-animation-duration})
|
||||
(fn [_]
|
||||
(start-gradient-reverse-animation context)))))
|
||||
|
||||
(defn- start-gradient-reverse-animation [{:keys [gradient-position sync-state] :as context}]
|
||||
(when (= @sync-state :in-progress)
|
||||
(anim/start
|
||||
(anim/timing gradient-position
|
||||
{:toValue (- 0 (* 2 (/ gradient-width 3)))
|
||||
:duration gradient-animation-duration})
|
||||
(fn [_]
|
||||
(start-gradient-animation context)))))
|
||||
|
||||
(defn- start-synced-animation [{:keys [sync-state-opacity in-progress-opacity synced-opacity]}]
|
||||
(anim/start
|
||||
(anim/timing in-progress-opacity {:toValue 0.0
|
||||
:duration 250}))
|
||||
(anim/start
|
||||
(anim/timing synced-opacity {:toValue 1.0
|
||||
:duration 250})
|
||||
(fn [_]
|
||||
(anim/start
|
||||
(anim/timing sync-state-opacity {:toValue 0.0
|
||||
:duration 250
|
||||
:delay synced-disappear-delay})
|
||||
(fn [_]
|
||||
(dispatch [:set :sync-state :done]))))))
|
||||
|
||||
(defn start-in-progress-animation [component]
|
||||
(r/set-state component
|
||||
{:pending? true
|
||||
:animation (js/setTimeout
|
||||
(fn []
|
||||
(dispatch [:set :sync-state :in-progress])
|
||||
(r/set-state component {:pending? false}))
|
||||
in-progress-animation-delay)}))
|
||||
|
||||
(defn start-offline-animation [{:keys [sync-state-opacity]}]
|
||||
(anim/start
|
||||
(anim/timing sync-state-opacity {:toValue 0.0
|
||||
:duration 250})))
|
||||
|
||||
(defn clear-pending-animation [component]
|
||||
(let [{:keys [pending? animation]} (r/state component)]
|
||||
(when pending?
|
||||
(r/set-state component {:pending? false})
|
||||
(js/clearTimeout animation))))
|
||||
|
||||
|
||||
(defn sync-state-gradient-view []
|
||||
(let [sync-state (subscribe [:get :sync-state])
|
||||
gradient-position (anim/create-value 0)
|
||||
sync-state-opacity (anim/create-value 0.0)
|
||||
in-progress-opacity (anim/create-value 0.0)
|
||||
synced-opacity (anim/create-value 0.0)
|
||||
|
||||
context {:sync-state sync-state
|
||||
:gradient-position gradient-position
|
||||
|
||||
:sync-state-opacity sync-state-opacity
|
||||
:in-progress-opacity in-progress-opacity
|
||||
:synced-opacity synced-opacity}
|
||||
on-update (fn [component _]
|
||||
(case @sync-state
|
||||
:pending (start-in-progress-animation component)
|
||||
:in-progress (do
|
||||
(anim/set-value gradient-position 0)
|
||||
(anim/set-value sync-state-opacity 1)
|
||||
(anim/set-value in-progress-opacity 1)
|
||||
(anim/set-value synced-opacity 0)
|
||||
(start-gradient-animation context))
|
||||
:synced (start-synced-animation context)
|
||||
:done (clear-pending-animation component)
|
||||
:offline (do (clear-pending-animation component)
|
||||
(start-offline-animation context))
|
||||
(log/debug "Sync state:" @sync-state)))]
|
||||
(r/create-class
|
||||
{:component-did-mount
|
||||
on-update
|
||||
:component-did-update
|
||||
on-update
|
||||
:reagent-render
|
||||
(fn []
|
||||
[view st/sync-style-gradient
|
||||
[animated-view {:style (st/loading-wrapper sync-state-opacity)}
|
||||
[animated-view {:style (st/gradient-wrapper in-progress-opacity gradient-position)}
|
||||
[linear-gradient {:colors ["#89b1fe" "#8b5fe4" "#8b5fe4" "#89b1fe"]
|
||||
:start [0, 1]
|
||||
:end [1, 1]
|
||||
:locations [0 0.3 0.7 1]
|
||||
:style (st/gradient gradient-width)}]]
|
||||
(when (not= @sync-state :in-progress)
|
||||
[animated-view {:style (st/synced-wrapper synced-opacity window-width)}])]])})))
|
|
@ -0,0 +1,38 @@
|
|||
(ns status-im.components.sync-state.offline
|
||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||
[reagent.core :as r]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
animated-view
|
||||
linear-gradient
|
||||
get-dimensions]]
|
||||
[status-im.components.sync-state.styles :as st]
|
||||
[status-im.components.animation :as anim]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(def window-width (:width (get-dimensions "window")))
|
||||
|
||||
(defn start-offline-animation [offline-opacity]
|
||||
(anim/start
|
||||
(anim/timing offline-opacity {:toValue 1.0
|
||||
:duration 250})))
|
||||
|
||||
(defn offline-view [_]
|
||||
(let [sync-state (subscribe [:get :sync-state])
|
||||
offline-opacity (anim/create-value 0.0)
|
||||
on-update (fn [_ _]
|
||||
(anim/set-value offline-opacity 0)
|
||||
(when (= @sync-state :offline)
|
||||
(start-offline-animation offline-opacity)))]
|
||||
(r/create-class
|
||||
{:component-did-mount
|
||||
on-update
|
||||
:component-did-update
|
||||
on-update
|
||||
:reagent-render
|
||||
(fn [{:keys [top]}]
|
||||
(when (= @sync-state :offline)
|
||||
[animated-view {:style (st/offline-wrapper top offline-opacity window-width)}
|
||||
[view
|
||||
[text {:style st/offline-text}
|
||||
(label :t/offline)]]]))})))
|
|
@ -0,0 +1,41 @@
|
|||
(ns status-im.components.sync-state.styles)
|
||||
|
||||
(def sync-style-gradient
|
||||
{:position :relative
|
||||
:height 0
|
||||
:top -2})
|
||||
|
||||
(defn loading-wrapper [opacity]
|
||||
{:background-color "#89b1fe"
|
||||
:opacity opacity
|
||||
:height 2})
|
||||
|
||||
(defn gradient-wrapper [in-progress-opacity position]
|
||||
{:position :absolute
|
||||
:left position
|
||||
:opacity in-progress-opacity})
|
||||
|
||||
(defn gradient [width]
|
||||
{:width width
|
||||
:height 2})
|
||||
|
||||
(defn synced-wrapper [opacity window-width]
|
||||
{:opacity opacity
|
||||
:position :absolute
|
||||
:width window-width
|
||||
:background-color "#5fc48d"
|
||||
:height 2})
|
||||
|
||||
(defn offline-wrapper [top opacity window-width]
|
||||
{:opacity opacity
|
||||
:width window-width
|
||||
:top (+ 56 top)
|
||||
:position :absolute
|
||||
:background-color "#828b92cc"
|
||||
:height 35})
|
||||
|
||||
(def offline-text
|
||||
{:text-align :center
|
||||
:color :white
|
||||
:font-size 14
|
||||
:top 8})
|
|
@ -13,12 +13,14 @@
|
|||
(def toolbar-gradient
|
||||
{:height 4})
|
||||
|
||||
(defn toolbar [background-color]
|
||||
{:flexDirection :row
|
||||
:backgroundColor (or background-color toolbar-background1)
|
||||
:height toolbar-height
|
||||
(defn toolbar-wrapper [background-color]
|
||||
{:backgroundColor (or background-color toolbar-background1)
|
||||
:elevation 2})
|
||||
|
||||
(def toolbar
|
||||
{:flex-direction :row
|
||||
:height toolbar-height})
|
||||
|
||||
(defn toolbar-nav-actions-container [actions]
|
||||
{:width (if (and actions (> (count actions) 0))
|
||||
(-> (+ toolbar-icon-width toolbar-icon-spacing)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
text
|
||||
image
|
||||
touchable-highlight]]
|
||||
[status-im.components.sync-state.gradient :refer [sync-state-gradient-view]]
|
||||
[status-im.components.styles :refer [icon-back]]
|
||||
[status-im.components.toolbar.styles :as st]))
|
||||
|
||||
|
@ -17,8 +18,9 @@
|
|||
background-color :background-color
|
||||
custom-content :custom-content
|
||||
style :style}]
|
||||
(let [style (merge (st/toolbar background-color) style)]
|
||||
(let [style (merge (st/toolbar-wrapper background-color) style)]
|
||||
[view {:style style}
|
||||
[view st/toolbar
|
||||
[view (st/toolbar-nav-actions-container actions)
|
||||
(when (not hide-nav?)
|
||||
(if nav-action
|
||||
|
@ -42,5 +44,6 @@
|
|||
[touchable-highlight {:on-press action-handler}
|
||||
[view st/toolbar-action
|
||||
[image action-image]]])
|
||||
custom-action)]]))
|
||||
custom-action)]]
|
||||
[sync-state-gradient-view]]))
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
(def response-suggesstion-resize-duration 100)
|
||||
|
||||
(def default-number-of-messages 20)
|
||||
(def blocks-per-hour 120)
|
||||
|
||||
(def default-number-of-discovery-search-results 20)
|
||||
|
||||
|
|
|
@ -47,7 +47,10 @@
|
|||
:keyboard-height 0
|
||||
:animations {;; todo clear this
|
||||
:tabs-bar-value (anim/create-value 0)}
|
||||
:loading-allowed true})
|
||||
:loading-allowed true
|
||||
|
||||
:sync-state :done
|
||||
:sync-listener nil})
|
||||
|
||||
(defn chat-staged-commands-path [chat-id]
|
||||
[:chats chat-id :staged-commands])
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
(fn [_ [_ address]]
|
||||
(dispatch [:initialize-account-db])
|
||||
(dispatch [:initialize-protocol address])
|
||||
(dispatch [:initialize-sync-listener])
|
||||
(dispatch [:initialize-chats])
|
||||
(dispatch [:load-contacts])
|
||||
(dispatch [:init-chat])
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
[status-im.data-store.pending-messages :as pending-messages]
|
||||
[status-im.data-store.chats :as chats]
|
||||
[status-im.protocol.core :as protocol]
|
||||
[status-im.constants :refer [text-content-type]]
|
||||
[status-im.constants :refer [text-content-type
|
||||
blocks-per-hour]]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.utils.random :as random]
|
||||
[taoensso.timbre :as log :refer-macros [debug]]))
|
||||
|
@ -43,6 +44,32 @@
|
|||
(contacts/get-all))})]
|
||||
(assoc db :web3 w3)))))
|
||||
|
||||
(register-handler :update-sync-state
|
||||
(u/side-effect!
|
||||
(fn [{:keys [sync-state]} [_ error sync]]
|
||||
(let [{:keys [highestBlock currentBlock]} (js->clj sync :keywordize-keys true)
|
||||
syncing? (> (- highestBlock currentBlock) blocks-per-hour)
|
||||
new-state (cond
|
||||
error :offline
|
||||
syncing? (if (= sync-state :done)
|
||||
:pending
|
||||
:in-progress)
|
||||
:else (if (or (= sync-state :done)
|
||||
(= sync-state :pending))
|
||||
:done
|
||||
:synced))]
|
||||
(when (not= sync-state new-state)
|
||||
(dispatch [:set :sync-state new-state]))))))
|
||||
|
||||
(register-handler :initialize-sync-listener
|
||||
(fn [{:keys [web3 sync-listener] :as db} _]
|
||||
(when sync-listener
|
||||
(.stopWatching sync-listener))
|
||||
(->> (.isSyncing (.-eth web3)
|
||||
(fn [error sync]
|
||||
(dispatch [:update-sync-state error sync])))
|
||||
(assoc db :sync-listener))))
|
||||
|
||||
(register-handler :incoming-message
|
||||
(u/side-effect!
|
||||
(fn [_ [_ type {:keys [payload] :as message}]]
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
:not-implemented "!not implemented"
|
||||
:chat-name "Chat name"
|
||||
:notifications-title "Notifications and sounds"
|
||||
:offline "Offline"
|
||||
|
||||
;drawer
|
||||
:invite-friends "Invite friends"
|
||||
|
@ -23,11 +24,15 @@
|
|||
:members-active {:one "1 member, 1 active"
|
||||
:other "{{count}} members, {{count}} active"
|
||||
:zero "no members"}
|
||||
:active-online "online"
|
||||
:active-unknown "unknown"
|
||||
:available "available"
|
||||
:active-online "Online"
|
||||
:active-unknown "Unknown"
|
||||
:available "Available"
|
||||
:no-messages "No messages"
|
||||
|
||||
;sync
|
||||
:sync-in-progress "Syncing..."
|
||||
:sync-synced "In sync"
|
||||
|
||||
;messages
|
||||
:status-sending "Sending"
|
||||
:status-pending "Sending"
|
||||
|
|
Loading…
Reference in New Issue