Shell & Bottom Tabs Migration (#14099)

* Shell & Bottom Tabs Migration

* Added accessibility ids for elements in the new UI
This commit is contained in:
Parvesh Monu 2022-10-24 18:35:06 +05:30 committed by GitHub
parent 38c95804ae
commit 0ff6fb25f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 546 additions and 671 deletions

View File

@ -1,4 +1,4 @@
import { useDerivedValue } from 'react-native-reanimated';
import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated';
// Generic Worklets
@ -32,38 +32,99 @@ export function applyAnimationsToStyle(animations, style) {
// Switcher Worklets
export function switcherCloseButtonOpacity (switcherButtonOpacity) {
export function stackOpacity (stackId, selectedStackId) {
return useDerivedValue(
function () {
'worklet'
return 1 - switcherButtonOpacity.value;
return selectedStackId.value == stackId ? 1 : 0;
}
);
}
export function switcherScreenRadius (switcherScreenSize) {
export function stackPointer (stackId, selectedStackId) {
return useDerivedValue(
function () {
'worklet'
return switcherScreenSize.value/2;
return selectedStackId.value == stackId ? "auto" : "none";
}
);
}
export function switcherScreenBottomPosition (switcherScreenRadius, switcherPressedRadius, initalPosition) {
export function bottomTabIconColor (stackId, selectedStackId, passThrough, selectedTabColor, defaultColor, passThroughColor) {
return useDerivedValue(
function () {
'worklet'
return initalPosition + switcherPressedRadius - switcherScreenRadius.value;
if (selectedStackId.value == stackId){
return selectedTabColor;
}
else if (passThrough.value){
return passThroughColor;
}
else {
return defaultColor;
}
}
);
}
export function switcherContainerBottomPosition (switcherScreenBottom, heightOffset) {
// Home Stack
const defaultDurationAndEasing = {
duration: 300,
easing: Easing.bezier(0, 0, 1, 1),
}
export function homeStackOpacity (homeStackOpen) {
return useDerivedValue(
function () {
'worklet'
return - (switcherScreenBottom.value + heightOffset);
return withTiming(homeStackOpen.value ? 1 : 0, defaultDurationAndEasing);
}
);
}
export function homeStackTop (homeStackOpen, top) {
return useDerivedValue(
function () {
'worklet'
return withTiming(homeStackOpen.value ? 0 : top, defaultDurationAndEasing);
}
);
}
export function homeStackLeft (selectedStackId, animateHomeStackLeft, homeStackOpen, left) {
return useDerivedValue(
function () {
'worklet'
if (animateHomeStackLeft.value) {
var leftValue = left[selectedStackId.value];
if (homeStackOpen.value) {
return withSequence(withTiming(leftValue, {duration: 0}), withTiming(0, defaultDurationAndEasing))
} else {
return withTiming(leftValue, defaultDurationAndEasing);
}
} else {
return 0;
}
}
);
}
export function homeStackPointer (homeStackOpen) {
return useDerivedValue(
function () {
'worklet'
return homeStackOpen.value ? "auto" : "none";
}
);
}
export function homeStackScale (homeStackOpen, minimizeScale) {
return useDerivedValue(
function () {
'worklet'
return withTiming(homeStackOpen.value ? 1 : minimizeScale, defaultDurationAndEasing);
}
);
}

View File

@ -56,6 +56,8 @@
{:keyboardVerticalOffset (+ 44 (:status-bar-height @navigation-const))})]
(reagent/children this))))
(def status-bar (.-StatusBar ^js rn))
(def keyboard (.-Keyboard ^js rn))
(def dismiss-keyboard! #(.dismiss ^js keyboard))

View File

@ -62,9 +62,10 @@
(let [pressed? (reagent/atom false)]
(fn [{:keys [type on-press count customization-color style]}]
[rn/touchable-without-feedback
{:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
:on-press on-press}
{:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
:on-press on-press
:accessibility-label type}
[rn/view {:style (merge
{:flex-direction :row
:height 24

View File

@ -1,71 +1,77 @@
(ns quo2.components.navigation.bottom-nav-tab
(:require [quo.react-native :as rn]
[reagent.core :as reagent]
[quo2.reanimated :as reanimated]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icon]
[quo2.components.icons.icons :as icons]
[quo2.components.counter.counter :as counter]))
(defn toggle-background-color [background-color press-out? pass-through?]
(let [color (cond
press-out? nil
pass-through? colors/white-opa-5
:else colors/neutral-70)]
(reset! background-color color)))
(reanimated/set-shared-value
background-color
(cond
press-out? "transparent"
pass-through? colors/white-opa-5
:else colors/neutral-70)))
(defn bottom-nav-tab
"[bottom-nav-tab opts]
opts
{:icon :main-icons2/communities
:selected? true/false
:new-notifications? true/false
:notification-indicator :unread-dot/:counter
:counter-label number
:on-press bottom-tab on-press function
:pass-through? true/false
:icon-color-anim reanimated shared value
"
[_]
(let [background-color (reagent/atom nil)]
(fn [{:keys [icon selected? new-notifications? notification-indicator counter-label on-press pass-through?]}]
[rn/touchable-without-feedback
{:on-press on-press
:on-press-in #(toggle-background-color background-color false pass-through?)
:on-press-out #(toggle-background-color background-color true pass-through?)}
[rn/view {:style {:width 90
:height 40
:background-color @background-color
:border-radius 10}}
[rn/hole-view {:style {:padding-left 33
:padding-top 8}
:key new-notifications? ;; Key is required to force removal of holes
:holes (cond
(not new-notifications?) ;; No new notifications, remove holes
[]
[{:keys [icon new-notifications? notification-indicator counter-label
on-press pass-through? icon-color-anim accessibility-label]}]
[:f>
(fn []
(let [icon-animated-style (reanimated/apply-animations-to-style
{:tint-color icon-color-anim}
{:width 24
:height 24})
background-color (reanimated/use-shared-value "transparent")
background-animated-style (reanimated/apply-animations-to-style
{:background-color background-color}
{:width 90
:height 40
:border-radius 10})]
[rn/touchable-without-feedback
{:on-press on-press
:on-press-in #(toggle-background-color background-color false pass-through?)
:on-press-out #(toggle-background-color background-color true pass-through?)
:accessibility-label accessibility-label}
[reanimated/view {:style background-animated-style}
[rn/hole-view {:style {:padding-left 33
:padding-top 8}
:key new-notifications? ;; Key is required to force removal of holes
:holes (cond
(not new-notifications?) ;; No new notifications, remove holes
[]
(= notification-indicator :unread-dot)
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
(= notification-indicator :unread-dot)
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
:else
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
[icon/icon
icon
{:size 24
:color (cond
selected? colors/white
pass-through? colors/white-opa-40
:else colors/neutral-50)}]]
(when new-notifications?
(if (= notification-indicator :counter)
[counter/counter {:outline false
:override-text-color colors/white
:override-bg-color colors/primary-50
:style {:position :absolute
:left 48
:top 2}}
counter-label]
[rn/view {:style {:width 8
:height 8
:border-radius 4
:top 6
:left 51
:position :absolute
:background-color colors/primary-50}}]))]])))
:else
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
[reanimated/image
{:style icon-animated-style
:source (icons/icon-source (keyword (str icon 24)))}]]
(when new-notifications?
(if (= notification-indicator :counter)
[counter/counter {:outline false
:override-text-color colors/white
:override-bg-color colors/primary-50
:style {:position :absolute
:left 48
:top 2}}
counter-label]
[rn/view {:style {:width 8
:height 8
:border-radius 4
:top 6
:left 51
:position :absolute
:background-color colors/primary-50}}]))]]))])

View File

@ -44,8 +44,7 @@
open-scanner show-qr open-activity-center style avatar counter-label]}]
(let [button-common-props (get-button-common-props type)]
[rn/view {:style (merge
{:height 56
:flex 1}
{:height 56}
style)}
;; Left Section
[rn/touchable-without-feedback {:on-press open-profile}

View File

@ -16,14 +16,16 @@
(defn open-reactions-menu
[{:keys [on-press]}]
(let [dark? (theme/dark?)]
[rn/touchable-opacity {:on-press on-press
:style (merge reaction-styling
[rn/touchable-opacity
{:on-press on-press
:accessibility-label :emoji-reaction-add
:style (merge reaction-styling
{:padding-horizontal 9
:border-width 1
:margin-top 5
:border-color (if dark?
colors/neutral-70
colors/neutral-30)})}
:border-width 1
:margin-top 5
:border-color (if dark?
colors/neutral-70
colors/neutral-30)})}
[icons/icon :main-icons2/add
{:size 20
:color (if dark?
@ -32,26 +34,30 @@
(defn reaction
"Add your emoji as a param here"
[{:keys [emoji clicks neutral? on-press]}]
[{:keys [emoji clicks neutral? on-press accessibility-label]}]
(let [dark? (theme/dark?)
text-color (if dark? colors/white
colors/neutral-100)
numeric-value (int clicks)
clicks-positive? (pos? numeric-value)]
[rn/touchable-opacity {:on-press on-press
:style (merge reaction-styling
(cond-> {:background-color
(if dark?
(if neutral?
colors/neutral-70
:transparent)
(if neutral?
colors/neutral-30
:transparent))}
(and dark? (not neutral?)) (assoc :border-color colors/neutral-70
:border-width 1)
(and (not dark?) (not neutral?)) (assoc :border-color colors/neutral-30
:border-width 1)))}
[rn/touchable-opacity
{:on-press on-press
:accessibility-label accessibility-label
:style (merge reaction-styling
(cond-> {:background-color
(if dark?
(if neutral?
colors/neutral-70
:transparent)
(if neutral?
colors/neutral-30
:transparent))}
(and dark? (not neutral?))
(assoc :border-color colors/neutral-70
:border-width 1)
(and (not dark?) (not neutral?))
(assoc :border-color colors/neutral-30
:border-width 1)))}
[icons/icon emoji {:no-color true
:size 16}]
[quo2.text/text {:size :paragraph-2

View File

@ -45,7 +45,6 @@
;;Blur
(def neutral-5-opa-70 (alpha neutral-5 0.7))
(def neutral-90-opa-70 (alpha neutral-90 0.7))
(def neutral-95-opa-70 (alpha neutral-95 0.7))
;;80 with transparency
(def neutral-80-opa-5 (alpha neutral-80 0.05))
@ -60,6 +59,20 @@
(def neutral-80-opa-90 (alpha neutral-80 0.9))
(def neutral-80-opa-95 (alpha neutral-80 0.95))
;;95 with transparency
(def neutral-95-opa-60 (alpha neutral-95 0.6))
(def neutral-95-opa-70 (alpha neutral-95 0.7))
(def neutral-95-opa-80 (alpha neutral-95 0.8))
(def neutral-95-opa-90 (alpha neutral-95 0.9))
(def neutral-95-opa-95 (alpha neutral-95 0.95))
;;100 with transparency
(def neutral-100-opa-60 (alpha neutral-100 0.6))
(def neutral-100-opa-70 (alpha neutral-100 0.7))
(def neutral-100-opa-80 (alpha neutral-100 0.8))
(def neutral-100-opa-90 (alpha neutral-100 0.9))
(def neutral-100-opa-95 (alpha neutral-100 0.95))
;;;;White
;;Solid

View File

@ -2,6 +2,7 @@
(:require [quo.react-native :as rn]
[quo.previews.preview :as preview]
[reagent.core :as reagent]
[quo2.reanimated :as reanimated]
[quo2.components.navigation.bottom-nav-tab :as quo2]
[quo2.foundations.colors :as colors]))
@ -36,21 +37,34 @@
:key :counter-label
:type :text}])
(defn get-icon-color [selected? pass-through?]
(cond
selected? colors/white
pass-through? colors/white-opa-40
:else colors/neutral-50))
(defn cool-preview []
(let [state (reagent/atom {:icon :main-icons2/communities
:selected? true
:pass-through? true
:new-notifications? true
:notification-indicator :counter
:counter-label 8
:preview-label-color colors/white})]
(fn []
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
[rn/view {:padding-bottom 150}
[preview/customizer state descriptor]
[rn/view {:padding-vertical 60
:align-items :center}
[quo2/bottom-nav-tab @state (:value @state)]]]])))
(let [state (reagent/atom {:icon :main-icons2/communities
:new-notifications? true
:notification-indicator :counter
:counter-label 8
:preview-label-color colors/white})
selected? (reagent/cursor state [:selected?])
pass-through? (reagent/cursor state [:pass-through?])]
[:f>
(fn []
(let [icon-color-anim (reanimated/use-shared-value colors/white)]
(reanimated/set-shared-value
icon-color-anim
(get-icon-color @selected? @pass-through?))
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
[rn/view {:padding-bottom 150}
[preview/customizer state descriptor]
[rn/view {:padding-vertical 60
:align-items :center}
[quo2/bottom-nav-tab
(merge @state {:icon-color-anim icon-color-anim})
(:value @state)]]]]))]))
(defn preview-bottom-nav-tab []
[rn/view {:background-color colors/neutral-100

View File

@ -18,6 +18,8 @@
[status-im.utils.logging.core :as utils.logs]
[status-im.utils.platform :as platform]
[status-im.utils.snoopy :as snoopy]
[status-im.switcher.animation :as animation]
[status-im.async-storage.core :as async-storage]
[status-im.utils.universal-links.core :as utils.universal-links]))
(set! interop/next-tick js/setTimeout)
@ -39,6 +41,9 @@
(utils.universal-links/initialize)
;; TODO(parvesh) - Remove while moving functionality to status-go
(async-storage/get-item :selected-stack-id #(animation/selected-stack-id-loaded %))
;;DEV
(snoopy/subscribe!)
(when (and js/goog.DEBUG platform/ios? DevSettings)

View File

@ -468,7 +468,7 @@
"Decides which root should be initialised depending on user and app state"
[db]
(if (get db :tos/accepted?)
(re-frame/dispatch [:init-root (if config/new-ui-enabled? :home-stack :chat-stack)])
(re-frame/dispatch [:init-root (if config/new-ui-enabled? :shell-stack :chat-stack)])
(re-frame/dispatch [:init-root :tos])))
(fx/defn login-only-events
@ -517,7 +517,7 @@
(logging/set-log-level (:log-level multiaccount))
(if config/new-ui-enabled?
(navigation/init-root :home-stack)
(navigation/init-root :shell-stack)
;; if it's a first account, the ToS will be accepted at welcome carousel
;; if not a first account, the ToS might have been accepted by other account logins
(if (or first-account? tos-accepted?)

View File

@ -3,14 +3,14 @@
[status-im.reloader :as reloader]
[status-im.utils.datetime :as datetime]))
(def parent-stack (atom :home-stack))
(def parent-stack (atom :shell-stack))
(fx/defn reload-new-ui
{:events [:reload-new-ui]}
[_]
(reloader/reload)
{:new-ui/reset-bottom-tabs nil
:dispatch [:init-root :home-stack]})
:dispatch [:init-root :shell-stack]})
(fx/defn init-root-nav2
{:events [:init-root-nav2]}
@ -27,7 +27,7 @@
[_ modal]
{:close-modal-fx-nav2 modal})
(defn navigate-from-home-stack [go-to-view-id id db]
(defn navigate-from-shell-stack [go-to-view-id id db]
(reset! parent-stack go-to-view-id)
{:navigate-to-fx-nav2 [go-to-view-id id]
:db (assoc-in db [:navigation2/navigation2-stacks id] {:type go-to-view-id
@ -49,8 +49,12 @@
(if from-switcher?
(navigate-from-switcher go-to-view-id id db from-home?)
(if from-home?
(navigate-from-home-stack go-to-view-id id db)
(navigate-from-shell-stack go-to-view-id id db)
;; TODO(parvesh) - new stacks created from other screens should be stacked on current stack, instead of creating new entry
(navigate-from-home-stack go-to-view-id id db)))))
(navigate-from-shell-stack go-to-view-id id db)))))
(fx/defn change-root-status-bar-style
{:events [:change-root-status-bar-style]}
[_ style]
{:change-root-status-bar-style-fx style})

View File

@ -13,8 +13,10 @@
:wallet 2
:browser 3})
;; (defonce set-navigation-default-options
;; (.setDefaultOptions Navigation (clj->js {:options {:topBar {:visible false}}})))
(defn change-root-status-bar-style [style]
(.mergeOptions Navigation
"shell-stack"
(clj->js {:statusBar {:style style}})))
;; TODO (parvesh) - improve open-modal and close-modal
(defn open-modal [comp]
@ -55,7 +57,7 @@
(let [{:keys [options]} (get views/screens comp)]
(reset! nav2-utils/container-stack-view-id comp)
(.push Navigation
(name :home-stack)
"shell-stack"
(clj->js {:stack {:id comp
:children [{:component {:id comp
:name comp
@ -79,3 +81,5 @@
(re-frame/reg-fx :navigate-to-fx-nav2 navigate)
(re-frame/reg-fx :navigate-from-switcher-fx navigate-from-switcher)
(re-frame/reg-fx :change-root-status-bar-style-fx change-root-status-bar-style)

View File

@ -1,20 +1,19 @@
(ns status-im.navigation2.roots
(:require [quo.theme :as theme]
[quo2.foundations.colors :as colors]
(:require [quo2.foundations.colors :as colors]
[status-im.utils.platform :as platform]))
(defn status-bar-options []
(if platform/android?
{:navigationBar {:backgroundColor colors/neutral-80}
{:navigationBar {:backgroundColor colors/neutral-100}
:statusBar {:backgroundColor :transparent
:style (if (theme/dark?) :light :dark)
:style :light
:drawBehind true}}
{:statusBar {:style (if (theme/dark?) :light :dark)}}))
{:statusBar {:style :light}}))
(defn roots []
{:home-stack
{:shell-stack
{:root
{:stack {:id :home-stack
{:stack {:id :shell-stack
:children [{:component {:name :chat-stack
:id :chat-stack
:options (merge (status-bar-options)

View File

@ -1,15 +1,14 @@
(ns status-im.navigation2.screens
(:require [status-im.ui2.screens.chat.view :as chat]
[status-im.switcher.home-stack :as home-stack]
[status-im.navigation2.stack-with-switcher :as stack-with-switcher]))
[status-im.switcher.shell-stack :as shell-stack]))
;; We have to use the home screen name :chat-stack for now, for compatibility with navigation.cljs
(def screens [{:name :chat-stack ;; TODO(parvesh) - rename to home-stack
(def screens [{:name :chat-stack ;; TODO(parvesh) - rename to shell-stack
:insets {:top false}
:component home-stack/home}])
:component shell-stack/shell-stack}])
;; These screens will overwrite navigation/screens.cljs screens on enabling new UI toggle
(def screen-overwrites
[{:name :chat
:options {:topBar {:visible false}}
:component #(stack-with-switcher/overlap-stack chat/chat :chat)}])
:component chat/chat}])

View File

@ -1,10 +0,0 @@
(ns status-im.navigation2.stack-with-switcher
(:require [quo.react-native :as rn]
[status-im.utils.platform :as platform]
[status-im.switcher.switcher :as switcher]))
(defn overlap-stack [comp view-id]
[rn/view {:style {:flex 1
:margin-bottom (if platform/ios? 30 0)}}
[comp]
[switcher/switcher view-id]])

View File

@ -1,65 +1,108 @@
(ns status-im.switcher.animation
(:require [quo2.reanimated :as reanimated]
(:require [re-frame.core :as re-frame]
[quo2.reanimated :as reanimated]
[quo2.foundations.colors :as colors]
[status-im.async-storage.core :as async-storage]
[status-im.switcher.constants :as constants]))
;;;; Switcher Animations
;; Component Animations
(defn switcher-touchable-on-press-in
[touchable-scale]
(reanimated/animate-shared-value-with-timing touchable-scale constants/switcher-pressed-scale 300 :easing1))
(defn switcher-touchable-on-press-out [switcher-opened? view-id shared-values]
(let [{:keys [width height]} (constants/dimensions)
switcher-bottom-position (constants/switcher-pressed-bottom-position view-id)
switcher-target-radius (Math/hypot
(/ width 2)
(- height constants/switcher-pressed-radius switcher-bottom-position))
switcher-size (* 2 switcher-target-radius)]
(reanimated/animate-shared-value-with-timing (:button-touchable-scale shared-values) 1 300 :easing1)
(if @switcher-opened?
(do
(reanimated/animate-shared-value-with-timing (:switcher-button-opacity shared-values) 1 300 :easing1)
(reanimated/animate-shared-value-with-timing (:switcher-screen-size shared-values) constants/switcher-pressed-size 300 :linear)
(reanimated/animate-shared-value-with-timing (:switcher-container-scale shared-values) 0.9 300 :linear))
(do
(reanimated/animate-shared-value-with-timing (:switcher-button-opacity shared-values) 0 300 :easing1)
(reanimated/animate-shared-value-with-timing (:switcher-screen-size shared-values) switcher-size 300 :linear)
(reanimated/animate-shared-value-with-timing (:switcher-container-scale shared-values) 1 300 :linear)))
(swap! switcher-opened? not)))
;; Derived Values
(defn switcher-close-button-opacity [switcher-button-opacity]
(.switcherCloseButtonOpacity ^js reanimated/worklet-factory switcher-button-opacity))
(defn switcher-screen-radius [switcher-screen-size]
(.switcherScreenRadius ^js reanimated/worklet-factory switcher-screen-size))
(defn switcher-screen-bottom-position [switcher-screen-radius view-id]
(.switcherScreenBottomPosition ^js reanimated/worklet-factory
switcher-screen-radius
constants/switcher-pressed-radius
(constants/switcher-pressed-bottom-position view-id)))
(defn switcher-container-bottom-position [switcher-screen-bottom]
(.switcherContainerBottomPosition ^js reanimated/worklet-factory
switcher-screen-bottom
(+ constants/switcher-container-height-padding
constants/switcher-height-offset)))
;;;; Bottom Tabs & Home Stack Animations
(def selected-stack-id (atom nil))
(def home-stack-open? (atom false))
(def pass-through? (atom false))
(def bottom-nav-tab-width 90)
(defn selected-stack-id-loaded [stack-id]
(reset! selected-stack-id stack-id)
(reset! home-stack-open? (some? stack-id)))
(defn calculate-home-stack-position []
(let [{:keys [width height]} (constants/dimensions)
minimize-scale (/ bottom-nav-tab-width width)
empty-space-half-scale (/ (- 1 minimize-scale) 2)
left-margin (/ (- width (* 4 bottom-nav-tab-width)) 2)
left-empty-space (* empty-space-half-scale width)
top-empty-space (* empty-space-half-scale
(- height (constants/bottom-tabs-container-height)))]
{:left (reduce
(fn [acc stack-id]
(assoc acc stack-id (+ (- left-margin left-empty-space)
(* (.indexOf constants/stacks-ids stack-id)
bottom-nav-tab-width))))
{:none 0} constants/stacks-ids)
:top (+ top-empty-space (constants/bottom-tabs-container-height))
:scale minimize-scale}))
(defn get-shared-values []
(let [selected-stack-id-sv (reanimated/use-shared-value
;; passing keywords or nil is not working with reanimated
(name (if @selected-stack-id @selected-stack-id :none)))
;; Second shared value of selected-stack-id required to make sure stack is still visible while minimizing
selected-stack-id-sv2 (reanimated/use-shared-value
(name (if @selected-stack-id @selected-stack-id :none)))
pass-through-sv (reanimated/use-shared-value @pass-through?)
home-stack-open-sv (reanimated/use-shared-value @home-stack-open?)
animate-home-stack-left (reanimated/use-shared-value (not @home-stack-open?))
home-stack-position (calculate-home-stack-position)]
(reduce
(fn [acc id]
(let [tabs-icon-color-keyword (get constants/tabs-icon-color-keywords id)
stack-opacity-keyword (get constants/stacks-opacity-keywords id)
stack-pointer-keyword (get constants/stacks-pointer-keywords id)]
(assoc
acc
stack-opacity-keyword (.stackOpacity
^js reanimated/worklet-factory
(name id) selected-stack-id-sv2)
stack-pointer-keyword (.stackPointer
^js reanimated/worklet-factory
(name id) selected-stack-id-sv2)
tabs-icon-color-keyword (.bottomTabIconColor
^js reanimated/worklet-factory
(name id) selected-stack-id-sv pass-through-sv
colors/white colors/neutral-50 colors/white-opa-40))))
{:selected-stack-id selected-stack-id-sv
:selected-stack-id2 selected-stack-id-sv2
:pass-through? pass-through-sv
:home-stack-open? home-stack-open-sv
:animate-home-stack-left animate-home-stack-left
:home-stack-left (.homeStackLeft
^js reanimated/worklet-factory
selected-stack-id-sv2 animate-home-stack-left home-stack-open-sv
(clj->js (:left home-stack-position)))
:home-stack-top (.homeStackTop
^js reanimated/worklet-factory
home-stack-open-sv (:top home-stack-position))
:home-stack-opacity (.homeStackOpacity
^js reanimated/worklet-factory home-stack-open-sv)
:home-stack-pointer (.homeStackPointer
^js reanimated/worklet-factory home-stack-open-sv)
:home-stack-scale (.homeStackScale
^js reanimated/worklet-factory home-stack-open-sv
(:scale home-stack-position))}
constants/stacks-ids)))
;; Animation
(defn change-tab [shared-values stack-id]
(when-not (colors/dark?)
(js/setTimeout #(re-frame/dispatch [:change-root-status-bar-style :dark]) 300))
(if @home-stack-open?
(reanimated/set-shared-value (:animate-home-stack-left shared-values) false)
(reset! home-stack-open? true))
(reset! selected-stack-id stack-id)
(reanimated/set-shared-value (:selected-stack-id2 shared-values) (name stack-id))
(reanimated/set-shared-value (:selected-stack-id shared-values) (name stack-id))
(reanimated/set-shared-value (:home-stack-open? shared-values) true)
(async-storage/set-item! :selected-stack-id stack-id))
(defn close-home-stack [shared-values]
(re-frame/dispatch [:change-root-status-bar-style :light])
(reanimated/set-shared-value (:animate-home-stack-left shared-values) true)
(reset! home-stack-open? false)
(reset! selected-stack-id nil)
(reanimated/set-shared-value (:home-stack-open? shared-values) false)
(reanimated/set-shared-value (:selected-stack-id shared-values) "none")
(async-storage/set-item! :selected-stack-id nil))
(defn bottom-tab-on-press [shared-values selected-stack-id]
(doseq [id constants/stacks-ids]
(let [selected-tab? (= id selected-stack-id)
tab-opacity-shared-value (get shared-values (get constants/tabs-opacity-keywords id))
stack-opacity-shared-value (get shared-values (get constants/stacks-opacity-keywords id))
stack-pointer-shared-value (get shared-values (get constants/stacks-pointer-keywords id))]
(reanimated/animate-shared-value-with-timing tab-opacity-shared-value (if selected-tab? 1 0) 300 :easing3)
(reanimated/set-shared-value stack-pointer-shared-value (if selected-tab? "auto" "none"))
(if selected-tab?
(reanimated/animate-shared-value-with-delay stack-opacity-shared-value 1 300 :easing3 150)
(reanimated/animate-shared-value-with-timing stack-opacity-shared-value 0 300 :easing3)))))

View File

@ -2,66 +2,58 @@
(:require [quo.react-native :as rn]
[reagent.core :as reagent]
[re-frame.core :as re-frame]
[quo2.reanimated :as reanimated]
[status-im.switcher.styles :as styles]
[status-im.utils.platform :as platform]
[status-im.switcher.constants :as constants]
[status-im.switcher.animation :as animation]
[quo2.components.icon :as icons]))
(def selected-stack-id (atom :communities-stack))
[quo2.components.navigation.bottom-nav-tab :as bottom-nav-tab]))
;; Reagent atoms used for lazily loading home screen tabs
(def load-communities-tab? (reagent/atom true))
(def load-communities-tab? (reagent/atom false))
(def load-chats-tab? (reagent/atom false))
(def load-wallet-tab? (reagent/atom false))
(def load-browser-tab? (reagent/atom false))
(defn load-selected-stack [stack-id]
(case stack-id
:communities-stack (reset! load-communities-tab? true)
:chats-stack (reset! load-chats-tab? true)
:wallet-stack (reset! load-wallet-tab? true)
:browser-stack (reset! load-browser-tab? true)
""))
(re-frame/reg-fx
:new-ui/reset-bottom-tabs
(fn []
(reset! selected-stack-id :communities-stack)
(reset! load-communities-tab? true)
(reset! load-chats-tab? false)
(reset! load-wallet-tab? false)
(reset! load-browser-tab? false)))
(let [selected-stack-id @animation/selected-stack-id]
(reset! load-communities-tab? (= selected-stack-id :communities-stack))
(reset! load-chats-tab? (= selected-stack-id :chats-stack))
(reset! load-wallet-tab? (= selected-stack-id :wallet-stack))
(reset! load-browser-tab? (= selected-stack-id :browser-stack)))))
(defn bottom-tab-on-press [shared-values stack-id]
(when-not (= stack-id @selected-stack-id)
(reset! selected-stack-id stack-id)
(animation/bottom-tab-on-press shared-values stack-id)
(case stack-id
:communities-stack (reset! load-communities-tab? true)
:chats-stack (reset! load-chats-tab? true)
:wallet-stack (reset! load-wallet-tab? true)
:browser-stack (reset! load-browser-tab? true))))
(when-not (= stack-id @animation/selected-stack-id)
(let [stack-load-delay (cond
@animation/home-stack-open? 0
platform/android? 250
:else 300)]
(animation/change-tab shared-values stack-id)
(js/setTimeout #(load-selected-stack stack-id) stack-load-delay))))
;; TODO(parvesh) - reimplement tab with counter, once design is complete
(defn bottom-tab [icon stack-id icons-only? shared-values]
[:f>
(fn []
(let [bottom-tab-original-style {:padding 16}]
(if icons-only?
[rn/touchable-opacity {:active-opacity 1
:style bottom-tab-original-style
:on-press #(bottom-tab-on-press shared-values stack-id)}
[reanimated/view {:style (reanimated/apply-animations-to-style
{:opacity (get
shared-values
(get constants/tabs-opacity-keywords stack-id))}
{})}
[icons/icon icon (styles/bottom-tab-icon :bottom-tabs-selected-tab)]]]
[rn/view {:style bottom-tab-original-style}
[icons/icon icon (styles/bottom-tab-icon :bottom-tabs-non-selected-tab)]])))])
(defn tabs [shared-values icons-only?]
[rn/view {:style (styles/bottom-tabs icons-only?)}
[bottom-tab :main-icons2/communities :communities-stack icons-only? shared-values]
[bottom-tab :main-icons2/messages :chats-stack icons-only? shared-values]
[rn/view {:width 50}]
[bottom-tab :main-icons2/wallet :wallet-stack icons-only? shared-values]
[bottom-tab :main-icons2/browser :browser-stack icons-only? shared-values]])
(defn bottom-tab [icon stack-id shared-values]
[bottom-nav-tab/bottom-nav-tab
{:icon icon
:icon-color-anim (get
shared-values
(get constants/tabs-icon-color-keywords stack-id))
:on-press #(bottom-tab-on-press shared-values stack-id)
:accessibility-label (str (name stack-id) "-tab")}])
(defn bottom-tabs [shared-values]
[:<>
[tabs shared-values false]
[tabs shared-values true]])
(load-selected-stack @animation/selected-stack-id)
[rn/view {:style (styles/bottom-tabs-container false)}
[rn/view {:style (styles/bottom-tabs)}
[bottom-tab :main-icons2/communities :communities-stack shared-values]
[bottom-tab :main-icons2/messages :chats-stack shared-values]
[bottom-tab :main-icons2/wallet :wallet-stack shared-values]
[bottom-tab :main-icons2/browser :browser-stack shared-values]]])

View File

@ -1,32 +0,0 @@
(ns status-im.switcher.cards.messaging-card
(:require [quo.react-native :as rn]
[quo2.components.markdown.text :as text]
[status-im.constants :as constants]
[quo2.components.buttons.button :as button]
[status-im.utils.handlers :refer [>evt <sub]]
[status-im.switcher.cards.styles :as styles]))
;; TODO - Add switcher close animation (fade) while opening screen from cards
;; currently dealy is added to avoid default circular animation
(defn on-press [id toggle-switcher-screen]
(js/setTimeout toggle-switcher-screen 100)
(>evt [:chat.ui/navigate-to-chat-nav2 id true]))
;; TODO - add last message for other content types
(defn last-message [{:keys [content content-type]}]
(cond
(= constants/content-type-text content-type)
[text/text (styles/messaging-card-last-message-text-props) (:text content)]))
(defn card [{:keys [id toggle-switcher-screen]}]
(let [chat (<sub [:chats/chat id])]
[rn/touchable-without-feedback {:on-press #(on-press id toggle-switcher-screen)}
[rn/view {:style (styles/messaging-card-main-container)}
[rn/view {:style (styles/messaging-card-secondary-container)}
[text/text (styles/messaging-card-title-props) (:alias chat)]
[text/text (styles/messaging-card-subtitle-props) "Message"]
[rn/view {:style (styles/messaging-card-details-container)}
[last-message (:last-message chat)]]]
[rn/view {:style (styles/messaging-card-avatar-container)}]
[button/button (styles/messaging-card-close-button-props) :main-icons/close]]]))

View File

@ -1,103 +0,0 @@
(ns status-im.switcher.cards.styles
(:require [quo.theme :as theme]
[quo2.foundations.colors :as colors]))
(def themes
{:light {:messaging-card-container-background-color "#26A69A"
:messaging-card-secondary-container-background-color colors/white
:messaging-card-title-color colors/neutral-100
:messaging-card-subtitle-color colors/neutral-50
:messaging-card-last-message-text-color colors/neutral-100
:messaging-card-close-button-bg-color colors/white-opa-50
:messaging-card-close-button-icon-color colors/neutral-100}
:dark {:messaging-card-container-background-color "#26A69A"
:messaging-card-secondary-container-background-color colors/neutral-90
:messaging-card-title-color colors/white
:messaging-card-subtitle-color colors/neutral-40
:messaging-card-last-message-text-color colors/white
:messaging-card-close-button-bg-color colors/neutral-80-opa-60
:messaging-card-close-button-icon-color colors/white}})
(defn get-color [key]
(get-in themes [(theme/get-theme) key]))
;; Messaging Card
(defn messaging-card-main-container []
{:width 160
:height 172
:border-radius 16
:margin 8
:background-color (get-color :messaging-card-container-background-color)})
(defn messaging-card-secondary-container []
{:width 160
:height 132
:background-color (get-color :messaging-card-secondary-container-background-color)
:border-radius 16
:position :absolute
:bottom 0})
(defn messaging-card-title []
{:position :absolute
:top 32
:margin-horizontal 12
:color (get-color :messaging-card-title-color)})
(defn messaging-card-title-props []
{:size :paragraph-1
:weight :semi-bold
:number-of-lines 1
:ellipsize-mode :tail
:style (messaging-card-title)})
(defn messaging-card-subtitle []
{:position :absolute
:top 54
:margin-horizontal 12
:color (get-color :messaging-card-subtitle-color)})
(defn messaging-card-subtitle-props []
{:size :paragraph-2
:weight :medium
:style (messaging-card-subtitle)})
(defn messaging-card-details-container []
{:position :absolute
:bottom 12
:margin-horizontal 12
:width 136
:height 36})
(defn messaging-card-last-message-text []
{:color (get-color :messaging-card-last-message-text-color)})
(defn messaging-card-last-message-text-props []
{:size :paragraph-2
:weight :regular
:number-of-lines 2
:ellipsize-mode :tail
:style (messaging-card-last-message-text)})
(defn messaging-card-close-button []
{:position :absolute
:right 8
:top 8
:background-color (get-color :messaging-card-close-button-bg-color)
:icon-color (get-color :messaging-card-icon-color)})
(defn messaging-card-close-button-props []
{:size 24
:type :grey
:icon true
:on-press #(print "close pressed")
:style (messaging-card-close-button)})
(defn messaging-card-avatar-container []
{:width 48
:height 48
:border-radius 24
:position :absolute
:left 12
:top 16
:background-color :pink})

View File

@ -1,57 +1,26 @@
(ns status-im.switcher.constants
(:require [quo.react-native :as rn]
[reagent.core :as reagent]
[status-im.utils.handlers :refer [<sub]]
[status-im.utils.platform :as platform]))
;; For translucent status bar(android), dimensions/window also includes status bar's height,
;; this offset is used for correctly calculating switcher position
(def switcher-height-offset
(if platform/android? (:status-bar-height @rn/navigation-const) 0))
(defn bottom-tabs-container-height []
(if platform/android? 57 82))
;; extra height of switcher container for show/peek hidden cards while opening animation
(def switcher-container-height-padding 100)
(defn bottom-tabs-extended-container-height []
(if platform/android? 90 120))
(def switcher-button-radius 24)
(def switcher-button-size
(* switcher-button-radius 2))
(def switcher-pressed-scale 0.9)
(def switcher-pressed-radius
(* switcher-pressed-scale switcher-button-radius))
(def switcher-pressed-size
(* 2 switcher-pressed-radius))
(def switcher-bottom-positions
{:android
{:home-stack 15
:chat 140}
:ios
{:home-stack 40
:chat 140}})
(defn switcher-bottom-position [view-id]
(get-in
switcher-bottom-positions
[(keyword platform/os) view-id]))
(defn switcher-pressed-bottom-position [view-id]
(+
(get-in
switcher-bottom-positions
[(keyword platform/os) view-id])
(- switcher-button-radius switcher-pressed-radius)))
;; TODO(parvesh) - use different height for android and ios(Confirm from Design)
(defn bottom-tabs-height []
(if platform/android? 55 80))
(def status-bar-offset
(if platform/android? (.-currentHeight ^js rn/status-bar) 0))
;; status bar height is not included in : the dimensions/window for devices with a notch
;; https://github.com/facebook/react-native/issues/23693#issuecomment-662860819
(defn dimensions []
(let [{:keys [width height]} (<sub [:dimensions/window])]
{:width width
:height (+ height switcher-height-offset)}))
:height (if (> status-bar-offset 28)
(+ height status-bar-offset)
height)}))
(def stacks-ids [:communities-stack :chats-stack :wallet-stack :browser-stack])
@ -67,8 +36,10 @@
:wallet-stack :wallet-stack-pointer
:browser-stack :browser-stack-pointer})
(def tabs-opacity-keywords
{:communities-stack :communities-tab-opacity
:chats-stack :chats-tab-opacity
:wallet-stack :wallet-tab-opacity
:browser-stack :browser-tab-opacity})
(def tabs-icon-color-keywords
{:communities-stack :communities-tab-icon-color
:chats-stack :chats-tab-icon-opacity
:wallet-stack :wallet-tab-icon-opacity
:browser-stack :browser-tab-icon-opacity})
(def pass-through? (reagent/atom false))

View File

@ -1,33 +1,36 @@
(ns status-im.switcher.home-stack
(:require [quo2.reanimated :as reanimated]
[status-im.utils.platform :as platform]
[status-im.switcher.switcher :as switcher]
[status-im.ui2.screens.chat.home :as chat.home]
[status-im.switcher.styles :as styles]
[status-im.switcher.animation :as animation]
[status-im.switcher.constants :as constants]
[status-im.ui2.screens.chat.home :as chat.home]
[status-im.switcher.bottom-tabs :as bottom-tabs]
[status-im.ui.screens.profile.user.views :as profile.user]
[status-im.ui.screens.communities.communities-list-redesign :as communities]
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]))
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
[quo2.components.navigation.floating-shell-button :as floating-shell-button]
[status-im.ui.screens.communities.communities-list-redesign :as communities]))
(defn load-stack? [stack-id]
(case stack-id
:communities-stack @bottom-tabs/load-communities-tab?
:chats-stack @bottom-tabs/load-chats-tab?
:browser-stack @bottom-tabs/load-browser-tab?
:wallet-stack @bottom-tabs/load-wallet-tab?))
:chats-stack @bottom-tabs/load-chats-tab?
:browser-stack @bottom-tabs/load-browser-tab?
:wallet-stack @bottom-tabs/load-wallet-tab?))
(defn stack-view [stack-id shared-values]
(when (load-stack? stack-id)
[:f>
(fn []
[reanimated/view {:style (reanimated/apply-animations-to-style
{:opacity (get shared-values (get constants/stacks-opacity-keywords stack-id))
:pointer-events (get shared-values (get constants/stacks-pointer-keywords stack-id))}
{:top 0
:bottom (if platform/ios? 79 54)
:left 0
:right 0
:position :absolute})}
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:opacity (get shared-values (get constants/stacks-opacity-keywords stack-id))
:pointer-events (get shared-values (get constants/stacks-pointer-keywords stack-id))}
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0
:accessibility-label stack-id})}
(case stack-id
:communities-stack [communities/communities-list]
:chats-stack [chat.home/home]
@ -35,29 +38,22 @@
:browser-stack [profile.user/my-profile])])]))
(defn home-stack [shared-values]
[:<>
[stack-view :communities-stack shared-values]
[stack-view :chats-stack shared-values]
[stack-view :browser-stack shared-values]
[stack-view :wallet-stack shared-values]])
(defn home []
[:f>
(fn []
(let [selected-stack-id @bottom-tabs/selected-stack-id
shared-values (reduce (fn [acc id]
(let [selected-tab? (= id selected-stack-id)
tab-opacity-keyword (get constants/tabs-opacity-keywords id)
stack-opacity-keyword (get constants/stacks-opacity-keywords id)
stack-pointer-keyword (get constants/stacks-pointer-keywords id)]
(assoc
acc
tab-opacity-keyword (reanimated/use-shared-value (if selected-tab? 1 0))
stack-opacity-keyword (reanimated/use-shared-value (if selected-tab? 1 0))
stack-pointer-keyword (reanimated/use-shared-value (if selected-tab? "auto" "none")))))
{}
constants/stacks-ids)]
[:<>
[home-stack shared-values]
[bottom-tabs/bottom-tabs shared-values]
[switcher/switcher :home-stack]]))])
(let [home-stack-original-style (styles/home-stack)
home-stack-animated-style (reanimated/apply-animations-to-style
{:top (:home-stack-top shared-values)
:left (:home-stack-left shared-values)
:opacity (:home-stack-opacity shared-values)
:pointer-events (:home-stack-pointer shared-values)
:transform [{:scale (:home-stack-scale shared-values)}]}
home-stack-original-style)]
[reanimated/view {:style home-stack-animated-style}
[stack-view :communities-stack shared-values]
[stack-view :chats-stack shared-values]
[stack-view :browser-stack shared-values]
[stack-view :wallet-stack shared-values]
[floating-shell-button/floating-shell-button
{:jump-to {:on-press #(animation/close-home-stack shared-values)}}
{:position :absolute
:bottom 12}]]))])

View File

@ -0,0 +1,54 @@
(ns status-im.switcher.shell
(:require [quo.react-native :as rn]
[status-im.i18n.i18n :as i18n]
[quo2.foundations.colors :as colors]
[quo2.components.markdown.text :as text]
[quo.components.safe-area :as safe-area]
[quo2.components.navigation.top-nav :as top-nav]))
(defn placeholder []
[rn/view {:style {:position :absolute
:top 0
:left 0
:right 0
:bottom -1
:justify-content :center
:align-items :center
:accessibility-label :shell-placeholder-view}}
[rn/view {:style {:margin-top 12
:width 80
:height 80
:border-radius 16
:background-color colors/neutral-90}}]
[text/text {:size :heading-2
:weight :semi-bold
:style {:margin-top 20
:color colors/white}}
(i18n/label :t/shell-placeholder-title)]
[text/text {:size :paragraph-1
:weight :regular
:align :center
:style {:margin-top 8
:color colors/white}}
(i18n/label :t/shell-placeholder-subtitle)]])
(defn shell []
[safe-area/consumer
(fn [insets]
[rn/view {:style {:top 0
:left 0
:right 0
:bottom -1
:position :absolute
:background-color colors/neutral-100}}
[top-nav/top-nav {:type :shell
:style {:margin-top (:top insets)}}]
[placeholder]
[rn/scroll-view {:style {:padding-horizontal 20
:flex-direction :row}}
[text/text {:size :heading-1
:weight :semi-bold
:style {:color colors/white
:margin-top 12}}
(i18n/label :t/jump-to)]]])])

View File

@ -0,0 +1,14 @@
(ns status-im.switcher.shell-stack
(:require [status-im.switcher.shell :as shell]
[status-im.switcher.animation :as animation]
[status-im.switcher.home-stack :as home-stack]
[status-im.switcher.bottom-tabs :as bottom-tabs]))
(defn shell-stack []
[:f>
(fn []
(let [shared-values (animation/get-shared-values)]
[:<>
[shell/shell]
[bottom-tabs/bottom-tabs shared-values]
[home-stack/home-stack shared-values]]))])

View File

@ -1,89 +1,35 @@
(ns status-im.switcher.styles
(:require [quo.theme :as theme]
[quo2.foundations.colors :as colors]
(:require [quo2.foundations.colors :as colors]
[status-im.utils.platform :as platform]
[status-im.switcher.constants :as constants]))
(def themes
{:light {:bottom-tabs-bg-color colors/neutral-80
:bottom-tabs-on-scroll-bg-color colors/neutral-80-opa-80
:bottom-tabs-non-selected-tab colors/neutral-50
:bottom-tabs-selected-tab colors/white
:switcher-close-button-bg-color colors/white}
:dark {:bottom-tabs-bg-color colors/neutral-80
:bottom-tabs-on-scroll-bg-color colors/neutral-80-opa-80
:bottom-tabs-non-selected-tab colors/neutral-50
:bottom-tabs-selected-tab colors/white
:switcher-close-button-bg-color colors/white}})
(defn get-color [key]
(get-in themes [(theme/get-theme) key]))
;; Bottom Tabs
(defn bottom-tab-icon [tab-state]
{:size 24
:color (get-color tab-state)})
(defn bottom-tabs-container [pass-through?]
{:background-color (if pass-through? colors/neutral-100-opa-70 colors/neutral-100)
:flex 1
:align-items :center
:flex-direction :column
:height (constants/bottom-tabs-container-height)
:position :absolute
:bottom -1
:right 0
:left 0
:accessibility-label :bottom-tabs-container})
(defn bottom-tabs [icons-only?]
{:background-color (if icons-only? nil (get-color :bottom-tabs-bg-color))
:flex-direction :row
:flex 1
:justify-content :space-between
:height (constants/bottom-tabs-height)
:position :absolute
:bottom -1
:right 0
:left 0
:padding-horizontal 16})
(defn bottom-tabs []
{:flex-direction :row
:position :absolute
:bottom (if platform/android? 8 34)
:flex 1
:accessibility-label :bottom-tabs})
;; Switcher
(defn switcher-button []
{:width constants/switcher-button-size
:height constants/switcher-button-size
:z-index 2})
(defn merge-switcher-button-common-styles [style]
(merge
{:width constants/switcher-button-size
:height constants/switcher-button-size
:border-radius constants/switcher-button-radius
:position :absolute
:z-index 2
:align-items :center
:align-self :center
:justify-content :center}
style))
(defn switcher-button-touchable [view-id]
(merge-switcher-button-common-styles
{:bottom (constants/switcher-bottom-position view-id)}))
(defn switcher-close-button []
(merge-switcher-button-common-styles
{:backgroundColor (get-color :switcher-close-button-bg-color)}))
(defn switcher-screen []
(cond-> (merge-switcher-button-common-styles
{:background-color colors/neutral-80-opa-80
:z-index 1
:overflow :hidden})
platform/android? (dissoc :background-color)
true (dissoc :justify-content)))
(defn switcher-blur-background []
;; Home Stack
(defn home-stack []
(let [{:keys [width height]} (constants/dimensions)]
{:style {:width width
:height (+ height constants/switcher-container-height-padding)}
:blur-amount 17
:overlay-color colors/neutral-80-opa-80}))
(defn switcher-screen-container []
(let [{:keys [width height]} (constants/dimensions)]
{:width width
:height (+ height constants/switcher-container-height-padding)
:align-items :center
:position :absolute}))
(defn switcher-switch-screen []
{:margin-top 40
:align-items :center})
{:border-bottom-left-radius 20
:border-bottom-right-radius 20
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)
:overflow :hidden
:position :absolute
:width width
:height (- height (constants/bottom-tabs-container-height))}))

View File

@ -1,80 +0,0 @@
(ns status-im.switcher.switcher
(:require [reagent.core :as reagent]
[quo2.reanimated :as reanimated]
[quo2.foundations.colors :as colors]
[status-im.switcher.styles :as styles]
[status-im.ui.components.react :as react]
[status-im.switcher.constants :as constants]
[status-im.switcher.animation :as animation]
[status-im.ui.components.icons.icons :as icons]
[status-im.react-native.resources :as resources]
[status-im.switcher.switcher-container :as switcher-container]
[quo.react-native :as rn]))
(defn switcher-button [view-id toggle-switcher-screen-fn shared-values]
[:f>
(fn []
(let [touchable-original-style (styles/switcher-button-touchable view-id)
close-button-original-style (styles/switcher-close-button)
switcher-button-original-style (styles/switcher-button)
touchable-animated-style (reanimated/apply-animations-to-style
{:transform [{:scale (:button-touchable-scale shared-values)}]}
touchable-original-style)
close-button-animated-style (reanimated/apply-animations-to-style
{:opacity (:close-button-opacity shared-values)}
close-button-original-style)
switcher-button-animated-style (reanimated/apply-animations-to-style
{:opacity (:switcher-button-opacity shared-values)}
switcher-button-original-style)]
[reanimated/touchable-opacity {:active-opacity 1
:on-press-in #(animation/switcher-touchable-on-press-in
(:button-touchable-scale shared-values))
:on-press-out toggle-switcher-screen-fn
:style touchable-animated-style}
[reanimated/view {:style close-button-animated-style}
[icons/icon :main-icons/close {:color colors/neutral-100}]]
[reanimated/image {:source (resources/get-image :switcher)
:style switcher-button-animated-style}]]))])
(defn switcher-screen [toggle-switcher-screen-fn shared-values]
[:f>
(fn []
(let [switcher-screen-original-style (styles/switcher-screen)
switcher-container-original-style (styles/switcher-screen-container)
switcher-screen-animated-style (reanimated/apply-animations-to-style
{:width (:switcher-screen-size shared-values)
:height (:switcher-screen-size shared-values)
:bottom (:switcher-screen-bottom shared-values)
:border-radius (:switcher-screen-radius shared-values)}
switcher-screen-original-style)
switcher-container-animated-style (reanimated/apply-animations-to-style
{:bottom (:switcher-container-bottom shared-values)
:transform [{:scale (:switcher-container-scale shared-values)}]}
switcher-container-original-style)]
[reanimated/view {:style switcher-screen-animated-style}
[react/blur-view (styles/switcher-blur-background)]
[reanimated/view {:style switcher-container-animated-style}
[switcher-container/tabs toggle-switcher-screen-fn]]]))])
(defn switcher [view-id]
[:f>
(fn []
(let [switcher-opened? (reagent/atom false)
switcher-button-opacity (reanimated/use-shared-value 1)
switcher-screen-size (reanimated/use-shared-value constants/switcher-pressed-size)
switcher-screen-radius (animation/switcher-screen-radius switcher-screen-size)
switcher-screen-bottom (animation/switcher-screen-bottom-position switcher-screen-radius view-id)
shared-values {:switcher-button-opacity switcher-button-opacity
:switcher-screen-size switcher-screen-size
:switcher-screen-radius switcher-screen-radius
:switcher-screen-bottom switcher-screen-bottom
:button-touchable-scale (reanimated/use-shared-value 1)
:switcher-container-scale (reanimated/use-shared-value 0.9)
:close-button-opacity (animation/switcher-close-button-opacity switcher-button-opacity)
:switcher-container-bottom (animation/switcher-container-bottom-position switcher-screen-bottom)}
toggle-switcher-screen-fn #(animation/switcher-touchable-on-press-out switcher-opened? view-id shared-values)
{:keys [keyboard-shown]} (rn/use-keyboard)]
(when-not keyboard-shown
[:<>
[switcher-screen toggle-switcher-screen-fn shared-values]
[switcher-button view-id toggle-switcher-screen-fn shared-values]])))])

View File

@ -1,31 +0,0 @@
(ns status-im.switcher.switcher-container
(:require [quo.react-native :as rn]
[status-im.switcher.cards.messaging-card :as messaging-card]
[status-im.switcher.styles :as styles]
[status-im.utils.handlers :refer [<sub]]))
;; TODO - use something like this to avoid multiple renders etc.
;; (defn switch-screen [toggle-switcher-screen]
;; (let [cards (<sub [:navigation2/switcher-cards])
;; new-cards (reduce (fn [acc card]
;; (conj acc (assoc card :toggle-switcher-screen toggle-switcher-screen)))
;; () cards)]
;; (fn []
;; [rn/view {:style (styles/switcher-switch-screen)}
;; [rn/flat-list {:width 352
;; :data new-cards
;; :render-fn messaging-card/card
;; :num-columns 2
;; :key-fn str}]])))
(defn switch-screen [toggle-switcher-screen]
(let [cards (<sub [:navigation2/switcher-cards toggle-switcher-screen])]
[rn/view {:style (styles/switcher-switch-screen)}
[rn/flat-list {:width 352
:data cards
:render-fn messaging-card/card
:num-columns 2
:key-fn str}]]))
(defn tabs [toggle-switcher-screen]
[switch-screen toggle-switcher-screen])

View File

@ -1,6 +0,0 @@
(ns status-im.switcher.utils
(:require [re-frame.core :as re-frame]))
(def switcher-container-view-id (atom nil))
(re-frame/reg-fx :switcher-container-view-id #(reset! switcher-container-view-id %))

View File

@ -10,13 +10,17 @@
(defn message-reactions [{:keys [content-type]} reactions timeline on-emoji-press on-open]
(when (seq reactions)
[rn/view {:style (styles/reactions-row timeline (if (= content-type constants/content-type-text) text-reaction-margin-top default-reaction-margin-top))}
[rn/view {:style (styles/reactions-row
timeline
(if (= content-type constants/content-type-text)
text-reaction-margin-top default-reaction-margin-top))}
(for [{:keys [own emoji-id quantity] :as emoji-reaction} reactions]
^{:key (str emoji-reaction)}
[rn/view {:style {:margin-right 6 :margin-top 5}}
[quo2.reaction/reaction {:emoji (get constants/reactions emoji-id)
:neutral? own
:clicks quantity
:on-press #(on-emoji-press emoji-id)}]])
[quo2.reaction/reaction {:emoji (get constants/reactions emoji-id)
:neutral? own
:clicks quantity
:on-press #(on-emoji-press emoji-id)
:accessibility-label (str "emoji-reaction-" emoji-id)}]])
;; on-press won't work until we integrate Message Context Drawer
[quo2.reaction/open-reactions-menu (when @on-open {:on-press @on-open})]]))

View File

@ -30,7 +30,7 @@
:type :grey
:icon true
:icon-no-color true
:accessibility-label :reply-cancel-button
:accessibility-label (str "emoji-picker-" id)
:on-press #(do
(send-emoji id)
(re-frame/dispatch [:bottom-sheet/hide]))}
@ -73,4 +73,4 @@
:icon (:icon action)
:on-press #(do
(when on-press (on-press))
(re-frame/dispatch [:bottom-sheet/hide]))}]))]])))
(re-frame/dispatch [:bottom-sheet/hide]))}]))]])))

View File

@ -48,7 +48,9 @@
(let [contact-name (<sub [:contacts/contact-name-by-identity from])
current-public-key (<sub [:multiaccount/public-key])
content-type (or content-type contentType)]
[rn/view {:style {:flex-direction :row :height (when-not pin? 24)}}
[rn/view {:style {:flex-direction :row
:height (when-not pin? 24)
:accessibility-label :reply-message}}
[rn/view {:style (styles/reply-content pin?)}
(when-not pin?
;;TODO quo2 icon should be used

View File

@ -148,7 +148,7 @@
(multiaccounts/displayed-photo row)]}]))
(defn chat-list-key-fn [item]
(or (:chat-id item) (:public-key item)))
(or (:chat-id item) (:public-key item) (:id item)))
(defn get-item-layout [_ index]
#js {:length 64 :offset (* 64 index) :index index})

View File

@ -1822,5 +1822,7 @@
"membership": "Membership",
"jump-to": "Jump to",
"blank-messages-text": "Your messages will be here",
"groups": "Groups"
"groups": "Groups",
"shell-placeholder-title": "Your apps will run here",
"shell-placeholder-subtitle": "Open tabs of your communities, messages,\nwallet account and browser windows"
}