Compare commits

..
75 changed files with 2198 additions and 2446 deletions
+31 -31
View File
@@ -22,37 +22,37 @@
:multi-lhs-hang]
:fn-map
{"reg-sub" :arg1-pair
"h/describe" :arg1-body
"h/describe-skip" :arg1-body
"h/describe-only" :arg1-body
"h/test" :arg1-body
"h/test-skip" :arg1-body
"h/test-only" :arg1-body
"test/async" :arg1-body
"test/use-fixtures" :arg1-body
"global.describe" :arg1-body
"global.test" :arg1-body
"list-comp" :binding
"defview" :arg1-body
"letsubs" :binding
"with-let" "let"
"reg-event-fx" :arg1-pair
"reg-fx" :arg1-pair
"testing" :arg1-body
"deftest-sub" :arg1-body
"h/integration-test" :arg1-body
"wait-for" :arg1-body
"with-deps-check" :arg1-body
"schema/=>" :arg1-body
"->" [:noarg1-body
{:list {:constant-pair? false :force-nl? false}
:next-inner-restore [[:list :constant-pair?]]}]
"set!" "reset!"
"assoc-when" "assoc"
"assoc-some" "assoc"
"conj-when" "conj"
"conj-some" "conj"}
{"reg-sub" :arg1-pair
"h/describe" :arg1-body
"h/describe-skip" :arg1-body
"h/describe-only" :arg1-body
"h/test" :arg1-body
"h/test-skip" :arg1-body
"h/test-only" :arg1-body
"test/async" :arg1-body
"test/use-fixtures" :arg1-body
"global.describe" :arg1-body
"global.test" :arg1-body
"list-comp" :binding
"defview" :arg1-body
"letsubs" :binding
"with-let" "let"
"reg-event-fx" :arg1-pair
"reg-fx" :arg1-pair
"testing" :arg1-body
"deftest-sub" :arg1-body
"test-async" :arg1-body
"wait-for" :arg1-body
"with-deps-check" :arg1-body
"schema/=>" :arg1-body
"->" [:noarg1-body
{:list {:constant-pair? false :force-nl? false}
:next-inner-restore [[:list :constant-pair?]]}]
"set!" "reset!"
"assoc-when" "assoc"
"assoc-some" "assoc"
"conj-when" "conj"
"conj-some" "conj"}
:style-map
{:no-comma {:map {:comma? false}}
+1 -1
View File
@@ -1 +1 @@
1.25.0
2.28.0
@@ -4,13 +4,6 @@
[clojure.walk :as walk]
[status-im.constants :as constants]))
(defn rpc->channel-permissions
[rpc-channels-permissions]
(update-vals rpc-channels-permissions
(fn [{:keys [viewAndPostPermissions viewOnlyPermissions]}]
{:view-only (set/rename-keys viewOnlyPermissions {:satisfied :satisfied?})
:view-and-post (set/rename-keys viewAndPostPermissions {:satisfied :satisfied?})})))
(defn <-revealed-accounts-rpc
[accounts]
(mapv
@@ -34,8 +27,9 @@
(assoc acc
(name k)
(-> v
(assoc :can-post? (:canPost v))
(dissoc :canPost)
(assoc :token-gated? (:tokenGated v)
:can-post? (:canPost v))
(dissoc :canPost :tokenGated)
(update :members walk/stringify-keys))))
{}
chats))
@@ -1,23 +0,0 @@
(ns legacy.status-im.data-store.communities-test
(:require
[cljs.test :refer [deftest is]]
[legacy.status-im.data-store.communities :as sut]))
(def permissions
{"community-id-chat-1"
{:viewOnlyPermissions {:satisfied false
:permissions {:token-permission-id-01 {:criteria [false]}}}
:viewAndPostPermissions {:satisfied true :permissions {}}}
"community-id-chat-2"
{:viewOnlyPermissions {:satisfied true :permissions {}}
:viewAndPostPermissions {:satisfied true :permissions {}}}})
(deftest rpc->channel-permissions-test
(is (= {"community-id-chat-1"
{:view-only {:satisfied? false
:permissions {:token-permission-id-01 {:criteria [false]}}}
:view-and-post {:satisfied? true :permissions {}}}
"community-id-chat-2"
{:view-only {:satisfied? true :permissions {}}
:view-and-post {:satisfied? true :permissions {}}}}
(sut/rpc->channel-permissions permissions))))
@@ -28,8 +28,7 @@
[{:keys [db] :as cofx} {:keys [auth-method logout?]}]
(let [key-uid (get-in db [:profile/profile :key-uid])]
(rf/merge cofx
{:set-root :progress
:effects.shell/reset-state nil
{:effects.shell/reset-state nil
:hide-popover nil
::logout nil
:profile.settings/webview-debug-changed false
+47 -40
View File
@@ -1,7 +1,6 @@
(ns legacy.status-im.ui.screens.screens
(:require
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.components.icons.icons :as icons]
[legacy.status-im.ui.screens.about-app.views :as about-app]
[legacy.status-im.ui.screens.advanced-settings.views :as advanced-settings]
[legacy.status-im.ui.screens.appearance.views :as appearance]
@@ -48,10 +47,18 @@
[status-im.contexts.chat.group-details.view :as group-details]
[utils.i18n :as i18n]))
(defn right-button-options
[id icon]
{:id id
:icon (icons/icon-source icon)})
(defn topbar-options
[title]
{:elevation 0
:title {:color (if (colors/dark?) colors/white colors/black)
:text (i18n/label title)}
:rightButtonColor (if (colors/dark?) colors/white colors/black)
:background {:color (if (colors/dark?) colors/black colors/white)}
:backButton {:color (if (colors/dark?) colors/white colors/black)
:id :legacy-back-button
:testID :back-button
:visible true
:popStackOnPress false}})
(defn screens
[]
@@ -77,7 +84,7 @@
{:name :stickers
:options {:insets {:top? true}
:topBar {:title {:text (i18n/label :t/sticker-market)}}}
:topBar (topbar-options :t/sticker-market)}
:component stickers/packs}
{:name :stickers-pack
@@ -90,7 +97,7 @@
:component group-chat/new-group}
{:name :currency-settings
:options {:topBar {:title {:text (i18n/label :t/main-currency)}}
:options {:topBar (topbar-options :t/main-currency)
:insets {:top? true}}
:component currency-settings/currency-settings}
@@ -100,27 +107,27 @@
:options {:topBar {:visible false}}
:component profile.user/my-profile}
{:name :contacts-list
:options {:topBar {:title {:text (i18n/label :t/contacts)}}
:options {:topBar (topbar-options :t/contacts)
:insets {:top? true}}
:component contacts-list/contacts-list}
{:name :ens-main
:options {:topBar {:title {:text (i18n/label :t/ens-usernames)}}
:options {:topBar (topbar-options :t/ens-usernames)
:insets {:top? true}}
:component ens/main}
{:name :ens-search
:options {:topBar {:title {:text (i18n/label :t/ens-your-username)}}
:options {:topBar (topbar-options :t/ens-your-username)
:insets {:top? true}}
:component ens/search}
{:name :ens-checkout
:options {:topBar {:title {:text (i18n/label :t/ens-your-username)}}
:options {:topBar (topbar-options :t/ens-your-username)
:insets {:top? true}}
:component ens/checkout}
{:name :ens-confirmation
:options {:topBar {:title {:text (i18n/label :t/ens-your-username)}}
:options {:topBar (topbar-options :t/ens-your-username)
:insets {:top? true}}
:component ens/confirmation}
{:name :ens-terms
:options {:topBar {:title {:text (i18n/label :t/ens-terms-registration)}}
:options {:topBar (topbar-options :t/ens-terms-registration)
:insets {:top? true}}
:component ens/terms}
{:name :ens-name-details
@@ -128,7 +135,7 @@
:options {:insets {:top? true}}
:component ens/name-details}
{:name :blocked-users-list
:options {:topBar {:title {:text (i18n/label :t/blocked-users)}}
:options {:topBar (topbar-options :t/blocked-users)
:insets {:top? true}}
:component contacts-list/blocked-users-list}
{:name :wakuv2-settings
@@ -144,7 +151,7 @@
:options {:insets {:top? true}}
:component bootnodes-settings/bootnodes-settings}
{:name :installations
:options {:topBar {:title {:text (i18n/label :t/devices)}}
:options {:topBar (topbar-options (i18n/label :t/devices))
:insets {:top? true}}
:component pairing/installations}
{:name :edit-bootnode
@@ -160,63 +167,63 @@
:options {:insets {:top? true}}
:component edit-mailserver/edit-mailserver}
{:name :dapps-permissions
:options {:topBar {:title {:text (i18n/label :t/dapps-permissions)}}
:options {:topBar (topbar-options :t/dapps-permissions)
:insets {:top? true}}
:component dapps-permissions/dapps-permissions}
{:name :privacy-and-security
:options {:topBar {:title {:text (i18n/label :t/privacy-and-security)}}
:options {:topBar (topbar-options :t/privacy-and-security)
:insets {:top? true}}
:component privacy-and-security/privacy-and-security}
{:name :messages-from-contacts-only
:options {:topBar {:title {:text (i18n/label :t/accept-new-chats-from)}}
:options {:topBar (topbar-options :t/accept-new-chats-from)
:insets {:top? true}}
:component messages-from-contacts-only/messages-from-contacts-only-view}
{:name :appearance
:options {:topBar {:title {:text (i18n/label :t/appearance)}}
:options {:topBar (topbar-options :t/appearance)
:insets {:top? true}}
:component appearance/appearance-view}
{:name :privacy-and-security-profile-pic-show-to
:options {:topbar {:title {:text (i18n/label :t/show-profile-pictures-to)}}
:options {:topbar (topbar-options :t/show-profile-pictures-to)
:insets {:top? true}}
:component privacy-and-security/profile-pic-show-to}
{:name :privacy-and-security-profile-pic
:options {:topBar {:title {:text (i18n/label :t/show-profile-pictures)}}
:options {:topBar (topbar-options :t/show-profile-pictures)
:insets {:top? true}}
:component privacy-and-security/profile-pic}
{:name :notifications
:options {:topBar {:title {:text (i18n/label :t/notification-settings)}}
:options {:topBar (topbar-options :t/notification-settings)
:insets {:top? true}}
:component notifications-settings/notifications-settings}
{:name :sync-settings
:options {:topBar {:title {:text (i18n/label :t/sync-settings)}}
:options {:topBar (topbar-options :t/sync-settings)
:insets {:top? true}}
:component sync-settings/sync-settings}
{:name :advanced-settings
:options {:topBar {:title {:text (i18n/label :t/advanced)}}
:options {:topBar (topbar-options :t/advanced)
:insets {:top? true}}
:component advanced-settings/advanced-settings}
{:name :help-center
:options {:topBar {:title {:text (i18n/label :t/need-help)}}
:options {:topBar (topbar-options :t/need-help)
:insets {:top? true}}
:component help-center/help-center}
{:name :glossary
:options {:topBar {:title {:text (i18n/label :t/glossary)}}
:options {:topBar (topbar-options :t/glossary)
:insets {:top? true}}
:component glossary/glossary}
{:name :about-app
:options {:topBar {:title {:text (i18n/label :t/about-app)}}
:options {:topBar (topbar-options :t/about-app)
:insets {:top? true}}
:component about-app/about-app}
{:name :privacy-policy
:options {:topBar {:title {:text (i18n/label :t/privacy-policy)}}
:options {:topBar (topbar-options :t/privacy-policy)
:insets {:top? true}}
:component about-app/privacy-policy}
{:name :terms-of-service
:options {:topBar {:title {:text (i18n/label :t/terms-of-service)}}
:options {:topBar (topbar-options :t/terms-of-service)
:insets {:top? true}}
:component about-app/tos}
{:name :principles
:options {:topBar {:title {:text (i18n/label :t/principles)}}
:options {:topBar (topbar-options :t/principles)
:insets {:top? true}}
:component about-app/principles}
{:name :manage-dapps-permissions
@@ -224,27 +231,27 @@
:options {:insets {:top? true}}
:component dapps-permissions/manage}
{:name :rpc-usage-info
:options {:topBar {:title {:text (i18n/label :t/rpc-usage-info)}}
:options {:topBar (topbar-options :t/rpc-usage-info)
:insets {:top? true}}
:component rpc-usage-info/usage-info}
{:name :peers-stats
:options {:topBar {:title {:text (i18n/label :t/peers-stats)}}
:options {:topBar (topbar-options :t/peers-stats)
:insets {:top? true}}
:component peers-stats/peers-stats}
{:name :log-level-settings
:options {:topBar {:title {:text (i18n/label :t/log-level-settings)}}
:options {:topBar (topbar-options :t/log-level-settings)
:insets {:top? true}}
:component log-level-settings/log-level-settings}
{:name :fleet-settings
:options {:topBar {:title {:text (i18n/label :t/fleet-settings)}}
:options {:topBar (topbar-options :t/fleet-settings)
:insets {:top? true}}
:component fleet-settings/fleet-settings}
{:name :mobile-network-settings
:options {:topBar {:title {:text (i18n/label :t/mobile-network-settings)}}
:options {:topBar (topbar-options :t/mobile-network-settings)
:insets {:top? true}}
:component mobile-network-settings/mobile-network-settings}
{:name :backup-settings
:options {:topBar {:title {:text (i18n/label :t/backup-settings)}}
:options {:topBar (topbar-options :t/backup-settings)
:insets {:top? true}}
:component backup-settings/backup-settings}
{:name :backup-seed
@@ -252,14 +259,14 @@
:options {:insets {:top? true}}
:component profile.seed/backup-seed}
{:name :reset-password
:options {:topBar {:title {:text (i18n/label :t/reset-password)}}
:options {:topBar (topbar-options :t/reset-password)
:insets {:top? true}}
:component reset-password/reset-password}
{:name :delete-profile
:insets {:bottom? true}
:component delete-profile/delete-profile}
{:name :default-sync-period-settings
:options {:topBar {:title {:text (i18n/label :t/default-sync-period)}}
:options {:topBar (topbar-options :t/default-sync-period)
:insets {:top? true}}
:component default-sync-period-settings/default-sync-period-settings}
@@ -268,7 +275,7 @@
;[Chat] Link preview settings
{:name :link-previews-settings
:options {:topBar {:title {:text (i18n/label :t/chat-link-previews)}}
:options {:topBar (topbar-options :t/chat-link-previews)
:insets {:top? true}}
:component link-previews-settings/link-previews-settings}
@@ -308,7 +315,7 @@
;;TODO WHY MODAL?
;[Profile] Notifications settings
{:name :notifications-settings
:options {:topBar {:title {:text (i18n/label :t/notification-settings)}}
:options {:topBar (topbar-options :t/notification-settings)
:popGesture false
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false}
@@ -3,7 +3,7 @@
[quo.foundations.colors :as colors]))
(def ^:private themes
{:light {:default {:bg colors/white
{:light {:default {:bg colors/neutral-5
:border colors/neutral-20
:icon colors/neutral-50
:text colors/neutral-100}
@@ -27,7 +27,6 @@
[:blur? {:optional true} [:maybe :boolean]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:on-press {:optional true} [:maybe fn?]]
[:theme :schema.common/theme]
[:title-icon {:optional true} [:maybe :keyword]]
[:account-props
[:map
+33 -35
View File
@@ -9,7 +9,6 @@
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- account-view
@@ -97,41 +96,40 @@
(colors/resolve-color customization-color theme))}]])
(defn- internal-view
[_]
(let [pressed? (reagent/atom false)
on-press-in #(reset! pressed? true)
on-press-out #(reset! pressed? false)]
(fn [{:keys [type state blur? customization-color on-press]
:or {customization-color :blue
type :default
state :default
blur? false}
:as props}]
[rn/pressable
{:style (style/container
{:state state
:blur? blur?
:customization-color customization-color
:pressed? @pressed?})
:on-press-in on-press-in
:on-press on-press
:on-press-out on-press-out
:accessibility-label :container}
[account-view props]
[rn/view {:style (when (= type :tag) style/token-tag-container)}
(cond
(#{:balance-neutral :balance-negative :balance-positive} type)
[balance-view props]
[{:keys [type state blur? customization-color on-press]
:or {customization-color :blue
type :default
state :default
blur? false}
:as props}]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))
props (assoc props :theme theme)]
[rn/pressable
{:style (style/container
{:state state
:blur? blur?
:customization-color customization-color
:pressed? pressed?})
:on-press-in on-press-in
:on-press on-press
:on-press-out on-press-out
:accessibility-label :container}
[account-view props]
[rn/view {:style (when (= type :tag) style/token-tag-container)}
(cond
(#{:balance-neutral :balance-negative :balance-positive} type)
[balance-view props]
(= type :tag)
[token-tag props]
(= type :tag)
[token-tag props]
(= type :action)
[options-button props]
(= type :action)
[options-button props]
(and (= type :default) (= state :selected))
[check-icon props])]])))
(and (= type :default) (= state :selected))
[check-icon props])]]))
(def view
(quo.theme/with-theme
(schema/instrument #'internal-view component-schema/?schema)))
(def view (schema/instrument #'internal-view component-schema/?schema))
@@ -5,7 +5,6 @@
[:action {:optional true} [:enum :icon :none]]
[:blur? {:optional true} [:maybe :boolean]]
[:on-press {:optional true} [:maybe fn?]]
[:theme :schema.common/theme]
[:account-props
[:map {:closed true}
[:type [:enum :default :watch-only]]
@@ -9,38 +9,37 @@
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- internal-view
[]
(let [state (reagent/atom :default)]
(fn [{:keys [action blur? account-props networks on-press on-options-press theme]}]
[rn/pressable
{:style (style/container {:state @state :blur? blur? :theme theme})
:on-press-in #(reset! state :pressed)
:on-press-out #(reset! state :default)
:on-press on-press
:accessibility-label :container}
[rn/view {:style style/left-container}
[account-avatar/view account-props]
[rn/view {:style {:margin-left 8}}
[text/text
{:weight :semi-bold
:size :paragraph-2}
(:name account-props)]
[address-text/view
{:networks networks
:address (:address account-props)
:format :short}]]]
(when (= action :icon)
[rn/pressable {:on-press on-options-press}
[icon/icon :i/options
{:color (if blur?
colors/white-opa-70
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))
:accessibility-label :icon}]])])))
[{:keys [action blur? account-props networks on-press on-options-press]}]
(let [theme (quo.theme/use-theme-value)
[state set-state] (rn/use-state :default)
on-press-in (rn/use-callback #(set-state :pressed))
on-press-out (rn/use-callback #(set-state :default))]
[rn/pressable
{:style (style/container {:state state :blur? blur? :theme theme})
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :container}
[rn/view {:style style/left-container}
[account-avatar/view account-props]
[rn/view {:style {:margin-left 8}}
[text/text
{:weight :semi-bold
:size :paragraph-2}
(:name account-props)]
[address-text/view
{:networks networks
:address (:address account-props)
:format :short}]]]
(when (= action :icon)
[rn/pressable {:on-press on-options-press}
[icon/icon :i/options
{:color (if blur?
colors/white-opa-70
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))
:accessibility-label :icon}]])]))
(def view
(quo.theme/with-theme
(schema/instrument #'internal-view component-schema/?schema)))
(def view (schema/instrument #'internal-view component-schema/?schema))
@@ -9,6 +9,5 @@
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:on-press {:optional true} [:maybe fn?]]
[:blur? {:optional true} [:maybe :boolean]]
[:theme :schema.common/theme]
[:active-state? {:optional true} [:maybe :boolean]]]]
:any])
+32 -32
View File
@@ -1,13 +1,12 @@
(ns quo.components.list-items.address.view
(:require
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
(quo.components.list-items.address.schema :as component-schema)
[quo.components.list-items.address.schema :as component-schema]
[quo.components.list-items.address.style :as style]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]
[utils.address :as address]))
@@ -39,34 +38,35 @@
(address/get-shortened-key address)]]]])
(defn- internal-view
[]
(let [state (reagent/atom :default)
active? (atom false)
timer (atom nil)
on-press-in (fn []
(when-not (= @state :selected)
(reset! timer (js/setTimeout #(reset! state :pressed) 100))))]
(fn [{:keys [networks address customization-color on-press active-state? blur? theme]
:or {customization-color :blue}}]
(let [on-press-out (fn []
(let [new-state (if (or (not active-state?) @active?) :default :active)]
(when @timer (js/clearTimeout @timer))
(reset! timer nil)
(reset! active? (= new-state :active))
(reset! state new-state)))]
[rn/pressable
{:style (style/container @state customization-color blur?)
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :container}
[left-container
{:theme theme
:networks networks
:address address
:blur? blur?}]]))))
(def view
(quo.theme/with-theme
(schema/instrument #'internal-view component-schema/?schema)))
[{:keys [networks address customization-color on-press active-state? blur?]
:or {customization-color :blue}}]
(let [theme (quo.theme/use-theme-value)
[state set-state] (rn/use-state :default)
active? (rn/use-ref-atom false)
timer (rn/use-ref-atom nil)
on-press-in (rn/use-callback
(fn []
(when-not (= state :selected)
(reset! timer (js/setTimeout #(set-state :pressed) 100))))
[state])
on-press-out (rn/use-callback
(fn []
(let [new-state (if (or (not active-state?) @active?) :default :active)]
(when @timer (js/clearTimeout @timer))
(reset! timer nil)
(reset! active? (= new-state :active))
(set-state new-state)))
[active-state?])]
[rn/pressable
{:style (style/container state customization-color blur?)
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :container}
[left-container
{:theme theme
:networks networks
:address address
:blur? blur?}]]))
(def view (schema/instrument #'internal-view component-schema/?schema))
+37 -39
View File
@@ -5,11 +5,10 @@
[quo.components.icon :as quo.icons]
[quo.components.list-items.channel.style :as style]
[quo.components.markdown.text :as quo.text]
[quo.theme :as theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
[quo.theme]
[react-native.core :as rn]))
(defn- view-internal
(defn view
"Options:
- notification - (nil/:notification/:mention/:mute, default: nil):
- :notification - Display a grey dot.
@@ -27,38 +26,37 @@
- on-press - (function, default: nil) - Function called when the component is pressed.
- on-long-press - (function, default: nil) - Function called when the component is long pressed.
- theme - Theme value from with-theme HOC"
[]
(let [pressed? (reagent/atom false)]
(fn [{:keys [notification locked? mentions-count customization-color emoji name on-press
on-long-press theme]}]
[rn/pressable
{:style (style/container @pressed? customization-color theme)
:accessibility-label :channel-list-item
:on-press on-press
:on-long-press on-long-press
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)}
[channel-avatar/view
{:size :size-32
:locked? locked?
:full-name name
:customization-color customization-color
:emoji emoji}]
[quo.text/text
{:style (style/label notification theme)
:weight :medium
:size :paragraph-1} (str "# " name)]
(when-not locked?
(condp = notification
:mute [quo.icons/icon :i/muted
{:color (style/mute-notification-icon-color theme)}]
:mention [counter/view
{:customization-color customization-color
:container-style (style/counter mentions-count)}
mentions-count]
:notification [quo.icons/icon :i/notification
{:color (style/mute-notification-icon-color theme)
:accessibility-label :unviewed-messages-public}]
nil))])))
(def view (theme/with-theme view-internal))
[{:keys [notification locked? mentions-count customization-color emoji name on-press on-long-press]}]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))]
[rn/pressable
{:style (style/container pressed? customization-color theme)
:accessibility-label :channel-list-item
:on-press on-press
:on-long-press on-long-press
:on-press-in on-press-in
:on-press-out on-press-out}
[channel-avatar/view
{:size :size-32
:locked? locked?
:full-name name
:customization-color customization-color
:emoji emoji}]
[quo.text/text
{:style (style/label notification theme)
:weight :medium
:size :paragraph-1} (str "# " name)]
(when-not locked?
(condp = notification
:mute [quo.icons/icon :i/muted
{:color (style/mute-notification-icon-color theme)}]
:mention [counter/view
{:customization-color customization-color
:container-style (style/counter mentions-count)}
mentions-count]
:notification [quo.icons/icon :i/notification
{:color (style/mute-notification-icon-color theme)
:accessibility-label :unviewed-messages-public}]
nil))]))
@@ -7,8 +7,7 @@
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
[react-native.core :as rn]))
(defn- logo-component
[logo]
@@ -83,7 +82,7 @@
:style {:margin-left 10}}
component])))
(defn- view-internal
(defn view
"Options:
:type - :discover/engage/share
@@ -116,49 +115,48 @@
:unread-count - number - When the info is :mention, it will be used to show
the number of unread mentions.
"
[]
(let [pressed? (reagent/atom false)]
(fn [{:keys [members type info tokens locked? title subtitle
logo blur? customization-color
on-press on-long-press on-press-info
container-style unread-count theme]}]
[rn/pressable
{:accessibility-label :container
:on-press-in (fn [] (reset! pressed? true))
:on-press on-press
:on-long-press on-long-press
:on-press-out (fn [] (reset! pressed? false))
:style (merge (style/container {:blur? blur?
:customization-color customization-color
:info info
:type type
:pressed? @pressed?
:theme theme})
container-style)}
[logo-component logo]
[rn/view {:style {:flex 1}}
[title-component
{:blur? blur?
:info info
:theme theme
:title title
:type type}]
(when (and (= type :share) subtitle)
[subtitle-component subtitle blur? theme])
(when (and members (= type :discover))
[community-view/community-stats-column
{:type :list-view
:members-count (:members-count members)
:active-count (:active-count members)}])]
[info-component
{:blur? blur?
:customization-color customization-color
:info info
:type type
:locked? locked?
:on-press-info on-press-info
:theme theme
:tokens tokens
:unread-count unread-count}]])))
[{:keys [members type info tokens locked? title subtitle logo blur? customization-color
on-press on-long-press on-press-info container-style unread-count]}]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))]
[rn/pressable
{:accessibility-label :container
(def view (quo.theme/with-theme view-internal))
:on-press on-press
:on-long-press on-long-press
:on-press-in on-press-in
:on-press-out on-press-out
:style (merge (style/container {:blur? blur?
:customization-color customization-color
:info info
:type type
:pressed? pressed?
:theme theme})
container-style)}
[logo-component logo]
[rn/view {:style {:flex 1}}
[title-component
{:blur? blur?
:info info
:theme theme
:title title
:type type}]
(when (and (= type :share) subtitle)
[subtitle-component subtitle blur? theme])
(when (and members (= type :discover))
[community-view/community-stats-column
{:type :list-view
:members-count (:members-count members)
:active-count (:active-count members)}])]
[info-component
{:blur? blur?
:customization-color customization-color
:info info
:type type
:locked? locked?
:on-press-info on-press-info
:theme theme
:tokens tokens
:unread-count unread-count}]]))
+37 -39
View File
@@ -6,43 +6,41 @@
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]
[reagent.core :as reagent]))
[react-native.fast-image :as fast-image]))
(defn- view-internal
[]
(let [pressed? (reagent/atom false)]
(fn [{:keys [dapp action on-press on-press-icon theme] :as props}]
[rn/pressable
{:style (style/container (assoc props :pressed? @pressed?))
:on-press on-press
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)}
[rn/view {:style style/container-info}
[fast-image/fast-image
{:source (:avatar dapp)
:style {:width 32 :height 32}}]
[rn/view {:style style/user-info}
[text/text
{:weight :semi-bold
:size :paragraph-1
:style (style/style-text-name theme)}
(:name dapp)]
[text/text
{:weight :regular
:size :paragraph-2
:style (style/style-text-value theme)}
(:value dapp)]]]
(when (= action :icon)
[rn/pressable
{:on-press on-press-icon
:testID "dapp-component-icon"}
[icons/icon :i/options
{:color (colors/theme-colors
colors/neutral-50
colors/neutral-40
theme)
:accessibility-label :icon}]])])))
(def view
(quo.theme/with-theme view-internal))
(defn view
[{:keys [dapp action on-press on-press-icon] :as props}]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))]
[rn/pressable
{:style (style/container (assoc props :pressed? pressed?))
:on-press on-press
:on-press-in on-press-in
:on-press-out on-press-out}
[rn/view {:style style/container-info}
[fast-image/fast-image
{:source (:avatar dapp)
:style {:width 32 :height 32}}]
[rn/view {:style style/user-info}
[text/text
{:weight :semi-bold
:size :paragraph-1
:style (style/style-text-name theme)}
(:name dapp)]
[text/text
{:weight :regular
:size :paragraph-2
:style (style/style-text-value theme)}
(:value dapp)]]]
(when (= action :icon)
[rn/pressable
{:on-press on-press-icon
:testID "dapp-component-icon"}
[icons/icon :i/options
{:color (colors/theme-colors
colors/neutral-50
colors/neutral-40
theme)
:accessibility-label :icon}]])]))
@@ -5,7 +5,6 @@
[quo.components.markdown.text :as text]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- info
@@ -22,18 +21,19 @@
(if (string/blank? label) "-" label)]]])
(defn- values
[{:keys [token-value fiat-value theme]}]
[rn/view {:style style/values-container}
[text/text
{:weight :medium
:size :paragraph-2
:number-of-lines 1}
token-value]
[text/text
{:style (style/fiat-value theme)
:size :paragraph-2
:number-of-lines 1}
fiat-value]])
[{:keys [token-value fiat-value]}]
(let [theme (quo.theme/use-theme-value)]
[rn/view {:style style/values-container}
[text/text
{:weight :medium
:size :paragraph-2
:number-of-lines 1}
token-value]
[text/text
{:style (style/fiat-value theme)
:size :paragraph-2
:number-of-lines 1}
fiat-value]]))
(def ?schema
[:=>
@@ -51,26 +51,21 @@
:any])
(defn- view-internal
[]
(let [pressed? (reagent/atom false)
on-press-in #(reset! pressed? true)
on-press-out #(reset! pressed? false)]
(fn [{:keys [on-press state customization-color theme]
:as props
:or {customization-color :blue}}]
(let [internal-state (if @pressed?
:pressed
state)]
[{:keys [on-press state customization-color]
:as props
:or {customization-color :blue}}]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))
internal-state (if pressed? :pressed state)]
[rn/pressable
{:style (style/container internal-state customization-color theme)
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :network-list}
[info props]
[values props]]))
[rn/pressable
{:style (style/container internal-state customization-color theme)
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :network-list}
[info props]
[values props]]))))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal ?schema)))
(def view (schema/instrument #'view-internal ?schema))
@@ -7,70 +7,69 @@
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[utils.address :as address]))
(defn- left-container
[{:keys [blur? theme name ens address customization-color]}]
[rn/view {:style style/left-container}
[wallet-user-avatar/wallet-user-avatar
{:size :size-32
:full-name name
:customization-color customization-color}]
[rn/view {:style style/account-container}
[text/text
{:weight :semi-bold
:size :paragraph-1
:style style/name-text}
name]
[text/text {:size :paragraph-2}
[text/text
{:size :paragraph-2
:weight :monospace
:style (style/account-address blur? theme)}
(or ens (address/get-shortened-key address))]]]])
[{:keys [blur? name ens address customization-color]}]
(let [theme (quo.theme/use-theme-value)]
[rn/view {:style style/left-container}
[wallet-user-avatar/wallet-user-avatar
{:size :size-32
:full-name name
:customization-color customization-color}]
[rn/view {:style style/account-container}
[text/text
{:weight :semi-bold
:size :paragraph-1
:style style/name-text}
name]
[text/text {:size :paragraph-2}
[text/text
{:size :paragraph-2
:weight :monospace
:style (style/account-address blur? theme)}
(or ens (address/get-shortened-key address))]]]]))
(defn- internal-view
[]
(let [state (reagent/atom :default)
active? (atom false)
timer (atom nil)
on-press-in (fn []
(when-not (= @state :selected)
(reset! timer (js/setTimeout #(reset! state :pressed) 100))))]
(fn [{:keys [blur? user-props active-state? customization-color
on-press
on-options-press
theme]
:or {customization-color :blue
blur? false}}]
(let [on-press-out (fn []
(let [new-state (if (or (not active-state?) @active?) :default :active)]
(when @timer (js/clearTimeout @timer))
(reset! timer nil)
(reset! active? (= new-state :active))
(reset! state new-state)))]
[rn/pressable
{:style (style/container
{:state @state :blur? blur? :customization-color customization-color})
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :container}
[left-container
{:blur? blur?
:theme theme
:name (:name user-props)
:ens (:ens user-props)
:address (:address user-props)
:customization-color (or (:customization-color user-props) :blue)}]
(when on-options-press
[rn/pressable
{:accessibility-label :options-button
:on-press on-options-press}
[icon/icon :i/options
{:color (if blur?
colors/white-opa-70
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}]])]))))
(def view (quo.theme/with-theme internal-view))
(defn view
[{:keys [blur? user-props active-state? customization-color on-press on-options-press]
:or {customization-color :blue
blur? false}}]
(let [theme (quo.theme/use-theme-value)
[state set-state] (rn/use-state :default)
active? (rn/use-ref-atom false)
timer (rn/use-ref-atom nil)
on-press-in (rn/use-callback
(fn []
(when-not (= state :selected)
(reset! timer (js/setTimeout #(set-state :pressed) 100))))
[state])
on-press-out (rn/use-callback
(fn []
(let [new-state (if (or (not active-state?) @active?) :default :active)]
(when @timer (js/clearTimeout @timer))
(reset! timer nil)
(reset! active? (= new-state :active))
(set-state new-state)))
[active-state?])]
[rn/pressable
{:style (style/container
{:state state :blur? blur? :customization-color customization-color})
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :container}
[left-container
{:blur? blur?
:theme theme
:name (:name user-props)
:ens (:ens user-props)
:address (:address user-props)
:customization-color (or (:customization-color user-props) :blue)}]
(when on-options-press
[rn/pressable
{:accessibility-label :options-button
:on-press on-options-press}
[icon/icon :i/options
{:color (if blur?
colors/white-opa-70
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}]])]))
@@ -23,7 +23,6 @@
[:contact-props ?contact]
[:accounts {:optional true} ?accounts]
[:on-press {:optional true} [:maybe fn?]]
[:theme :schema.common/theme]
[:active-state? {:optional true} [:maybe :boolean]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]]]
:any])
@@ -9,91 +9,89 @@
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]
[utils.address :as address]
[utils.i18n :as i18n]))
(defn- account
[{:keys [emoji name address customization-color theme]}]
[rn/view
{:accessibility-label :account-container
:style style/account-container}
[account-avatar/view
{:emoji emoji
:size 16
:customization-color customization-color}]
[text/text
{:size :paragraph-2
:weight :monospace
:style (style/account-name theme)}
name]
[rn/view {:style (style/dot-divider theme)}]
[text/text
{:size :paragraph-2
:weight :monospace
:style (style/account-address theme)}
(address/get-shortened-key address)]])
[{:keys [emoji name address customization-color]}]
(let [theme (quo.theme/use-theme-value)]
[rn/view
{:accessibility-label :account-container
:style style/account-container}
[account-avatar/view
{:emoji emoji
:size 16
:customization-color customization-color}]
[text/text
{:size :paragraph-2
:weight :monospace
:style (style/account-name theme)}
name]
[rn/view {:style (style/dot-divider theme)}]
[text/text
{:size :paragraph-2
:weight :monospace
:style (style/account-address theme)}
(address/get-shortened-key address)]]))
(defn- internal-view
[]
(let [state (reagent/atom :default)
active? (atom false)
timer (atom nil)
on-press-in (fn []
(when-not (= @state :selected)
(reset! timer (js/setTimeout #(reset! state :pressed) 100))))]
(fn [{:keys [contact-props accounts active-state? customization-color on-press theme]
:or {customization-color :blue
accounts []
active-state? true}}]
(let [accounts-count (count accounts)
account-props (when (= accounts-count 1)
(first accounts))
on-press-out (fn []
[{:keys [contact-props accounts active-state? customization-color on-press]
:or {customization-color :blue
accounts []
active-state? true}}]
(let [theme (quo.theme/use-theme-value)
[state set-state] (rn/use-state :default)
active? (rn/use-ref-atom false)
timer (rn/use-ref-atom nil)
on-press (rn/use-callback #(when on-press (on-press)))
on-press-in (rn/use-callback
(fn []
(when-not (= state :selected)
(reset! timer (js/setTimeout #(set-state :pressed) 100))))
[state])
accounts-count (count accounts)
account-props (when (= accounts-count 1) (first accounts))
on-press-out (rn/use-callback
(fn []
(let [new-state (if (or (not active-state?) @active?) :default :active)]
(when @timer (js/clearTimeout @timer))
(reset! timer nil)
(reset! active? (= new-state :active))
(reset! state new-state)))]
[rn/pressable
{:style (style/container
{:state @state :customization-color customization-color})
:on-press-in on-press-in
:on-press-out on-press-out
:on-press #(when on-press
(on-press))
:accessibility-label :container}
[rn/view {:style style/left-container}
[user-avatar/user-avatar (assoc contact-props :size :small)]
[rn/view {:style style/saved-contact-container}
[rn/view
{:style style/account-title-container}
[text/text
{:weight :semi-bold
:size :paragraph-1}
(:full-name contact-props)]
[icon/icon :i/contact
{:container-style style/contact-icon-container
:accessibility-label :contact-icon
:size 12
:color (colors/theme-colors colors/primary-50 colors/primary-60 theme)
:color-2 colors/white}]]
(if account-props
[account (assoc account-props :theme theme)]
[text/text
{:accessibility-label :accounts-count
:size :paragraph-2
:style (style/accounts-count theme)}
(i18n/label :t/accounts-count {:count accounts-count})])]]
(when (> accounts-count 1)
[icon/icon :i/chevron-right
{:accessibility-label :check-icon
:size 20
:color (colors/theme-colors colors/neutral-50
colors/neutral-40
theme)}])]))))
(set-state new-state)))
[active-state?])]
[rn/pressable
{:style (style/container {:state state :customization-color customization-color})
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :container}
[rn/view {:style style/left-container}
[user-avatar/user-avatar (assoc contact-props :size :small)]
[rn/view {:style style/saved-contact-container}
[rn/view
{:style style/account-title-container}
[text/text
{:weight :semi-bold
:size :paragraph-1}
(:full-name contact-props)]
[icon/icon :i/contact
{:container-style style/contact-icon-container
:accessibility-label :contact-icon
:size 12
:color (colors/theme-colors colors/primary-50 colors/primary-60 theme)
:color-2 colors/white}]]
(if account-props
[account (assoc account-props :theme theme)]
[text/text
{:accessibility-label :accounts-count
:size :paragraph-2
:style (style/accounts-count theme)}
(i18n/label :t/accounts-count {:count accounts-count})])]]
(when (> accounts-count 1)
[icon/icon :i/chevron-right
{:accessibility-label :check-icon
:size 20
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}])]))
(def view
(quo.theme/with-theme
(schema/instrument #'internal-view component-schema/?schema)))
(def view (schema/instrument #'internal-view component-schema/?schema))
@@ -11,6 +11,5 @@
[:networks [:* [:map [:source :schema.common/image-source]]]]
[:on-press {:optional true} [:maybe fn?]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:state {:optional true} [:enum :default :active :selected]]
[:theme :schema.common/theme]]]
[:state {:optional true} [:enum :default :active :selected]]]]
:any])
@@ -8,7 +8,6 @@
[quo.components.utilities.token.view :as token]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- info
@@ -31,44 +30,40 @@
networks]]])
(defn- values
[{:keys [state token-value fiat-value customization-color theme]}]
(if (= state :selected)
[icon/icon :i/check
{:color (style/check-color customization-color theme)
:accessibility-label :check-icon}]
[rn/view {:style style/values-container}
[text/text
{:weight :medium
:size :paragraph-2
:number-of-lines 1}
token-value]
[text/text
{:style (style/fiat-value theme)
:size :paragraph-2
:number-of-lines 1}
fiat-value]]))
[{:keys [state token-value fiat-value customization-color]}]
(let [theme (quo.theme/use-theme-value)]
(if (= state :selected)
[icon/icon :i/check
{:color (style/check-color customization-color theme)
:accessibility-label :check-icon}]
[rn/view {:style style/values-container}
[text/text
{:weight :medium
:size :paragraph-2
:number-of-lines 1}
token-value]
[text/text
{:style (style/fiat-value theme)
:size :paragraph-2
:number-of-lines 1}
fiat-value]])))
(defn- view-internal
[]
(let [pressed? (reagent/atom false)
on-press-in #(reset! pressed? true)
on-press-out #(reset! pressed? false)]
(fn [{:keys [on-press state customization-color
_token _networks _token-value _fiat-value theme]
:as props
:or {customization-color :blue}}]
(let [internal-state (if @pressed?
:pressed
state)]
[rn/pressable
{:style (style/container internal-state customization-color theme)
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :token-network}
[info props]
[values props]]))))
[{:keys [on-press state customization-color _token _networks _token-value _fiat-value]
:as props
:or {customization-color :blue}}]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))
internal-state (if pressed? :pressed state)]
[rn/pressable
{:style (style/container internal-state customization-color theme)
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :token-network}
[info props]
[values props]]))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
(def view (schema/instrument #'view-internal component-schema/?schema))
@@ -17,7 +17,6 @@
[:values ?values]
[:on-press {:optional true} [:maybe fn?]]
[:on-long-press {:optional true} [:maybe fn?]]
[:theme :schema.common/theme]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:metrics? {:optional true} [:maybe :boolean]]]]
:any])
@@ -9,64 +9,67 @@
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- internal-view
[]
(let [state (reagent/atom :default)]
(fn [{:keys [theme customization-color status token metrics? values on-press on-long-press
token-name]}]
(let [bg-opacity (case @state
:active 10
:pressed 5
0)
{:keys [crypto-value fiat-value percentage-change fiat-change]} values]
[rn/pressable
{:style (style/container customization-color bg-opacity theme)
:on-press-in #(reset! state :pressed)
:on-press-out #(reset! state :default)
:on-press (fn []
(reset! state :active)
(js/setTimeout #(reset! state :default) 300)
on-press)
:on-long-press on-long-press
:accessibility-label :container}
[rn/view
{:style {:flex-direction :row
:align-items :center
:flex 1}}
[token/view {:token token :size :size-32}]
[rn/view {:style {:margin-left 8}}
[text/text {:weight :semi-bold} token-name]
[text/text
{:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
(str crypto-value " " (if token (string/upper-case (clj->js token)) ""))]]]
[rn/view
{:style {:align-items :flex-end
:justify-content :space-between}}
[text/text
{:weight :medium
:size :paragraph-2} fiat-value]
(when metrics?
[rn/view
{:style {:flex-direction :row
:align-items :center}}
[text/text
{:size :paragraph-2
:style (style/metric-text status theme)} (str percentage-change "%")]
[rn/view {:style (style/dot-divider status theme)}]
[text/text
{:size :paragraph-2
:style (style/metric-text status theme)} fiat-change]
(when (not= status :empty)
[rn/view
{:style {:margin-left 4}
:accessibility-label :arrow-icon}
[icon/icon (if (= status :positive) :i/positive :i/negative)
(style/arrow-icon status theme)]])])]]))))
[{:keys [customization-color status token metrics? values on-press on-long-press token-name]}]
(let [theme (quo.theme/use-theme-value)
[state set-state] (rn/use-state :default)
bg-opacity (case state
:active 10
:pressed 5
0)
{:keys [crypto-value
fiat-value
percentage-change
fiat-change]} values
on-press-in (rn/use-callback #(set-state :pressed))
on-press-out (rn/use-callback #(set-state :default))
on-press (rn/use-callback
(fn []
(set-state :active)
(js/setTimeout #(set-state :default) 300)
on-press))]
[rn/pressable
{:style (style/container customization-color bg-opacity theme)
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:on-long-press on-long-press
:accessibility-label :container}
[rn/view
{:style {:flex-direction :row
:align-items :center
:flex 1}}
[token/view {:token token :size :size-32}]
[rn/view {:style {:margin-left 8}}
[text/text {:weight :semi-bold} token-name]
[text/text
{:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
(str crypto-value " " (if token (string/upper-case (clj->js token)) ""))]]]
[rn/view
{:style {:align-items :flex-end
:justify-content :space-between}}
[text/text
{:weight :medium
:size :paragraph-2} fiat-value]
(when metrics?
[rn/view
{:style {:flex-direction :row
:align-items :center}}
[text/text
{:size :paragraph-2
:style (style/metric-text status theme)} (str percentage-change "%")]
[rn/view {:style (style/dot-divider status theme)}]
[text/text
{:size :paragraph-2
:style (style/metric-text status theme)} fiat-change]
(when (not= status :empty)
[rn/view
{:style {:margin-left 4}
:accessibility-label :arrow-icon}
[icon/icon (if (= status :positive) :i/positive :i/negative)
(style/arrow-icon status theme)]])])]]))
(def view
(quo.theme/with-theme
(schema/instrument #'internal-view component-schema/?schema)))
(def view (schema/instrument #'internal-view component-schema/?schema))
@@ -4,10 +4,10 @@
[quo.components.record-audio.record-audio.helpers :as helpers]
[quo.components.record-audio.record-audio.style :as style]
[quo.foundations.colors :as colors]
[react-native.core :refer [use-effect]]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]))
(defn f-delete-button
(defn delete-button
[recording? ready-to-delete? reviewing-audio? force-show-controls?]
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
translate-x (reanimated/use-shared-value (if force-show-controls? 35 20))
@@ -17,62 +17,68 @@
connector-height (reanimated/use-shared-value 12)
border-radius-first-half (reanimated/use-shared-value 8)
border-radius-second-half (reanimated/use-shared-value 8)
start-x-animation (fn []
(helpers/animate-linear-with-delay translate-x 12 50 133.33)
(helpers/animate-easing-with-delay connector-opacity 1 0 93.33)
(helpers/animate-easing-with-delay connector-width 56 83.33 80)
(helpers/animate-easing-with-delay connector-height 56 83.33 80)
(helpers/animate-easing-with-delay border-radius-first-half
28
83.33
80)
(helpers/animate-easing-with-delay border-radius-second-half
28
83.33
80))
reset-x-animation (fn []
(helpers/animate-linear translate-x 0 100)
(helpers/set-value connector-opacity 0)
(helpers/set-value connector-width 24)
(helpers/set-value connector-height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16))
fade-in-animation (fn []
(helpers/animate-linear translate-x 0 200)
(helpers/animate-linear opacity 1 200))
fade-out-animation (fn []
(helpers/animate-linear
translate-x
(if @reviewing-audio? 35 20)
200)
(if @reviewing-audio?
(helpers/animate-linear scale 0.75 200)
(helpers/animate-linear opacity 0 200))
(helpers/set-value connector-opacity 0)
(helpers/set-value connector-width 24)
(helpers/set-value connector-height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16))
fade-out-reset-animation (fn []
(helpers/animate-linear opacity 0 200)
(helpers/animate-linear-with-delay translate-x 20 0 200)
(helpers/animate-linear-with-delay scale 1 0 200))]
(use-effect (fn []
(if @recording?
(fade-in-animation)
(fade-out-animation)))
[@recording?])
(use-effect (fn []
(when-not @reviewing-audio?
(fade-out-reset-animation)))
[@reviewing-audio?])
(use-effect (fn []
(cond
@ready-to-delete?
(start-x-animation)
@recording?
(reset-x-animation)))
[@ready-to-delete?])
start-x-animation (rn/use-callback
(fn []
(helpers/animate-linear-with-delay translate-x 12 50 133.33)
(helpers/animate-easing-with-delay connector-opacity 1 0 93.33)
(helpers/animate-easing-with-delay connector-width 56 83.33 80)
(helpers/animate-easing-with-delay connector-height 56 83.33 80)
(helpers/animate-easing-with-delay border-radius-first-half
28
83.33
80)
(helpers/animate-easing-with-delay border-radius-second-half
28
83.33
80)))
reset-x-animation (rn/use-callback
(fn []
(helpers/animate-linear translate-x 0 100)
(helpers/set-value connector-opacity 0)
(helpers/set-value connector-width 24)
(helpers/set-value connector-height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16)))
fade-in-animation (rn/use-callback
(fn []
(helpers/animate-linear translate-x 0 200)
(helpers/animate-linear opacity 1 200)))
fade-out-animation (rn/use-callback
(fn []
(helpers/animate-linear
translate-x
(if reviewing-audio? 35 20)
200)
(if reviewing-audio?
(helpers/animate-linear scale 0.75 200)
(helpers/animate-linear opacity 0 200))
(helpers/set-value connector-opacity 0)
(helpers/set-value connector-width 24)
(helpers/set-value connector-height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16))
[reviewing-audio?])
fade-out-reset-animation (rn/use-callback
(fn []
(helpers/animate-linear opacity 0 200)
(helpers/animate-linear-with-delay translate-x 20 0 200)
(helpers/animate-linear-with-delay scale 1 0 200)))]
(rn/use-effect (fn []
(if recording?
(fade-in-animation)
(fade-out-animation)))
[recording?])
(rn/use-effect (fn []
(when-not reviewing-audio?
(fade-out-reset-animation)))
[reviewing-audio?])
(rn/use-effect (fn []
(cond
ready-to-delete?
(start-x-animation)
recording?
(reset-x-animation)))
[ready-to-delete?])
[:<>
[reanimated/view {:style (style/delete-button-container opacity)}
[reanimated/view
@@ -5,10 +5,10 @@
[quo.components.record-audio.record-audio.style :as style]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :refer [use-effect]]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]))
(defn f-lock-button
(defn lock-button
[recording? ready-to-lock? locked?]
(let [theme (quo.theme/use-theme-value)
translate-x-y (reanimated/use-shared-value 20)
@@ -18,54 +18,58 @@
height (reanimated/use-shared-value 12)
border-radius-first-half (reanimated/use-shared-value 8)
border-radius-second-half (reanimated/use-shared-value 8)
start-x-y-animation (fn []
(helpers/animate-linear-with-delay translate-x-y 8 50 116.66)
(helpers/animate-easing-with-delay connector-opacity 1 0 80)
(helpers/animate-easing-with-delay width 56 83.33 63.33)
(helpers/animate-easing-with-delay height 56 83.33 63.33)
(helpers/animate-easing-with-delay border-radius-first-half
28
83.33
63.33)
(helpers/animate-easing-with-delay border-radius-second-half
28
83.33
63.33))
reset-x-y-animation (fn []
(helpers/animate-linear translate-x-y 0 100)
(helpers/set-value connector-opacity 0)
(helpers/set-value width 24)
(helpers/set-value height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16))
fade-in-animation (fn []
(helpers/animate-linear translate-x-y 0 220)
(helpers/animate-linear opacity 1 220))
fade-out-animation (fn []
(helpers/animate-linear translate-x-y 20 200)
(helpers/animate-linear opacity 0 200)
(helpers/set-value connector-opacity 0)
(helpers/set-value width 24)
(helpers/set-value height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16))]
(use-effect (fn []
(if @recording?
(fade-in-animation)
(fade-out-animation)))
[@recording?])
(use-effect (fn []
(cond
@ready-to-lock?
(start-x-y-animation)
(and @recording? (not @locked?))
(reset-x-y-animation)))
[@ready-to-lock?])
(use-effect (fn []
(if @locked?
(fade-out-animation)
(reset-x-y-animation)))
[@locked?])
start-x-y-animation (rn/use-callback
(fn []
(helpers/animate-linear-with-delay translate-x-y 8 50 116.66)
(helpers/animate-easing-with-delay connector-opacity 1 0 80)
(helpers/animate-easing-with-delay width 56 83.33 63.33)
(helpers/animate-easing-with-delay height 56 83.33 63.33)
(helpers/animate-easing-with-delay border-radius-first-half
28
83.33
63.33)
(helpers/animate-easing-with-delay border-radius-second-half
28
83.33
63.33)))
reset-x-y-animation (rn/use-callback
(fn []
(helpers/animate-linear translate-x-y 0 100)
(helpers/set-value connector-opacity 0)
(helpers/set-value width 24)
(helpers/set-value height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16)))
fade-in-animation (rn/use-callback
(fn []
(helpers/animate-linear translate-x-y 0 220)
(helpers/animate-linear opacity 1 220)))
fade-out-animation (rn/use-callback
(fn []
(helpers/animate-linear translate-x-y 20 200)
(helpers/animate-linear opacity 0 200)
(helpers/set-value connector-opacity 0)
(helpers/set-value width 24)
(helpers/set-value height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16)))]
(rn/use-effect (fn []
(if recording?
(fade-in-animation)
(fade-out-animation)))
[recording?])
(rn/use-effect (fn []
(cond
ready-to-lock?
(start-x-y-animation)
(and recording? (not locked?))
(reset-x-y-animation)))
[ready-to-lock?])
(rn/use-effect (fn []
(if locked?
(fade-out-animation)
(reset-x-y-animation)))
[locked?])
[:<>
[reanimated/view {:style (style/lock-button-container opacity)}
[reanimated/view
@@ -77,6 +81,6 @@
[reanimated/view
{:style (style/lock-button translate-x-y opacity)
:pointer-events :none}
[icons/icon (if @ready-to-lock? :i/locked :i/unlocked)
[icons/icon (if ready-to-lock? :i/locked :i/unlocked)
{:color (colors/theme-colors colors/black colors/white theme)
:size 20}]]]))
@@ -3,19 +3,17 @@
[quo.components.buttons.button.view :as button]
[quo.components.record-audio.record-audio.helpers :as helpers]
[quo.components.record-audio.record-audio.style :as style]
[react-native.core :as rn :refer [use-effect]]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]))
(defn f-record-button
(defn record-button
[recording? reviewing-audio?]
(let [opacity (reanimated/use-shared-value 1)
show-animation #(helpers/set-value opacity 1)
hide-animation #(helpers/set-value opacity 0)]
(use-effect (fn []
(if (or @recording? @reviewing-audio?)
(hide-animation)
(show-animation)))
[@recording? @reviewing-audio?])
(let [opacity (reanimated/use-shared-value 1)]
(rn/use-effect (fn []
(if (or recording? reviewing-audio?)
(helpers/set-value opacity 0)
(helpers/set-value opacity 1)))
[recording? reviewing-audio?])
[reanimated/view {:style (style/record-button-container opacity)}
[button/button
{:type :outline
@@ -5,8 +5,9 @@
[quo.components.record-audio.record-audio.helpers :as helpers]
[quo.components.record-audio.record-audio.style :as style]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.audio-toolkit :as audio]
[react-native.core :as rn :refer [use-effect]]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[taoensso.timbre :as log]
@@ -28,13 +29,16 @@
(reagent/as-element
[reanimated/view {:style (style/animated-circle scale opacity color)}]))))))
(defn f-record-button-big
[{:keys [recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating?
record-button-at-initial-position? locked? reviewing-audio? recording-length-ms
clear-timeout touch-active? recorder-ref reload-recorder-fn idle? on-send on-cancel theme]}]
(let [scale (reanimated/use-shared-value 1)
(defn record-button-big
[{:keys [recording? set-recording ready-to-send? set-ready-to-send ready-to-lock? set-ready-to-lock
ready-to-delete? set-ready-to-delete record-button-is-animating?
record-button-at-initial-position? locked? set-locked reviewing-audio? recording-length-ms
set-recording-length-ms
clear-timeout touch-active? recorder-ref reload-recorder-fn idle? on-send on-cancel]}]
(let [theme (quo.theme/use-theme-value)
scale (reanimated/use-shared-value 1)
opacity (reanimated/use-shared-value 0)
opacity-from (if @ready-to-lock? opacity-from-lock opacity-from-default)
opacity-from (if ready-to-lock? opacity-from-lock opacity-from-default)
animations (map
(fn [index]
(let [ring-scale (worklets.record-audio/ring-scale scale
@@ -46,141 +50,155 @@
[opacity-from 0])}))
(range 0 5))
rings-color (cond
@ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
colors/neutral-80
theme)
@ready-to-delete? colors/danger-50
:else colors/primary-50)
ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
colors/neutral-80
theme)
ready-to-delete? colors/danger-50
:else colors/primary-50)
translate-y (reanimated/use-shared-value 0)
translate-x (reanimated/use-shared-value 0)
button-color colors/primary-50
icon-color (if (and (not (= :dark theme)) @ready-to-lock?) colors/black colors/white)
icon-color (if (and (not (= :dark theme)) ready-to-lock?) colors/black colors/white)
icon-opacity (reanimated/use-shared-value 1)
red-overlay-opacity (reanimated/use-shared-value 0)
gray-overlay-opacity (reanimated/use-shared-value 0)
complete-animation
(fn []
(cond
(and @ready-to-lock? (not @record-button-is-animating?))
(do
(reset! locked? true)
(reset! ready-to-lock? false))
(and (not @locked?) (not @reviewing-audio?))
(audio/stop-recording
@recorder-ref
(fn []
(cond
@ready-to-send?
(when on-send
(on-send {:file-path (audio/get-recorder-file-path @recorder-ref)
:duration @recording-length-ms}))
@ready-to-delete?
(when on-cancel
(on-cancel)))
(reload-recorder-fn)
(reset! recording? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(reset! ready-to-lock? false)
(reset! idle? true)
(js/setTimeout #(reset! idle? false) 1000)
(reset! recording-length-ms 0)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))))
start-animation (fn []
(helpers/set-value opacity 1)
(helpers/animate-linear scale 2.6 signal-anim-duration)
;; TODO: Research if we can implement this with withSequence method
;; from Reanimated 2
;; GitHub issue [#14561]:
;; https://github.com/status-im/status-mobile/issues/14561
(reset! clear-timeout
(js/setTimeout
(rn/use-callback
(fn []
(cond
(and ready-to-lock? (not @record-button-is-animating?))
(do
(set-locked true)
(set-ready-to-lock false))
(and (not locked?) (not reviewing-audio?))
(audio/stop-recording
@recorder-ref
(fn []
(cond
ready-to-send?
(when on-send
(on-send {:file-path (audio/get-recorder-file-path @recorder-ref)
:duration recording-length-ms}))
ready-to-delete?
(when on-cancel
(on-cancel)))
(reload-recorder-fn)
(set-recording false)
(set-ready-to-send false)
(set-ready-to-delete false)
(set-ready-to-lock false)
(reset! idle? true)
(js/setTimeout #(reset! idle? false) 1000)
(set-recording-length-ms 0)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))))
[ready-to-lock? locked? reviewing-audio? ready-to-send? recording-length-ms ready-to-delete?
idle?])
start-animation (rn/use-callback
(fn []
(helpers/set-value opacity 1)
(helpers/animate-linear scale 2.6 signal-anim-duration)
;; TODO: Research if we can implement this with withSequence method
;; from Reanimated 2
;; GitHub issue [#14561]:
;; https://github.com/status-im/status-mobile/issues/14561
(reset! clear-timeout
(js/setTimeout
(fn []
(helpers/set-value scale scale-to-each)
(helpers/animate-linear-with-delay-loop scale
scale-to-total
signal-anim-duration-2
0))
signal-anim-duration))))
stop-animation (rn/use-callback
(fn []
(helpers/set-value opacity 0)
(reanimated/cancel-animation scale)
(helpers/set-value scale 1)
(when @clear-timeout (js/clearTimeout @clear-timeout))))
start-y-animation (rn/use-callback
(fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(helpers/animate-easing translate-y -64 250)
(helpers/animate-linear-with-delay icon-opacity 0 33.33 76.66)
(js/setTimeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
250))
[complete-animation])
reset-y-animation (rn/use-callback
(fn []
(helpers/animate-easing translate-y 0 300)
(helpers/animate-linear icon-opacity 1 500)
(js/setTimeout (fn []
(reset! record-button-at-initial-position? true))
500)))
start-x-animation (rn/use-callback
(fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(helpers/animate-easing translate-x -64 250)
(helpers/animate-linear-with-delay icon-opacity 0 33.33 76.66)
(helpers/animate-linear red-overlay-opacity 1 33.33)
(js/setTimeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
250))
[complete-animation])
reset-x-animation (rn/use-callback
(fn []
(helpers/animate-easing translate-x 0 300)
(helpers/animate-linear icon-opacity 1 500)
(helpers/animate-linear red-overlay-opacity 0 100)
(js/setTimeout (fn []
(reset! record-button-at-initial-position? true))
500)))
start-x-y-animation (rn/use-callback
(fn []
(helpers/set-value scale scale-to-each)
(helpers/animate-linear-with-delay-loop scale
scale-to-total
signal-anim-duration-2
0))
signal-anim-duration)))
stop-animation (fn []
(helpers/set-value opacity 0)
(reanimated/cancel-animation scale)
(helpers/set-value scale 1)
(when @clear-timeout (js/clearTimeout @clear-timeout)))
start-y-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(helpers/animate-easing translate-y -64 250)
(helpers/animate-linear-with-delay icon-opacity 0 33.33 76.66)
(js/setTimeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
250))
reset-y-animation (fn []
(helpers/animate-easing translate-y 0 300)
(helpers/animate-linear icon-opacity 1 500)
(js/setTimeout (fn []
(reset! record-button-at-initial-position? true))
500))
start-x-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(helpers/animate-easing translate-x -64 250)
(helpers/animate-linear-with-delay icon-opacity 0 33.33 76.66)
(helpers/animate-linear red-overlay-opacity 1 33.33)
(js/setTimeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
250))
reset-x-animation (fn []
(helpers/animate-easing translate-x 0 300)
(helpers/animate-linear icon-opacity 1 500)
(helpers/animate-linear red-overlay-opacity 0 100)
(js/setTimeout (fn []
(reset! record-button-at-initial-position? true))
500))
start-x-y-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(helpers/animate-easing translate-y -44 200)
(helpers/animate-easing translate-x -44 200)
(helpers/animate-linear-with-delay icon-opacity 0 33.33 33.33)
(helpers/animate-linear gray-overlay-opacity 1 33.33)
(js/setTimeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
200))
reset-x-y-animation (fn []
(helpers/animate-easing translate-y 0 300)
(helpers/animate-easing translate-x 0 300)
(helpers/animate-linear icon-opacity 1 500)
(helpers/animate-linear gray-overlay-opacity 0 800)
(js/setTimeout (fn []
(reset! record-button-at-initial-position? true))
800))]
(use-effect (fn []
(cond
@recording?
(start-animation)
(not @ready-to-lock?)
(stop-animation)))
[@recording?])
(use-effect (fn []
(if @ready-to-lock?
(start-x-y-animation)
(reset-x-y-animation)))
[@ready-to-lock?])
(use-effect (fn []
(if @ready-to-send?
(start-y-animation)
(reset-y-animation)))
[@ready-to-send?])
(use-effect (fn []
(if @ready-to-delete?
(start-x-animation)
(reset-x-animation)))
[@ready-to-delete?])
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(helpers/animate-easing translate-y -44 200)
(helpers/animate-easing translate-x -44 200)
(helpers/animate-linear-with-delay icon-opacity 0 33.33 33.33)
(helpers/animate-linear gray-overlay-opacity 1 33.33)
(js/setTimeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
200))
[complete-animation])
reset-x-y-animation (rn/use-callback
(fn []
(helpers/animate-easing translate-y 0 300)
(helpers/animate-easing translate-x 0 300)
(helpers/animate-linear icon-opacity 1 500)
(helpers/animate-linear gray-overlay-opacity 0 800)
(js/setTimeout (fn []
(reset! record-button-at-initial-position? true))
800)))]
(rn/use-effect (fn []
(cond
recording?
(start-animation)
(not ready-to-lock?)
(stop-animation)))
[recording?])
(rn/use-effect (fn []
(if ready-to-lock?
(start-x-y-animation)
(reset-x-y-animation)))
[ready-to-lock?])
(rn/use-effect (fn []
(if ready-to-send?
(start-y-animation)
(reset-y-animation)))
[ready-to-send?])
(rn/use-effect (fn []
(if ready-to-delete?
(start-x-animation)
(reset-x-animation)))
[ready-to-delete?])
[reanimated/view
{:style (style/record-button-big-container translate-x translate-y opacity)
:pointer-events :none}
@@ -197,6 +215,6 @@
[reanimated/view {:style (style/record-button-big-red-overlay red-overlay-opacity)}]
[reanimated/view {:style (style/record-button-big-gray-overlay gray-overlay-opacity)}]
[reanimated/view {:style (style/record-button-big-icon-container icon-opacity)}
(if @locked?
(if locked?
[rn/view {:style style/stop-icon}]
[icons/icon :i/audio {:color icon-color}])]]]))
@@ -4,10 +4,10 @@
[quo.components.record-audio.record-audio.helpers :as helpers]
[quo.components.record-audio.record-audio.style :as style]
[quo.foundations.colors :as colors]
[react-native.core :refer [use-effect]]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]))
(defn f-send-button
(defn send-button
[recording? ready-to-send? reviewing-audio? force-show-controls?]
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
translate-y (reanimated/use-shared-value (if force-show-controls? 76 20))
@@ -16,65 +16,71 @@
height (reanimated/use-shared-value 24)
border-radius-first-half (reanimated/use-shared-value 16)
border-radius-second-half (reanimated/use-shared-value 8)
start-y-animation (fn []
(helpers/animate-linear-with-delay translate-y 12 50 133.33)
(helpers/animate-easing-with-delay connector-opacity 1 0 93.33)
(helpers/animate-easing-with-delay width 56 83.33 80)
(helpers/animate-easing-with-delay height 56 83.33 80)
(helpers/animate-easing-with-delay border-radius-first-half
28
83.33
80)
(helpers/animate-easing-with-delay border-radius-second-half
28
83.33
80))
reset-y-animation (fn []
(helpers/animate-linear translate-y 0 100)
(helpers/set-value connector-opacity 0)
(helpers/set-value width 12)
(helpers/set-value height 24)
(helpers/set-value border-radius-first-half 16)
(helpers/set-value border-radius-second-half 8))
fade-in-animation (fn []
(helpers/animate-linear translate-y 0 200)
(helpers/animate-linear opacity 1 200))
fade-out-animation (fn []
(when-not force-show-controls?
(helpers/animate-linear
translate-y
(if @reviewing-audio? 76 20)
200))
(when-not @reviewing-audio?
(helpers/animate-linear opacity 0 200))
(helpers/set-value connector-opacity 0)
(helpers/set-value width 24)
(helpers/set-value height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16))
fade-out-reset-animation (fn []
(helpers/animate-linear opacity 0 200)
(helpers/animate-linear-with-delay translate-y 20 0 200)
(helpers/set-value connector-opacity 0)
(helpers/set-value width 24)
(helpers/set-value height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16))]
(use-effect (fn []
(if @recording?
(fade-in-animation)
(fade-out-animation)))
[@recording?])
(use-effect (fn []
(when-not @reviewing-audio?
(fade-out-reset-animation)))
[@reviewing-audio?])
(use-effect (fn []
(cond
@ready-to-send?
(start-y-animation)
@recording? (reset-y-animation)))
[@ready-to-send?])
start-y-animation (rn/use-callback
(fn []
(helpers/animate-linear-with-delay translate-y 12 50 133.33)
(helpers/animate-easing-with-delay connector-opacity 1 0 93.33)
(helpers/animate-easing-with-delay width 56 83.33 80)
(helpers/animate-easing-with-delay height 56 83.33 80)
(helpers/animate-easing-with-delay border-radius-first-half
28
83.33
80)
(helpers/animate-easing-with-delay border-radius-second-half
28
83.33
80)))
reset-y-animation (rn/use-callback
(fn []
(helpers/animate-linear translate-y 0 100)
(helpers/set-value connector-opacity 0)
(helpers/set-value width 12)
(helpers/set-value height 24)
(helpers/set-value border-radius-first-half 16)
(helpers/set-value border-radius-second-half 8)))
fade-in-animation (rn/use-callback
(fn []
(helpers/animate-linear translate-y 0 200)
(helpers/animate-linear opacity 1 200)))
fade-out-animation (rn/use-callback
(fn []
(when-not force-show-controls?
(helpers/animate-linear
translate-y
(if reviewing-audio? 76 20)
200))
(when-not reviewing-audio?
(helpers/animate-linear opacity 0 200))
(helpers/set-value connector-opacity 0)
(helpers/set-value width 24)
(helpers/set-value height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16))
[reviewing-audio? force-show-controls?])
fade-out-reset-animation (rn/use-callback
(fn []
(helpers/animate-linear opacity 0 200)
(helpers/animate-linear-with-delay translate-y 20 0 200)
(helpers/set-value connector-opacity 0)
(helpers/set-value width 24)
(helpers/set-value height 12)
(helpers/set-value border-radius-first-half 8)
(helpers/set-value border-radius-second-half 16)))]
(rn/use-effect (fn []
(if recording?
(fade-in-animation)
(fade-out-animation)))
[recording?])
(rn/use-effect (fn []
(when-not reviewing-audio?
(fade-out-reset-animation)))
[reviewing-audio?])
(rn/use-effect (fn []
(cond
ready-to-send?
(start-y-animation)
recording? (reset-y-animation)))
[ready-to-send?])
[:<>
[reanimated/view {:style (style/send-button-container opacity)}
[reanimated/view
@@ -2,7 +2,6 @@
(:require
[quo.components.record-audio.record-audio.view :as record-audio]
[react-native.audio-toolkit :as audio]
[reagent.core :as reagent]
[test-helpers.component :as h]
[utils.datetime :as datetime]))
@@ -11,8 +10,10 @@
(h/test "renders record-audio"
(h/render [record-audio/record-audio])
(-> (h/expect (h/get-by-test-id "record-audio"))
(.toBeTruthy)))
(-> (h/wait-for #(h/get-by-test-id "record-audio"))
(.then (fn []
(-> (h/expect (h/get-by-test-id "record-audio"))
(.toBeTruthy))))))
(h/test "record-audio on-start-recording works"
(let [event (js/jest.fn)]
@@ -31,7 +32,7 @@
(h/test "record-audio on-reviewing-audio works"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)]
on-meter (atom nil)]
(h/render [record-audio/record-audio
{:on-reviewing-audio event
:record-audio-permission-granted true}])
@@ -39,7 +40,7 @@
(reset! on-meter on-meter-fn))
audio/start-recording (fn [_ on-start _]
(on-start)
(js/setInterval #(@on-meter) 100))
(js/setInterval @on-meter 100))
audio/get-recorder-file-path (fn [] "file-path")]
(h/fire-event
:on-start-should-set-responder
@@ -48,6 +49,7 @@
:locationY 70
:timestamp 0
:identifier 0}})
(with-redefs [datetime/timestamp (fn [] (+ (.now js/Date) 1000))]
(h/advance-timers-by-time 100)
(h/fire-event
@@ -63,7 +65,7 @@
(h/test "record-audio on-send works after reviewing audio"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)]
on-meter (atom nil)]
(h/render [record-audio/record-audio
{:on-send event
:record-audio-permission-granted true}])
@@ -114,14 +116,15 @@
(h/test "record-audio on-send works after sliding to the send button"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)
on-meter (atom nil)
last-now-ms (atom nil)
duration-ms (atom nil)]
(h/render [record-audio/record-audio
{:on-send event
:record-audio-permission-granted true}])
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
(reset! on-meter on-meter-fn))
(reset! on-meter on-meter-fn)
#js {:destroy #()})
audio/start-recording (fn [_ on-start _]
(on-start)
(js/setInterval #(@on-meter) 100))
@@ -170,15 +173,16 @@
(h/test "record-audio on-cancel works after reviewing audio"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)]
on-meter (atom nil)]
(h/render [record-audio/record-audio
{:on-cancel event
:record-audio-permission-granted true}])
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
(reset! on-meter on-meter-fn))
(reset! on-meter on-meter-fn)
#js {:destroy #()})
audio/start-recording (fn [_ on-start _]
(on-start)
(js/setInterval #(@on-meter) 100))]
(js/setInterval @on-meter 100))]
(h/fire-event
:on-start-should-set-responder
(h/get-by-test-id "record-audio")
@@ -195,6 +199,7 @@
:locationY 70
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
@@ -202,13 +207,12 @@
:locationY 80
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1))))))
(h/test "record-audio on-cancel works after sliding to the cancel button"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)]
on-meter (atom nil)]
(h/render [record-audio/record-audio
{:on-cancel event
:record-audio-permission-granted true}])
@@ -0,0 +1,81 @@
(ns quo.components.record-audio.record-audio.constants
(:require [react-native.audio-toolkit :as audio]
[react-native.platform :as platform]
[utils.datetime :as datetime]))
(def min-audio-duration-ms 1000)
(def max-audio-duration-ms (if platform/ios? 120800 120500))
(def metering-interval 25)
(def base-filename "am")
(def default-format ".aac")
(def min-touch-duration 150)
(def record-button-area-big
{:width 56
:height 56
:x 64
:y 64})
(def record-button-area
{:width 48
:height 48
:x 68
:y 68})
(defn delete-button-area
[{:keys [active? reviewing-audio?]}]
{:width (cond
active? 72
reviewing-audio? 32
:else 82)
:height (if reviewing-audio? 32 56)
:x (cond
active? -16
reviewing-audio? 36
:else -32)
:y (cond
active? 64
reviewing-audio? 76
:else 70)})
(defn lock-button-area
[{:keys [active?]}]
{:width (if active? 72 100)
:height (if active? 72 102)
:x -32
:y -32})
(defn send-button-area
[{:keys [active? reviewing-audio?]}]
{:width (if reviewing-audio? 32 56)
:height (cond
active? 72
reviewing-audio? 47
:else 92)
:x (if reviewing-audio? 76 32)
:y (cond
active? -16
reviewing-audio? 76
:else -32)})
(defn touch-inside-area?
[{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]}
{:keys [width height x y]}]
(let [max-x (+ x width)
max-y (+ y height)]
(and
(and
(or ignore-min-x? (>= location-x x))
(or ignore-max-x? (<= location-x max-x)))
(and
(or ignore-min-y? (>= location-y y))
(or ignore-max-y? (<= location-y max-y))))))
(def rec-options
(merge
audio/default-recorder-options
{:filename (str base-filename
(datetime/timestamp)
default-format)
:meteringInterval metering-interval}))
@@ -0,0 +1,345 @@
(ns quo.components.record-audio.record-audio.handlers
(:require [oops.core :as oops]
[quo.components.record-audio.record-audio.constants :as record-audio.constants]
[react-native.audio-toolkit :as audio]
[taoensso.timbre :as log]
[utils.datetime :as datetime]))
(def get-reload-player
(memoize
(fn
[[player-ref destroy-player set-playing-audio playing-timer set-audio-current-time-ms
set-seeking-audio]]
(fn [audio-file]
(when @player-ref
(destroy-player))
(reset! player-ref
(audio/new-player
(or audio-file (:filename record-audio.constants/rec-options))
{:autoDestroy false
:continuesToPlayInBackground false
:category audio/PLAYBACK}
(fn []
(set-playing-audio false)
(when @playing-timer
(js/clearInterval @playing-timer)
(reset! playing-timer nil)
(set-audio-current-time-ms 0)
(set-seeking-audio false)))))
(audio/prepare-player
@player-ref
#(log/debug "[record-audio] prepare player - success")
#(log/error "[record-audio] prepare player - error: " %))))))
(def get-recorder-on-meter
(memoize
(fn
[[recording-start-ms set-recording-length-ms reached-max-duration? locked? set-locked
set-reviewing-audio
idle? set-recording
set-ready-to-lock set-ready-to-send set-ready-to-delete recorder-ref output-file reload-player
on-reviewing-audio]]
(fn []
(when @recording-start-ms
(let [now-ms (datetime/timestamp)
recording-duration (- now-ms @recording-start-ms)]
(set-recording-length-ms recording-duration)
(when (>= recording-duration record-audio.constants/max-audio-duration-ms)
(reset! reached-max-duration? (not locked?))
(set-reviewing-audio true)
(reset! idle? false)
(set-locked false)
(set-recording false)
(set-ready-to-lock false)
(set-ready-to-send false)
(set-ready-to-delete false)
(audio/stop-recording
@recorder-ref
(fn []
(reset! output-file (audio/get-recorder-file-path @recorder-ref))
(reload-player nil)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))
(js/setTimeout #(reset! idle? false) 1000)
(set-recording-length-ms 0)
(reset! recording-start-ms nil)
(when on-reviewing-audio
(on-reviewing-audio (audio/get-recorder-file-path @recorder-ref))))
(log/debug "[record-audio] new recorder - on meter")))))))
(def get-reload-recorder
(memoize
(fn
[[recorder-ref recorder-on-meter]]
(fn []
(when @recorder-ref
(audio/destroy-recorder @recorder-ref)
(reset! recorder-ref nil))
(reset! recorder-ref (audio/new-recorder
record-audio.constants/rec-options
recorder-on-meter
#(log/debug "[record-audio] new recorder - on ended")))))))
(def get-on-start-should-set-responder
(memoize
(fn
[[locked? idle? disabled? recorder-on-meter touch-timestamp touch-identifier
reviewing-audio? record-audio-permission-granted set-recording set-playing-audio output-file
recorder-ref recording-start-ms set-audio-current-time-ms on-start-recording
on-request-record-audio-permission touch-active?]]
(fn [e]
(when-not (or locked? @idle? (nil? e) @disabled?)
(let [pressed-record-button? (record-audio.constants/touch-inside-area?
{:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-audio.constants/record-button-area)
new-recorder (audio/new-recorder
record-audio.constants/rec-options
recorder-on-meter
#(log/debug "[record-audio] new recorder - on ended"))]
(reset! touch-timestamp (oops/oget e "nativeEvent.timestamp"))
(reset! touch-identifier (oops/oget e "nativeEvent.identifier"))
(when-not reviewing-audio?
(if record-audio-permission-granted
(do
(when (not @idle?)
(set-recording pressed-record-button?))
(when pressed-record-button?
(set-playing-audio false)
(reset! output-file nil)
(reset! recorder-ref new-recorder)
(audio/start-recording
new-recorder
(fn []
(reset! recording-start-ms (datetime/timestamp))
(set-audio-current-time-ms 0)
(log/debug "[record-audio] start recording - success"))
#(log/error "[record-audio] start recording - error: " %))
(when on-start-recording
(on-start-recording))))
(when on-request-record-audio-permission
(on-request-record-audio-permission))))
(when record-audio-permission-granted
(reset! touch-active? true))))
(and (not @idle?) (not @disabled?))))))
(def get-on-responder-move
(memoize
(fn
[[locked? ready-to-send? set-ready-to-send ready-to-delete? set-ready-to-delete ready-to-lock?
set-ready-to-lock touch-identifier
record-button-at-initial-position? recording?]]
(fn [^js e]
(when-not locked?
(let [location-x (oops/oget e "nativeEvent.locationX")
location-y (oops/oget e "nativeEvent.locationY")
page-x (oops/oget e "nativeEvent.pageX")
page-y (oops/oget e "nativeEvent.pageY")
identifier (oops/oget e "nativeEvent.identifier")
moved-to-send-button? (record-audio.constants/touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? true
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? true}
(record-audio.constants/send-button-area
{:active? ready-to-send?
:reviewing-audio? false}))
moved-to-delete-button? (record-audio.constants/touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? true
:ignore-min-x? true
:ignore-max-x? false}
(record-audio.constants/delete-button-area
{:active? ready-to-delete?
:reviewing-audio? false}))
moved-to-lock-button? (record-audio.constants/touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
(record-audio.constants/lock-button-area {:active?
ready-to-lock?}))
moved-to-record-button? (and
(record-audio.constants/touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-audio.constants/record-button-area-big)
(not= location-x page-x)
(not= location-y page-y))]
(when (= identifier @touch-identifier)
(cond
(and
(or
(and moved-to-record-button? ready-to-lock?)
(and (not locked?) moved-to-lock-button? @record-button-at-initial-position?))
(not ready-to-delete?)
(not ready-to-send?)
recording?)
(set-ready-to-lock moved-to-lock-button?)
(and
(or
(and moved-to-record-button? ready-to-delete?)
(and moved-to-delete-button? @record-button-at-initial-position?))
(not ready-to-lock?)
(not ready-to-send?)
recording?)
(set-ready-to-delete moved-to-delete-button?)
(and
(or
(and moved-to-record-button? ready-to-send?)
(and moved-to-send-button? @record-button-at-initial-position?))
(not ready-to-lock?)
(not ready-to-delete?)
recording?)
(set-ready-to-send moved-to-send-button?)))))))))
(def get-on-responder-release
(memoize
(fn
[[idle? reached-max-duration? touch-timestamp recording-length-ms set-recording-length-ms
reviewing-audio? set-reviewing-audio
set-audio-current-time-ms set-force-show-controls on-send output-file player-ref
destroy-player on-cancel record-button-is-animating? ready-to-lock? set-ready-to-lock
locked? set-locked on-lock ready-to-delete? set-ready-to-delete ready-to-send? set-ready-to-send
disabled? on-reviewing-audio recorder-ref set-recording reload-player recording-start-ms
touch-active?]]
(fn [^js e]
(when (and
(not @idle?)
(not @reached-max-duration?))
(let [touch-area {:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
on-record-button? (record-audio.constants/touch-inside-area?
touch-area
record-audio.constants/record-button-area-big)
on-send-button? (record-audio.constants/touch-inside-area?
touch-area
(record-audio.constants/send-button-area
{:active? false
:reviewing-audio? true}))
on-delete-button? (record-audio.constants/touch-inside-area?
touch-area
(record-audio.constants/delete-button-area
{:active? false
:reviewing-audio? true}))
release-touch-timestamp (oops/oget e "nativeEvent.timestamp")
touch-timestamp-diff (- release-touch-timestamp @touch-timestamp)
audio-length recording-length-ms]
(cond
(and reviewing-audio? on-send-button?)
(do
(set-reviewing-audio false)
(set-audio-current-time-ms 0)
(set-force-show-controls false)
(when on-send
(on-send {:file-path @output-file
:duration (min record-audio.constants/max-audio-duration-ms
(int (audio/get-player-duration @player-ref)))}))
(when @player-ref
(audio/stop-playing
@player-ref
(fn []
(destroy-player)
(log/debug "[record-audio] stop playing - success"))
#(log/error "[record-audio] stop playing - error: " %))))
(and reviewing-audio? on-delete-button?)
(do
(set-reviewing-audio false)
(set-audio-current-time-ms 0)
(set-force-show-controls false)
(destroy-player)
(when on-cancel
(on-cancel)))
(and ready-to-lock? (not @record-button-is-animating?))
(do
(set-locked true)
(set-ready-to-lock false)
(when on-lock
(on-lock)))
(and (not reviewing-audio?)
(or on-record-button?
(and (not ready-to-delete?)
(not ready-to-lock?)
(not ready-to-send?))))
(do
(reset! disabled? (<= touch-timestamp-diff record-audio.constants/min-touch-duration))
(js/setTimeout
(fn []
(if (>= recording-length-ms record-audio.constants/min-audio-duration-ms)
(do (set-reviewing-audio true)
(reset! idle? false)
(when on-reviewing-audio
(on-reviewing-audio (audio/get-recorder-file-path @recorder-ref))))
(do (when on-cancel
(on-cancel))
(reset! idle? true)))
(set-locked false)
(set-recording false)
(set-ready-to-lock false)
(audio/stop-recording
@recorder-ref
(fn []
(reset! output-file (audio/get-recorder-file-path @recorder-ref))
(when (>= audio-length record-audio.constants/min-audio-duration-ms)
(reload-player nil))
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))
(js/setTimeout #(reset! idle? false) 1000)
(set-recording-length-ms 0)
(reset! recording-start-ms nil)
(reset! disabled? false))
(if (> touch-timestamp-diff record-audio.constants/min-touch-duration) 0 250)))
(and (not locked?) (not reviewing-audio?) (not @record-button-is-animating?))
(do
(reset! disabled? (<= touch-timestamp-diff record-audio.constants/min-touch-duration))
(js/setTimeout
(fn []
(audio/stop-recording
@recorder-ref
(fn []
(cond
ready-to-send?
(when on-send
(on-send {:file-path (audio/get-recorder-file-path @recorder-ref)
:duration recording-length-ms}))
ready-to-delete?
(when on-cancel
(on-cancel)))
(set-recording false)
(set-ready-to-send false)
(set-ready-to-delete false)
(set-ready-to-lock false)
(reset! idle? true)
(js/setTimeout #(reset! idle? false) 1000)
(set-recording-length-ms 0)
(reset! recording-start-ms nil)
(reset! disabled? false)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %)))
(if (> touch-timestamp-diff record-audio.constants/min-touch-duration) 0 250)))))
(reset! touch-active? false))
(when @reached-max-duration?
(reset! reached-max-duration? false))
(reset! touch-timestamp nil)))))
@@ -2,7 +2,6 @@
(:require
[clojure.string :as string]
[goog.string :as gstring]
[oops.core :as oops]
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.record-audio.record-audio.buttons.delete-button :as delete-button]
@@ -10,93 +9,23 @@
[quo.components.record-audio.record-audio.buttons.record-button :as record-button]
[quo.components.record-audio.record-audio.buttons.record-button-big :as record-button-big]
[quo.components.record-audio.record-audio.buttons.send-button :as send-button]
[quo.components.record-audio.record-audio.constants :as record-audio.constants]
[quo.components.record-audio.record-audio.handlers :as handlers]
[quo.components.record-audio.record-audio.style :as style]
[quo.components.record-audio.soundtrack.view :as soundtrack]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.audio-toolkit :as audio]
[react-native.core :as rn]
[react-native.platform :as platform]
[reagent.core :as reagent]
[taoensso.timbre :as log]
[utils.datetime :as datetime]))
(def ^:private min-audio-duration-ms 1000)
(def ^:private max-audio-duration-ms (if platform/ios? 120800 120500))
(def ^:private metering-interval 25)
(def ^:private base-filename "am")
(def ^:private default-format ".aac")
(def min-touch-duration 150)
(def ^:private record-button-area-big
{:width 56
:height 56
:x 64
:y 64})
(def ^:private record-button-area
{:width 48
:height 48
:x 68
:y 68})
(defn- delete-button-area
[{:keys [active? reviewing-audio?]}]
{:width (cond
active? 72
reviewing-audio? 32
:else 82)
:height (if reviewing-audio? 32 56)
:x (cond
active? -16
reviewing-audio? 36
:else -32)
:y (cond
active? 64
reviewing-audio? 76
:else 70)})
(defn- lock-button-area
[{:keys [active?]}]
{:width (if active? 72 100)
:height (if active? 72 102)
:x -32
:y -32})
(defn- send-button-area
[{:keys [active? reviewing-audio?]}]
{:width (if reviewing-audio? 32 56)
:height (cond
active? 72
reviewing-audio? 47
:else 92)
:x (if reviewing-audio? 76 32)
:y (cond
active? -16
reviewing-audio? 76
:else -32)})
(defn touch-inside-area?
[{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]}
{:keys [width height x y]}]
(let [max-x (+ x width)
max-y (+ y height)]
(and
(and
(or ignore-min-x? (>= location-x x))
(or ignore-max-x? (<= location-x max-x)))
(and
(or ignore-min-y? (>= location-y y))
(or ignore-max-y? (<= location-y max-y))))))
(defn- f-recording-bar
(defn- recording-bar
[recording-length-ms ready-to-delete?]
(let [fill-percentage (/ (* recording-length-ms 100) max-audio-duration-ms)]
(let [fill-percentage (/ (* recording-length-ms 100) record-audio.constants/max-audio-duration-ms)]
[rn/view {:style (style/recording-bar-container)}
[rn/view {:style (style/recording-bar fill-percentage ready-to-delete?)}]]))
(defn- f-time-counter
(defn- time-counter
[recording? recording-length-ms ready-to-delete? reviewing-audio? audio-current-time-ms]
(let [s (quot (if recording? recording-length-ms audio-current-time-ms) 1000)
time-str (gstring/format "%02d:%02d" (quot s 60) (mod s 60))]
@@ -111,10 +40,12 @@
{:style (style/timer-text)}))
time-str]]))
(defn- f-play-button
[playing-audio? player-ref playing-timer audio-current-time-ms seeking-audio? max-duration-ms]
(defn- play-button
[playing-audio? set-playing-audio player-ref playing-timer set-audio-current-time-ms seeking-audio?
set-seeking-audio
max-duration-ms]
(let [on-play (fn []
(reset! playing-audio? true)
(set-playing-audio true)
(reset! playing-timer
(js/setInterval
(fn []
@@ -122,24 +53,24 @@
player-state (audio/get-state @player-ref)
playing? (= player-state audio/PLAYING)]
(when (and playing?
(not @seeking-audio?)
(not seeking-audio?)
(> current-time 0)
(< current-time max-duration-ms))
(reset! audio-current-time-ms current-time))
(set-audio-current-time-ms current-time))
(when (>= current-time max-duration-ms)
(audio/stop-playing
@player-ref
(fn []
(reset! playing-audio? false)
(set-playing-audio false)
(when @playing-timer
(js/clearInterval @playing-timer)
(reset! playing-timer nil)
(reset! audio-current-time-ms 0)
(reset! seeking-audio? false)))
(set-audio-current-time-ms 0)
(set-seeking-audio false)))
#(log/error "[record-audio] stop player - error: " %)))))
100)))
on-pause (fn []
(reset! playing-audio? false)
(set-playing-audio false)
(when @playing-timer
(js/clearInterval @playing-timer)
(reset! playing-timer nil))
@@ -154,445 +85,186 @@
{:style (style/play-button)
:on-press on-press}
[icons/icon
(if @playing-audio? :i/pause :i/play)
(if playing-audio? :i/pause :i/play)
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]))
(defn- record-audio-internal
[{:keys [on-init on-start-recording on-send on-cancel on-reviewing-audio
record-audio-permission-granted
on-request-record-audio-permission on-check-audio-permissions
audio-file on-lock max-duration-ms theme]}]
[:f>
;; TODO we need to refactor this, and use :f> with defined function, currenly state is reseted each
;; time parent component
;; is re-rendered
(fn []
(let [recording? (reagent/atom false)
locked? (reagent/atom false)
ready-to-send? (reagent/atom false)
ready-to-lock? (reagent/atom false)
ready-to-delete? (reagent/atom false)
reviewing-audio? (reagent/atom (some? audio-file))
playing-audio? (reagent/atom false)
recording-length-ms (reagent/atom 0)
audio-current-time-ms (reagent/atom 0)
seeking-audio? (reagent/atom false)
force-show-controls? (reagent/atom (some? audio-file))
recording-start-ms (atom (datetime/timestamp))
clear-timeout (atom nil)
record-button-at-initial-position? (atom true)
record-button-is-animating? (atom false)
idle? (atom false)
recorder-ref (atom nil)
player-ref (atom nil)
touch-active? (atom false)
playing-timer (atom nil)
output-file (atom audio-file)
reached-max-duration? (atom false)
touch-timestamp (atom nil)
touch-identifier (atom nil)
disabled? (atom false)
app-state-listener (atom nil)
rec-options
(merge
audio/default-recorder-options
{:filename (str base-filename (datetime/timestamp) default-format)
:meteringInterval metering-interval})
destroy-player
(fn []
(audio/destroy-player @player-ref)
(reset! player-ref nil))
reload-player
(fn [audio-file]
(when @player-ref
(destroy-player))
(reset! player-ref
(audio/new-player
(or audio-file (:filename rec-options))
{:autoDestroy false
:continuesToPlayInBackground false
:category audio/PLAYBACK}
(fn []
(reset! playing-audio? false)
(when @playing-timer
(js/clearInterval @playing-timer)
(reset! playing-timer nil)
(reset! audio-current-time-ms 0)
(reset! seeking-audio? false)))))
(audio/prepare-player
@player-ref
#(log/debug "[record-audio] prepare player - success")
#(log/error "[record-audio] prepare player - error: " %)))
destroy-recorder
(fn []
(audio/destroy-recorder @recorder-ref)
(reset! recorder-ref nil))
recorder-on-meter
(fn []
(when @recording-start-ms
(let [now-ms (datetime/timestamp)
recording-duration (- now-ms
@recording-start-ms)]
(reset! recording-length-ms recording-duration)
(when (>= recording-duration max-audio-duration-ms)
(reset! reached-max-duration? (not @locked?))
(reset! reviewing-audio? true)
(reset! idle? false)
(reset! locked? false)
(reset! recording? false)
(reset! ready-to-lock? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(audio/stop-recording
@recorder-ref
(fn []
(reset! output-file (audio/get-recorder-file-path
@recorder-ref))
(reload-player nil)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))
(js/setTimeout #(reset! idle? false) 1000)
(reset! recording-length-ms 0)
(reset! recording-start-ms nil)
(when on-reviewing-audio
(on-reviewing-audio (audio/get-recorder-file-path
@recorder-ref))))
(log/debug "[record-audio] new recorder - on meter"))))
reload-recorder
(fn []
(when @recorder-ref
(destroy-recorder))
(reset! recorder-ref (audio/new-recorder
rec-options
recorder-on-meter
#(log/debug "[record-audio] new recorder - on ended"))))
reset-recorder
(fn []
(reset! recording? false)
(reset! reviewing-audio? false)
(reset! locked? false)
(reset! ready-to-send? false)
(reset! ready-to-lock? false)
(reset! ready-to-delete? false)
(reset! audio-current-time-ms 0)
(reset! recording-length-ms 0)
(reset! seeking-audio? false)
(reset! playing-audio? false)
(reset! touch-active? false)
(reset! reached-max-duration? false)
(reset! output-file nil)
(reset! idle? false)
(reset! record-button-is-animating? false)
(reset! record-button-at-initial-position? true)
(when @clear-timeout
(js/clearTimeout @clear-timeout)
(reset! clear-timeout nil))
(when @playing-timer
(js/clearInterval @playing-timer)
(reset! playing-timer nil))
(reload-recorder))
on-start-should-set-responder
(fn [^js e]
(when-not (or @locked? @idle? (nil? e) @disabled?)
(let [pressed-record-button? (touch-inside-area?
{:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-button-area)
new-recorder (audio/new-recorder
rec-options
recorder-on-meter
#(log/debug "[record-audio] new recorder - on ended"))]
(reset! touch-timestamp (oops/oget e "nativeEvent.timestamp"))
(reset! touch-identifier (oops/oget e "nativeEvent.identifier"))
(when-not @reviewing-audio?
(if record-audio-permission-granted
(do
(when (not @idle?)
(reset! recording? pressed-record-button?))
(when pressed-record-button?
(reset! playing-audio? false)
(reset! output-file nil)
(reset! recorder-ref new-recorder)
(audio/start-recording
new-recorder
(fn []
(reset! recording-start-ms (datetime/timestamp))
(reset! audio-current-time-ms 0)
(log/debug "[record-audio] start recording - success"))
#(log/error "[record-audio] start recording - error: " %))
(when on-start-recording
(on-start-recording))))
(when on-request-record-audio-permission
(on-request-record-audio-permission))))
(when record-audio-permission-granted
(reset! touch-active? true))))
(and (not @idle?) (not @disabled?)))
on-responder-move
(fn [^js e]
(when-not @locked?
(let [location-x (oops/oget e "nativeEvent.locationX")
location-y (oops/oget e "nativeEvent.locationY")
page-x (oops/oget e "nativeEvent.pageX")
page-y (oops/oget e "nativeEvent.pageY")
identifier (oops/oget e "nativeEvent.identifier")
moved-to-send-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? true
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? true}
(send-button-area
{:active? @ready-to-send?
:reviewing-audio? false}))
moved-to-delete-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? true
:ignore-min-x? true
:ignore-max-x? false}
(delete-button-area
{:active? @ready-to-delete?
:reviewing-audio? false}))
moved-to-lock-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
(lock-button-area {:active? @ready-to-lock?}))
moved-to-record-button? (and
(touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-button-area-big)
(not= location-x page-x)
(not= location-y page-y))]
(when (= identifier @touch-identifier)
(cond
(and
(or
(and moved-to-record-button? @ready-to-lock?)
(and (not @locked?) moved-to-lock-button? @record-button-at-initial-position?))
(not @ready-to-delete?)
(not @ready-to-send?)
@recording?)
(reset! ready-to-lock? moved-to-lock-button?)
(and
(or
(and moved-to-record-button? @ready-to-delete?)
(and moved-to-delete-button? @record-button-at-initial-position?))
(not @ready-to-lock?)
(not @ready-to-send?)
@recording?)
(reset! ready-to-delete? moved-to-delete-button?)
(and
(or
(and moved-to-record-button? @ready-to-send?)
(and moved-to-send-button? @record-button-at-initial-position?))
(not @ready-to-lock?)
(not @ready-to-delete?)
@recording?)
(reset! ready-to-send? moved-to-send-button?))))))
on-responder-release
(fn [^js e]
(when (and
(not @idle?)
(not @reached-max-duration?))
(let [touch-area {:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
on-record-button? (touch-inside-area?
touch-area
record-button-area-big)
on-send-button? (touch-inside-area?
touch-area
(send-button-area
{:active? false
:reviewing-audio? true}))
on-delete-button? (touch-inside-area?
touch-area
(delete-button-area
{:active? false
:reviewing-audio? true}))
release-touch-timestamp (oops/oget e "nativeEvent.timestamp")
touch-timestamp-diff (- release-touch-timestamp @touch-timestamp)
audio-length @recording-length-ms]
(cond
(and @reviewing-audio? on-send-button?)
(do
(reset! reviewing-audio? false)
(reset! audio-current-time-ms 0)
(reset! force-show-controls? false)
(when on-send
(on-send {:file-path @output-file
:duration (min max-audio-duration-ms
(int (audio/get-player-duration @player-ref)))}))
(when @player-ref
(audio/stop-playing
@player-ref
(fn []
(destroy-player)
(log/debug "[record-audio] stop playing - success"))
#(log/error "[record-audio] stop playing - error: " %))))
(and @reviewing-audio? on-delete-button?)
(do
(reset! reviewing-audio? false)
(reset! audio-current-time-ms 0)
(reset! force-show-controls? false)
(destroy-player)
(when on-cancel
(on-cancel)))
(and @ready-to-lock? (not @record-button-is-animating?))
(do
(reset! locked? true)
(reset! ready-to-lock? false)
(when on-lock
(on-lock)))
(and (not @reviewing-audio?)
(or on-record-button?
(and (not @ready-to-delete?)
(not @ready-to-lock?)
(not @ready-to-send?))))
(do
(reset! disabled? (<= touch-timestamp-diff min-touch-duration))
(js/setTimeout
(fn []
(if (>= @recording-length-ms min-audio-duration-ms)
(do (reset! reviewing-audio? true)
(reset! idle? false)
(when on-reviewing-audio
(on-reviewing-audio (audio/get-recorder-file-path @recorder-ref))))
(do (when on-cancel
(on-cancel))
(reset! idle? true)))
(reset! locked? false)
(reset! recording? false)
(reset! ready-to-lock? false)
(audio/stop-recording
@recorder-ref
(fn []
(reset! output-file (audio/get-recorder-file-path @recorder-ref))
(when (>= audio-length min-audio-duration-ms)
(reload-player nil))
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))
(js/setTimeout #(reset! idle? false) 1000)
(reset! recording-length-ms 0)
(reset! recording-start-ms nil)
(reset! disabled? false))
(if (> touch-timestamp-diff min-touch-duration) 0 250)))
(and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?))
(do
(reset! disabled? (<= touch-timestamp-diff min-touch-duration))
(js/setTimeout
(fn []
(audio/stop-recording
@recorder-ref
(fn []
(cond
@ready-to-send?
(when on-send
(on-send {:file-path (audio/get-recorder-file-path @recorder-ref)
:duration @recording-length-ms}))
@ready-to-delete?
(when on-cancel
(on-cancel)))
(reset! recording? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(reset! ready-to-lock? false)
(reset! idle? true)
(js/setTimeout #(reset! idle? false) 1000)
(reset! recording-length-ms 0)
(reset! recording-start-ms nil)
(reset! disabled? false)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %)))
(if (> touch-timestamp-diff min-touch-duration) 0 250)))))
(reset! touch-active? false))
(when @reached-max-duration?
(reset! reached-max-duration? false))
(reset! touch-timestamp nil))]
(fn []
(rn/use-mount (fn []
(when on-check-audio-permissions
(on-check-audio-permissions))
(when on-init
(on-init reset-recorder))
(when audio-file
(let [filename (last (string/split audio-file "/"))]
(reload-player filename)))
(reset! app-state-listener
(.addEventListener rn/app-state
"change"
#(when (= % "background")
(reset! playing-audio? false))))
#(.remove @app-state-listener)))
[rn/view
{:style style/bar-container
:pointer-events :box-none}
(when @reviewing-audio?
[:<>
[:f> f-play-button playing-audio? player-ref playing-timer audio-current-time-ms
seeking-audio? max-duration-ms]
[:f> soundtrack/f-soundtrack
{:audio-current-time-ms audio-current-time-ms
:player-ref @player-ref
:seeking-audio? seeking-audio?
:max-audio-duration-ms max-duration-ms}]])
(when (or @recording? @reviewing-audio?)
[:f> f-time-counter @recording? @recording-length-ms @ready-to-delete? @reviewing-audio?
@audio-current-time-ms])
(when @recording?
[:f> f-recording-bar @recording-length-ms @ready-to-delete?])
[rn/view
{:test-ID "record-audio"
:style style/button-container
:hit-slop {:top -70
:bottom 0
:left 0
:right 0}
:pointer-events :box-only
:on-start-should-set-responder on-start-should-set-responder
:on-responder-move on-responder-move
:on-responder-release on-responder-release}
[:f> delete-button/f-delete-button recording? ready-to-delete? reviewing-audio?
@force-show-controls?]
[:f> lock-button/f-lock-button recording? ready-to-lock? locked?]
[:f> send-button/f-send-button recording? ready-to-send? reviewing-audio?
@force-show-controls?]
[:f> record-button-big/f-record-button-big
{:recording? recording?
:ready-to-send? ready-to-send?
:ready-to-lock? ready-to-lock?
:ready-to-delete? ready-to-delete?
:record-button-is-animating? record-button-is-animating?
:record-button-at-initial-position? record-button-at-initial-position?
:locked? locked?
:reviewing-audio? reviewing-audio?
:recording-length-ms recording-length-ms
:clear-timeout clear-timeout
:touch-active? touch-active?
:recorder-ref recorder-ref
:reload-recorder-fn reload-recorder
:idle? idle?
:on-send on-send
:on-cancel on-cancel
:theme theme}]
[:f> record-button/f-record-button recording? reviewing-audio?]]])))])
(defn record-audio
[{:keys [on-init on-start-recording on-send on-cancel on-reviewing-audio audio-file on-lock
max-duration-ms on-check-audio-permissions record-audio-permission-granted
on-request-record-audio-permission]}]
(let [;;STATE
[recording? set-recording] (rn/use-state false)
[locked? set-locked] (rn/use-state false)
[ready-to-send? set-ready-to-send] (rn/use-state false)
[ready-to-lock? set-ready-to-lock] (rn/use-state false)
[ready-to-delete?
set-ready-to-delete] (rn/use-state false)
[reviewing-audio?
set-reviewing-audio] (rn/use-state (some? audio-file))
[playing-audio?
set-playing-audio] (rn/use-state false)
[recording-length-ms
set-recording-length-ms] (rn/use-state 0)
[audio-current-time-ms
set-audio-current-time-ms] (rn/use-state 0)
[seeking-audio? set-seeking-audio] (rn/use-state false)
[force-show-controls?
set-force-show-controls] (rn/use-state (some? audio-file))
;;ATOMS
recording-start-ms (rn/use-ref-atom (datetime/timestamp))
clear-timeout (rn/use-ref-atom nil)
record-button-at-initial-position? (rn/use-ref-atom true)
record-button-is-animating? (rn/use-ref-atom false)
idle? (rn/use-ref-atom false)
recorder-ref (rn/use-ref-atom nil)
player-ref (rn/use-ref-atom nil)
touch-active? (rn/use-ref-atom false)
playing-timer (rn/use-ref-atom nil)
output-file (rn/use-ref-atom audio-file)
reached-max-duration? (rn/use-ref-atom false)
touch-timestamp (rn/use-ref-atom nil)
touch-identifier (rn/use-ref-atom nil)
disabled? (rn/use-ref-atom false)
app-state-listener (rn/use-ref-atom nil)
;;HANDLERS
destroy-player (rn/use-callback
(fn []
(audio/destroy-player @player-ref)
(reset! player-ref nil)))
reload-player (handlers/get-reload-player
[player-ref destroy-player set-playing-audio playing-timer
set-audio-current-time-ms set-seeking-audio])
recorder-on-meter (handlers/get-recorder-on-meter
[recording-start-ms set-recording-length-ms
reached-max-duration?
locked? set-locked set-reviewing-audio idle? set-recording
set-ready-to-lock set-ready-to-send set-ready-to-delete
recorder-ref
output-file reload-player
on-reviewing-audio])
reload-recorder (handlers/get-reload-recorder [recorder-ref
recorder-on-meter])
on-start-should-set-responder (handlers/get-on-start-should-set-responder
[locked? idle? disabled? recorder-on-meter touch-timestamp
touch-identifier
reviewing-audio? record-audio-permission-granted
set-recording set-playing-audio output-file
recorder-ref recording-start-ms set-audio-current-time-ms
on-start-recording
on-request-record-audio-permission touch-active?])
on-responder-move (handlers/get-on-responder-move
[locked? ready-to-send? set-ready-to-send
ready-to-delete? set-ready-to-delete
ready-to-lock? set-ready-to-lock
touch-identifier
record-button-at-initial-position?
recording?])
on-responder-release (handlers/get-on-responder-release
[idle? reached-max-duration? touch-timestamp
recording-length-ms set-recording-length-ms
reviewing-audio? set-reviewing-audio
set-audio-current-time-ms set-force-show-controls on-send
output-file player-ref
destroy-player on-cancel record-button-is-animating?
ready-to-lock? set-ready-to-lock
locked? set-locked on-lock ready-to-delete?
set-ready-to-delete ready-to-send? set-ready-to-send
disabled? on-reviewing-audio
recorder-ref set-recording reload-player recording-start-ms
touch-active?])]
(def record-audio (quo.theme/with-theme record-audio-internal))
;;ON MOUNT
(rn/use-mount
(fn []
(when on-check-audio-permissions (on-check-audio-permissions))
(when on-init
(on-init
(fn reset-recorder []
(set-recording false)
(set-reviewing-audio false)
(set-locked false)
(set-ready-to-send false)
(set-ready-to-lock false)
(set-ready-to-delete false)
(set-audio-current-time-ms 0)
(set-recording-length-ms 0)
(set-seeking-audio false)
(set-playing-audio false)
(reset! touch-active? false)
(reset! reached-max-duration? false)
(reset! output-file nil)
(reset! idle? false)
(reset! record-button-is-animating? false)
(reset! record-button-at-initial-position? true)
(when @clear-timeout
(js/clearTimeout @clear-timeout)
(reset! clear-timeout nil))
(when @playing-timer
(js/clearInterval @playing-timer)
(reset! playing-timer nil))
(reload-recorder))))
(when audio-file
(let [filename (last (string/split audio-file "/"))]
(reload-player filename)))
(reset! app-state-listener
(.addEventListener rn/app-state
"change"
#(when (= % "background")
(set-playing-audio false))))))
;;ON UNMOUNT
(rn/use-unmount #(.remove @app-state-listener))
[rn/view
{:style style/bar-container
:pointer-events :box-none}
(when reviewing-audio?
[:<>
[play-button playing-audio? set-playing-audio player-ref playing-timer set-audio-current-time-ms
seeking-audio? set-seeking-audio max-duration-ms]
[soundtrack/soundtrack
{:audio-current-time-ms audio-current-time-ms
:set-audio-current-time-ms set-audio-current-time-ms
:player-ref @player-ref
:seeking-audio? seeking-audio?
:set-seeking-audio set-seeking-audio
:max-audio-duration-ms max-duration-ms}]])
(when (or recording? reviewing-audio?)
[time-counter recording? recording-length-ms ready-to-delete? reviewing-audio?
audio-current-time-ms])
(when recording?
[recording-bar recording-length-ms ready-to-delete?])
[rn/view
{:test-ID "record-audio"
:style style/button-container
:hit-slop {:top -70 :bottom 0 :left 0 :right 0}
:pointer-events :box-only
:on-start-should-set-responder on-start-should-set-responder
:on-responder-move on-responder-move
:on-responder-release on-responder-release}
[delete-button/delete-button recording? ready-to-delete? reviewing-audio? force-show-controls?]
[lock-button/lock-button recording? ready-to-lock? locked?]
[send-button/send-button recording? ready-to-send? reviewing-audio? force-show-controls?]
[record-button-big/record-button-big
{:recording? recording?
:set-recording set-recording
:ready-to-send? ready-to-send?
:set-ready-to-send set-ready-to-send
:ready-to-lock? ready-to-lock?
:set-ready-to-lock set-ready-to-lock
:ready-to-delete? ready-to-delete?
:set-ready-to-delete set-ready-to-delete
:record-button-is-animating? record-button-is-animating?
:record-button-at-initial-position? record-button-at-initial-position?
:locked? locked?
:set-locked set-locked
:reviewing-audio? reviewing-audio?
:recording-length-ms recording-length-ms
:set-recording-length-ms set-recording-length-ms
:clear-timeout clear-timeout
:touch-active? touch-active?
:recorder-ref recorder-ref
:reload-recorder-fn reload-recorder
:idle? idle?
:on-send on-send
:on-cancel on-cancel}]
[record-button/record-button recording? reviewing-audio?]]]))
@@ -2,7 +2,6 @@
(:require
[quo.components.record-audio.soundtrack.view :as soundtrack]
[react-native.audio-toolkit :as audio]
[reagent.core :as reagent]
[test-helpers.component :as h]))
(h/describe "soundtrack component"
@@ -10,23 +9,21 @@
(h/test "renders soundtrack"
(with-redefs [audio/get-player-duration (fn [] 2000)]
(let [player-ref (reagent/atom {})
audio-current-time-ms (reagent/atom 0)]
(h/render [:f> soundtrack/f-soundtrack
{:player-ref @player-ref
:audio-current-time-ms audio-current-time-ms}])
(-> (h/expect (h/get-by-test-id "soundtrack"))
(.toBeTruthy)))))
(h/render [soundtrack/soundtrack
{:player-ref {}
:audio-current-time-ms 0}])
(-> (h/expect (h/get-by-test-id "soundtrack"))
(.toBeTruthy))))
(h/test "soundtrack on-sliding-start works"
(with-redefs [audio/get-player-duration (fn [] 2000)]
(let [seeking-audio? (reagent/atom false)
player-ref (reagent/atom {})
audio-current-time-ms (reagent/atom 0)]
(h/render [:f> soundtrack/f-soundtrack
{:seeking-audio? seeking-audio?
:player-ref @player-ref
:audio-current-time-ms audio-current-time-ms}])
(let [seeking-audio? (atom false)]
(h/render [soundtrack/soundtrack
{:seeking-audio? seeking-audio?
:set-seeking-audio #(reset! seeking-audio? %)
:player-ref {}
:audio-current-time-ms 0
:set-audio-current-time-ms #()}])
(h/fire-event
:on-sliding-start
(h/get-by-test-id "soundtrack"))
@@ -36,13 +33,13 @@
(h/test "soundtrack on-sliding-complete works"
(with-redefs [audio/get-player-duration (fn [] 2000)
audio/seek-player (js/jest.fn)]
(let [seeking-audio? (reagent/atom false)
player-ref (reagent/atom {})
audio-current-time-ms (reagent/atom 0)]
(h/render [:f> soundtrack/f-soundtrack
{:seeking-audio? seeking-audio?
:player-ref @player-ref
:audio-current-time-ms audio-current-time-ms}])
(let [seeking-audio? (atom false)]
(h/render [soundtrack/soundtrack
{:seeking-audio? seeking-audio?
:set-seeking-audio #(reset! seeking-audio? %)
:player-ref {}
:audio-current-time-ms 0
:set-audio-current-time-ms #()}])
(h/fire-event
:on-sliding-start
(h/get-by-test-id "soundtrack"))
@@ -58,13 +55,14 @@
(h/test "soundtrack on-value-change when seeking audio works"
(with-redefs [audio/get-player-duration (fn [] 2000)
audio/seek-player (js/jest.fn)]
(let [seeking-audio? (reagent/atom false)
player-ref (reagent/atom {})
audio-current-time-ms (reagent/atom 0)]
(h/render [:f> soundtrack/f-soundtrack
{:seeking-audio? seeking-audio?
:player-ref @player-ref
:audio-current-time-ms audio-current-time-ms}])
(let [seeking-audio? (atom false)
audio-current-time-ms (atom 0)]
(h/render [soundtrack/soundtrack
{:seeking-audio? seeking-audio?
:set-seeking-audio #(reset! seeking-audio? %)
:player-ref {}
:audio-current-time-ms audio-current-time-ms
:set-audio-current-time-ms #(reset! audio-current-time-ms %)}])
(h/fire-event
:on-sliding-start
(h/get-by-test-id "soundtrack"))
@@ -1,42 +1,47 @@
(ns quo.components.record-audio.soundtrack.view
(:require
[quo.components.icons.icons :as icons]
[quo.components.record-audio.soundtrack.style :as style]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.audio-toolkit :as audio]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.slider :as slider]
[taoensso.timbre :as log]))
(def ^:private thumb-light (js/require "../resources/images/icons2/12x12/thumb-light.png"))
(def ^:private thumb-dark (js/require "../resources/images/icons2/12x12/thumb-dark.png"))
(def ^:private thumb-light (icons/icon-source :thumb-light12))
(def ^:private thumb-dark (icons/icon-source :thumb-dark12))
(defn f-soundtrack
[{:keys [audio-current-time-ms player-ref style seeking-audio? max-audio-duration-ms]}]
(let [audio-duration-ms (min max-audio-duration-ms (audio/get-player-duration player-ref))
theme (quo.theme/use-theme-value)]
[:<>
[slider/slider
{:test-ID "soundtrack"
:style (merge
(style/player-slider-container)
(or style {}))
:minimum-value 0
:maximum-value audio-duration-ms
:value @audio-current-time-ms
:on-sliding-start #(reset! seeking-audio? true)
:on-sliding-complete (fn [seek-time]
(reset! seeking-audio? false)
(audio/seek-player
player-ref
seek-time
#(log/debug "[record-audio] on seek - seek time: " seek-time)
#(log/error "[record-audio] on seek - error: " %)))
:on-value-change #(when @seeking-audio?
(reset! audio-current-time-ms %))
:thumb-image (quo.theme/theme-value thumb-light thumb-dark theme)
:minimum-track-tint-color (colors/theme-colors colors/primary-50 colors/primary-60 theme)
:maximum-track-tint-color (colors/theme-colors
(if platform/ios? colors/neutral-20 colors/neutral-40)
(if platform/ios? colors/neutral-80 colors/neutral-60)
theme)}]]))
(defn soundtrack
[{:keys [audio-current-time-ms set-audio-current-time-ms player-ref style
seeking-audio? set-seeking-audio max-audio-duration-ms]}]
(let [audio-duration-ms (min max-audio-duration-ms (audio/get-player-duration player-ref))
theme (quo.theme/use-theme-value)
on-sliding-start (rn/use-callback #(set-seeking-audio true))
on-sliding-complete (rn/use-callback
(fn [seek-time]
(set-seeking-audio false)
(audio/seek-player
player-ref
seek-time
#(log/debug "[record-audio] on seek - seek time: " seek-time)
#(log/error "[record-audio] on seek - error: " %)))
[player-ref])
on-value-change (rn/use-callback #(when seeking-audio? (set-audio-current-time-ms %))
[seeking-audio?])]
[slider/slider
{:test-ID "soundtrack"
:style (merge (style/player-slider-container) style)
:minimum-value 0
:maximum-value audio-duration-ms
:value audio-current-time-ms
:on-sliding-start on-sliding-start
:on-sliding-complete on-sliding-complete
:on-value-change on-value-change
:thumb-image (quo.theme/theme-value thumb-light thumb-dark theme)
:minimum-track-tint-color (colors/theme-colors colors/primary-50 colors/primary-60 theme)
:maximum-track-tint-color (colors/theme-colors
(if platform/ios? colors/neutral-20 colors/neutral-40)
(if platform/ios? colors/neutral-80 colors/neutral-60)
theme)}]))
+1 -1
View File
@@ -357,7 +357,7 @@
;;;; Record Audio
(def record-audio quo.components.record-audio.record-audio.view/record-audio)
(def soundtrack quo.components.record-audio.soundtrack.view/f-soundtrack)
(def soundtrack quo.components.record-audio.soundtrack.view/soundtrack)
;;;; Selectors
(def disclaimer quo.components.selectors.disclaimer.view/view)
+6 -8
View File
@@ -38,7 +38,8 @@
(when on-meter
(.on ^js recorder "meter" on-meter))
(when on-ended
(.on ^js recorder "ended" on-ended))))
(.on ^js recorder "ended" on-ended))
recorder))
(defn new-player
[audio options on-ended]
@@ -46,7 +47,8 @@
audio
(clj->js options))]
(when on-ended
(.on ^js player "ended" on-ended))))
(.on ^js player "ended" on-ended))
player))
(defn prepare-player
[player on-prepared on-error]
@@ -157,20 +159,16 @@
(on-error {:error (.-err err) :message (.-message err)})
(on-seek))))))
(defn can-play?
[player]
(and player (.-canPlay ^js player)))
(defn destroy-recorder
[recorder]
(stop-recording recorder
#(when (and recorder (not= (get-state recorder) DESTROYED))
#(when (and recorder (.-destroy ^js recorder) (not= (get-state recorder) DESTROYED))
(.destroy ^js recorder))
#()))
(defn destroy-player
[player]
(stop-playing player
#(when (and player (not= (get-state player) IDLE))
#(when (and player (.-destroy ^js player) (not= (get-state player) IDLE))
(.destroy ^js player))
#()))
@@ -210,12 +210,14 @@
:else :i/play-audio)
{:size 20
:color colors/white}]]
[:f> quo/soundtrack
{:style style/slider-container
:audio-current-time-ms progress
:player-ref (@active-players player-key)
:seeking-audio? seeking-audio?
:max-audio-duration-ms constants/audio-max-duration-ms}]
[quo/soundtrack
{:style style/slider-container
:audio-current-time-ms @progress
:set-audio-current-time-ms #(reset! progress %)
:player-ref (@active-players player-key)
:seeking-audio? @seeking-audio?
:set-seeking-audio? #(reset! seeking-audio? %)
:max-audio-duration-ms constants/audio-max-duration-ms}]
[quo/text
{:style style/timestamp
:accessibility-label :audio-duration-label
@@ -14,6 +14,7 @@
:canManageUsers :can-manage-users?
:categoryID :category-id
:canPost :can-post?
:tokenGated :token-gated?
:isControlNode :is-control-node?
:pinMessageAllMembersEnabled :pin-message-all-members-enabled
:isMember :is-member?
+13 -20
View File
@@ -21,28 +21,21 @@
(defn handle-community
[{:keys [db]} [community-js]]
(when community-js
(let [{:keys [token-permissions
(let [{:keys [clock
token-permissions-check joined id last-opened-at]
:as community} (data-store.communities/<-rpc community-js)
has-channel-perm? (fn [id-perm-tuple]
(let [{:keys [type]} (second id-perm-tuple)]
(or (= type constants/community-token-permission-can-view-channel)
(=
type
constants/community-token-permission-can-view-and-post-channel))))
:as community} (data-store.communities/<-rpc community-js)
previous-last-opened-at (get-in db [:communities id :last-opened-at])]
{:db (assoc-in db
[:communities id]
(assoc community :last-opened-at (max last-opened-at previous-last-opened-at)))
:fx [[:dispatch [:communities/initialize-permission-addresses id]]
(when (not joined)
[:dispatch [:chat.ui/spectate-community id]])
(when (nil? token-permissions-check)
[:dispatch [:communities/check-permissions-to-join-community id]])
(when (some has-channel-perm? token-permissions)
[:dispatch [:communities/check-all-community-channels-permissions id]])
(when joined
[:dispatch [:communities/get-revealed-accounts id]])]})))
(when (> clock (get-in db [:communities id :clock]))
{:db (assoc-in db
[:communities id]
(assoc community :last-opened-at (max last-opened-at previous-last-opened-at)))
:fx [[:dispatch [:communities/initialize-permission-addresses id]]
(when (not joined)
[:dispatch [:chat.ui/spectate-community id]])
(when (nil? token-permissions-check)
[:dispatch [:communities/check-permissions-to-join-community id]])
(when joined
[:dispatch [:communities/get-revealed-accounts id]])]}))))
(rf/reg-event-fx :communities/handle-community handle-community)
@@ -2,7 +2,6 @@
(:require [cljs.test :refer [deftest is testing]]
[legacy.status-im.mailserver.core :as mailserver]
matcher-combinators.test
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.messages.link-preview.events :as link-preview.events]
[status-im.contexts.communities.events :as events]))
@@ -262,7 +261,7 @@
(-> effects :json-rpc/call first (select-keys [:method :params]))))))))
(deftest handle-community
(let [community {:id community-id}]
(let [community {:id community-id :clock 2}]
(testing "given a unjoined community"
(let [effects (events/handle-community {} [community])]
(is (match? community-id
@@ -287,29 +286,10 @@
[[:dispatch [:communities/initialize-permission-addresses community-id]]
[:dispatch [:chat.ui/spectate-community community-id]]]
(filter some? (:fx effects))))))
(testing "given a community with view channel permission"
(let [community (assoc community
:token-permissions
[["perm-id" {:type constants/community-token-permission-can-view-channel}]])
(testing "given a community with lower clock"
(let [effects (events/handle-community {:db {:communities {community-id {:clock 3}}}} [community])]
(is (nil? effects))))
(testing "given a community without clock"
(let [community (dissoc community :clock)
effects (events/handle-community {} [community])]
(is (match?
[[:dispatch [:communities/initialize-permission-addresses community-id]]
[:dispatch [:chat.ui/spectate-community community-id]]
[:dispatch [:communities/check-permissions-to-join-community community-id]]
[:dispatch
[:communities/check-all-community-channels-permissions community-id]]]
(filter some? (:fx effects))))))
(testing "given a community with post in channel permission"
(let [community (assoc community
:token-permissions
[["perm-id"
{:type constants/community-token-permission-can-view-and-post-channel}]])
effects (events/handle-community {} [community])]
(is (match?
[[:dispatch [:communities/initialize-permission-addresses community-id]]
[:dispatch [:chat.ui/spectate-community community-id]]
[:dispatch [:communities/check-permissions-to-join-community community-id]]
[:dispatch
[:communities/check-all-community-channels-permissions community-id]]]
(filter some? (:fx effects))))))))
(is (nil? effects))))))
@@ -1,37 +1,10 @@
(ns status-im.contexts.communities.overview.events
(:require
[legacy.status-im.data-store.communities :as data-store]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(rf/reg-event-fx :communities/check-all-community-channels-permissions-success
(fn [{:keys [db]} [community-id response]]
{:db (-> db
(assoc-in [:community-channels-permissions community-id]
(data-store/rpc->channel-permissions (:channels response)))
(assoc-in [:communities/channel-permissions-check community-id] false))}))
(rf/reg-event-fx :communities/check-all-community-channels-permissions-failed
(fn [{:keys [db]} [community-id]]
{:db (assoc-in db [:communities/channel-permissions-check community-id] false)}))
(rf/reg-event-fx :communities/check-all-community-channels-permissions
(fn [{:keys [db]} [community-id]]
(when (get-in db [:communities community-id])
{:db (assoc-in db [:communities/channel-permissions-check community-id] true)
:fx [[:json-rpc/call
[{:method "wakuext_checkAllCommunityChannelsPermissions"
:params [{:CommunityID community-id}]
:on-success [:communities/check-all-community-channels-permissions-success community-id]
:on-error (fn [error]
(rf/dispatch [:communities/check-all-community-channels-permissions-failed
community-id])
(log/error "failed to check channels permissions"
{:error error
:community-id community-id
:event
:communities/check-all-community-channels-permissions}))}]]]})))
(rf/reg-event-fx :communities/check-permissions-to-join-community-success
(fn [{:keys [db]} [community-id based-on-client-selection? result]]
@@ -48,15 +48,16 @@
:emoji emoji
:customization-color community-color
:mentions-count mentions-count
;; NOTE: this is a troolean, nil/true/false have different meaning
:locked? locked?
:notification notification}
channel-sheet-data {:selected-item (fn [] [quo/channel channel-options])
:content (fn [] sheet-content)}]
[rn/view {:key id}
[quo/channel
(merge channel-options
{:on-press on-press
:on-long-press #(rf/dispatch [:show-bottom-sheet channel-sheet-data])})]]))
(assoc channel-options
:on-press on-press
:on-long-press #(rf/dispatch [:show-bottom-sheet channel-sheet-data]))]]))
(defn- channel-list-component
[{:keys [on-category-layout community-id community-color on-first-channel-height-changed]}
@@ -253,8 +254,7 @@
:description-accessibility-label :community-description}])
(defn- community-content
[id]
(rf/dispatch [:communities/check-all-community-channels-permissions id])
[_]
(fn [id
{:keys [on-category-layout
collapsed?
@@ -5,21 +5,28 @@
[status-im.common.not-implemented :as not-implemented]
[status-im.constants :as constants]
[status-im.contexts.profile.contact.add-nickname.view :as add-nickname]
[status-im.contexts.profile.contact.block-contact.view :as block-contact]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn on-add-nickname
[]
(rf/dispatch [:show-bottom-sheet
{:content
(fn [] [add-nickname/view])}]))
(defn on-block-contact
[]
(rf/dispatch [:show-bottom-sheet
{:content
(fn [] [block-contact/view])}]))
(defn view
[]
(let [{:keys [nickname
public-key
contact-request-state]
(let [{:keys [nickname public-key contact-request-state blocked?]
:as contact} (rf/sub [:contacts/current-contact])
full-name (profile.utils/displayed-name contact)
on-add-nickname (rn/use-callback #(rf/dispatch [:show-bottom-sheet
{:content
(fn [] [add-nickname/view])}]))
on-remove-nickname (rn/use-callback
(fn []
(rf/dispatch [:hide-bottom-sheet])
@@ -55,41 +62,43 @@
(rf/dispatch [:contact.ui/remove-contact-pressed contact]))
[public-key full-name])]
[quo/action-drawer
[[{:icon :i/edit
:label (if has-nickname?
(i18n/label :t/edit-nickname)
(i18n/label :t/add-nickname-title))
:on-press on-add-nickname
:accessibility-label (if nickname :edit-nickname :add-nickname)}
{:icon :i/qr-code
:label (i18n/label :t/show-qr)
:on-press on-show-qr
:accessibility-label :show-qr-code}
{:icon :i/share
:label (i18n/label :t/share-profile)
:on-press on-share-profile
:accessibility-label :share-profile}
(when has-nickname?
{:icon :i/delete
:label (i18n/label :t/remove-nickname)
:on-press on-remove-nickname
:add-divider? true
:accessibility-label :remove-nickname
:danger? true})
{:icon :i/untrustworthy
:label (i18n/label :t/mark-untrustworthy)
:on-press not-implemented/alert
:accessibility-label :mark-untrustworthy
:add-divider? (when-not has-nickname? true)
:danger? true}
(when (= constants/contact-request-state-mutual contact-request-state)
{:icon :i/remove-user
:label (i18n/label :t/remove-contact)
:on-press on-remove-contact
:accessibility-label :remove-contact
:danger? true})
{:icon :i/block
:label (i18n/label :t/block-user)
:on-press not-implemented/alert
:accessibility-label :block-user
:danger? true}]]]))
[(concat
[{:icon :i/edit
:label (if has-nickname?
(i18n/label :t/edit-nickname)
(i18n/label :t/add-nickname-title))
:on-press on-add-nickname
:accessibility-label (if nickname :edit-nickname :add-nickname)}
{:icon :i/qr-code
:label (i18n/label :t/show-qr)
:on-press on-show-qr
:accessibility-label :show-qr-code}
{:icon :i/share
:label (i18n/label :t/share-profile)
:on-press on-share-profile
:accessibility-label :share-profile}
(when has-nickname?
{:icon :i/delete
:label (i18n/label :t/remove-nickname)
:on-press on-remove-nickname
:add-divider? true
:accessibility-label :remove-nickname
:danger? true})]
(when-not blocked?
[{:icon :i/untrustworthy
:label (i18n/label :t/mark-untrustworthy)
:on-press not-implemented/alert
:accessibility-label :mark-untrustworthy
:add-divider? (when-not has-nickname? true)
:danger? true}
(when (= constants/contact-request-state-mutual contact-request-state)
{:icon :i/remove-user
:label (i18n/label :t/remove-contact)
:on-press on-remove-contact
:accessibility-label :remove-contact
:danger? true})
{:icon :i/block
:label (i18n/label :t/block-user)
:on-press on-block-contact
:accessibility-label :block-user
:danger? true}]))]]))
@@ -14,8 +14,8 @@
[]
(let [{:keys [public-key primary-name nickname customization-color]
:as profile} (rf/sub [:contacts/current-contact])
;; TODO(@mohsen): remove :blue, https://github.com/status-im/status-mobile/issues/18733
customization-color (or customization-color :blue)
;; TODO(@mohsen): remove default color, https://github.com/status-im/status-mobile/issues/18733
customization-color (or customization-color constants/profile-default-color)
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
[unsaved-nickname set-unsaved-nickname] (rn/use-state nickname)
@@ -54,15 +54,15 @@
:customization-color customization-color}]
[rn/view {:style style/input-wrapper}
[quo/input
{:type :text
:char-limit constants/profile-name-max-length
:max-length constants/profile-name-max-length
:auto-focus true
:default-value unsaved-nickname
:error? (not (string/blank? error-msg))
:label (i18n/label :t/nickname)
:on-change-text on-nickname-change}
:on-submit-editing on-nickname-submit]
{:type :text
:char-limit constants/profile-name-max-length
:max-length constants/profile-name-max-length
:auto-focus true
:default-value unsaved-nickname
:error? (not (string/blank? error-msg))
:label (i18n/label :t/nickname)
:on-change-text on-nickname-change
:on-submit-editing on-nickname-submit}]
[quo/info-message
{:icon :i/info
:size :default
@@ -0,0 +1,11 @@
(ns status-im.contexts.profile.contact.block-contact.style)
(def content-wrapper
{:padding-vertical 8
:padding-horizontal 20
:gap 12})
(def checkbox-wrapper
{:flex-direction :row
:align-items :center
:gap 8})
@@ -0,0 +1,70 @@
(ns status-im.contexts.profile.contact.block-contact.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.profile.contact.block-contact.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn on-close [] (rf/dispatch [:hide-bottom-sheet]))
(defn view
[]
(let [{:keys [customization-color contact-request-state public-key]
:as contact} (rf/sub [:contacts/current-contact])
;; TODO(@mohsen): remove default color, https://github.com/status-im/status-mobile/issues/18733
customization-color (or customization-color constants/profile-default-color)
full-name (profile.utils/displayed-name contact)
profile-picture (profile.utils/photo contact)
[remove-contact? set-remove-contact?] (rn/use-state false)
on-remove-toggle (rn/use-callback #(set-remove-contact? not) [])
on-block-press (rn/use-callback
(fn []
(rf/dispatch [:toasts/upsert
{:id :user-blocked
:type :positive
:text (i18n/label :t/user-blocked
{:username
full-name})}])
(rf/dispatch [:contact.ui/block-contact-confirmed
public-key])
(when remove-contact?
(rf/dispatch [:contact.ui/remove-contact-pressed
{:public-key public-key}]))
(on-close))
[remove-contact? public-key full-name])]
[:<>
[quo/drawer-top
{:type :context-tag
:context-tag-type :default
:title (i18n/label :t/block)
:full-name full-name
:profile-picture profile-picture
:customization-color customization-color}]
[rn/view {:style style/content-wrapper}
[quo/text
{:weight :medium
:size :paragraph-1}
(i18n/label :t/block-user-title-message {:username full-name})]
[quo/information-box
{:icon :i/info
:type :default}
(i18n/label :t/blocking-a-user-message {:username full-name})]
(when (= constants/contact-request-state-mutual contact-request-state)
[rn/pressable
{:style style/checkbox-wrapper
:on-press on-remove-toggle}
[quo/selectors
{:type :checkbox
:checked? remove-contact?
:on-change on-remove-toggle}]
[quo/text (i18n/label :t/remove-contact)]])]
[quo/bottom-actions
{:actions :two-actions
:button-one-label (i18n/label :t/block)
:button-one-props {:type :danger
:on-press on-block-press}
:button-two-label (i18n/label :t/cancel)
:button-two-props {:type :grey
:on-press on-close}}]]))
@@ -2,6 +2,7 @@
(:require [clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.profile.contact.contact-request.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
@@ -11,8 +12,8 @@
[]
(let [{:keys [public-key customization-color]
:as profile} (rf/sub [:contacts/current-contact])
;; TODO: remove :blue when #18733 merged.
customization-color (or customization-color :blue)
;; TODO: remove default color when #18733 merged.
customization-color (or customization-color constants/profile-default-color)
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
[message set-message] (rn/use-state "")
@@ -1,6 +1,7 @@
(ns status-im.contexts.profile.contact.contact-review.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.profile.contact.contact-review.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
@@ -14,7 +15,7 @@
:keys [message]} (rf/sub [:activity-center/pending-contact-request-from-contact-id
public-key])
;; TODO(@seanstrom): https://github.com/status-im/status-mobile/issues/18733
customization-color (or customization-color :blue)
customization-color (or customization-color constants/profile-default-color)
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
on-contact-accept (rn/use-callback
@@ -13,24 +13,41 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn on-contact-request
[]
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [contact-request/view])}]))
(defn on-contact-review
[]
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [contact-review/view])}]))
(defn view
[{:keys [scroll-y]}]
(let [{:keys [public-key customization-color ens-name
emoji-hash bio contact-request-state]
:as profile} (rf/sub [:contacts/current-contact])
customization-color (or customization-color :blue)
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
emoji-hash bio blocked? contact-request-state]
:as contact} (rf/sub [:contacts/current-contact])
;; TODO(@mohsen): remove default color, https://github.com/status-im/status-mobile/issues/18733
customization-color (or customization-color constants/profile-default-color)
full-name (profile.utils/displayed-name contact)
profile-picture (profile.utils/photo contact)
online? (rf/sub [:visibility-status-updates/online? public-key])
theme (quo.theme/use-theme-value)
on-contact-request (rn/use-callback #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [contact-request/view])}]))
on-contact-review (rn/use-callback #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [contact-review/view])}]))
on-start-chat (rn/use-callback #(rf/dispatch [:chat.ui/start-chat
public-key
ens-name])
[ens-name public-key])]
[ens-name public-key])
on-unblock-press (rn/use-callback (fn []
(rf/dispatch [:contact.ui/unblock-contact-pressed
public-key])
(rf/dispatch [:toasts/upsert
{:id :user-unblocked
:type :positive
:text (i18n/label :t/user-unblocked
{:username
full-name})}]))
[public-key full-name])]
[rn/view {:style style/header-container}
[rn/view {:style style/header-top-wrapper}
[rn/view {:style style/avatar-wrapper}
@@ -53,10 +70,20 @@
:description-text bio
:emoji-dash emoji-hash}]
(when blocked?
[quo/button
{:container-style style/button-wrapper
:on-press on-unblock-press
:icon-left :i/block}
(i18n/label :t/unblock)])
(cond
(or (not contact-request-state)
(= contact-request-state constants/contact-request-state-none)
(= contact-request-state constants/contact-request-state-dismissed))
(and (not blocked?)
(or
(not contact-request-state)
(= contact-request-state constants/contact-request-state-none)
(= contact-request-state constants/contact-request-state-dismissed)))
[quo/button
{:container-style style/button-wrapper
:on-press on-contact-request
@@ -4,6 +4,7 @@
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im.common.scroll-page.view :as scroll-page]
[status-im.constants :as constants]
[status-im.contexts.profile.contact.actions.view :as actions]
[status-im.contexts.profile.contact.header.view :as contact-header]
[utils.re-frame :as rf]))
@@ -21,8 +22,8 @@
{:navigate-back? true
:height 148
:on-scroll #(reanimated/set-shared-value scroll-y %)
;; TODO(@mohsen): remove colors/primary-50, https://github.com/status-im/status-mobile/issues/18733
:cover-color (or customization-color colors/primary-50)
;; TODO(@mohsen): remove default color, https://github.com/status-im/status-mobile/issues/18733
:cover-color (or customization-color constants/profile-default-color)
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:page-nav-props {:right-side [{:icon-name :i/options
:on-press on-action-press}]}}
@@ -170,20 +170,18 @@
(defn password-input
[]
(let [auth-method (rf/sub [:auth-method])
on-press-biometrics (rn/use-callback
(fn []
(when (= auth-method constants/auth-method-biometric)
(rf/dispatch [:biometric/authenticate
{:on-success #(rf/dispatch
[:profile.login/biometric-success])
:on-fail #(rf/dispatch
[:profile.login/biometric-auth-fail
%])}])))
[auth-method])]
on-press-biometrics (fn []
(rf/dispatch [:biometric/authenticate
{:on-success #(rf/dispatch
[:profile.login/biometric-success])
:on-fail #(rf/dispatch
[:profile.login/biometric-auth-fail
%])}]))
]
[standard-authentication/password-input
{:shell? true
:blur? true
:on-press-biometrics on-press-biometrics}]))
:on-press-biometrics (when (= auth-method constants/auth-method-biometric) on-press-biometrics)}]))
(defn login-section
[{:keys [show-profiles]}]
@@ -153,9 +153,10 @@
(reagent/flush))))
handle-delete (fn [loading-routes? current-limit-amount]
(when-not loading-routes?
(let [{:keys [start end]} @input-selection]
(reset-input-error @input-value current-limit-amount input-error)
(let [{:keys [start end]} @input-selection
new-value (delete-from-string @input-value start)]
(when (= start end)
(reset-input-error new-value current-limit-amount input-error)
(swap! input-value delete-from-string start)
(move-input-cursor input-selection (dec start)))
(reagent/flush))))
@@ -213,16 +214,16 @@
crypto-limit (or default-limit-crypto
(utils/get-standard-crypto-format token token-balance))
fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-limit (if @crypto-currency? crypto-limit fiat-limit)
current-limit #(if @crypto-currency? crypto-limit fiat-limit)
current-currency (if @crypto-currency? token-symbol fiat-currency)
limit-label (make-limit-label {:amount current-limit
limit-label (make-limit-label {:amount (current-limit)
:currency current-currency})
input-num-value (parse-double @input-value)
confirm-disabled? (or (nil? route)
(empty? route)
(empty? @input-value)
(<= input-num-value 0)
(> input-num-value current-limit))
(> input-num-value (current-limit)))
amount-text (str @input-value " " token-symbol)
native-currency-symbol (when-not confirm-disabled?
(get-in route [:from :native-currency-symbol]))
@@ -248,7 +249,7 @@
app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)]
#(.remove app-keyboard-listener))))
(rn/use-effect
#(fetch-routes input-num-value current-limit)
#(fetch-routes input-num-value (current-limit))
[@input-value])
[rn/view
{:style style/screen
@@ -269,7 +270,7 @@
:show-keyboard? false
:value @input-value
:selection @input-selection
:on-change-text #(handle-on-change % current-limit)
:on-change-text #(handle-on-change % (current-limit))
:on-selection-change selection-change
:on-swap #(handle-swap
{:crypto? %
@@ -282,7 +283,7 @@
:routes suggested-routes
:token token
:input-value @input-value
:fetch-routes #(fetch-routes % current-limit)}]
:fetch-routes #(fetch-routes % (current-limit))}]
(when (or loading-routes? (seq route))
[estimated-fees
{:loading-suggested-routes? loading-routes?
@@ -299,8 +300,8 @@
{:container-style (style/keyboard-container bottom)
:left-action :dot
:delete-key? true
:on-press #(handle-keyboard-press % loading-routes? current-limit)
:on-delete #(handle-delete loading-routes? current-limit)
:on-press #(handle-keyboard-press % loading-routes? (current-limit))
:on-delete #(handle-delete loading-routes? (current-limit))
:on-long-press-delete #(on-long-press-delete loading-routes?)}]]))))
(defn- view-internal
+14 -31
View File
@@ -56,7 +56,9 @@
(rf/dispatch event))
(effects/dismiss-modal))
(when-let [handler (get-in views/screens [(keyword id) :right-handler])]
(handler)))))
(handler)))
(when (= "legacy-back-button" id)
(rf/dispatch [:navigate-back]))))
(navigation/reg-modal-dismissed-listener
(fn []
@@ -92,35 +94,16 @@
(navigation/register-component
"bottom-sheet"
(fn [] (gesture/gesture-handler-root-hoc views/bottom-sheet))
(fn [] views/bottom-sheet)))
(fn [] views/bottom-sheet))
;;;; Alert Banner
(navigation/register-component
"alert-banner"
(fn [] (gesture/gesture-handler-root-hoc views/alert-banner #js {:flex 0}))
(fn [] views/alert-banner))
;;;; Alert Banner
(navigation/register-component
"alert-banner"
(fn [] (gesture/gesture-handler-root-hoc views/alert-banner #js {:flex 0}))
(fn [] views/alert-banner))
;; LEGACY (should be removed in status 2.0)
(defonce
_
[(navigation/register-component
"popover"
(fn [] (gesture/gesture-handler-root-hoc views/popover-comp))
(fn [] views/popover-comp))
(navigation/register-component
"visibility-status-popover"
(fn [] (gesture/gesture-handler-root-hoc views/visibility-status-popover-comp))
(fn [] views/visibility-status-popover-comp))
(navigation/register-component
"bottom-sheet-old"
(fn [] (gesture/gesture-handler-root-hoc views/sheet-comp-old))
(fn [] views/sheet-comp-old))
(navigation/register-component
"signing-sheet"
(fn [] (gesture/gesture-handler-root-hoc views/signing-comp))
(fn [] views/signing-comp))])
;;;; LEGACY (should be removed in status 2.0)
(navigation/register-component
"bottom-sheet-old"
(fn [] (gesture/gesture-handler-root-hoc views/sheet-comp-old))
(fn [] views/sheet-comp-old)))
+9 -17
View File
@@ -63,12 +63,8 @@
(name @state/root-id)
{:component {:id component
:name component
:options (merge (options/default-root)
(options/statusbar-and-navbar)
options
(if (:topBar options)
(options/merge-top-bar (options/topbar-options) options)
{:topBar {:visible false}}))}})))
:options (merge (options/root-options {:theme (:theme options)})
options)}})))
(rf/reg-fx :navigate-to navigate)
@@ -81,11 +77,9 @@
(name comp-id)
{:component {:id component
:name component
:options (merge (options/statusbar-and-navbar)
options
(if (:topBar options)
(options/merge-top-bar (options/topbar-options) options)
{:topBar {:visible false}}))}})))
:options (merge
(options/root-options {:theme (:theme options)})
options)}})))
(rf/reg-fx :navigate-to-within-stack navigate-to-within-stack)
@@ -140,8 +134,7 @@
{:stack {:children [{:component
{:name component
:id component
:options (merge (options/default-root)
(options/statusbar-and-navbar)
:options (merge (options/root-options {:theme (:theme options)})
options
(when sheet?
options/sheet-options))}}]}})))))
@@ -157,7 +150,7 @@
(navigation/show-overlay
{:component {:name component
:id component
:options (merge (options/statusbar)
:options (merge (options/statusbar-and-navbar-options (:theme opts) nil nil)
{:layout {:componentBackgroundColor :transparent
:orientation ["portrait"]}
:overlay {:interceptTouchOutside true}}
@@ -201,9 +194,8 @@
(let [{:keys [options]} (get views/screens component)]
{:component {:id component
:name component
:options (merge (options/statusbar-and-navbar)
options
(options/merge-top-bar (options/topbar-options) options))}}))
:options (merge (options/statusbar-and-navbar-options (:theme options) nil nil)
options)}}))
(rf/reg-fx :set-stack-root-fx
(fn [[stack component]]
-17
View File
@@ -155,22 +155,6 @@
#(when-not hide?
(if (seq sheets) (hide-bottom-sheet %) {:show-bottom-sheet nil})))))
;; LEGACY (should be removed in status 2.0)
(rf/defn hide-signing-sheet
{:events [:hide-signing-sheet]}
[_]
{:hide-signing-sheet nil})
(rf/defn set-multiaccount-root
{:events [:set-multiaccount-root]}
[{:keys [db]}]
(let [key-uid (get-in db [:profile/login :key-uid])
keycard-account? (boolean (get-in db
[:profile/profiles-overview
key-uid
:keycard-pairing]))]
{:set-root (if keycard-account? :multiaccounts-keycard :multiaccounts)}))
(rf/defn dismiss-all-overlays
{:events [:dismiss-all-overlays]}
[_]
@@ -178,7 +162,6 @@
[:hide-visibility-status-popover]
[:hide-bottom-sheet]
[:bottom-sheet-hidden]
[:hide-signing-sheet]
[:bottom-sheet/hide-old-navigation-overlay]
[:toasts/close-all-toasts]]})
+34 -80
View File
@@ -10,23 +10,23 @@
{:layout {:orientation ["portrait"]}
:topBar {:visible false}})
;; Note: Currently, the status bar style provided while setting the root has a high preference,
;; and even if we change the status bar style later dynamically, the style gets restored to this
;; set root style while navigating
;; https://github.com/status-im/status-mobile/pull/15596
(defn statusbar-and-navbar-root
[& [status-bar-theme]]
(if platform/android?
{:navigationBar {:backgroundColor colors/neutral-100}
:statusBar {:translucent true
:backgroundColor :transparent
:style (or status-bar-theme :light)
:drawBehind true}}
{:statusBar {:style (or status-bar-theme :light)}}))
(defn statusbar-and-navbar-options
[theme status-bar-theme nav-bar-color]
(let [[status-bar-theme nav-bar-color]
(if (= :dark (or theme (quo.theme/get-theme)))
[(or status-bar-theme :light) (or nav-bar-color colors/neutral-100)]
[(or status-bar-theme :dark) (or nav-bar-color colors/white)])]
(if platform/android?
{:navigationBar {:backgroundColor nav-bar-color}
:statusBar {:translucent true
:backgroundColor :transparent
:style (or status-bar-theme :light)
:drawBehind true}}
{:statusBar {:style (or status-bar-theme :light)}})))
(defn default-root
[& [status-bar-theme background-color]]
(merge (statusbar-and-navbar-root status-bar-theme)
(defn root-options
[{:keys [background-color theme status-bar-theme nav-bar-color]}]
(merge (statusbar-and-navbar-options theme status-bar-theme nav-bar-color)
{:topBar {:visible false}
:layout {:componentBackgroundColor (or background-color
(colors/theme-colors colors/white colors/neutral-100))
@@ -35,6 +35,15 @@
(colors/theme-colors colors/white
colors/neutral-100))}}))
(defn dark-root-options
[]
(root-options
{:background-color colors/neutral-100
:theme :dark
:status-bar-theme :light
:nav-bar-color colors/neutral-100}))
;;;; Screen Specific Options
(def onboarding-layout
{:componentBackgroundColor colors/neutral-80-opa-80-blur
:orientation ["portrait"]
@@ -45,49 +54,12 @@
:orientation ["portrait"]
:backgroundColor :transparent})
(defn navbar
([dark?]
{:navigationBar {:backgroundColor (if (or dark? (= :dark (quo.theme/get-theme)))
colors/neutral-100
colors/white)}})
([] (navbar nil)))
(defn statusbar
([dark?]
(let [style (if (or dark? (= :dark (quo.theme/get-theme))) :light :dark)]
(if platform/android?
{:statusBar {:translucent true
:backgroundColor :transparent
:drawBehind true
:style style}}
{:statusBar {:style style}})))
([] (statusbar nil)))
(defn statusbar-and-navbar
([dark?]
(merge (navbar dark?) (statusbar dark?)))
([] (statusbar-and-navbar nil)))
(defn topbar-options
[]
{:noBorder true
:scrollEdgeAppearance {:active false
:noBorder true}
:elevation 0
:title {:color (colors/theme-colors colors/neutral-100 colors/white)}
:rightButtonColor (colors/theme-colors colors/neutral-100 colors/white)
:background {:color (colors/theme-colors colors/white colors/neutral-100)}
:backButton {:color (colors/theme-colors colors/neutral-100 colors/white)
:testID :back-button}})
(def transparent-screen-options
(merge
(statusbar-and-navbar-root)
{:modalPresentationStyle :overCurrentContext
:theme :dark
:layout {:componentBackgroundColor :transparent
:orientation ["portrait"]
:backgroundColor :transparent}}))
{:modalPresentationStyle :overCurrentContext
:theme :dark
:layout {:componentBackgroundColor :transparent
:orientation ["portrait"]
:backgroundColor :transparent}})
(def transparent-modal-screen-options
(merge
@@ -109,11 +81,10 @@
{})})
(def dark-screen
(merge (statusbar-and-navbar-root)
{:theme :dark
:layout {:componentBackgroundColor colors/neutral-95
:orientation ["portrait"]
:backgroundColor colors/neutral-95}}))
{:theme :dark
:layout {:componentBackgroundColor colors/neutral-95
:orientation ["portrait"]
:backgroundColor colors/neutral-95}})
(def lightbox
{:topBar {:visible false}
@@ -136,20 +107,3 @@
:interpolation {:type
:decelerate
:factor 1.5}}]}}})
(defn merge-top-bar
[root-options options]
(let [options (:topBar options)]
{:topBar
(merge root-options
options
(when (or (:title root-options) (:title options))
{:title (merge (:title root-options) (:title options))})
(when (or (:background root-options) (:background options))
{:background (merge (:background root-options) (:background options))})
(when (or (:backButton root-options) (:backButton options))
{:backButton (merge (:backButton root-options) (:backButton options))})
(when (or (:leftButtons root-options) (:leftButtons options))
{:leftButtons (merge (:leftButtons root-options) (:leftButtons options))})
(when (or (:rightButtons root-options) (:rightButtons options))
{:rightButtons (merge (:rightButtons root-options) (:rightButtons options))}))}))
+32 -101
View File
@@ -1,74 +1,8 @@
(ns status-im.navigation.roots
(:require
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[status-im.constants :as constants]
[status-im.navigation.options :as options]
[status-im.navigation.view :as views]))
(defn get-screen-options
[screen]
(merge (get-in views/screens [screen :options])
(options/statusbar-and-navbar-root)
(options/merge-top-bar (options/topbar-options)
(get-in views/screens [screen :options]))))
(defn old-roots
[]
{;; ONBOARDING
:onboarding
{:root {:stack {:id :onboarding
:children [{:component {:name :get-your-keys
:id :get-your-keys
:options (options/statusbar-and-navbar-root)}}]
:options (merge (options/default-root)
(options/statusbar-and-navbar-root)
{:topBar (assoc (options/topbar-options)
:elevation 0
:noBorder true
:animate false)})}}}
;;PROGRESS
:progress
{:root {:stack {:children [{:component {:name :progress
:id :progress
:options (options/statusbar-and-navbar-root)}}]
:options (merge (options/default-root)
{:topBar (assoc (options/topbar-options) :visible false)})}}}
;;LOGIN
:multiaccounts
{:root {:stack {:id :multiaccounts-stack
:children [{:component {:name :multiaccounts
:id :multiaccounts
:options (get-screen-options :multiaccounts)}}
{:component {:name :login
:id :login
:options (get-screen-options :login)}}]
:options (merge (options/default-root)
(options/statusbar-and-navbar-root)
{:topBar (options/topbar-options)})}}}
:multiaccounts-keycard
{:root {:stack {:id :multiaccounts-stack
:children [{:component {:name :profiles
:id :profiles
:options (get-screen-options :multiaccounts)}}
{:component {:name :keycard-login-pin
:id :keycard-login-pin
:options (get-screen-options :keycard-login-pin)}}]
:options (merge (options/default-root)
(options/statusbar-and-navbar-root)
{:topBar (options/topbar-options)})}}}
;;NOTIFICATIONS
:onboarding-notification
{:root {:stack {:children [{:component {:name :onboarding-notification
:id :onboarding-notification
:options (options/statusbar-and-navbar-root)}}]
:options (merge (options/default-root)
(options/statusbar-and-navbar-root)
{:topBar (assoc (options/topbar-options) :visible false)})}}}})
[status-im.navigation.options :as options]))
;; Theme Order for navigation roots
;; 1. Themes hardcoded in below map
@@ -82,39 +16,36 @@
(defn roots
[]
(merge
(old-roots)
{:intro
{:root
{:stack {:id :intro
:children [{:component {:name :intro
:id :intro
:options (options/dark-root-options)}}]}}}
:shell-stack
{:root
{:stack {:id :shell-stack
:children [{:component {:name :shell-stack
:id :shell-stack
:options (options/root-options
{:nav-bar-color colors/neutral-100})}}]}}}
:profiles
{:root
{:stack {:id :profiles
:children [{:component {:name :profiles
:id :profiles
:options (options/dark-root-options)}}]}}}
{:intro
{:root
{:stack {:id :intro
:children [{:component {:name :intro
:id :intro
:options (options/default-root nil colors/neutral-100)}}]}}}
:shell-stack
{:root
{:stack {:id :shell-stack
:children [{:component {:name :shell-stack
:id :shell-stack
:options (options/default-root
(if (= :dark (quo.theme/get-theme)) :light :dark))}}]}}}
:profiles
{:root
{:stack {:id :profiles
:children [{:component {:name :profiles
:id :profiles
:options (options/default-root)}}]}}}
:enable-notifications
{:root {:stack {:children [{:component {:name :enable-notifications
:id :enable-notifications
:options (options/dark-root-options)}}]}}}
:enable-notifications
{:root {:stack {:children [{:component {:name :enable-notifications
:id :enable-notifications
:options (options/default-root)}}]}}}
:welcome
{:root {:stack {:children [{:component {:name :welcome
:id :welcome
:options (options/default-root)}}]}}}
:syncing-results
{:root {:stack {:children [{:component {:name :syncing-results
:id :syncing-results
:options (options/default-root)}}]}}}}))
:welcome
{:root {:stack {:children [{:component {:name :welcome
:id :welcome
:options (options/dark-root-options)}}]}}}
:syncing-results
{:root {:stack {:children [{:component {:name :syncing-results
:id :syncing-results
:options (options/dark-root-options)}}]}}}})
+22 -30
View File
@@ -130,7 +130,7 @@
:component lightbox/lightbox}
{:name :photo-selector
:options (merge {:sheet? true} (options/statusbar-and-navbar-root))
:options {:sheet? true}
:component photo-selector/photo-selector}
{:name :camera-screen
@@ -230,17 +230,15 @@
:component create-password/create-password}
{:name :enable-biometrics
:options (merge
(options/statusbar-and-navbar-root)
{:theme :dark
:layout options/onboarding-transparent-layout
:animations (merge
transitions/new-to-status-modal-animations
transitions/push-animations-for-transparent-background)
:popGesture false
:modalPresentationStyle :overCurrentContext
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false}})
:options {:theme :dark
:layout options/onboarding-transparent-layout
:animations (merge
transitions/new-to-status-modal-animations
transitions/push-animations-for-transparent-background)
:popGesture false
:modalPresentationStyle :overCurrentContext
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false}}
:component enable-biometrics/view}
{:name :generating-keys
@@ -260,14 +258,12 @@
:component enter-seed-phrase/enter-seed-phrase}
{:name :enable-notifications
:options (merge
(options/statusbar-and-navbar-root)
{:theme :dark
:layout options/onboarding-transparent-layout
:animations (merge
transitions/new-to-status-modal-animations
transitions/push-animations-for-transparent-background)
:modalPresentationStyle :overCurrentContext})
:options {:theme :dark
:layout options/onboarding-transparent-layout
:animations (merge
transitions/new-to-status-modal-animations
transitions/push-animations-for-transparent-background)
:modalPresentationStyle :overCurrentContext}
:component enable-notifications/view}
{:name :identifiers
@@ -298,11 +294,9 @@
:component sign-in/view}
{:name :syncing-progress
:options (merge
(options/statusbar-and-navbar-root)
{:theme :dark
:layout options/onboarding-layout
:popGesture false})
:options {:theme :dark
:layout options/onboarding-layout
:popGesture false}
:component syncing-devices/view}
{:name :syncing-progress-intro
@@ -317,11 +311,9 @@
:component syncing-results/view}
{:name :welcome
:options (merge
(options/statusbar-and-navbar-root)
{:theme :dark
:layout options/onboarding-transparent-layout
:animations transitions/push-animations-for-transparent-background})
:options {:theme :dark
:layout options/onboarding-transparent-layout
:animations transitions/push-animations-for-transparent-background}
:component welcome/view}
{:name :emoji-picker
+8 -44
View File
@@ -1,9 +1,6 @@
(ns status-im.navigation.view
(:require
[legacy.status-im.bottom-sheet.sheets :as bottom-sheets-old]
[legacy.status-im.ui.screens.popover.views :as popover]
[legacy.status-im.ui.screens.profile.visibility-status.views :as visibility-status-views]
[legacy.status-im.ui.screens.signing.views :as signing]
[quo.foundations.colors :as colors]
[quo.theme :as theme]
[react-native.core :as rn]
@@ -111,29 +108,15 @@
(def toasts (reagent/reactify-component toasts/toasts))
(def alert-banner
(reagent/reactify-component
(fn []
^{:key (str "alert-banner" @reloader/cnt)}
[theme/provider {:theme :dark}
[alert-banner/view]])
functional-compiler))
;; LEGACY (should be removed in status 2.0)
(def popover-comp
(reagent/reactify-component
(fn []
^{:key (str "popover" @reloader/cnt)}
[:<>
[inactive]
[popover/popover]
(when js/goog.DEBUG
[reloader/reload-view])])
functional-compiler))
(def visibility-status-popover-comp
(reagent/reactify-component
(fn []
^{:key (str "visibility-status-popover" @reloader/cnt)}
[rn/view
[inactive]
[visibility-status-views/visibility-status-popover]
(when js/goog.DEBUG
[reloader/reload-view])])
functional-compiler))
(def sheet-comp-old
(reagent/reactify-component
(fn []
@@ -142,22 +125,3 @@
[inactive]
[bottom-sheets-old/bottom-sheet]])
functional-compiler))
(def signing-comp
(reagent/reactify-component
(fn []
^{:key (str "signing-sheet" @reloader/cnt)}
[:<>
[inactive]
[signing/signing]
(when js/goog.DEBUG
[reloader/reload-view])])
functional-compiler))
(def alert-banner
(reagent/reactify-component
(fn []
^{:key (str "alert-banner" @reloader/cnt)}
[theme/provider {:theme :dark}
[alert-banner/view]])
functional-compiler))
+5 -4
View File
@@ -181,15 +181,16 @@
(chat.events/public-chat? current-chat)
(assoc :able-to-send-message? true)
(and (chat.events/community-chat? current-chat)
(get-in community [:chats (subs (:chat-id current-chat) 68) :can-post?]))
(assoc :able-to-send-message? true)
(and (chat.events/group-chat? current-chat)
(group-chats.db/member? my-public-key current-chat))
(assoc :able-to-send-message? true
:member? true)
(and (chat.events/community-chat? current-chat)
(get-in community [:chats (subs (:chat-id current-chat) 68) :can-post?]))
(assoc :able-to-send-message? true)
(not group-chat)
(assoc
:contact-request-state (get-in contacts [chat-id :contact-request-state])
+10 -40
View File
@@ -229,42 +229,13 @@
(sort-by :position)
(into []))))
(defn- get-chat-lock-state
"Returns the chat lock state.
- Nil: no lock (there are no permissions for the chat)
- True: locked (there are permissions and can-post? is false)
- False: unlocked (there are permissions and can-post? is true)"
[community-id channels-permissions {chat-id :id}]
(let [composite-key (keyword (str community-id chat-id))
permissions (get channels-permissions composite-key)
{view-only-satisfied? :satisfied?
view-only-permissions :permissions} (:view-only permissions)
{view-and-post-satisfied? :satisfied?
view-and-post-permissions :permissions} (:view-and-post permissions)
can-access? (or (and (seq view-only-permissions)
view-only-satisfied?)
(and (seq view-and-post-permissions)
view-and-post-satisfied?))]
(if (and (empty? view-only-permissions)
(empty? view-and-post-permissions))
nil
(not can-access?))))
(re-frame/reg-sub
:communities/community-channels-permissions
:<- [:communities/channels-permissions]
(fn [channel-permissions [_ community-id]]
(get channel-permissions community-id)))
(defn- reduce-over-categories
[community-id
categories
collapsed-categories
full-chats-data
channels-permissions]
full-chats-data]
(fn [acc
[_ {:keys [name categoryID position id emoji] :as chat}]]
[_ {:keys [name categoryID position id emoji can-post? token-gated?]}]]
(let [category-id (if (seq categoryID) categoryID constants/empty-category-id)
{:keys [unviewed-messages-count
unviewed-mentions-count
@@ -286,9 +257,11 @@
:unread-messages? (pos? unviewed-messages-count)
:position position
:mentions-count (or unviewed-mentions-count 0)
:locked? (get-chat-lock-state community-id
channels-permissions
chat)
:can-post? can-post?
;; NOTE: this is a troolean nil->no permissions, true->no access, false ->
;; has access
:locked? (when token-gated?
(not can-post?))
:id id}]
(update-in acc-with-category [category-id :chats] conj categorized-chat))))
@@ -297,17 +270,14 @@
(fn [[_ community-id]]
[(re-frame/subscribe [:communities/community community-id])
(re-frame/subscribe [:chats/chats])
(re-frame/subscribe [:communities/collapsed-categories-for-community community-id])
(re-frame/subscribe [:communities/community-channels-permissions community-id])])
(fn [[{:keys [categories chats]} full-chats-data collapsed-categories
channels-permissions]
(re-frame/subscribe [:communities/collapsed-categories-for-community community-id])])
(fn [[{:keys [categories chats]} full-chats-data collapsed-categories]
[_ community-id]]
(let [reduce-fn (reduce-over-categories
community-id
categories
collapsed-categories
full-chats-data
channels-permissions)
full-chats-data)
categories-and-chats
(->> chats
(reduce reduce-fn {})
+78 -175
View File
@@ -84,26 +84,29 @@
:communities
{"0x1" {:id "0x1"
:chats {"0x1"
{:id "0x1"
:position 1
:name "chat1"
:muted? nil
:categoryID "1"
:can-post? true}
{:id "0x1"
:position 1
:name "chat1"
:muted? nil
:categoryID "1"
:token-gated? false
:can-post? true}
"0x2"
{:id "0x2"
:position 2
:name "chat2"
:muted? nil
:categoryID "1"
:can-post? false}
{:id "0x2"
:position 2
:name "chat2"
:muted? nil
:categoryID "1"
:token-gated? true
:can-post? false}
"0x3"
{:id "0x3"
:position 3
:name "chat3"
:muted? nil
:categoryID "2"
:can-post? true}}
{:id "0x3"
:position 3
:name "chat3"
:muted? nil
:categoryID "2"
:token-gated? true
:can-post? true}}
:categories {"1" {:id "1"
:position 2
:name "category1"}
@@ -112,141 +115,41 @@
:name "category2"}}
:joined true}})
(is
(= [["2"
{:id "2"
:name "category2"
:collapsed? nil
:position 1
:chats [{:name "chat3"
:position 3
:emoji nil
:muted? nil
:locked? nil
:id "0x3"
:unread-messages? false
:mentions-count 0}]}]
["1"
{:id "1"
:name "category1"
:collapsed? nil
:position 2
:chats [{:name "chat1"
:emoji nil
:position 1
:muted? nil
:locked? nil
:id "0x1"
:unread-messages? false
:mentions-count 0}
{:name "chat2"
:emoji nil
:position 2
:muted? nil
:locked? nil
:id "0x2"
:unread-messages? false
:mentions-count 0}]}]]
(rf/sub [sub-name "0x1"]))))
(testing "Channels with categories and token permissions"
(swap! rf-db/app-db assoc
:community-channels-permissions
{community-id
{(keyword (str community-id "0x100"))
{:view-only {:satisfied? false
:permissions {:token-permission-id-01 {:criteria [false]}}}
:view-and-post {:satisfied? true :permissions {}}}
(keyword (str community-id "0x200"))
{:view-only {:satisfied? true :permissions {}}
:view-and-post {:satisfied? true :permissions {}}}
(keyword (str community-id "0x300"))
{:view-only {:satisfied? false :permissions {}}
:view-and-post {:satisfied? true
:permissions {:token-permission-id-03 {:criteria [true]}}}}
(keyword (str community-id "0x400"))
{:view-only {:satisfied? true
:permissions {}}
:view-and-post {:satisfied? false
:permissions {:token-permission-id-04 {:criteria [false]}}}}}}
:communities
{community-id {:id community-id
:chats {"0x100" {:id "0x100"
:position 1
:name "chat1"
:muted? nil
:categoryID "1"
:can-post? false}
"0x200" {:id "0x200"
:position 2
:name "chat2"
:muted? nil
:categoryID "1"
:can-post? false}
"0x300" {:id "0x300"
:position 3
:name "chat3"
:muted? nil
:categoryID "2"
:can-post? true}
"0x400" {:id "0x400"
:position 4
:name "chat4"
:muted? nil
:categoryID "2"
:can-post? true}}
:categories {"1" {:id "1"
:position 2
:name "category1"}
"2" {:id "2"
:position 1
:name "category2"}}
:joined true}})
(is
(= [["2"
{:id "2"
:name "category2"
:collapsed? nil
:position 1
:chats [{:name "chat3"
:position 3
:emoji nil
:muted? nil
:locked? false
:id "0x300"
:unread-messages? false
:mentions-count 0}
{:name "chat4"
:position 4
:emoji nil
:muted? nil
:locked? true
:id "0x400"
:unread-messages? false
:mentions-count 0}]}]
["1"
{:id "1"
:name "category1"
:collapsed? nil
:position 2
:chats [{:name "chat1"
:emoji nil
:position 1
:muted? nil
:locked? true
:id "0x100"
:unread-messages? false
:mentions-count 0}
{:name "chat2"
:emoji nil
:position 2
:muted? nil
:locked? nil
:id "0x200"
:unread-messages? false
:mentions-count 0}]}]]
(rf/sub [sub-name "0x1"]))))
(match? [["2"
{:id "2"
:name "category2"
:collapsed? nil
:position 1
:chats [{:name "chat3"
:position 3
:emoji nil
:muted? nil
:locked? false
:id "0x3"
:unread-messages? false
:mentions-count 0}]}]
["1"
{:id "1"
:name "category1"
:collapsed? nil
:position 2
:chats [{:name "chat1"
:emoji nil
:position 1
:muted? nil
:locked? nil
:id "0x1"
:unread-messages? false
:mentions-count 0}
{:name "chat2"
:emoji nil
:position 2
:muted? nil
:locked? true
:id "0x2"
:unread-messages? false
:mentions-count 0}]}]]
(rf/sub [sub-name "0x1"]))))
(testing "Channels without categories"
(swap! rf-db/app-db assoc
:communities
@@ -274,7 +177,7 @@
:name "category2"}}
:joined true}})
(is
(=
(match?
[[constants/empty-category-id
{:name (i18n/label :t/none)
:collapsed? nil
@@ -323,27 +226,27 @@
{"0x10x1" {:unviewed-messages-count 1 :unviewed-mentions-count 2}
"0x10x2" {:unviewed-messages-count 0 :unviewed-mentions-count 0}})
(is
(= [["1"
{:name "category1"
:id "1"
:collapsed? nil
:chats [{:name "chat1"
:emoji nil
:position 1
:locked? nil
:id "0x1"
:muted? nil
:unread-messages? true
:mentions-count 2}
{:name "chat2"
:emoji nil
:position 2
:locked? nil
:muted? nil
:id "0x2"
:unread-messages? false
:mentions-count 0}]}]]
(rf/sub [sub-name "0x1"])))))
(match? [["1"
{:name "category1"
:id "1"
:collapsed? nil
:chats [{:name "chat1"
:emoji nil
:position 1
:locked? nil
:id "0x1"
:muted? nil
:unread-messages? true
:mentions-count 2}
{:name "chat2"
:emoji nil
:position 2
:locked? nil
:muted? nil
:id "0x2"
:unread-messages? false
:mentions-count 0}]}]]
(rf/sub [sub-name "0x1"])))))
(h/deftest-sub :communities/my-pending-requests-to-join
[sub-name]
-1
View File
@@ -141,7 +141,6 @@
(reg-root-key-sub :communities :communities)
(reg-root-key-sub :communities/create :communities/create)
(reg-root-key-sub :communities/create-channel :communities/create-channel)
(reg-root-key-sub :communities/channels-permissions :community-channels-permissions)
(reg-root-key-sub :communities/requests-to-join :communities/requests-to-join)
(reg-root-key-sub :communities/community-id-input :communities/community-id-input)
(reg-root-key-sub :communities/fetching-community :communities/fetching-community)
-36
View File
@@ -1,36 +0,0 @@
(ns test-helpers.integration
(:require [day8.re-frame.test :as rf-test]
[re-frame.core :as rf]))
(defmacro with-app-initialized
[& body]
`(do
(tests.test-utils/init!)
(if (test-helpers.integration/app-initialized)
(do ~@body)
(do
(rf/dispatch [:app-started])
(rf-test/wait-for [:profile/get-profiles-overview-success]
~@body)))))
(defmacro with-account
[& body]
`(if (test-helpers.integration/messenger-started)
(do ~@body)
(do
(test-helpers.integration/create-multiaccount!)
(rf-test/wait-for [:messenger-started]
(test-helpers.integration/assert-messenger-started)
~@body))))
(defmacro with-recovered-account
[& body]
`(if (test-helpers.integration/messenger-started)
(do ~@body)
(do
(test-helpers.integration/recover-multiaccount!)
(rf-test/wait-for
[:messenger-started]
(test-helpers.integration/enable-testnet!)
(test-helpers.integration/assert-messenger-started)
~@body))))
+46 -26
View File
@@ -1,5 +1,4 @@
(ns test-helpers.integration
(:require-macros [test-helpers.integration])
(:require
[cljs.test :refer [is] :as test]
legacy.status-im.events
@@ -20,14 +19,16 @@
[utils.transforms :as transforms]))
(defn validate-mnemonic
[mnemonic on-success on-error]
(native-module/validate-mnemonic
(security/safe-unmask-data mnemonic)
(fn [result]
(let [{:keys [error keyUID]} (transforms/json->clj result)]
(if (seq error)
(when on-error (on-error error))
(on-success mnemonic keyUID))))))
[mnemonic]
(p/create
(fn [p-resolve p-reject]
(native-module/validate-mnemonic
(security/safe-unmask-data mnemonic)
(fn [result]
(let [{:keys [error keyUID]} (transforms/json->clj result)]
(if (seq error)
(p-reject error)
(p-resolve [mnemonic keyUID]))))))))
(def default-re-frame-wait-for-timeout-ms
"Controls the maximum time allowed to wait for all events to be processed by
@@ -153,7 +154,7 @@
(-> (wait-for [:messenger-started])
(.then #(assert-messenger-started))))))
(defn integration-test
(defn test-async
"Runs `f` inside `cljs.test/async` macro in a restorable re-frame checkpoint.
`f` will be called with one argument, the `done` function exposed by the
@@ -175,10 +176,11 @@
fail more often.
"
([test-name f]
(integration-test test-name
{:fail-fast? true
:timeout-ms default-integration-test-timeout-ms}
f))
(test-async
test-name
{:fail-fast? true
:timeout-ms default-integration-test-timeout-ms}
f))
([test-name {:keys [fail-fast? timeout-ms]} f]
(test/async
done
@@ -194,6 +196,29 @@
(restore-fn)
(done))))))))
(defn test-app-initialization
[]
(test-async ::initialize-app
(fn []
(p/do
(test-utils/init!)
(rf/dispatch [:app-started])
;; Use initialize-view because it has the longest avg. time and is
;; dispatched by initialize-multiaccounts (last non-view event).
(wait-for [:profile/get-profiles-overview-success
:font/init-font-file-for-initials-avatar])
(assert-app-initialized)))))
(defn test-account-creation
[]
(test-async ::create-account
(fn []
(p/do
(setup-app)
(setup-account)
(logout)
(wait-for [::logout/logout-method])))))
;;;; Fixtures
(defn fixture-session
@@ -246,19 +271,14 @@
(defn enable-testnet!
[]
(rf/dispatch [:profile.settings/profile-update :test-networks-enabled?
true {}])
(rf/dispatch [:profile.settings/profile-update :test-networks-enabled? true {}])
(rf/dispatch [:wallet/initialize]))
(defn recover-multiaccount!
[]
(let [masked-seed-phrase (security/mask-data (:seed-phrase constants/recovery-account))]
(validate-mnemonic
masked-seed-phrase
(fn [mnemonic key-uid]
(rf/dispatch [:onboarding/seed-phrase-validated
(security/mask-data mnemonic) key-uid])
(rf/dispatch [:pop-to-root :profiles])
(rf/dispatch [:profile/profile-selected key-uid])
(recover-and-login mnemonic))
#())))
(p/let [masked-seed-phrase (security/mask-data (:seed-phrase constants/recovery-account))
[mnemonic key-uid] (validate-mnemonic masked-seed-phrase)]
(rf/dispatch [:onboarding/seed-phrase-validated (security/mask-data mnemonic) key-uid])
(rf/dispatch [:pop-to-root :profiles])
(rf/dispatch [:profile/profile-selected key-uid])
(recover-and-login mnemonic)))
+3 -22
View File
@@ -1,34 +1,15 @@
(ns tests.contract-test.core-test
(:require
[cljs.test :refer [deftest]]
[day8.re-frame.test :as rf-test]
legacy.status-im.events
[legacy.status-im.multiaccounts.logout.core :as logout]
legacy.status-im.subs.root
[re-frame.core :as rf]
status-im.events
status-im.navigation.core
status-im.subs.root
[test-helpers.integration :as h]
[tests.test-utils :as utils.test]))
[test-helpers.integration :as h]))
(deftest initialize-app-test
(h/log-headline :initialize-app-test)
(rf-test/run-test-async
(utils.test/init!)
(rf/dispatch [:app-started])
(rf-test/wait-for
;; use initialize-view because it has the longest avg. time and
;; is dispatched by initialize-multiaccounts (last non-view event)
[:profile/get-profiles-overview-success]
(rf-test/wait-for
[:font/init-font-file-for-initials-avatar]
(h/assert-app-initialized)))))
(h/test-app-initialization))
(deftest create-account-test
(h/log-headline :create-account-test)
(rf-test/run-test-async
(h/with-app-initialized
(h/with-account
(h/logout)
(rf-test/wait-for [::logout/logout-method])))))
(h/test-account-creation))
+8 -14
View File
@@ -1,25 +1,19 @@
(ns tests.contract-test.profile-test
(:require
[cljs.test :refer [deftest is]]
[day8.re-frame.test :as rf-test]
[cljs.test :refer [deftest is use-fixtures]]
legacy.status-im.events
[legacy.status-im.multiaccounts.logout.core :as logout]
legacy.status-im.subs.root
[promesa.core :as p]
status-im.events
status-im.navigation.core
status-im.subs.root
[test-helpers.integration :as h]
[tests.contract-test.utils :as contract-utils]))
(use-fixtures :each (h/fixture-session))
(deftest profile-set-bio-contract
(h/log-headline :contract/wakuext_setBio)
(rf-test/run-test-async
(h/with-app-initialized
(h/with-account
(contract-utils/call-rpc-endpoint
{:rpc-endpoint "wakuext_setBio"
:params ["new bio"]
:on-error #(is (nil? %) "Set bio RPC call should have succeeded")})
(h/logout)
(rf-test/wait-for
[::logout/logout-method])))))
(h/test-async :contract/wakuext_setBio
(fn []
(-> (contract-utils/call-rpc "wakuext_setBio" "new bio")
(p/catch #(is (nil? %) "Set bio RPC call should have succeeded"))))))
+9 -9
View File
@@ -1,14 +1,14 @@
(ns tests.contract-test.utils
(:require
[promesa.core :as p]
[status-im.common.json-rpc.events :as rpc-events]
[utils.number]))
(defn call-rpc-endpoint
[{:keys [rpc-endpoint
params
action
on-error]}]
(rpc-events/call {:method rpc-endpoint
:params params
:on-success action
:on-error on-error}))
(defn call-rpc
[method & args]
(p/create
(fn [p-resolve p-reject]
(rpc-events/call {:method method
:params args
:on-success p-resolve
:on-error p-reject}))))
+38 -57
View File
@@ -1,11 +1,10 @@
(ns tests.contract-test.wallet-test
(:require
[cljs.test :refer [deftest is]]
[day8.re-frame.test :as rf-test]
[cljs.test :refer [deftest is use-fixtures]]
legacy.status-im.events
[legacy.status-im.multiaccounts.logout.core :as logout]
legacy.status-im.subs.root
[native-module.core :as native-module]
[promesa.core :as p]
[status-im.common.emoji-picker.utils :as emoji-picker.utils]
[status-im.constants :as constants]
[status-im.contexts.wallet.data-store :as data-store]
@@ -16,23 +15,19 @@
[tests.contract-test.utils :as contract-utils]
[tests.integration-test.constants :as integration-constants]))
(use-fixtures :each (h/fixture-session))
(defn assert-accounts-get-accounts
[result]
(is (true? (some :wallet result)))
(is (true? (some :chat result)))
(is (= 2 (count result))))
(deftest accounts-get-accounts-contract
(h/log-headline :contract/accounts-get-accounts)
(rf-test/run-test-async
(h/with-app-initialized
(h/with-account
(contract-utils/call-rpc-endpoint
{:rpc-endpoint "accounts_getAccounts"
:action assert-accounts-get-accounts})
(h/logout)
(rf-test/wait-for
[::logout/logout-method])))))
(deftest accounts-get-accounts-contract-test
(h/test-async :contract/accounts-get-accounts
(fn []
(p/let [result (contract-utils/call-rpc "accounts_getAccounts")]
(assert-accounts-get-accounts result)))))
(defn get-default-account
[accounts]
@@ -44,24 +39,16 @@
(is (= (:emoji default-account) test-emoji))))
(deftest accounts-save-accounts-contract
(h/log-headline :contract/accounts-save-account)
(rf-test/run-test-async
(h/with-app-initialized
(h/with-account
(let [test-emoji (emoji-picker.utils/random-emoji)
account (contract-utils/call-rpc-endpoint
{:rpc-endpoint "accounts_getAccounts"
:action get-default-account})]
(contract-utils/call-rpc-endpoint
{:rpc-endpoint "accounts_saveAccount"
:action identity
:params [(data-store/<-account (merge account {:emoji test-emoji}))]})
(contract-utils/call-rpc-endpoint
{:rpc-endpoint "accounts_getAccounts"
:action #(check-emoji-is-updated test-emoji %)})
(h/logout)
(rf-test/wait-for
[::logout/logout-method]))))))
(h/test-async :contract/accounts-save-account
(fn []
(p/let [test-emoji (emoji-picker.utils/random-emoji)
account (contract-utils/call-rpc "accounts_getAccounts")
default-account (get-default-account account)
_ (contract-utils/call-rpc
"accounts_saveAccount"
(data-store/<-account (merge default-account {:emoji test-emoji})))
accounts (contract-utils/call-rpc "accounts_getAccounts")]
(check-emoji-is-updated test-emoji accounts)))))
(def number-of-networks 3)
@@ -76,16 +63,10 @@
(is (some #(= constants/optimism-sepolia-chain-id (get-in % [:Test :chainId])) response)))
(deftest accounts-get-chains-contract
(h/log-headline :contract/wallet_get-ethereum-chains)
(rf-test/run-test-async
(h/with-app-initialized
(h/with-account
(contract-utils/call-rpc-endpoint
{:rpc-endpoint "wallet_getEthereumChains"
:action assert-ethereum-chains})
(h/logout)
(rf-test/wait-for
[::logout/logout-method])))))
(h/test-async :contract/wallet_get-ethereum-chains
(fn []
(p/let [response (contract-utils/call-rpc "wallet_getEthereumChains")]
(assert-ethereum-chains response)))))
(defn get-main-account
[accounts]
@@ -99,18 +80,18 @@
(is (= (:public-key response) (:public-key response)))
(is (= "m/43'/60'/1581'/0'/0" (:path (first response)))))
(deftest wallet-get-derived-addressess-contract
(h/log-headline :wallet/create-derived-addresses)
(rf-test/run-test-async
(h/with-app-initialized
(h/with-recovered-account
(let [sha3-pwd (native-module/sha3 test-password)
derivation-path ["m/43'/60'/1581'/0'/0"]
main-account (contract-utils/call-rpc-endpoint
{:rpc-endpoint "accounts_getAccounts"
:action get-main-account})]
(contract-utils/call-rpc-endpoint
{:rpc-endpoint "wallet_getDerivedAddresses"
:params [sha3-pwd main-account derivation-path]
:action assert-derived-account}))))))
(deftest wallet-get-derived-addressess-contract-test
(h/test-async :wallet/create-derived-addresses
(fn []
(p/let [_ (h/enable-testnet!)
_ (h/recover-multiaccount!)
sha3-pwd (native-module/sha3 test-password)
derivation-path ["m/43'/60'/1581'/0'/0"]
accounts (contract-utils/call-rpc "accounts_getAccounts")
main-account (get-main-account accounts)
response (contract-utils/call-rpc
"wallet_getDerivedAddresses"
sha3-pwd
main-account
derivation-path)]
(assert-derived-account response)))))
+4 -4
View File
@@ -17,7 +17,7 @@
"0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd")
(deftest one-to-one-chat-test
(h/integration-test ::one-to-one-chat
(h/test-async ::one-to-one-chat
(fn []
(p/do
(rf/dispatch-sync [:chat.ui/start-chat chat-id])
@@ -26,7 +26,7 @@
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))))))
(deftest delete-chat-test
(h/integration-test ::delete-chat
(h/test-async ::delete-chat
(fn []
(p/do
(rf/dispatch-sync [:chat.ui/start-chat chat-id])
@@ -38,7 +38,7 @@
(rf/dispatch-sync [:chat.ui/remove-chat chat-id])))))
(deftest mute-chat-test
(h/integration-test ::mute-chat
(h/test-async ::mute-chat
(fn []
(p/do
(rf/dispatch-sync [:chat.ui/start-chat chat-id])
@@ -57,7 +57,7 @@
(is (not @(rf/subscribe [:chats/muted chat-id])))))))
(deftest add-contact-test
(h/integration-test ::add-contact
(h/test-async ::add-contact
(fn []
(let [compressed-key "zQ3shMwgSMKHVznoowceZMxWde9HUnkQEVSGvvex8UFpFNErL"
public-key (str "0x0407e9dc435fe366cb0b4c4f35cbd925438c0f46fe0"
+3 -22
View File
@@ -2,33 +2,14 @@
(:require
[cljs.test :refer [deftest]]
legacy.status-im.events
[legacy.status-im.multiaccounts.logout.core :as logout]
legacy.status-im.subs.root
[promesa.core :as p]
[re-frame.core :as rf]
status-im.events
status-im.navigation.core
status-im.subs.root
[test-helpers.integration :as h]
[tests.test-utils :as test-utils]))
[test-helpers.integration :as h]))
(deftest initialize-app-test
(h/integration-test ::initialize-app
(fn []
(p/do
(test-utils/init!)
(rf/dispatch [:app-started])
;; Use initialize-view because it has the longest avg. time and is
;; dispatched by initialize-multiaccounts (last non-view event).
(h/wait-for [:profile/get-profiles-overview-success
:font/init-font-file-for-initials-avatar])
(h/assert-app-initialized)))))
(h/test-app-initialization))
(deftest create-account-test
(h/integration-test ::create-account
(fn []
(p/do
(h/setup-app)
(h/setup-account)
(h/logout)
(h/wait-for [::logout/logout-method])))))
(h/test-account-creation))
+4 -4
View File
@@ -10,7 +10,7 @@
(use-fixtures :each (h/fixture-session))
(deftest edit-profile-name-test
(h/integration-test ::edit-profile-name
(h/test-async ::edit-profile-name
(fn []
(let [new-name "John Doe"]
(p/do
@@ -21,7 +21,7 @@
(is (= new-name display-name))))))))
(deftest edit-profile-picture-test
(h/integration-test ::edit-profile-picture
(h/test-async ::edit-profile-picture
(fn []
(let [mock-image "resources/images/mock2/monkey.png"
absolute-path (.resolve test-utils/path mock-image)]
@@ -32,7 +32,7 @@
(is (not (nil? (:images profile))))))))))
(deftest delete-profile-picture-test
(h/integration-test ::delete-profile-picture
(h/test-async ::delete-profile-picture
(fn []
(p/do
(rf/dispatch [:profile/delete-picture])
@@ -41,7 +41,7 @@
(is (nil? (:image profile))))))))
(deftest edit-profile-bio-test
(h/integration-test ::edit-profile-bio
(h/test-async ::edit-profile-bio
(fn []
(let [new-bio "New bio text"]
(p/do
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v0.176.8",
"commit-sha1": "8c0e24dc260031a822604f7a2771f22daea79aa4",
"src-sha256": "1c6r5gizcvixawl1c0lxd022wg54m5qxw8rr1q1i9221pzbfmmsl"
"version": "apply-migrations-back",
"commit-sha1": "0e4af0d38934b9ebed8fcb99fe652fcfae8998e0",
"src-sha256": "0gzy35nxr5jpwfzjhamkhiqjkfff13hxz2vk8l260y4j8fqb39q4"
}
+4
View File
@@ -94,6 +94,8 @@
"blank-keycard-text": "You can proceed with your keycard once you've generated your keys and name",
"blank-keycard-title": "Looks like youve tapped \na blank keycard",
"block": "Block",
"user-blocked": "{{username}} blocked",
"user-unblocked": "{{username}} unblocked",
"unblock": "Unblock",
"block-contact": "Block this user",
"block-contact-details": "Blocking will delete this user's previous messages and stop new ones from reaching you",
@@ -2065,6 +2067,8 @@
"rename": "Rename",
"mark-untrustworthy": "Mark as Untrustworthy",
"block-user": "Block user",
"block-user-title-message": "You will not see {{username}}'s messages, but {{username}} still can see your messages in mutual group chats and communities. {{username}} will be unable to message you.",
"blocking-a-user-message": "Blocking a user purges the database of all messages that youve previously received from {{username}} in all contexts.",
"group-details": "Group details",
"edit-name-and-image": "Edit name and image",
"change-group-privacy": "Change group privacy",