Shell & Bottom Tabs Migration (#14099)
* Shell & Bottom Tabs Migration * Added accessibility ids for elements in the new UI
This commit is contained in:
parent
38c95804ae
commit
0ff6fb25f4
|
@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -64,7 +64,8 @@
|
|||
[rn/touchable-without-feedback
|
||||
{:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:on-press on-press}
|
||||
:on-press on-press
|
||||
:accessibility-label type}
|
||||
[rn/view {:style (merge
|
||||
{:flex-direction :row
|
||||
:height 24
|
||||
|
|
|
@ -1,39 +1,49 @@
|
|||
(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
|
||||
(reanimated/set-shared-value
|
||||
background-color
|
||||
(cond
|
||||
press-out? "transparent"
|
||||
pass-through? colors/white-opa-5
|
||||
:else colors/neutral-70)]
|
||||
(reset! background-color color)))
|
||||
: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?]}]
|
||||
[{: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?)}
|
||||
[rn/view {:style {:width 90
|
||||
:height 40
|
||||
:background-color @background-color
|
||||
:border-radius 10}}
|
||||
: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
|
||||
|
@ -46,13 +56,9 @@
|
|||
|
||||
: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)}]]
|
||||
[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
|
||||
|
@ -68,4 +74,4 @@
|
|||
:top 6
|
||||
:left 51
|
||||
:position :absolute
|
||||
:background-color colors/primary-50}}]))]])))
|
||||
:background-color colors/primary-50}}]))]]))])
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
(defn open-reactions-menu
|
||||
[{:keys [on-press]}]
|
||||
(let [dark? (theme/dark?)]
|
||||
[rn/touchable-opacity {:on-press on-press
|
||||
[rn/touchable-opacity
|
||||
{:on-press on-press
|
||||
:accessibility-label :emoji-reaction-add
|
||||
:style (merge reaction-styling
|
||||
{:padding-horizontal 9
|
||||
:border-width 1
|
||||
|
@ -32,13 +34,15 @@
|
|||
|
||||
(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
|
||||
[rn/touchable-opacity
|
||||
{:on-press on-press
|
||||
:accessibility-label accessibility-label
|
||||
:style (merge reaction-styling
|
||||
(cond-> {:background-color
|
||||
(if dark?
|
||||
|
@ -48,9 +52,11 @@
|
|||
(if neutral?
|
||||
colors/neutral-30
|
||||
:transparent))}
|
||||
(and dark? (not neutral?)) (assoc :border-color colors/neutral-70
|
||||
(and dark? (not neutral?))
|
||||
(assoc :border-color colors/neutral-70
|
||||
:border-width 1)
|
||||
(and (not dark?) (not neutral?)) (assoc :border-color colors/neutral-30
|
||||
(and (not dark?) (not neutral?))
|
||||
(assoc :border-color colors/neutral-30
|
||||
:border-width 1)))}
|
||||
[icons/icon emoji {:no-color true
|
||||
:size 16}]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})]
|
||||
: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 @state (:value @state)]]]])))
|
||||
[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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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?)
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}])
|
||||
|
|
|
@ -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]])
|
|
@ -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)))))
|
||||
|
|
|
@ -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))
|
||||
|
||||
(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)))
|
||||
|
||||
(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)
|
||||
(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))))
|
||||
:browser-stack (reset! load-browser-tab? true)
|
||||
""))
|
||||
|
||||
;; TODO(parvesh) - reimplement tab with counter, once design is complete
|
||||
(defn bottom-tab [icon stack-id icons-only? shared-values]
|
||||
[:f>
|
||||
(re-frame/reg-fx
|
||||
:new-ui/reset-bottom-tabs
|
||||
(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)]])))])
|
||||
(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 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-on-press [shared-values stack-id]
|
||||
(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))))
|
||||
|
||||
(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]]])
|
||||
|
|
|
@ -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]]]))
|
||||
|
|
@ -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})
|
|
@ -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))
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
(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
|
||||
|
@ -20,14 +21,16 @@
|
|||
(when (load-stack? stack-id)
|
||||
[:f>
|
||||
(fn []
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style
|
||||
[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)
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0
|
||||
:position :absolute})}
|
||||
: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]
|
||||
[:<>
|
||||
[:f>
|
||||
(fn []
|
||||
(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]])
|
||||
|
||||
(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]]))])
|
||||
[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}]]))])
|
||||
|
|
|
@ -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)]]])])
|
||||
|
|
@ -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]]))])
|
|
@ -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 [icons-only?]
|
||||
{:background-color (if icons-only? nil (get-color :bottom-tabs-bg-color))
|
||||
:flex-direction :row
|
||||
(defn bottom-tabs-container [pass-through?]
|
||||
{:background-color (if pass-through? colors/neutral-100-opa-70 colors/neutral-100)
|
||||
:flex 1
|
||||
:justify-content :space-between
|
||||
:height (constants/bottom-tabs-height)
|
||||
:align-items :center
|
||||
:flex-direction :column
|
||||
:height (constants/bottom-tabs-container-height)
|
||||
:position :absolute
|
||||
:bottom -1
|
||||
:right 0
|
||||
:left 0
|
||||
:padding-horizontal 16})
|
||||
:accessibility-label :bottom-tabs-container})
|
||||
|
||||
;; 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
|
||||
(defn bottom-tabs []
|
||||
{:flex-direction :row
|
||||
:position :absolute
|
||||
:z-index 2
|
||||
:align-items :center
|
||||
:align-self :center
|
||||
:justify-content :center}
|
||||
style))
|
||||
:bottom (if platform/android? 8 34)
|
||||
:flex 1
|
||||
:accessibility-label :bottom-tabs})
|
||||
|
||||
(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))}))
|
||||
|
|
|
@ -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]])))])
|
|
@ -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])
|
|
@ -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 %))
|
|
@ -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)}]])
|
||||
: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})]]))
|
||||
|
|
|
@ -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]))}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue