From bcc20c7458582a119eeef53846ac57824014a8a3 Mon Sep 17 00:00:00 2001 From: Parvesh Monu Date: Tue, 13 Jun 2023 00:51:58 +0530 Subject: [PATCH] Shell navigation and animations --- src/js/worklets/shell.js | 186 -------------- src/js/worklets/shell/bottom_tabs.js | 46 ++++ src/js/worklets/shell/constants.js | 27 ++ src/js/worklets/shell/floating_screen.js | 89 +++++++ src/js/worklets/shell/home_stack.js | 126 ++++++++++ src/mocks/js_dependencies.cljs | 4 +- src/status_im/communities/core.cljs | 2 +- src/status_im/events.cljs | 5 - src/status_im/group_chats/core.cljs | 2 +- src/status_im/multiaccounts/core.cljs | 4 +- src/status_im/multiaccounts/login/core.cljs | 8 +- src/status_im/multiaccounts/logout/core.cljs | 2 +- .../profile/visibility_status/views.cljs | 1 + src/status_im2/contexts/chat/events.cljs | 15 +- src/status_im2/contexts/chat/events_test.cljs | 2 +- .../contexts/chat/messages/list/view.cljs | 33 ++- .../contexts/chat/messages/view.cljs | 34 +-- .../contexts/communities/overview/view.cljs | 79 +++--- .../onboarding/common/background/view.cljs | 6 +- .../onboarding/enable_notifications/view.cljs | 6 +- .../quo_preview/switcher/switcher_cards.cljs | 2 +- src/status_im2/contexts/shell/animation.cljs | 235 ++++++------------ .../shell/components/bottom_tabs/style.cljs | 36 +++ .../bottom_tabs/view.cljs} | 27 +- .../components/floating_screens/style.cljs | 20 ++ .../components/floating_screens/view.cljs | 43 ++++ .../shell/components/home_stack/style.cljs | 32 +++ .../shell/components/home_stack/view.cljs | 48 ++++ .../shell/components/shell_screen/style.cljs | 64 +++++ .../shell/components/shell_screen/view.cljs | 113 +++++++++ .../switcher_cards}/style.cljs | 2 +- .../switcher_cards}/view.cljs | 109 +++++--- src/status_im2/contexts/shell/constants.cljs | 63 +++-- src/status_im2/contexts/shell/events.cljs | 165 +++++++++--- src/status_im2/contexts/shell/home_stack.cljs | 59 ----- .../contexts/shell/shared_values.cljs | 127 ++++++++++ src/status_im2/contexts/shell/state.cljs | 18 ++ src/status_im2/contexts/shell/style.cljs | 108 -------- src/status_im2/contexts/shell/utils.cljs | 120 +++++++++ src/status_im2/contexts/shell/view.cljs | 147 +++-------- src/status_im2/core.cljs | 10 +- src/status_im2/navigation/core.cljs | 26 +- src/status_im2/navigation/events.cljs | 43 ++-- src/status_im2/subs/root.cljs | 2 + src/status_im2/subs/shell.cljs | 49 ++-- src/utils/worklets/shell.cljs | 61 +++-- 46 files changed, 1475 insertions(+), 931 deletions(-) delete mode 100644 src/js/worklets/shell.js create mode 100644 src/js/worklets/shell/bottom_tabs.js create mode 100644 src/js/worklets/shell/constants.js create mode 100644 src/js/worklets/shell/floating_screen.js create mode 100644 src/js/worklets/shell/home_stack.js create mode 100644 src/status_im2/contexts/shell/components/bottom_tabs/style.cljs rename src/status_im2/contexts/shell/{bottom_tabs.cljs => components/bottom_tabs/view.cljs} (79%) create mode 100644 src/status_im2/contexts/shell/components/floating_screens/style.cljs create mode 100644 src/status_im2/contexts/shell/components/floating_screens/view.cljs create mode 100644 src/status_im2/contexts/shell/components/home_stack/style.cljs create mode 100644 src/status_im2/contexts/shell/components/home_stack/view.cljs create mode 100644 src/status_im2/contexts/shell/components/shell_screen/style.cljs create mode 100644 src/status_im2/contexts/shell/components/shell_screen/view.cljs rename src/status_im2/contexts/shell/{cards => components/switcher_cards}/style.cljs (97%) rename src/status_im2/contexts/shell/{cards => components/switcher_cards}/view.cljs (71%) delete mode 100644 src/status_im2/contexts/shell/home_stack.cljs create mode 100644 src/status_im2/contexts/shell/shared_values.cljs create mode 100644 src/status_im2/contexts/shell/state.cljs delete mode 100644 src/status_im2/contexts/shell/style.cljs create mode 100644 src/status_im2/contexts/shell/utils.cljs diff --git a/src/js/worklets/shell.js b/src/js/worklets/shell.js deleted file mode 100644 index 7e76ad7a6b..0000000000 --- a/src/js/worklets/shell.js +++ /dev/null @@ -1,186 +0,0 @@ -import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated'; - -// Shell Worklets - -// Home Stack States -const CLOSE_WITH_ANIMATION = 0; -const OPEN_WITH_ANIMATION = 1; -const CLOSE_WITHOUT_ANIMATION = 3; -const OPEN_WITHOUT_ANIMATION = 4; - -// Derived Values -export function stackOpacity (stackId, selectedStackId) { - return useDerivedValue( - function () { - 'worklet' - return selectedStackId.value == stackId ? 1 : 0; - } - ); -} - -export function stackZIndex (stackId, selectedStackId) { - return useDerivedValue( - function () { - 'worklet' - return selectedStackId.value == stackId ? 10 : 9; - } - ); -} - -export function bottomTabIconColor (stackId, selectedStackId, homeStackState, - passThrough, selectedTabColor, defaultColor, - passThroughColor) { - return useDerivedValue( - function () { - 'worklet' - var homeStackStateValue = homeStackState.value; - if (selectedStackId.value == stackId && - (homeStackStateValue == OPEN_WITH_ANIMATION || - homeStackStateValue == OPEN_WITHOUT_ANIMATION)){ - return selectedTabColor; - } - else if (passThrough.value){ - return passThroughColor; - } - else { - return defaultColor; - } - } - ); -} - -export function bottomTabsHeight(homeStackState, height, extendedHeight) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { - case OPEN_WITH_ANIMATION: - return withTiming(extendedHeight, defaultDurationAndEasing); - break; - case CLOSE_WITH_ANIMATION: - return withTiming(height, defaultDurationAndEasing); - break; - case OPEN_WITHOUT_ANIMATION: - return extendedHeight; - break; - case CLOSE_WITHOUT_ANIMATION: - return height; - break; - } - } - ) -} - - -// Home Stack - -const shellAnimationTime = 200; - -const defaultDurationAndEasing = { - duration: shellAnimationTime, - easing: Easing.bezier(0, 0, 1, 1), -} - -export function homeStackOpacity (homeStackState) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { - case OPEN_WITH_ANIMATION: - return withTiming(1, defaultDurationAndEasing); - break; - case CLOSE_WITH_ANIMATION: - return withTiming(0, defaultDurationAndEasing); - break; - case OPEN_WITHOUT_ANIMATION: - return 1; - break; - case CLOSE_WITHOUT_ANIMATION: - return 0; - break; - } - } - ); -} - -export function homeStackTop (homeStackState, top) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { - case OPEN_WITH_ANIMATION: - return withTiming(0, defaultDurationAndEasing); - break; - case CLOSE_WITH_ANIMATION: - return withTiming(top, defaultDurationAndEasing); - break; - case OPEN_WITHOUT_ANIMATION: - return 0; - break; - case CLOSE_WITHOUT_ANIMATION: - return top; - break; - } - } - ); -} - -export function homeStackLeft (selectedStackId, animateHomeStackLeft, homeStackState, left) { - return useDerivedValue( - function () { - 'worklet' - if (animateHomeStackLeft.value) { - var leftValue = left[selectedStackId.value]; - switch (homeStackState.value) { - case OPEN_WITH_ANIMATION: - return withSequence(withTiming(leftValue, {duration: 0}), withTiming(0, defaultDurationAndEasing)) - break; - case CLOSE_WITH_ANIMATION: - return withTiming(leftValue, defaultDurationAndEasing); - break; - case OPEN_WITHOUT_ANIMATION: - return 0; - break; - case CLOSE_WITHOUT_ANIMATION: - return leftValue; - break; - } - } else { - return 0; - } - } - ); -} - -export function homeStackPointer (homeStackState) { - return useDerivedValue( - function () { - 'worklet' - var homeStackStateValue = homeStackState.value; - return (homeStackStateValue == OPEN_WITH_ANIMATION || - homeStackStateValue == OPEN_WITHOUT_ANIMATION) ? "auto" : "none"; - } - ); -} - -export function homeStackScale (homeStackState, minimizeScale) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { - case OPEN_WITH_ANIMATION: - return withTiming(1, defaultDurationAndEasing); - break; - case CLOSE_WITH_ANIMATION: - return withTiming(minimizeScale, defaultDurationAndEasing); - break; - case OPEN_WITHOUT_ANIMATION: - return 1; - break; - case CLOSE_WITHOUT_ANIMATION: - return minimizeScale; - break; - } - } - ); -} diff --git a/src/js/worklets/shell/bottom_tabs.js b/src/js/worklets/shell/bottom_tabs.js new file mode 100644 index 0000000000..f38290d17d --- /dev/null +++ b/src/js/worklets/shell/bottom_tabs.js @@ -0,0 +1,46 @@ +import { useDerivedValue, withTiming } from 'react-native-reanimated'; +import * as constants from './constants'; + +export function bottomTabIconColor(stackId, selectedStackId, homeStackState, + passThrough, selectedTabColor, defaultColor, + passThroughColor) { + return useDerivedValue( + function () { + 'worklet' + var homeStackStateValue = homeStackState.value; + if (selectedStackId.value == stackId && + (homeStackStateValue == constants.OPEN_WITH_ANIMATION || + homeStackStateValue == constants.OPEN_WITHOUT_ANIMATION)){ + return selectedTabColor; + } + else if (passThrough.value){ + return passThroughColor; + } + else { + return defaultColor; + } + } + ); +} + +export function bottomTabsHeight(homeStackState, height, extendedHeight) { + return useDerivedValue( + function () { + 'worklet' + switch (homeStackState.value) { + case constants.OPEN_WITH_ANIMATION: + return withTiming(extendedHeight, constants.LINEAR_EASING); + break; + case constants.CLOSE_WITH_ANIMATION: + return withTiming(height, constants.LINEAR_EASING); + break; + case constants.OPEN_WITHOUT_ANIMATION: + return extendedHeight; + break; + case constants.CLOSE_WITHOUT_ANIMATION: + return height; + break; + } + } + ) +} diff --git a/src/js/worklets/shell/constants.js b/src/js/worklets/shell/constants.js new file mode 100644 index 0000000000..6126b4b080 --- /dev/null +++ b/src/js/worklets/shell/constants.js @@ -0,0 +1,27 @@ +import { Easing } from 'react-native-reanimated'; + +// Home Stack States +export const CLOSE_WITH_ANIMATION = 0; +export const OPEN_WITH_ANIMATION = 1; +export const CLOSE_WITHOUT_ANIMATION = 2; +export const OPEN_WITHOUT_ANIMATION = 3; + +// Floating Screen States +export const CLOSE_SCREEN_WITH_SLIDE_ANIMATION = 0; +export const OPEN_SCREEN_WITH_SLIDE_ANIMATION = 1; +export const CLOSE_SCREEN_WITH_SHELL_ANIMATION = 2; +export const OPEN_SCREEN_WITH_SHELL_ANIMATION = 3; +export const CLOSE_SCREEN_WITHOUT_ANIMATION = 4; +export const OPEN_SCREEN_WITHOUT_ANIMATION = 5; + +export const SHELL_ANIMATION_TIME = 200; + +export const LINEAR_EASING = { + duration: SHELL_ANIMATION_TIME, + easing: Easing.bezier(0, 0, 1, 1), +} + +export const EASE_OUT_EASING = { + duration: SHELL_ANIMATION_TIME, + easing: Easing.bezier(0, 0, 0.58, 1), +} diff --git a/src/js/worklets/shell/floating_screen.js b/src/js/worklets/shell/floating_screen.js new file mode 100644 index 0000000000..1fcc0b3286 --- /dev/null +++ b/src/js/worklets/shell/floating_screen.js @@ -0,0 +1,89 @@ +import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated'; +import * as constants from './constants'; + +// Derived Values +export function screenLeft (screenState, screenWidth, switcherCardLeftPosition) { + return useDerivedValue( + function () { + 'worklet' + switch (screenState.value) { + case constants.CLOSE_SCREEN_WITH_SLIDE_ANIMATION: + return withTiming(screenWidth, constants.EASE_OUT_EASING); + case constants.OPEN_SCREEN_WITH_SLIDE_ANIMATION: + return withTiming(0, constants.EASE_OUT_EASING); + case constants.CLOSE_SCREEN_WITHOUT_ANIMATION: + return screenWidth; + case constants.OPEN_SCREEN_WITHOUT_ANIMATION: + // Note - don't use return 0; its not working in ios + // https://github.com/software-mansion/react-native-reanimated/issues/3296#issuecomment-1573900172 + return withSequence(withTiming(-1, {duration: 0}), withTiming(0, {duration: 0})); + case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: + return withTiming(switcherCardLeftPosition, constants.EASE_OUT_EASING); + case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION: + return withTiming(0, constants.EASE_OUT_EASING); + default: + return screenWidth; + } + }); +} + +export function screenTop (screenState, switcherCardTopPosition) { + return useDerivedValue( + function () { + 'worklet' + switch (screenState.value) { + case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: + return withTiming(switcherCardTopPosition, constants.EASE_OUT_EASING); + case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION: + return withTiming(0, constants.EASE_OUT_EASING); + default: + return 0; + } + }); +} + +export function screenWidth (screenState, screenWidth, switcherCardSize) { + return useDerivedValue( + function () { + 'worklet' + switch (screenState.value) { + case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: + return withTiming(switcherCardSize, constants.EASE_OUT_EASING); + case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION: + return withSequence(withTiming(switcherCardSize, {duration: 0}), withTiming(screenWidth, constants.EASE_OUT_EASING)); + default: + return screenWidth; + } + }); +} + +export function screenHeight (screenState, screenHeight, switcherCardSize) { + return useDerivedValue( + function () { + 'worklet' + switch (screenState.value) { + case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: + return withTiming(switcherCardSize, constants.EASE_OUT_EASING); + case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION: + return withSequence(withTiming(switcherCardSize, {duration: 0}), withTiming(screenHeight, constants.EASE_OUT_EASING)); + default: + return screenHeight; + } + }); +} + +export function screenZIndex (screenState) { + return useDerivedValue( + function () { + 'worklet' + switch (screenState.value) { + case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: + case constants.CLOSE_SCREEN_WITH_SLIDE_ANIMATION: + return withDelay(constants.SHELL_ANIMATION_TIME, withTiming(-1, {duration: 0})); + case constants.CLOSE_SCREEN_WITHOUT_ANIMATION: + return -1; + default: + return 1; + } + }); +} diff --git a/src/js/worklets/shell/home_stack.js b/src/js/worklets/shell/home_stack.js new file mode 100644 index 0000000000..a98e36aec5 --- /dev/null +++ b/src/js/worklets/shell/home_stack.js @@ -0,0 +1,126 @@ +import { useDerivedValue, withTiming, withSequence } from 'react-native-reanimated'; +import * as constants from './constants'; + +// Derived values for each stack (communities, chat, wallet, browser) +export function stackOpacity (stackId, selectedStackId) { + return useDerivedValue( + function () { + 'worklet' + return selectedStackId.value == stackId ? 1 : 0; + } + ); +} + +export function stackZIndex (stackId, selectedStackId) { + return useDerivedValue( + function () { + 'worklet' + return selectedStackId.value == stackId ? 10 : 9; + } + ); +} + +// Derived values for home stack (container) +export function homeStackOpacity (homeStackState) { + return useDerivedValue( + function () { + 'worklet' + switch (homeStackState.value) { + case constants.OPEN_WITH_ANIMATION: + return withTiming(1, constants.LINEAR_EASING); + break; + case constants.CLOSE_WITH_ANIMATION: + return withTiming(0, constants.LINEAR_EASING); + break; + case constants.OPEN_WITHOUT_ANIMATION: + return 1; + break; + case constants.CLOSE_WITHOUT_ANIMATION: + return 0; + break; + } + } + ); +} + +export function homeStackTop (homeStackState, top) { + return useDerivedValue( + function () { + 'worklet' + switch (homeStackState.value) { + case constants.OPEN_WITH_ANIMATION: + return withTiming(0, constants.LINEAR_EASING); + break; + case constants.CLOSE_WITH_ANIMATION: + return withTiming(top, constants.LINEAR_EASING); + break; + case constants.OPEN_WITHOUT_ANIMATION: + return 0; + break; + case constants.CLOSE_WITHOUT_ANIMATION: + return top; + break; + } + } + ); +} + +export function homeStackLeft (selectedStackId, animateHomeStackLeft, homeStackState, left) { + return useDerivedValue( + function () { + 'worklet' + if (animateHomeStackLeft.value) { + var leftValue = left[selectedStackId.value]; + switch (homeStackState.value) { + case constants.OPEN_WITH_ANIMATION: + return withSequence(withTiming(leftValue, {duration: 0}), withTiming(0, constants.LINEAR_EASING)) + break; + case constants.CLOSE_WITH_ANIMATION: + return withTiming(leftValue, constants.LINEAR_EASING); + break; + case constants.OPEN_WITHOUT_ANIMATION: + return 0; + break; + case constants.CLOSE_WITHOUT_ANIMATION: + return leftValue; + break; + } + } else { + return 0; + } + } + ); +} + +export function homeStackPointer (homeStackState) { + return useDerivedValue( + function () { + 'worklet' + var homeStackStateValue = homeStackState.value; + return (homeStackStateValue == constants.OPEN_WITH_ANIMATION || + homeStackStateValue == constants.OPEN_WITHOUT_ANIMATION) ? "auto" : "none"; + } + ); +} + +export function homeStackScale (homeStackState, minimizeScale) { + return useDerivedValue( + function () { + 'worklet' + switch (homeStackState.value) { + case constants.OPEN_WITH_ANIMATION: + return withTiming(1, constants.LINEAR_EASING); + break; + case constants.CLOSE_WITH_ANIMATION: + return withTiming(minimizeScale, constants.LINEAR_EASING); + break; + case constants.OPEN_WITHOUT_ANIMATION: + return 1; + break; + case constants.CLOSE_WITHOUT_ANIMATION: + return minimizeScale; + break; + } + } + ); +} diff --git a/src/mocks/js_dependencies.cljs b/src/mocks/js_dependencies.cljs index d56a34a5ca..9ed8a244df 100644 --- a/src/mocks/js_dependencies.cljs +++ b/src/mocks/js_dependencies.cljs @@ -405,7 +405,9 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( "react-native-svg" react-native-svg "react-native-orientation-locker" react-native-orientation-locker "../src/js/worklets/core.js" worklet-factory - "../src/js/worklets/shell.js" #js {} + "../src/js/worklets/shell/bottom_tabs.js" #js {} + "../src/js/worklets/shell/home_stack.js" #js {} + "../src/js/worklets/shell/floating_screen.js" #js {} "../src/js/worklets/bottom_sheet.js" #js {} "../src/js/worklets/record_audio.js" #js {} "../src/js/worklets/scroll_view.js" #js {} diff --git a/src/status_im/communities/core.cljs b/src/status_im/communities/core.cljs index 3096cf44e7..ba914f9830 100644 --- a/src/status_im/communities/core.cljs +++ b/src/status_im/communities/core.cljs @@ -296,7 +296,7 @@ (vals (get-in db [:communities community-id :chats])))] (when (and id (not= (:current-chat-id db) (str community-id id))) - (chat.events/navigate-to-chat cofx (str community-id id))))) + (chat.events/navigate-to-chat cofx (str community-id id) nil)))) (rf/defn fetch [_] diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 48be2fb83f..448326b5b1 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -232,11 +232,6 @@ [{:keys [db]} k v] {:db (assoc db k v)}) -(rf/defn set-view-id - {:events [:set-view-id]} - [{:keys [db]} view-id] - {:db (assoc db :view-id view-id)}) - ;;TODO :replace by named events (rf/defn set-once-event {:events [:set-once]} diff --git a/src/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index a7c4b7c166..f6b80797af 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -15,7 +15,7 @@ {:events [:navigate-chat-updated]} [cofx chat-id] (when (get-in cofx [:db :chats chat-id]) - (chat.events/navigate-to-chat cofx chat-id))) + (chat.events/navigate-to-chat cofx chat-id nil))) (rf/defn handle-chat-removed {:events [:chat-removed]} diff --git a/src/status_im/multiaccounts/core.cljs b/src/status_im/multiaccounts/core.cljs index 6a2c589e2d..b85fdeef62 100644 --- a/src/status_im/multiaccounts/core.cljs +++ b/src/status_im/multiaccounts/core.cljs @@ -12,7 +12,7 @@ [status-im2.setup.hot-reload :as hot-reload] [status-im2.common.theme.core :as theme] [taoensso.timbre :as log] - [status-im2.contexts.shell.animation :as shell.animation] + [status-im2.contexts.shell.utils :as shell.utils] [status-im.contact.db :as contact.db])) ;; validate that the given mnemonic was generated from Status Dictionary @@ -135,7 +135,7 @@ [:light :dark colors/white])] (theme/set-theme theme) (re-frame/dispatch [:change-shell-status-bar-style - (if (shell.animation/home-stack-open?) status-bar-theme :light)]) + (if (shell.utils/home-stack-open?) status-bar-theme :light)]) (when reload-ui? (rf/dispatch [:dissmiss-all-overlays]) (hot-reload/reload) diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index 0bd541d629..ade14df617 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -42,9 +42,10 @@ [status-im2.contexts.chat.messages.link-preview.events :as link-preview] [status-im2.contexts.contacts.events :as contacts] [status-im2.navigation.events :as navigation] + [status-im2.contexts.shell.constants :as shell.constants] [status-im2.common.log :as logging] [taoensso.timbre :as log] - [status-im2.contexts.shell.animation :as shell.animation] + [status-im2.contexts.shell.utils :as shell.utils] [utils.security.core :as security])) (re-frame/reg-fx @@ -458,7 +459,8 @@ :key-uid (fn [stored-key-uid] (when (= stored-key-uid key-uid) - (re-frame/dispatch [:chat/navigate-to-chat chat-id]))))))))) + (re-frame/dispatch [:chat/navigate-to-chat chat-id + shell.constants/open-screen-without-animation]))))))))) (rf/defn check-last-chat {:events [::check-last-chat]} @@ -552,7 +554,7 @@ tos-accepted? (get db :tos/accepted?) {:networks/keys [current-network networks]} db network-id (str (get-in networks [current-network :config :NetworkId]))] - (shell.animation/change-selected-stack-id :communities-stack true) + (shell.utils/change-selected-stack-id :communities-stack true nil) (rf/merge cofx {:db (-> db (dissoc :multiaccounts/login) diff --git a/src/status_im/multiaccounts/logout/core.cljs b/src/status_im/multiaccounts/logout/core.cljs index 37fac16fe1..86816d7d21 100644 --- a/src/status_im/multiaccounts/logout/core.cljs +++ b/src/status_im/multiaccounts/logout/core.cljs @@ -16,7 +16,7 @@ (rf/merge cofx {:set-root :progress :chat.ui/clear-inputs nil - :shell/reset-bottom-tabs nil + :shell/reset-state nil :hide-popover nil ::logout nil ::multiaccounts/webview-debug-changed false diff --git a/src/status_im/ui/screens/profile/visibility_status/views.cljs b/src/status_im/ui/screens/profile/visibility_status/views.cljs index 863dd90df3..81ba572d42 100644 --- a/src/status_im/ui/screens/profile/visibility_status/views.cljs +++ b/src/status_im/ui/screens/profile/visibility_status/views.cljs @@ -30,6 +30,7 @@ (defn calculate-button-height-and-dispatch-popover [] (.measure + ^js @button-ref (fn [_ _ _ _ _ page-y] (dispatch-popover page-y)))) diff --git a/src/status_im2/contexts/chat/events.cljs b/src/status_im2/contexts/chat/events.cljs index 38104d1eaf..77252cfecb 100644 --- a/src/status_im2/contexts/chat/events.cljs +++ b/src/status_im2/contexts/chat/events.cljs @@ -168,16 +168,7 @@ (when-let [chat-id (:current-chat-id db)] (chat.state/reset-visible-item) (rf/merge cofx - (merge - {:db (dissoc db :current-chat-id)} - (let [community-id (get-in db [:chats chat-id :community-id])] - ;; When navigating back from community chat to community, update switcher card - ;; A close chat event is also called while opening any chat. - ;; That might lead to duplicate :dispatch keys in fx/merge, that's why dispatch-n is - ;; used here. - (when (and community-id (not navigate-to-shell?)) - {:dispatch-n [[:shell/add-switcher-card - :community-overview community-id]]}))) + {:db (dissoc db :current-chat-id)} (link-preview/reset-all) (delete-for-me/sync-all) (delete-message/send-all) @@ -206,9 +197,9 @@ (rf/defn navigate-to-chat "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" {:events [:chat/navigate-to-chat]} - [{db :db :as cofx} chat-id] + [{db :db :as cofx} chat-id animation] (rf/merge cofx - {:dispatch [:navigate-to :chat chat-id]} + {:dispatch [(if animation :shell/navigate-to :navigate-to) :chat chat-id animation]} (when-not (or (= (:view-id db) :community) (= (:view-id db) :community-overview)) (navigation/pop-to-root :shell-stack)) (close-chat false) diff --git a/src/status_im2/contexts/chat/events_test.cljs b/src/status_im2/contexts/chat/events_test.cljs index 94d5775f0d..71e633b6a2 100644 --- a/src/status_im2/contexts/chat/events_test.cljs +++ b/src/status_im2/contexts/chat/events_test.cljs @@ -87,5 +87,5 @@ (let [chat-id "test_chat" db {:pagination-info {chat-id {:all-loaded? true}}}] (testing "Pagination info should be reset on navigation" - (let [res (chat/navigate-to-chat {:db db} chat-id)] + (let [res (chat/navigate-to-chat {:db db} chat-id nil)] (is (nil? (get-in res [:db :pagination-info chat-id :all-loaded?]))))))) diff --git a/src/status_im2/contexts/chat/messages/list/view.cljs b/src/status_im2/contexts/chat/messages/list/view.cljs index 332dca4349..ad5c333806 100644 --- a/src/status_im2/contexts/chat/messages/list/view.cljs +++ b/src/status_im2/contexts/chat/messages/list/view.cljs @@ -110,13 +110,13 @@ :extrapolateRight "clamp"}) (defn loading-view - [chat-id] + [chat-id shell-animation-complete?] (let [loading-messages? (rf/sub [:chats/loading-messages? chat-id]) all-loaded? (rf/sub [:chats/all-loaded? chat-id]) messages (rf/sub [:chats/raw-chat-messages-stream chat-id]) loading-first-page? (= (count messages) 0) top-spacing (if loading-first-page? 0 navigation.style/navigation-bar-height)] - (when (or loading-messages? (not all-loaded?)) + (when (or (not shell-animation-complete?) loading-messages? (not all-loaded?)) [rn/view {:padding-top top-spacing} [quo/skeleton (if loading-first-page? @@ -174,7 +174,7 @@ (assoc :scale-y -1))) (defn f-list-footer - [{:keys [chat scroll-y cover-bg-color on-layout]}] + [{:keys [chat scroll-y cover-bg-color on-layout shell-animation-complete?]}] (let [{:keys [chat-id chat-name emoji chat-type group-chat]} chat all-loaded? (rf/sub [:chats/all-loaded? chat-id]) @@ -217,7 +217,7 @@ (when bio [quo/text {:style style/bio} bio])]]] - [loading-view chat-id]])) + [loading-view chat-id shell-animation-complete?]])) (defn list-footer [props] @@ -258,10 +258,15 @@ (defn messages-list-content [{:keys [chat insets scroll-y cover-bg-color keyboard-shown?]}] - (let [context (rf/sub [:chats/current-chat-message-list-view-context]) - messages (rf/sub [:chats/raw-chat-messages-stream (:chat-id chat)]) - recording? (rf/sub [:chats/recording?]) - all-loaded? (rf/sub [:chats/all-loaded? (:chat-id chat)])] + (let [shell-animation-complete? (rf/sub [:shell/animation-complete? (:chat-type chat)]) + context (when shell-animation-complete? + (rf/sub [:chats/current-chat-message-list-view-context])) + messages (when shell-animation-complete? + (rf/sub [:chats/raw-chat-messages-stream (:chat-id chat)])) + recording? (when shell-animation-complete? + (rf/sub [:chats/recording?])) + all-loaded? (when shell-animation-complete? + (rf/sub [:chats/all-loaded? (:chat-id chat)]))] [rn/view {:style {:flex 1}} [rn/flat-list {:key-fn list-key-fn @@ -271,10 +276,11 @@ [list-group-chat-header chat]) [list-header insets]] :footer [list-footer - {:chat chat - :scroll-y scroll-y - :cover-bg-color cover-bg-color - :on-layout footer-on-layout}] + {:chat chat + :scroll-y scroll-y + :cover-bg-color cover-bg-color + :on-layout footer-on-layout + :shell-animation-complete? shell-animation-complete?}] :data messages :render-data {:context context :keyboard-shown? keyboard-shown?} @@ -323,7 +329,8 @@ [keyboard-shown keyboard-height]) [rn/keyboard-avoiding-view {:style (style/keyboard-avoiding-container insets) - :keyboard-vertical-offset (- (:bottom insets))} + :keyboard-vertical-offset (- (:bottom insets)) + :behavior :height} (when header-comp [header-comp {:scroll-y scroll-y}]) diff --git a/src/status_im2/contexts/chat/messages/view.cljs b/src/status_im2/contexts/chat/messages/view.cljs index 72696af7e3..c8ee3249fd 100644 --- a/src/status_im2/contexts/chat/messages/view.cljs +++ b/src/status_im2/contexts/chat/messages/view.cljs @@ -1,29 +1,23 @@ (ns status-im2.contexts.chat.messages.view (:require [quo2.foundations.colors :as colors] [re-frame.db] - [react-native.core :as rn] - [reagent.core :as reagent] [status-im2.contexts.chat.composer.view :as composer] [status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as contact-requests.bottom-drawer] [status-im2.contexts.chat.messages.list.view :as messages.list] [status-im2.contexts.chat.messages.navigation.view :as messages.navigation] - [status-im2.navigation.state :as navigation.state] [utils.re-frame :as rf])) -(defn navigate-back-handler - [] - (when (and (not @navigation.state/curr-modal) (= (get @re-frame.db/app-db :view-id) :chat)) - (rn/hw-back-remove-listener navigate-back-handler) - (rf/dispatch [:chat/close]) - (rf/dispatch [:navigate-back]) - ;; If true is not returned back button event will bubble up, - ;; and will call system back button action - true)) +(defn load-composer + [insets chat-type] + (let [shell-animation-complete? (rf/sub [:shell/animation-complete? chat-type])] + (when shell-animation-complete? + [:f> composer/composer insets]))) -(defn chat-render +(defn chat [] (let [{:keys [chat-id + chat-type contact-request-state group-chat able-to-send-message?] @@ -35,14 +29,6 @@ [messages.navigation/navigation-view {:scroll-y scroll-y}]) :footer-comp (fn [{:keys [insets]}] (if-not able-to-send-message? - [contact-requests.bottom-drawer/view chat-id contact-request-state group-chat] - [:f> composer/composer insets]))}])) - -(defn chat - [] - (reagent/create-class - {:component-did-mount (fn [] - (rn/hw-back-remove-listener navigate-back-handler) - (rn/hw-back-add-listener navigate-back-handler)) - :component-will-unmount (fn [] (rn/hw-back-remove-listener navigate-back-handler)) - :reagent-render chat-render})) + [contact-requests.bottom-drawer/view chat-id contact-request-state + group-chat] + [load-composer insets chat-type]))}])) diff --git a/src/status_im2/contexts/communities/overview/view.cljs b/src/status_im2/contexts/communities/overview/view.cljs index 8044ef51a3..7efaaf7f5e 100644 --- a/src/status_im2/contexts/communities/overview/view.cljs +++ b/src/status_im2/contexts/communities/overview/view.cljs @@ -303,53 +303,54 @@ category))))) (defn community-card-page-view - [{:keys [name images id]}] + [] (let [categories-heights (reagent/atom {}) first-channel-height (reagent/atom 0) - scroll-height (reagent/atom 0) - cover {:uri (get-in images [:banner :uri])} - logo {:uri (get-in images [:thumbnail :uri])}] - (fn [community pending?] - [scroll-page/scroll-page - {:cover-image cover - :logo logo - :page-nav-right-section-buttons (page-nav-right-section-buttons id) - :name name - :on-scroll #(reset! scroll-height %) - :navigate-back? true - :background-color (colors/theme-colors - colors/white - colors/neutral-90) - :height (if platform/ios? - 100 - 148)} + scroll-height (reagent/atom 0)] + (fn [id] + (let [{:keys [name images id] :as community} + (rf/sub [:communities/community id]) + pending? (rf/sub [:communities/my-pending-request-to-join id]) + cover {:uri (get-in images [:banner :uri])} + logo {:uri (get-in images [:thumbnail :uri])}] + [scroll-page/scroll-page + {:cover-image cover + :logo logo + :page-nav-right-section-buttons (page-nav-right-section-buttons id) + :name name + :on-scroll #(reset! scroll-height %) + :navigate-back? true + :background-color (colors/theme-colors + colors/white + colors/neutral-90) + :height (if platform/ios? + 100 + 148)} - [sticky-category-header - {:enabled (> @scroll-height @first-channel-height) - :label (pick-first-category-by-height - @scroll-height - @first-channel-height - @categories-heights)}] + [sticky-category-header + {:enabled (> @scroll-height @first-channel-height) + :label (pick-first-category-by-height + @scroll-height + @first-channel-height + @categories-heights)}] - [community-content - community - pending? - {:on-category-layout (partial add-category-height categories-heights) - :on-first-channel-height-changed - ;; Here we set the height of the component - ;; and we filter out the categories, as some might have been removed - (fn [height categories] - (swap! categories-heights select-keys categories) - (reset! first-channel-height height))}]]))) + [community-content + community + pending? + {:on-category-layout (partial add-category-height categories-heights) + :on-first-channel-height-changed + ;; Here we set the height of the component + ;; and we filter out the categories, as some might have been removed + (fn [height categories] + (swap! categories-heights select-keys categories) + (reset! first-channel-height height))}]])))) (defn overview - [] - (let [id (rf/sub [:get-screen-params :community-overview]) - community (rf/sub [:communities/community id]) - pending? (rf/sub [:communities/my-pending-request-to-join id])] + [id] + (let [id (or id (rf/sub [:get-screen-params :community-overview]))] [rn/view {:style style/community-overview-container} - [community-card-page-view community pending?] + [community-card-page-view id] [floating-shell-button/floating-shell-button {:jump-to {:on-press #(rf/dispatch [:shell/navigate-to-jump-to]) :label (i18n/label :t/jump-to)}} diff --git a/src/status_im2/contexts/onboarding/common/background/view.cljs b/src/status_im2/contexts/onboarding/common/background/view.cljs index 8bf1502e51..dbf5598b73 100644 --- a/src/status_im2/contexts/onboarding/common/background/view.cljs +++ b/src/status_im2/contexts/onboarding/common/background/view.cljs @@ -6,7 +6,7 @@ [oops.core :refer [oget]] [status-im2.common.resources :as resources] [status-im.async-storage.core :as async-storage] - [status-im2.contexts.shell.animation :as shell.animation] + [status-im2.contexts.shell.state :as shell.state] [status-im2.contexts.onboarding.common.carousel.view :as carousel] [status-im2.contexts.onboarding.common.background.style :as style] [react-native.reanimated :as reanimated] @@ -50,8 +50,8 @@ ;; but actual values differ in some pixels, so arbitrary 5 pixels is allowed (when (and (> height width) (>= (+ height 5) (or window-height 0)) - (not= height @shell.animation/screen-height)) - (reset! shell.animation/screen-height height) + (not= height @shell.state/screen-height)) + (reset! shell.state/screen-height height) (async-storage/set-item! :screen-height height)))) (defn f-view diff --git a/src/status_im2/contexts/onboarding/enable_notifications/view.cljs b/src/status_im2/contexts/onboarding/enable_notifications/view.cljs index 17adb6acee..c5b45aaa62 100644 --- a/src/status_im2/contexts/onboarding/enable_notifications/view.cljs +++ b/src/status_im2/contexts/onboarding/enable_notifications/view.cljs @@ -11,7 +11,7 @@ [status-im2.contexts.onboarding.common.background.view :as background] [status-im2.contexts.onboarding.common.navigation-bar.view :as navigation-bar] [status-im2.contexts.onboarding.enable-notifications.style :as style] - [status-im2.contexts.shell.animation :as shell.animation])) + [status-im2.contexts.shell.utils :as shell.utils])) (defn page-title [] @@ -28,7 +28,7 @@ [rn/view {:style (style/buttons insets)} [quo/button {:on-press (fn [] - (shell.animation/change-selected-stack-id :communities-stack true) + (shell.utils/change-selected-stack-id :communities-stack true nil) (rf/dispatch [::notifications/switch true platform/ios?]) (rf/dispatch [:init-root :welcome])) :type :primary @@ -38,7 +38,7 @@ (i18n/label :t/intro-wizard-title6)] [quo/button {:on-press (fn [] - (shell.animation/change-selected-stack-id :communities-stack true) + (shell.utils/change-selected-stack-id :communities-stack true nil) (rf/dispatch [:init-root :welcome])) :accessibility-label :enable-notifications-later-button :override-background-color colors/white-opa-5 diff --git a/src/status_im2/contexts/quo_preview/switcher/switcher_cards.cljs b/src/status_im2/contexts/quo_preview/switcher/switcher_cards.cljs index 5b20b6c891..7b7774422b 100644 --- a/src/status_im2/contexts/quo_preview/switcher/switcher_cards.cljs +++ b/src/status_im2/contexts/quo_preview/switcher/switcher_cards.cljs @@ -5,7 +5,7 @@ [status-im2.constants :as constants] [status-im2.common.resources :as resources] [status-im2.contexts.quo-preview.preview :as preview] - [status-im2.contexts.shell.cards.view :as switcher-cards] + [status-im2.contexts.shell.components.switcher-cards.view :as switcher-cards] [status-im2.contexts.shell.constants :as shell.constants])) (def descriptor diff --git a/src/status_im2/contexts/shell/animation.cljs b/src/status_im2/contexts/shell/animation.cljs index 672fe2dde1..35cd9af67d 100644 --- a/src/status_im2/contexts/shell/animation.cljs +++ b/src/status_im2/contexts/shell/animation.cljs @@ -1,177 +1,86 @@ (ns status-im2.contexts.shell.animation - (:require [quo2.foundations.colors :as colors] - [utils.re-frame :as rf] + (:require [utils.re-frame :as rf] [react-native.reanimated :as reanimated] - [reagent.core :as reagent] - [status-im.async-storage.core :as async-storage] ;;TODO remove when not used anymore - [status-im2.contexts.shell.constants :as shell.constants] - [utils.worklets.shell :as worklets.shell])) - -;; Atoms -(def selected-stack-id (atom nil)) -(def screen-height (atom nil)) -(def home-stack-state (atom shell.constants/close-with-animation)) -(def shared-values-atom (atom nil)) - -;; Reagent atoms used for lazily loading home screen tabs -(def load-communities-stack? (reagent/atom false)) -(def load-chats-stack? (reagent/atom false)) -(def load-wallet-stack? (reagent/atom false)) -(def load-browser-stack? (reagent/atom false)) - -;; Helper Functions -(defn home-stack-open? - [] - (let [state @home-stack-state] - (or (= state shell.constants/open-with-animation) - (= state shell.constants/open-without-animation)))) - -(defn calculate-home-stack-state-value - [stack-id & animate?] - (if animate? - (if (some? stack-id) - shell.constants/open-with-animation - shell.constants/close-with-animation) - (if (some? stack-id) - shell.constants/open-without-animation - shell.constants/close-without-animation))) - -(defn load-stack - [stack-id] - (case stack-id - :communities-stack (reset! load-communities-stack? true) - :chats-stack (reset! load-chats-stack? true) - :wallet-stack (reset! load-wallet-stack? true) - :browser-stack (reset! load-browser-stack? true) - "")) - -(defn change-selected-stack-id - [stack-id & [store? home-stack-state-value]] - (let [home-stack-state-value (or home-stack-state-value - (calculate-home-stack-state-value stack-id))] - (reset! selected-stack-id stack-id) - (reset! home-stack-state home-stack-state-value) - (rf/dispatch [:set-view-id (or stack-id :shell)]) - (when store? - (async-storage/set-item! :selected-stack-id stack-id)))) - -(defn calculate-home-stack-position - [] - (let [{:keys [width height]} (shell.constants/dimensions) - height (or @screen-height height) - bottom-nav-tab-width 90 - 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 (shell.constants/bottom-tabs-container-height)))] - {:left (reduce - (fn [acc stack-id] - (assoc acc - stack-id - (+ (- left-margin left-empty-space) - (* (.indexOf shell.constants/stacks-ids stack-id) - bottom-nav-tab-width)))) - {:none 0} - shell.constants/stacks-ids) - :top (+ top-empty-space (shell.constants/bottom-tabs-container-height)) - :scale minimize-scale})) - -;; Shared Values -(defn calculate-shared-values - [] - (let [selected-stack-id-sv (reanimated/use-shared-value - ;; passing keywords or nil is not working with reanimated - (name (or @selected-stack-id :communities-stack))) - pass-through-sv (reanimated/use-shared-value false) - home-stack-state-sv (reanimated/use-shared-value @home-stack-state) - animate-home-stack-left (reanimated/use-shared-value (not (home-stack-open?))) - home-stack-position (calculate-home-stack-position)] - (reset! shared-values-atom - (reduce - (fn [acc id] - (let [tabs-icon-color-keyword (get shell.constants/tabs-icon-color-keywords id) - stack-opacity-keyword (get shell.constants/stacks-opacity-keywords id) - stack-z-index-keyword (get shell.constants/stacks-z-index-keywords id)] - (assoc - acc - stack-opacity-keyword - (worklets.shell/stack-opacity (name id) selected-stack-id-sv) - stack-z-index-keyword - (worklets.shell/stack-z-index (name id) selected-stack-id-sv) - tabs-icon-color-keyword - (worklets.shell/bottom-tab-icon-color - (name id) - selected-stack-id-sv - home-stack-state-sv - pass-through-sv - colors/white - colors/neutral-50 - colors/white-opa-40)))) - {:selected-stack-id selected-stack-id-sv - :pass-through? pass-through-sv - :home-stack-state home-stack-state-sv - :animate-home-stack-left animate-home-stack-left - :home-stack-left (worklets.shell/home-stack-left - selected-stack-id-sv - animate-home-stack-left - home-stack-state-sv - (clj->js (:left home-stack-position))) - :home-stack-top (worklets.shell/home-stack-top - home-stack-state-sv - (:top home-stack-position)) - :home-stack-opacity (worklets.shell/home-stack-opacity home-stack-state-sv) - :home-stack-pointer (worklets.shell/home-stack-pointer home-stack-state-sv) - :home-stack-scale (worklets.shell/home-stack-scale home-stack-state-sv - (:scale home-stack-position)) - :bottom-tabs-height (worklets.shell/bottom-tabs-height - home-stack-state-sv - (shell.constants/bottom-tabs-container-height) - (shell.constants/bottom-tabs-extended-container-height))} - shell.constants/stacks-ids))) - @shared-values-atom) - -;; Animations - -(defn change-shell-status-bar-style - [] - (rf/dispatch [:change-shell-status-bar-style - (if (or (colors/dark?) - (not (home-stack-open?))) - :light - :dark)])) + [status-im2.contexts.shell.utils :as utils] + [status-im2.contexts.shell.state :as state] + [status-im2.contexts.shell.constants :as shell.constants])) +;; Home stack (defn open-home-stack [stack-id animate?] - (let [home-stack-state-value (calculate-home-stack-state-value stack-id animate?)] - (reanimated/set-shared-value (:selected-stack-id @shared-values-atom) (name stack-id)) - (reanimated/set-shared-value (:home-stack-state @shared-values-atom) home-stack-state-value) - (change-selected-stack-id stack-id true home-stack-state-value) - (js/setTimeout change-shell-status-bar-style shell.constants/shell-animation-time))) + (let [home-stack-state-value (utils/calculate-home-stack-state-value stack-id animate?)] + (reanimated/set-shared-value (:selected-stack-id @state/shared-values-atom) (name stack-id)) + (reanimated/set-shared-value (:home-stack-state @state/shared-values-atom) home-stack-state-value) + (utils/change-selected-stack-id stack-id true home-stack-state-value) + (js/setTimeout + (fn [] + (utils/load-stack stack-id) + (utils/change-shell-status-bar-style)) + (if animate? shell.constants/shell-animation-time 0)))) (defn change-tab [stack-id] - (reanimated/set-shared-value (:animate-home-stack-left @shared-values-atom) false) - (reanimated/set-shared-value (:selected-stack-id @shared-values-atom) (name stack-id)) - (change-selected-stack-id stack-id true)) + (reanimated/set-shared-value (:animate-home-stack-left @state/shared-values-atom) false) + (reanimated/set-shared-value (:selected-stack-id @state/shared-values-atom) (name stack-id)) + (utils/load-stack stack-id) + (utils/change-selected-stack-id stack-id true nil)) (defn bottom-tab-on-press - [stack-id] - (when (and @shared-values-atom (not= stack-id @selected-stack-id)) - (let [stack-load-delay (if (home-stack-open?) - 0 - shell.constants/shell-animation-time)] - (if (home-stack-open?) - (change-tab stack-id) - (open-home-stack stack-id true)) - (js/setTimeout #(load-stack stack-id) stack-load-delay)))) + [stack-id animate?] + (when (and @state/shared-values-atom (not= stack-id @state/selected-stack-id)) + (if (utils/home-stack-open?) + (change-tab stack-id) + (open-home-stack stack-id animate?)) + (when animate? (utils/update-view-id (or stack-id :shell))))) (defn close-home-stack [animate?] (let [stack-id nil - home-stack-state-value (calculate-home-stack-state-value stack-id animate?)] - (reanimated/set-shared-value (:animate-home-stack-left @shared-values-atom) true) - (reanimated/set-shared-value (:home-stack-state @shared-values-atom) home-stack-state-value) - (change-selected-stack-id stack-id true home-stack-state-value) - (change-shell-status-bar-style))) + home-stack-state-value (utils/calculate-home-stack-state-value stack-id animate?)] + (reanimated/set-shared-value (:animate-home-stack-left @state/shared-values-atom) true) + (reanimated/set-shared-value (:home-stack-state @state/shared-values-atom) home-stack-state-value) + (utils/change-selected-stack-id stack-id true home-stack-state-value) + (utils/change-shell-status-bar-style) + (when animate? (utils/update-view-id (or stack-id :shell))))) + +;; Floating Screen +(defn animate-floating-screen + [screen-id {:keys [id animation community-id hidden-screen?]}] + (when (not= animation (get @state/floating-screens-state screen-id)) + ;; Animate Floating Screen + (reanimated/set-shared-value + (get-in @state/shared-values-atom [screen-id :screen-state]) + animation) + (reset! state/floating-screens-state + (assoc @state/floating-screens-state screen-id animation))) + (js/setTimeout + (fn [floating-screen-open?] + (if floating-screen-open? + ;; Events realted to opening of a screen + (rf/dispatch [:shell/floating-screen-opened screen-id + id community-id hidden-screen?]) + ;; Events realted to closing of a screen + (rf/dispatch [:shell/floating-screen-closed screen-id]))) + shell.constants/shell-animation-time + (utils/floating-screen-open? screen-id))) + +(defn set-floating-screen-position + [left top card-type] + (let [screen-id (cond + (#{shell.constants/one-to-one-chat-card + shell.constants/private-group-chat-card + shell.constants/community-channel-card} + card-type) + shell.constants/chat-screen + + (= card-type shell.constants/community-card) + shell.constants/community-screen + + :else nil)] + (when screen-id + (reanimated/set-shared-value + (get-in @state/shared-values-atom [screen-id :screen-left]) + left) + (reanimated/set-shared-value + (get-in @state/shared-values-atom [screen-id :screen-top]) + top)))) diff --git a/src/status_im2/contexts/shell/components/bottom_tabs/style.cljs b/src/status_im2/contexts/shell/components/bottom_tabs/style.cljs new file mode 100644 index 0000000000..07b8aee0ad --- /dev/null +++ b/src/status_im2/contexts/shell/components/bottom_tabs/style.cljs @@ -0,0 +1,36 @@ +(ns status-im2.contexts.shell.components.bottom-tabs.style + (:require [quo2.foundations.colors :as colors] + [react-native.platform :as platform] + [react-native.reanimated :as reanimated] + [status-im2.contexts.shell.utils :as utils])) + +(defn bottom-tabs-container + [pass-through? height] + (reanimated/apply-animations-to-style + {:height height} + {:background-color (if pass-through? :transparent colors/neutral-100) + :flex 1 + :align-items :center + :height (utils/bottom-tabs-container-height) + :position :absolute + :bottom 0 + :right 0 + :left 0 + :overflow :hidden + :accessibility-label :bottom-tabs-container})) + +(defn bottom-tabs + [] + {:flex-direction :row + :position :absolute + :bottom (if platform/android? 8 34) + :flex 1 + :accessibility-label :bottom-tabs}) + +(def bottom-tabs-blur-overlay + {:position :absolute + :left 0 + :right 0 + :bottom 0 + :height (utils/bottom-tabs-extended-container-height) + :background-color colors/neutral-100-opa-70}) diff --git a/src/status_im2/contexts/shell/bottom_tabs.cljs b/src/status_im2/contexts/shell/components/bottom_tabs/view.cljs similarity index 79% rename from src/status_im2/contexts/shell/bottom_tabs.cljs rename to src/status_im2/contexts/shell/components/bottom_tabs/view.cljs index 5f2e1e64b4..23472b4fbe 100644 --- a/src/status_im2/contexts/shell/bottom_tabs.cljs +++ b/src/status_im2/contexts/shell/components/bottom_tabs/view.cljs @@ -1,13 +1,15 @@ -(ns status-im2.contexts.shell.bottom-tabs +(ns status-im2.contexts.shell.components.bottom-tabs.view (:require [utils.re-frame :as rf] [react-native.core :as rn] [react-native.blur :as blur] + [react-native.gesture :as gesture] [react-native.reanimated :as reanimated] - [status-im2.contexts.shell.style :as style] + [status-im2.contexts.shell.utils :as utils] + [status-im2.contexts.shell.state :as state] [status-im2.contexts.shell.animation :as animation] [status-im2.contexts.shell.constants :as shell.constants] [quo2.components.navigation.bottom-nav-tab :as bottom-nav-tab] - [react-native.gesture :as gesture])) + [status-im2.contexts.shell.components.bottom-tabs.style :as style])) (defn blur-overlay-params [style] @@ -26,18 +28,14 @@ :icon-color-anim (get shared-values (get shell.constants/tabs-icon-color-keywords stack-id)) - :on-press #(animation/bottom-tab-on-press stack-id) + :on-press #(animation/bottom-tab-on-press stack-id true) :accessibility-label (str (name stack-id) "-tab"))]) -(defn- f-bottom-tabs +(defn f-bottom-tabs [] (let [notifications-data (rf/sub [:shell/bottom-tabs-notifications-data]) pass-through? (rf/sub [:shell/shell-pass-through?]) - shared-values @animation/shared-values-atom - original-style (style/bottom-tabs-container pass-through?) - animated-style (reanimated/apply-animations-to-style - {:height (:bottom-tabs-height shared-values)} - original-style) + shared-values @state/shared-values-atom communities-double-tab-gesture (-> (gesture/gesture-tap) (gesture/number-of-taps 2) (gesture/on-start @@ -48,9 +46,10 @@ (gesture/on-start (fn [_event] (rf/dispatch [:messages-home/select-tab :tab/recent]))))] - (animation/load-stack @animation/selected-stack-id) + (utils/load-stack @state/selected-stack-id) (reanimated/set-shared-value (:pass-through? shared-values) pass-through?) - [reanimated/view {:style animated-style} + [reanimated/view + {:style (style/bottom-tabs-container pass-through? (:bottom-tabs-height shared-values))} (when pass-through? [blur/view (blur-overlay-params style/bottom-tabs-blur-overlay)]) [rn/view {:style (style/bottom-tabs)} @@ -60,7 +59,3 @@ [bottom-tab :i/messages :chats-stack shared-values notifications-data]] [bottom-tab :i/wallet :wallet-stack shared-values notifications-data] [bottom-tab :i/browser :browser-stack shared-values notifications-data]]])) - -(defn bottom-tabs - [] - [:f> f-bottom-tabs]) diff --git a/src/status_im2/contexts/shell/components/floating_screens/style.cljs b/src/status_im2/contexts/shell/components/floating_screens/style.cljs new file mode 100644 index 0000000000..6becbc38ef --- /dev/null +++ b/src/status_im2/contexts/shell/components/floating_screens/style.cljs @@ -0,0 +1,20 @@ +(ns status-im2.contexts.shell.components.floating-screens.style + (:require [quo2.foundations.colors :as colors] + [react-native.reanimated :as reanimated])) + +(defn screen + [{:keys [screen-left screen-top screen-width screen-height screen-z-index]}] + (reanimated/apply-animations-to-style + {:left screen-left + :top screen-top + :width screen-width + :height screen-height + :z-index screen-z-index} + {:background-color (colors/theme-colors colors/white colors/neutral-95) + :overflow :hidden + :position :absolute})) + +(defn screen-container + [{:keys [width height]}] + {:width width + :height height}) diff --git a/src/status_im2/contexts/shell/components/floating_screens/view.cljs b/src/status_im2/contexts/shell/components/floating_screens/view.cljs new file mode 100644 index 0000000000..96334a14c9 --- /dev/null +++ b/src/status_im2/contexts/shell/components/floating_screens/view.cljs @@ -0,0 +1,43 @@ +(ns status-im2.contexts.shell.components.floating-screens.view + (:require [utils.re-frame :as rf] + [react-native.core :as rn] + [react-native.reanimated :as reanimated] + [status-im2.contexts.shell.state :as state] + [status-im2.contexts.shell.utils :as utils] + [status-im2.contexts.chat.messages.view :as chat] + [status-im2.contexts.shell.animation :as animation] + [status-im2.contexts.shell.constants :as shell.constants] + [status-im2.contexts.shell.components.floating-screens.style :as style] + [status-im2.contexts.communities.overview.view :as communities.overview])) + +(def screens-map + {shell.constants/community-screen communities.overview/overview + shell.constants/chat-screen chat/chat}) + +(defn f-screen + [screen-id {:keys [id animation] :as screen-param}] + ;; First render screen, then animate (smoother animation) + (rn/use-effect + (fn [] + (animation/animate-floating-screen screen-id screen-param)) + [animation id]) + [reanimated/view + {:style (style/screen (get @state/shared-values-atom screen-id))} + [rn/view + {:style (style/screen-container (utils/dimensions)) + :key id} + [(get screens-map screen-id) id]]]) + +;; Currently chat screen and events both depends on current-chat-id, once we remove +;; use of current-chat-id from view then we can keep last chat loaded, for fast navigation +(defn lazy-screen + [screen-id] + (let [screen-param (rf/sub [:shell/floating-screen screen-id])] + (when screen-param + [:f> f-screen screen-id screen-param]))) + +(defn view + [] + [:<> + [lazy-screen shell.constants/community-screen] + [lazy-screen shell.constants/chat-screen]]) diff --git a/src/status_im2/contexts/shell/components/home_stack/style.cljs b/src/status_im2/contexts/shell/components/home_stack/style.cljs new file mode 100644 index 0000000000..b2bac39db2 --- /dev/null +++ b/src/status_im2/contexts/shell/components/home_stack/style.cljs @@ -0,0 +1,32 @@ +(ns status-im2.contexts.shell.components.home-stack.style + (:require [quo2.foundations.colors :as colors] + [react-native.reanimated :as reanimated] + [status-im2.contexts.shell.utils :as utils])) + +(defn home-stack + [shared-values {:keys [width height]}] + (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)}]} + {:border-bottom-left-radius 20 + :border-bottom-right-radius 20 + :background-color (colors/theme-colors colors/white colors/neutral-95) + :overflow :hidden + :position :absolute + :width width + :height (- height (utils/bottom-tabs-container-height))})) + +(defn stack-view + [stack-id {:keys [opacity z-index]}] + (reanimated/apply-animations-to-style + {:opacity opacity + :z-index z-index} + {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0 + :accessibility-label stack-id})) diff --git a/src/status_im2/contexts/shell/components/home_stack/view.cljs b/src/status_im2/contexts/shell/components/home_stack/view.cljs new file mode 100644 index 0000000000..ff9544d392 --- /dev/null +++ b/src/status_im2/contexts/shell/components/home_stack/view.cljs @@ -0,0 +1,48 @@ +(ns status-im2.contexts.shell.components.home-stack.view + (:require [react-native.reanimated :as reanimated] + [status-im.ui.screens.wallet.accounts.views :as wallet.accounts] + [status-im2.contexts.chat.home.view :as chat] + [status-im2.contexts.shell.state :as state] + [status-im2.contexts.shell.utils :as utils] + [status-im2.contexts.communities.home.view :as communities] + [status-im2.contexts.shell.constants :as shell.constants] + [status-im2.contexts.shell.components.home-stack.style :as style] + [status-im.ui.screens.browser.stack :as browser.stack])) + +(defn load-stack? + [stack-id] + (case stack-id + :communities-stack @state/load-communities-stack? + :chats-stack @state/load-chats-stack? + :browser-stack @state/load-browser-stack? + :wallet-stack @state/load-wallet-stack?)) + +(defn- f-stack-view + [stack-id shared-values] + [reanimated/view + {:style (style/stack-view + stack-id + {:opacity (get shared-values + (get shell.constants/stacks-opacity-keywords stack-id)) + :z-index (get shared-values + (get shell.constants/stacks-z-index-keywords stack-id))})} + (case stack-id + :communities-stack [communities/home] + :chats-stack [chat/home] + :wallet-stack [wallet.accounts/accounts-overview-old] + :browser-stack [browser.stack/browser-stack] + [:<>])]) + +(defn lazy-screen + [stack-id shared-values] + (when (load-stack? stack-id) + [:f> f-stack-view stack-id shared-values])) + +(defn f-home-stack + [] + (let [shared-values @state/shared-values-atom] + [reanimated/view {:style (style/home-stack shared-values (utils/dimensions))} + [lazy-screen :communities-stack shared-values] + [lazy-screen :chats-stack shared-values] + [lazy-screen :browser-stack shared-values] + [lazy-screen :wallet-stack shared-values]])) diff --git a/src/status_im2/contexts/shell/components/shell_screen/style.cljs b/src/status_im2/contexts/shell/components/shell_screen/style.cljs new file mode 100644 index 0000000000..35688be695 --- /dev/null +++ b/src/status_im2/contexts/shell/components/shell_screen/style.cljs @@ -0,0 +1,64 @@ +(ns status-im2.contexts.shell.components.shell-screen.style + (:require [quo2.foundations.colors :as colors] + [status-im2.contexts.shell.utils :as utils])) + +;;;; Placeholder +(defn placeholder-container + [status-bar-height] + {:position :absolute + :top (+ 112 status-bar-height) + :left 0 + :right 0 + :bottom (utils/bottom-tabs-container-height) + :align-items :center + :accessibility-label :shell-placeholder-view}) + +(def placeholder-image + {:margin-top 186 + :width 120 + :height 120 + ;; Code to remove once placeholder image/vector will be available + :border-width 5 + :border-radius 10 + :border-color :red}) + +(def placeholder-title + {:margin-top 20 + :color colors/white}) + +(def placeholder-subtitle + {:margin-top 4 + :color colors/white}) + +;;;; Shell +(defn jump-to-text + [status-bar-height] + {:color colors/white + :margin-top (+ 68 status-bar-height) + :margin-bottom 20 + :margin-left 20}) + +(def jump-to-list + {:top 0 + :left 0 + :right 0 + :bottom 0 + :position :absolute}) + +(defn top-nav-blur-overlay-container + [height pass-through?] + {:height height + :position :absolute + :left 0 + :top 0 + :right 0 + :overflow :hidden + :background-color (if pass-through? :transparent colors/neutral-100)}) + +(def top-nav-blur-overlay + {:height 100 + :position :absolute + :left 0 + :right 0 + :top 0 + :background-color colors/neutral-100-opa-70}) diff --git a/src/status_im2/contexts/shell/components/shell_screen/view.cljs b/src/status_im2/contexts/shell/components/shell_screen/view.cljs new file mode 100644 index 0000000000..41564fbc71 --- /dev/null +++ b/src/status_im2/contexts/shell/components/shell_screen/view.cljs @@ -0,0 +1,113 @@ +(ns status-im2.contexts.shell.components.shell-screen.view + (:require [utils.i18n :as i18n] + [quo2.core :as quo] + [quo2.foundations.colors :as colors] + [utils.re-frame :as rf] + [react-native.core :as rn] + [react-native.blur :as blur] + [react-native.linear-gradient :as linear-gradient] + [react-native.safe-area :as safe-area] + [status-im2.contexts.shell.state :as state] + [status-im2.contexts.shell.utils :as utils] + [status-im2.common.home.view :as common.home] + [status-im2.contexts.shell.constants :as shell.constants] + [status-im2.contexts.shell.components.shell-screen.style :as style] + [status-im2.contexts.shell.components.bottom-tabs.view :as bottom-tabs] + [status-im2.contexts.shell.components.switcher-cards.view :as switcher-cards])) + +(defn placeholder + [] + [linear-gradient/linear-gradient + {:colors [colors/neutral-100-opa-0 colors/neutral-100-opa-100] + :start {:x 0 :y 0} + :end {:x 0 :y 1} + :style (style/placeholder-container (safe-area/get-top))} + [rn/image + {:source nil ;; TODO(parvesh) - add placeholder image + :style style/placeholder-image}] + [quo/text + {:size :paragraph-1 + :weight :semi-bold + :style style/placeholder-title} + (i18n/label :t/shell-placeholder-title)] + [quo/text + {:size :paragraph-2 + :weight :regular + :align :center + :style style/placeholder-subtitle} + (i18n/label :t/shell-placeholder-subtitle)]]) + +(defn jump-to-text + [] + [quo/text + {:size :heading-1 + :weight :semi-bold + :style (style/jump-to-text (safe-area/get-top))} + (i18n/label :t/jump-to)]) + +(defn render-card + [{:keys [type screen-id] :as card}] + (let [card-data (cond + (= type shell.constants/one-to-one-chat-card) + (rf/sub [:shell/one-to-one-chat-card screen-id]) + + (= type shell.constants/private-group-chat-card) + (rf/sub [:shell/private-group-chat-card screen-id]) + + (= type shell.constants/community-card) + (rf/sub [:shell/community-card screen-id]) + + (= type shell.constants/community-channel-card) + (rf/sub [:shell/community-channel-card screen-id]) + + :else nil)] + [switcher-cards/card (merge card card-data)])) + +(def empty-cards (repeat 6 {:type shell.constants/empty-card})) + +(defn jump-to-list + [switcher-cards shell-margin] + (let [data (if (seq switcher-cards) switcher-cards empty-cards)] + [:<> + [rn/flat-list + {:data data + :render-fn render-card + :key-fn :id + :header (jump-to-text) + :ref #(reset! state/jump-to-list-ref %) + :num-columns 2 + :column-wrapper-style {:margin-horizontal shell-margin + :justify-content :space-between + :margin-bottom 16} + :style style/jump-to-list + :content-container-style {:padding-bottom (utils/bottom-tabs-container-height)}}] + (when-not (seq switcher-cards) + [placeholder])])) + +(defn top-nav-blur-overlay + [top] + (let [pass-through? (rf/sub [:shell/shell-pass-through?])] + [rn/view {:style (style/top-nav-blur-overlay-container (+ 56 top) pass-through?)} + (when pass-through? + [blur/view (bottom-tabs/blur-overlay-params style/top-nav-blur-overlay)])])) + +(defn view + [customization-color] + (let [switcher-cards (rf/sub [:shell/sorted-switcher-cards]) + width (rf/sub [:dimensions/window-width]) + top (safe-area/get-top) + shell-margin (/ (- width (* 2 shell.constants/switcher-card-size)) 3)] + [rn/view + {:style {:top 0 + :left 0 + :right 0 + :bottom -1 + :position :absolute + :background-color colors/neutral-100}} + [jump-to-list switcher-cards shell-margin] + [top-nav-blur-overlay top] + [common.home/top-nav + {:type :shell + :avatar {:customization-color customization-color} + :style {:margin-top top + :z-index 2}}]])) diff --git a/src/status_im2/contexts/shell/cards/style.cljs b/src/status_im2/contexts/shell/components/switcher_cards/style.cljs similarity index 97% rename from src/status_im2/contexts/shell/cards/style.cljs rename to src/status_im2/contexts/shell/components/switcher_cards/style.cljs index e28d8e35b9..d95f48558e 100644 --- a/src/status_im2/contexts/shell/cards/style.cljs +++ b/src/status_im2/contexts/shell/components/switcher_cards/style.cljs @@ -1,4 +1,4 @@ -(ns status-im2.contexts.shell.cards.style +(ns status-im2.contexts.shell.components.switcher-cards.style (:require [quo2.foundations.colors :as colors])) (def colors-map diff --git a/src/status_im2/contexts/shell/cards/view.cljs b/src/status_im2/contexts/shell/components/switcher_cards/view.cljs similarity index 71% rename from src/status_im2/contexts/shell/cards/view.cljs rename to src/status_im2/contexts/shell/components/switcher_cards/view.cljs index f4d2f86a9f..b9f726c6c5 100644 --- a/src/status_im2/contexts/shell/cards/view.cljs +++ b/src/status_im2/contexts/shell/components/switcher_cards/view.cljs @@ -1,13 +1,15 @@ -(ns status-im2.contexts.shell.cards.view +(ns status-im2.contexts.shell.components.switcher-cards.view (:require [clojure.string :as string] [utils.i18n :as i18n] [quo2.core :as quo] + [utils.re-frame :as rf] [quo2.foundations.colors :as colors] [react-native.core :as rn] [react-native.fast-image :as fast-image] [status-im2.constants :as constants] - [status-im2.contexts.shell.cards.style :as style] + [status-im2.contexts.shell.animation :as animation] [status-im2.contexts.shell.constants :as shell.constants] + [status-im2.contexts.shell.components.switcher-cards.style :as style] [status-im2.contexts.chat.messages.resolver.message-resolver :as resolver])) (defn content-container @@ -180,43 +182,76 @@ ""))) +(defn open-screen + [card-type id] + (cond + (#{shell.constants/one-to-one-chat-card + shell.constants/private-group-chat-card + shell.constants/community-channel-card} + card-type) + (rf/dispatch [:chat/navigate-to-chat id]) + + (= card-type shell.constants/community-card) + (rf/dispatch [:navigate-to :community-overview id]))) + +(defn calculate-card-position-and-open-screen + [card-ref card-type id] + (when @card-ref + (.measure + ^js + @card-ref + (fn [_ _ _ _ page-x page-y] + (animation/set-floating-screen-position + page-x + page-y + card-type) + (open-screen card-type id))))) + ;; Screens Card (defn screens-card - [{:keys [avatar-params title type customization-color - on-press on-close content banner]}] - (let [color-50 (colors/custom-color customization-color 50) - color-60 (colors/custom-color customization-color 60)] - [rn/touchable-without-feedback {:on-press on-press} - [rn/view {:style (style/base-container color-50)} - (when banner - [rn/image - {:source (:source banner) - :style {:width 160}}]) - [rn/view {:style style/secondary-container} - [quo/text - {:size :paragraph-1 - :weight :semi-bold - :number-of-lines 1 - :ellipsize-mode :tail - :style style/title} - title] - [quo/text - {:size :paragraph-2 - :weight :medium - :style style/subtitle} - (subtitle type content)] - [bottom-container type (merge {:color-50 color-50 :color-60 color-60} content)]] - (when avatar-params - [rn/view {:style style/avatar-container} - [avatar avatar-params type customization-color]]) - [quo/button - {:size 24 - :type :grey - :icon true - :on-press on-close - :override-theme :dark - :style style/close-button} - :i/close]]])) + [] + (let [card-ref (atom nil)] + (fn [{:keys [avatar-params title type customization-color + content banner id channel-id]}] + (let [color-50 (colors/custom-color customization-color 50) + color-60 (colors/custom-color customization-color 60)] + [rn/touchable-opacity + {:on-press #(calculate-card-position-and-open-screen + card-ref + type + (or channel-id id)) + :ref #(reset! card-ref %) + :active-opacity 1} + [rn/view {:style (style/base-container color-50)} + (when banner + [rn/image + {:source (:source banner) + :style {:width 160}}]) + [rn/view {:style style/secondary-container} + [quo/text + {:size :paragraph-1 + :weight :semi-bold + :number-of-lines 1 + :ellipsize-mode :tail + :style style/title} + title] + [quo/text + {:size :paragraph-2 + :weight :medium + :style style/subtitle} + (subtitle type content)] + [bottom-container type (merge {:color-50 color-50 :color-60 color-60} content)]] + (when avatar-params + [rn/view {:style style/avatar-container} + [avatar avatar-params type customization-color]]) + [quo/button + {:size 24 + :type :grey + :icon true + :on-press #(rf/dispatch [:shell/close-switcher-card id]) + :override-theme :dark + :style style/close-button} + :i/close]]])))) ;; browser Card (defn browser-card diff --git a/src/status_im2/contexts/shell/constants.cljs b/src/status_im2/contexts/shell/constants.cljs index e76b94304c..321443b7da 100644 --- a/src/status_im2/contexts/shell/constants.cljs +++ b/src/status_im2/contexts/shell/constants.cljs @@ -1,59 +1,42 @@ -(ns status-im2.contexts.shell.constants - (:require [react-native.platform :as platform] - [utils.re-frame :as rf] - [react-native.safe-area :as safe-area])) +(ns status-im2.contexts.shell.constants) -(def shell-animation-time 200) +(def ^:const shell-animation-time 200) +(def ^:const switcher-card-size 160) -(defn bottom-tabs-container-height - [] - (if platform/android? 57 82)) +;; Bottom tabs +(def ^:const bottom-tabs-container-height-android 57) +(def ^:const bottom-tabs-container-height-ios 82) +(def ^:const bottom-tabs-container-extended-height-android 90) +(def ^:const bottom-tabs-container-extended-height-ios 120) +(def ^:const bottom-tab-width 90) -(defn bottom-tabs-extended-container-height - [] - (if platform/android? 90 120)) +;; Stacks +(def ^:const stacks-ids [:communities-stack :chats-stack :wallet-stack :browser-stack]) -(defn status-bar-offset - [] - (if platform/android? (safe-area/get-top) 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 -;; More info - https://github.com/status-im/status-mobile/issues/14633 -(defn dimensions - [] - (let [{:keys [width height]} (rf/sub [:dimensions/window])] - {:width width - :height (if (> (status-bar-offset) 28) - (+ height (status-bar-offset)) - height)})) - -(def stacks-ids [:communities-stack :chats-stack :wallet-stack :browser-stack]) - -(def stacks-opacity-keywords +;; Keywords +(def ^:const stacks-opacity-keywords {:communities-stack :communities-stack-opacity :chats-stack :chats-stack-opacity :wallet-stack :wallet-stack-opacity :browser-stack :browser-stack-opacity}) -(def tabs-icon-color-keywords +(def ^:const 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 stacks-z-index-keywords +(def ^:const stacks-z-index-keywords {:communities-stack :communities-stack-z-index :chats-stack :chats-stack-z-index :wallet-stack :wallet-stack-z-index :browser-stack :browser-stack-z-index}) ;; Home stack states - (def ^:const close-with-animation 0) (def ^:const open-with-animation 1) -(def ^:const close-without-animation 3) -(def ^:const open-without-animation 4) +(def ^:const close-without-animation 2) +(def ^:const open-without-animation 3) ;; Switcher Cards (def ^:const empty-card 0) @@ -66,3 +49,15 @@ (def ^:const wallet-collectible 7) (def ^:const wallet-graph 8) (def ^:const communities-discover 9) + +;; Floating Screens +(def ^:const community-screen :community-overview) +(def ^:const chat-screen :chat) + +;; Floating Screen states +(def ^:const close-screen-with-slide-animation 0) +(def ^:const open-screen-with-slide-animation 1) +(def ^:const close-screen-with-shell-animation 2) +(def ^:const open-screen-with-shell-animation 3) +(def ^:const close-screen-without-animation 4) +(def ^:const open-screen-without-animation 5) diff --git a/src/status_im2/contexts/shell/events.cljs b/src/status_im2/contexts/shell/events.cljs index ae51f6baa3..72fd570a3b 100644 --- a/src/status_im2/contexts/shell/events.cljs +++ b/src/status_im2/contexts/shell/events.cljs @@ -3,42 +3,42 @@ [re-frame.core :as re-frame] [status-im.utils.core :as utils] [status-im2.constants :as constants] - [status-im2.navigation.events :as navigation] - [status-im.async-storage.core :as async-storage] + [status-im2.contexts.shell.state :as state] + [status-im2.contexts.shell.utils :as shell.utils] + [status-im2.navigation.state :as navigation.state] [status-im2.contexts.shell.animation :as animation] [status-im2.contexts.shell.constants :as shell.constants] [status-im.data-store.switcher-cards :as switcher-cards-store])) -;; Effects +;;;; Effects +;; Navigation (re-frame/reg-fx :shell/change-tab-fx (fn [stack-id] (when (some #(= stack-id %) shell.constants/stacks-ids) - (animation/bottom-tab-on-press stack-id)))) + (animation/bottom-tab-on-press stack-id false)))) (re-frame/reg-fx :shell/navigate-to-jump-to-fx (fn [] - (animation/close-home-stack false))) + (animation/close-home-stack false) + (some-> ^js @state/jump-to-list-ref + (.scrollToOffset #js {:y 0 :animated false})))) (re-frame/reg-fx - :shell/navigate-from-shell-fx - (fn [stack-id] - (animation/bottom-tab-on-press stack-id))) - -(re-frame/reg-fx - :shell/reset-bottom-tabs + :shell/pop-to-root-fx (fn [] - (let [selected-stack-id @animation/selected-stack-id] - (async-storage/set-item! :selected-stack-id nil) - (reset! animation/load-communities-stack? (= selected-stack-id :communities-stack)) - (reset! animation/load-chats-stack? (= selected-stack-id :chats-stack)) - (reset! animation/load-wallet-stack? (= selected-stack-id :wallet-stack)) - (reset! animation/load-browser-stack? (= selected-stack-id :browser-stack))))) + (reset! state/floating-screens-state {}))) -;; Events +(re-frame/reg-fx + :shell/reset-state + (fn [] + (reset! state/floating-screens-state {}))) +;;;; Events + +;; Switcher (rf/defn switcher-cards-loaded {:events [:shell/switcher-cards-loaded]} [{:keys [db]} loaded-switcher-cards] @@ -53,24 +53,21 @@ (let [chat (get-in db [:chats id])] (case (:chat-type chat) constants/one-to-one-chat-type - {:navigate-from :chats-stack - :card-id id + {:card-id id :switcher-card {:type shell.constants/one-to-one-chat-card :card-id id :clock now :screen-id id}} constants/private-group-chat-type - {:navigate-from :chats-stack - :card-id id + {:card-id id :switcher-card {:type shell.constants/private-group-chat-card :card-id id :clock now :screen-id id}} constants/community-chat-type - {:navigate-from :communities-stack - :card-id (:community-id chat) + {:card-id (:community-id chat) :switcher-card {:type shell.constants/community-channel-card :card-id (:community-id chat) :clock now @@ -79,8 +76,7 @@ nil)) :community-overview - {:navigate-from :communities-stack - :card-id id + {:card-id id :switcher-card {:type shell.constants/community-card :card-id id :clock now @@ -95,11 +91,10 @@ (when card-data (rf/merge cofx - {:db (assoc-in - db - [:shell/switcher-cards (:card-id card-data)] - switcher-card) - :shell/navigate-from-shell-fx (:navigate-from card-data)} + {:db (assoc-in + db + [:shell/switcher-cards (:card-id card-data)] + switcher-card)} (switcher-cards-store/upsert-switcher-card-rpc switcher-card))))) (rf/defn close-switcher-card @@ -110,13 +105,29 @@ {:db (update db :shell/switcher-cards dissoc card-id)} (switcher-cards-store/delete-switcher-card-rpc card-id))) +;; Navigation (rf/defn navigate-to-jump-to {:events [:shell/navigate-to-jump-to]} - [cofx] - (rf/merge - cofx - {:shell/navigate-to-jump-to-fx nil} - (navigation/pop-to-root :shell-stack))) + [{:keys [db]}] + (let [chat-screen-open? (shell.utils/floating-screen-open? shell.constants/chat-screen) + community-screen-open? (shell.utils/floating-screen-open? shell.constants/community-screen)] + {:db + (cond-> db + + chat-screen-open? + (assoc-in [:shell/floating-screens shell.constants/chat-screen :animation] + shell.constants/close-screen-with-shell-animation) + + (and chat-screen-open? community-screen-open?) + (assoc-in [:shell/floating-screens shell.constants/community-screen :animation] + shell.constants/close-screen-without-animation) + + (and (not chat-screen-open?) community-screen-open?) + (assoc-in [:shell/floating-screens shell.constants/community-screen :animation] + shell.constants/close-screen-with-shell-animation)) + + :dispatch [:set-view-id :shell] + :shell/navigate-to-jump-to-fx nil})) (rf/defn change-shell-status-bar-style {:events [:change-shell-status-bar-style]} @@ -127,3 +138,85 @@ {:events [:change-shell-nav-bar-color]} [_ color] {:merge-options {:id "shell-stack" :options {:navigationBar {:backgroundColor color}}}}) + +(rf/defn shell-navigate-to + {:events [:shell/navigate-to]} + [{:keys [db]} go-to-view-id screen-params animation hidden-screen?] + (if (shell.utils/shell-navigation? go-to-view-id) + (let [current-view-id (:view-id db) + community-id (get-in db [:chats screen-params :community-id])] + {:db (assoc-in + db + [:shell/floating-screens go-to-view-id] + {:id screen-params + :community-id community-id + :hidden-screen? hidden-screen? + :animation (or animation + (case current-view-id + :shell shell.constants/open-screen-with-shell-animation + :chat shell.constants/open-screen-without-animation + shell.constants/open-screen-with-slide-animation))}) + :dispatch-n (cond-> [] + (not hidden-screen?) + (conj [:set-view-id go-to-view-id]) + (and (= go-to-view-id shell.constants/community-screen) + (not hidden-screen?) + (:current-chat-id db)) + (conj [:chat/close]))}) + {:db (assoc db :view-id go-to-view-id) + :navigate-to go-to-view-id})) + +(rf/defn shell-navigate-back + {:events [:shell/navigate-back]} + [{:keys [db]}] + (let [chat-screen-open? (shell.utils/floating-screen-open? shell.constants/chat-screen) + community-screen-open? (shell.utils/floating-screen-open? shell.constants/community-screen) + current-chat-id (:current-chat-id db) + community-id (when current-chat-id + (get-in db [:chats current-chat-id :community-id]))] + (if (and (not @navigation.state/curr-modal) + (or chat-screen-open? community-screen-open?)) + {:db (assoc-in + db + [:shell/floating-screens + (if chat-screen-open? shell.constants/chat-screen shell.constants/community-screen) + :animation] + shell.constants/close-screen-with-slide-animation) + :dispatch-n (cond-> [[:set-view-id + (cond + (and chat-screen-open? community-screen-open?) + shell.constants/community-screen + community-screen-open? + :communities-stack + :else :chats-stack)]] + ;; When navigating back from community chat to community, update switcher card + (and chat-screen-open? community-screen-open? community-id) + (conj [:shell/add-switcher-card shell.constants/community-screen community-id]))} + {:navigate-back nil}))) + +(rf/defn floating-screen-opened + {:events [:shell/floating-screen-opened]} + [{:keys [db]} screen-id id community-id hidden-screen?] + (merge + {:db (assoc-in db [:shell/loaded-screens screen-id] true) + :shell/change-tab-fx (if (or (= screen-id shell.constants/community-screen) + community-id) + :communities-stack + :chats-stack)} + (when community-id + ;; When opening community chat, open community screen in background + {:dispatch [:shell/navigate-to shell.constants/community-screen + community-id shell.constants/open-screen-without-animation true]}) + ;; Only update switcher cards for top screen + (when-not hidden-screen? + {:dispatch-later [{:ms (* 2 shell.constants/shell-animation-time) + :dispatch [:shell/add-switcher-card screen-id id]}]}))) + +(rf/defn floating-screen-closed + {:events [:shell/floating-screen-closed]} + [{:keys [db]} screen-id] + (merge + {:db (-> (update db :shell/floating-screens dissoc screen-id) + (update :shell/loaded-screens dissoc screen-id))} + (when (= screen-id shell.constants/chat-screen) + {:dispatch [:chat/close]}))) diff --git a/src/status_im2/contexts/shell/home_stack.cljs b/src/status_im2/contexts/shell/home_stack.cljs deleted file mode 100644 index 3bc173901e..0000000000 --- a/src/status_im2/contexts/shell/home_stack.cljs +++ /dev/null @@ -1,59 +0,0 @@ -(ns status-im2.contexts.shell.home-stack - (:require [react-native.reanimated :as reanimated] - [status-im.ui.screens.wallet.accounts.views :as wallet.accounts] - [status-im2.contexts.chat.home.view :as chat] - [status-im2.contexts.communities.home.view :as communities] - [status-im2.contexts.shell.animation :as animation] - [status-im2.contexts.shell.constants :as shell.constants] - [status-im2.contexts.shell.style :as styles] - [status-im.ui.screens.browser.stack :as browser.stack])) - -(defn load-stack? - [stack-id] - (case stack-id - :communities-stack @animation/load-communities-stack? - :chats-stack @animation/load-chats-stack? - :browser-stack @animation/load-browser-stack? - :wallet-stack @animation/load-wallet-stack?)) - -(defn- f-stack-view - [stack-id shared-values] - [reanimated/view - {:style (reanimated/apply-animations-to-style - {:opacity (get shared-values - (get shell.constants/stacks-opacity-keywords stack-id)) - :z-index (get shared-values - (get shell.constants/stacks-z-index-keywords stack-id))} - {:position :absolute - :top 0 - :bottom 0 - :left 0 - :right 0 - :accessibility-label stack-id})} - (case stack-id - :communities-stack [communities/home] - :chats-stack [chat/home] - :wallet-stack [wallet.accounts/accounts-overview-old] - :browser-stack [browser.stack/browser-stack])]) - -(defn lazy-screen - [stack-id shared-values] - (when (load-stack? stack-id) - [:f> f-stack-view stack-id shared-values])) - -(defn f-home-stack - [] - (let [shared-values @animation/shared-values-atom - home-stack-original-style (styles/home-stack @animation/screen-height) - 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} - [lazy-screen :communities-stack shared-values] - [lazy-screen :chats-stack shared-values] - [lazy-screen :browser-stack shared-values] - [lazy-screen :wallet-stack shared-values]])) diff --git a/src/status_im2/contexts/shell/shared_values.cljs b/src/status_im2/contexts/shell/shared_values.cljs new file mode 100644 index 0000000000..756ff11a31 --- /dev/null +++ b/src/status_im2/contexts/shell/shared_values.cljs @@ -0,0 +1,127 @@ +(ns status-im2.contexts.shell.shared-values + (:require [quo2.foundations.colors :as colors] + [react-native.safe-area :as safe-area] + [react-native.reanimated :as reanimated] + [utils.worklets.shell :as worklets.shell] + [status-im2.contexts.shell.utils :as utils] + [status-im2.contexts.shell.state :as state] + [status-im2.contexts.shell.constants :as shell.constants])) + +(defn calculate-home-stack-position + [{:keys [width height]}] + (let [bottom-nav-tab-width shell.constants/bottom-tab-width + 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 (utils/bottom-tabs-container-height)))] + {:left (reduce + (fn [acc stack-id] + (assoc acc + stack-id + (+ (- left-margin left-empty-space) + (* (.indexOf shell.constants/stacks-ids stack-id) + bottom-nav-tab-width)))) + {:none 0} + shell.constants/stacks-ids) + :top (+ top-empty-space (utils/bottom-tabs-container-height)) + :scale minimize-scale})) + +(defn stacks-and-bottom-tabs-derived-values + [{:keys [selected-stack-id home-stack-state]}] + (let [pass-through (reanimated/use-shared-value false)] + (reduce + (fn [acc id] + (let [tabs-icon-color-keyword (get shell.constants/tabs-icon-color-keywords id) + stack-opacity-keyword (get shell.constants/stacks-opacity-keywords id) + stack-z-index-keyword (get shell.constants/stacks-z-index-keywords id)] + (assoc + acc + stack-opacity-keyword + (worklets.shell/stack-opacity (name id) selected-stack-id) + stack-z-index-keyword + (worklets.shell/stack-z-index (name id) selected-stack-id) + tabs-icon-color-keyword + (worklets.shell/bottom-tab-icon-color + (name id) + selected-stack-id + home-stack-state + pass-through + colors/white + colors/neutral-50 + colors/white-opa-40)))) + {:bottom-tabs-height (worklets.shell/bottom-tabs-height + home-stack-state + (utils/bottom-tabs-container-height) + (utils/bottom-tabs-extended-container-height)) + :pass-through pass-through} + shell.constants/stacks-ids))) + +(defn home-stack-derived-values + [{:keys [selected-stack-id home-stack-state]} dimensions] + (let [home-stack-position (calculate-home-stack-position dimensions) + animate-home-stack-left (reanimated/use-shared-value (not (utils/home-stack-open?)))] + {:animate-home-stack-left animate-home-stack-left + :home-stack-left (worklets.shell/home-stack-left + selected-stack-id + animate-home-stack-left + home-stack-state + (clj->js (:left home-stack-position))) + :home-stack-top (worklets.shell/home-stack-top + home-stack-state + (:top home-stack-position)) + :home-stack-opacity (worklets.shell/home-stack-opacity home-stack-state) + :home-stack-pointer (worklets.shell/home-stack-pointer home-stack-state) + :home-stack-scale (worklets.shell/home-stack-scale + home-stack-state + (:scale home-stack-position))})) + +(defn floating-screen-derived-values + [screen-id {:keys [width height]} switcher-card-left-position switcher-card-top-position] + (let [screen-state (reanimated/use-shared-value + (if (utils/floating-screen-open? screen-id) + shell.constants/open-screen-without-animation + shell.constants/close-screen-without-animation))] + {:screen-state screen-state + :screen-left (worklets.shell/floating-screen-left screen-state width switcher-card-left-position) + :screen-top (worklets.shell/floating-screen-top screen-state switcher-card-top-position) + :screen-z-index (worklets.shell/floating-screen-z-index screen-state) + :screen-width (worklets.shell/floating-screen-width screen-state + width + shell.constants/switcher-card-size) + :screen-height (worklets.shell/floating-screen-height screen-state + height + shell.constants/switcher-card-size)})) + +(defn calculate-and-set-shared-values + [] + (let [{:keys [width] :as dimensions} (utils/dimensions) + switcher-card-left-position (/ (- width (* 2 shell.constants/switcher-card-size)) 3) + switcher-card-top-position (+ (safe-area/get-top) 120) + shared-values + {:selected-stack-id (reanimated/use-shared-value + (name (or @state/selected-stack-id :communities-stack))) + :home-stack-state (reanimated/use-shared-value @state/home-stack-state)}] + ;; Whenever shell stack is created, calculate shared values function is called + ;; Means On login and on UI reloading (like changing theme) + ;; So we are also resetting bottom tabs here (disabling loading of unselected tabs), + ;; for Speed up UI reloading + (utils/reset-bottom-tabs) + (reset! + state/shared-values-atom + (merge + shared-values + (stacks-and-bottom-tabs-derived-values shared-values) + (home-stack-derived-values shared-values dimensions) + {shell.constants/community-screen (floating-screen-derived-values + shell.constants/community-screen + dimensions + switcher-card-left-position + switcher-card-top-position) + shell.constants/chat-screen (floating-screen-derived-values + shell.constants/chat-screen + dimensions + switcher-card-left-position + switcher-card-top-position)})) + @state/shared-values-atom)) diff --git a/src/status_im2/contexts/shell/state.cljs b/src/status_im2/contexts/shell/state.cljs new file mode 100644 index 0000000000..73625d8fc3 --- /dev/null +++ b/src/status_im2/contexts/shell/state.cljs @@ -0,0 +1,18 @@ +(ns status-im2.contexts.shell.state + (:require [reagent.core :as reagent] + [status-im2.contexts.shell.constants :as shell.constants])) + +;; Atoms +(def selected-stack-id (atom nil)) +(def screen-height (atom nil)) +(def shared-values-atom (atom nil)) +(def jump-to-list-ref (atom nil)) + +(def home-stack-state (atom shell.constants/close-with-animation)) +(def floating-screens-state (atom {})) + +;; Reagent atoms used for lazily loading home screen tabs +(def load-communities-stack? (reagent/atom false)) +(def load-chats-stack? (reagent/atom false)) +(def load-wallet-stack? (reagent/atom false)) +(def load-browser-stack? (reagent/atom false)) diff --git a/src/status_im2/contexts/shell/style.cljs b/src/status_im2/contexts/shell/style.cljs deleted file mode 100644 index b36d9d5137..0000000000 --- a/src/status_im2/contexts/shell/style.cljs +++ /dev/null @@ -1,108 +0,0 @@ -(ns status-im2.contexts.shell.style - (:require [quo2.foundations.colors :as colors] - [react-native.platform :as platform] - [status-im2.contexts.shell.constants :as shell.constants])) - -;; Bottom Tabs -(defn bottom-tabs-container - [pass-through?] - {:background-color (if pass-through? :transparent colors/neutral-100) - :flex 1 - :align-items :center - :height (shell.constants/bottom-tabs-container-height) - :position :absolute - :bottom 0 - :right 0 - :left 0 - :overflow :hidden - :accessibility-label :bottom-tabs-container}) - -(defn bottom-tabs - [] - {:flex-direction :row - :position :absolute - :bottom (if platform/android? 8 34) - :flex 1 - :accessibility-label :bottom-tabs}) - -(def bottom-tabs-blur-overlay - {:position :absolute - :left 0 - :right 0 - :bottom 0 - :height (shell.constants/bottom-tabs-extended-container-height) - :background-color colors/neutral-100-opa-70}) - -;; Home Stack -(defn home-stack - [screen-height] - (let [{:keys [width height]} (shell.constants/dimensions) - height (or screen-height height)] - {:border-bottom-left-radius 20 - :border-bottom-right-radius 20 - :background-color (colors/theme-colors colors/white colors/neutral-95) - :overflow :hidden - :position :absolute - :width width - :height (- height (shell.constants/bottom-tabs-container-height))})) - -;; Placeholder -(defn placeholder-container - [status-bar-height] - {:position :absolute - :top (+ 112 status-bar-height) - :left 0 - :right 0 - :bottom (shell.constants/bottom-tabs-container-height) - :align-items :center - :accessibility-label :shell-placeholder-view}) - -(def placeholder-image - {:margin-top 186 - :width 120 - :height 120 - ;; Code to remove once placeholder image/vector will be available - :border-width 5 - :border-radius 10 - :border-color :red}) - -(def placeholder-title - {:margin-top 20 - :color colors/white}) - -(def placeholder-subtitle - {:margin-top 4 - :color colors/white}) - -;; Shell -(defn jump-to-text - [status-bar-height] - {:color colors/white - :margin-top (+ 68 status-bar-height) - :margin-bottom 20 - :margin-left 20}) - -(def jump-to-list - {:top 0 - :left 0 - :right 0 - :bottom 0 - :position :absolute}) - -(defn top-nav-blur-overlay-container - [height pass-through?] - {:height height - :position :absolute - :left 0 - :top 0 - :right 0 - :overflow :hidden - :background-color (if pass-through? :transparent colors/neutral-100)}) - -(def top-nav-blur-overlay - {:height 100 - :position :absolute - :left 0 - :right 0 - :top 0 - :background-color colors/neutral-100-opa-70}) diff --git a/src/status_im2/contexts/shell/utils.cljs b/src/status_im2/contexts/shell/utils.cljs new file mode 100644 index 0000000000..513a6278d3 --- /dev/null +++ b/src/status_im2/contexts/shell/utils.cljs @@ -0,0 +1,120 @@ +(ns status-im2.contexts.shell.utils + (:require [utils.re-frame :as rf] + [react-native.core :as rn] + [quo2.foundations.colors :as colors] + [react-native.platform :as platform] + [react-native.safe-area :as safe-area] + [status-im2.contexts.shell.state :as state] + [status-im.async-storage.core :as async-storage] + [status-im2.contexts.shell.constants :as shell.constants])) + +;;;; Helper Functions + +;;; UI +(defn bottom-tabs-container-height + [] + (if platform/android? + shell.constants/bottom-tabs-container-height-android + shell.constants/bottom-tabs-container-height-ios)) + +(defn bottom-tabs-extended-container-height + [] + (if platform/android? + shell.constants/bottom-tabs-container-extended-height-android + shell.constants/bottom-tabs-container-extended-height-ios)) + +(defn status-bar-offset + [] + (if platform/android? (safe-area/get-top) 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 +;; More info - https://github.com/status-im/status-mobile/issues/14633 +(defn dimensions + [] + (let [{:keys [width height]} (rn/get-window)] + {:width width + :height (or @state/screen-height + (if (> (status-bar-offset) 28) + (+ height (status-bar-offset)) + height))})) + +;;;; State + +;;; Home Stack +(defn home-stack-open? + [] + (let [state @state/home-stack-state] + (or (= state shell.constants/open-with-animation) + (= state shell.constants/open-without-animation)))) + +(defn calculate-home-stack-state-value + [stack-id animate?] + (if animate? + (if (some? stack-id) + shell.constants/open-with-animation + shell.constants/close-with-animation) + (if (some? stack-id) + shell.constants/open-without-animation + shell.constants/close-without-animation))) + +(defn load-stack + [stack-id] + (case stack-id + :communities-stack (reset! state/load-communities-stack? true) + :chats-stack (reset! state/load-chats-stack? true) + :wallet-stack (reset! state/load-wallet-stack? true) + :browser-stack (reset! state/load-browser-stack? true) + "")) + +(defn change-selected-stack-id + [stack-id store? home-stack-state-value] + (let [home-stack-state-value (or home-stack-state-value + (calculate-home-stack-state-value stack-id nil))] + (reset! state/selected-stack-id stack-id) + (reset! state/home-stack-state home-stack-state-value) + (when store? + (async-storage/set-item! :selected-stack-id stack-id)))) + +(defn reset-bottom-tabs + [] + (let [selected-stack-id @state/selected-stack-id] + (reset! state/load-communities-stack? (= selected-stack-id :communities-stack)) + (reset! state/load-chats-stack? (= selected-stack-id :chats-stack)) + (reset! state/load-wallet-stack? (= selected-stack-id :wallet-stack)) + (reset! state/load-browser-stack? (= selected-stack-id :browser-stack)))) + +;;; Floating screen +(defn floating-screen-open? + [screen-id] + (let [state (get @state/floating-screens-state screen-id)] + (or (= state shell.constants/open-screen-with-slide-animation) + (= state shell.constants/open-screen-with-shell-animation) + (= state shell.constants/open-screen-without-animation)))) + +;;; Navigation +(defn shell-navigation? + [view-id] + (#{:chat :community-overview} view-id)) + +(defn calculate-view-id + [] + (cond + (floating-screen-open? shell.constants/chat-screen) + shell.constants/chat-screen + (floating-screen-open? shell.constants/community-screen) + shell.constants/community-screen + :else (or @state/selected-stack-id :shell))) + +(defn update-view-id + [view-id] + (rf/dispatch [:set-view-id view-id])) + +;;; Misc +(defn change-shell-status-bar-style + [] + (rf/dispatch [:change-shell-status-bar-style + (if (or (colors/dark?) + (not (home-stack-open?))) + :light + :dark)])) diff --git a/src/status_im2/contexts/shell/view.cljs b/src/status_im2/contexts/shell/view.cljs index 774f49c18b..96d1a6ff54 100644 --- a/src/status_im2/contexts/shell/view.cljs +++ b/src/status_im2/contexts/shell/view.cljs @@ -1,136 +1,53 @@ (ns status-im2.contexts.shell.view - (:require [utils.i18n :as i18n] - [quo2.core :as quo] - [quo2.foundations.colors :as colors] + (:require [quo2.core :as quo] + [utils.i18n :as i18n] + [utils.re-frame :as rf] [react-native.core :as rn] - [react-native.blur :as blur] - [react-native.linear-gradient :as linear-gradient] - [react-native.safe-area :as safe-area] - [status-im2.common.home.view :as common.home] + [status-im2.contexts.shell.utils :as utils] + [status-im2.navigation.state :as navigation.state] [status-im2.contexts.shell.animation :as animation] - [status-im2.contexts.shell.bottom-tabs :as bottom-tabs] - [status-im2.contexts.shell.cards.view :as switcher-cards] [status-im2.contexts.shell.constants :as shell.constants] - [status-im2.contexts.shell.home-stack :as home-stack] - [status-im2.contexts.shell.style :as style] - [utils.re-frame :as rf])) + [status-im2.contexts.shell.shared-values :as shared-values] + [status-im2.contexts.shell.components.home-stack.view :as home-stack] + [status-im2.contexts.shell.components.bottom-tabs.view :as bottom-tabs] + [status-im2.contexts.shell.components.shell-screen.view :as shell-screen] + [status-im2.contexts.shell.components.floating-screens.view :as floating-screens])) -(defn placeholder +(defn navigate-back-handler [] - [linear-gradient/linear-gradient - {:colors [colors/neutral-100-opa-0 colors/neutral-100-opa-100] - :start {:x 0 :y 0} - :end {:x 0 :y 1} - :style (style/placeholder-container (safe-area/get-top))} - [rn/image - {:source nil ;; TODO(parvesh) - add placeholder image - :style style/placeholder-image}] - [quo/text - {:size :paragraph-1 - :weight :semi-bold - :style style/placeholder-title} - (i18n/label :t/shell-placeholder-title)] - [quo/text - {:size :paragraph-2 - :weight :regular - :align :center - :style style/placeholder-subtitle} - (i18n/label :t/shell-placeholder-subtitle)]]) - -(defn jump-to-text - [] - [quo/text - {:size :heading-1 - :weight :semi-bold - :style (style/jump-to-text (safe-area/get-top))} - (i18n/label :t/jump-to)]) - -(defn render-card - [{:keys [type screen-id] :as card}] - (let [card-data (case type - shell.constants/one-to-one-chat-card - (rf/sub [:shell/one-to-one-chat-card screen-id]) - - shell.constants/private-group-chat-card - (rf/sub [:shell/private-group-chat-card screen-id]) - - shell.constants/community-card - (rf/sub [:shell/community-card screen-id]) - - shell.constants/community-channel-card - (rf/sub [:shell/community-channel-card screen-id]) - - nil)] - [switcher-cards/card (merge card card-data)])) - -(def empty-cards (repeat 6 {:type shell.constants/empty-card})) - -(defn jump-to-list - [switcher-cards shell-margin] - (let [data (if (seq switcher-cards) switcher-cards empty-cards)] - [:<> - [rn/flat-list - {:data data - :render-fn render-card - :key-fn :id - :header (jump-to-text) - :num-columns 2 - :column-wrapper-style {:margin-horizontal shell-margin - :justify-content :space-between - :margin-bottom 16} - :style style/jump-to-list - :content-container-style {:padding-bottom (shell.constants/bottom-tabs-container-height)}}] - (when-not (seq switcher-cards) - [placeholder])])) - -(defn top-nav-blur-overlay - [top] - (let [pass-through? (rf/sub [:shell/shell-pass-through?])] - [rn/view {:style (style/top-nav-blur-overlay-container (+ 56 top) pass-through?)} - (when pass-through? - [blur/view (bottom-tabs/blur-overlay-params style/top-nav-blur-overlay)])])) - -(defn shell - [customization-color] - (let [switcher-cards (rf/sub [:shell/sorted-switcher-cards]) - width (rf/sub [:dimensions/window-width]) - top (safe-area/get-top) - shell-margin (/ (- width 320) 3)] ;; 320 - two cards width - [rn/view - {:style {:top 0 - :left 0 - :right 0 - :bottom -1 - :position :absolute - :background-color colors/neutral-100}} - [jump-to-list switcher-cards shell-margin] - [top-nav-blur-overlay top] - [common.home/top-nav - {:type :shell - :avatar {:customization-color customization-color} - :style {:margin-top top - :z-index 2}}]])) + (if (and (not @navigation.state/curr-modal) + (or + (utils/floating-screen-open? shell.constants/community-screen) + (utils/floating-screen-open? shell.constants/chat-screen))) + (do (rf/dispatch [:navigate-back]) + true) + false)) (defn f-shell-stack [] - (let [shared-values (animation/calculate-shared-values) + (let [shared-values (shared-values/calculate-and-set-shared-values) {:keys [key-uid]} (rf/sub [:multiaccount]) profile-color (:color (rf/sub [:onboarding-2/profile])) - customization-color (if profile-color - profile-color + customization-color (if profile-color ;; Todo - 1. Use single sub for customization color + profile-color ;; Todo - 2. Move sub to child view (rf/sub [:profile/customization-color key-uid]))] - [rn/view - {:style {:flex 1}} - [shell customization-color] - [bottom-tabs/bottom-tabs] + (rn/use-effect + (fn [] + (rn/hw-back-add-listener navigate-back-handler) + #(rn/hw-back-remove-listener navigate-back-handler)) + []) + [:<> + [shell-screen/view customization-color] + [:f> bottom-tabs/f-bottom-tabs] [:f> home-stack/f-home-stack] [quo/floating-shell-button {:jump-to {:on-press #(animation/close-home-stack true) :label (i18n/label :t/jump-to) :customization-color customization-color}} {:position :absolute - :bottom (+ (shell.constants/bottom-tabs-container-height) 12)} - (:home-stack-opacity shared-values)]])) + :bottom (+ (utils/bottom-tabs-container-height) 12)} + (:home-stack-opacity shared-values)] + [floating-screens/view]])) (defn shell-stack [] diff --git a/src/status_im2/core.cljs b/src/status_im2/core.cljs index 8a2315aa9c..864f061013 100644 --- a/src/status_im2/core.cljs +++ b/src/status_im2/core.cljs @@ -9,7 +9,8 @@ [react-native.platform :as platform] [react-native.shake :as react-native-shake] [reagent.impl.batching :as batching] - [status-im2.contexts.shell.animation :as animation] + [status-im2.contexts.shell.utils :as shell.utils] + [status-im2.contexts.shell.state :as shell.state] [status-im2.config :as config] [status-im2.setup.dev :as dev] [status-im2.setup.global-error :as global-error] @@ -44,10 +45,9 @@ (react-native-shake/add-shake-listener #(re-frame/dispatch [:shake-event])) (utils.universal-links/initialize) - ;; TODO(parvesh) - Remove while moving functionality to status-go - (async-storage/get-item :selected-stack-id #(animation/change-selected-stack-id %)) - - (async-storage/get-item :screen-height #(reset! animation/screen-height %)) + ;; Shell + (async-storage/get-item :selected-stack-id #(shell.utils/change-selected-stack-id % nil nil)) + (async-storage/get-item :screen-height #(reset! shell.state/screen-height %)) (dev/setup) diff --git a/src/status_im2/navigation/core.cljs b/src/status_im2/navigation/core.cljs index d0378f111c..a7b363a4bf 100644 --- a/src/status_im2/navigation/core.cljs +++ b/src/status_im2/navigation/core.cljs @@ -4,7 +4,6 @@ [react-native.gesture :as gesture] [react-native.navigation :as navigation] [status-im.multiaccounts.login.core :as login-core] - [status-im2.contexts.shell.animation :as shell.animation] [status-im2.navigation.roots :as roots] [status-im2.navigation.state :as state] [status-im2.navigation.view :as views] @@ -36,14 +35,16 @@ (defn set-view-id [view-id] - (when-let [{:keys [on-focus]} (get views/screens view-id)] - (re-frame/dispatch [:screens/on-will-focus view-id]) - (re-frame/dispatch [:set-view-id - (if (= view-id :shell-stack) - (or @shell.animation/selected-stack-id :shell) - view-id)]) - (when on-focus - (re-frame/dispatch on-focus)))) + (when (get views/screens view-id) + (re-frame/dispatch [:set-view-id view-id]))) + +(re-frame/reg-fx + :set-view-id-fx + (fn [view-id] + (re-frame/dispatch [:screens/on-will-focus view-id]) + (when-let [{:keys [on-focus]} (get views/screens view-id)] + (when on-focus + (re-frame/dispatch on-focus))))) (navigation/reg-component-did-appear-listener (fn [view-id] @@ -106,7 +107,12 @@ (dissmissModal) (navigation/pop (name @state/root-id))))) -(re-frame/reg-fx :pop-to-root-fx navigation/pop-to-root) +(defn pop-to-root + [root-id] + (navigation/pop-to-root root-id) + (dismiss-all-modals)) + +(re-frame/reg-fx :pop-to-root-fx pop-to-root) ;; MODAL (defn open-modal diff --git a/src/status_im2/navigation/events.cljs b/src/status_im2/navigation/events.cljs index cd9bdcd2ee..2d9aa05df6 100644 --- a/src/status_im2/navigation/events.cljs +++ b/src/status_im2/navigation/events.cljs @@ -1,5 +1,7 @@ (ns status-im2.navigation.events - (:require [utils.re-frame :as rf])) + (:require [utils.re-frame :as rf] + [status-im2.contexts.shell.utils :as shell.utils] + [status-im2.contexts.shell.events :as shell.events])) (defn- all-screens-params [db view screen-params] @@ -12,17 +14,12 @@ (rf/defn navigate-to {:events [:navigate-to]} - [{:keys [db]} go-to-view-id screen-params] - (merge - {:db (-> (assoc db :view-id go-to-view-id) - (all-screens-params go-to-view-id screen-params)) - :navigate-to go-to-view-id - :dispatch [:hide-bottom-sheet]} - (when (#{:chat :community-overview} go-to-view-id) - {:dispatch-later - ;; 300 ms delay because, navigation is priority over shell card update - [{:dispatch [:shell/add-switcher-card go-to-view-id screen-params] - :ms 300}]}))) + [{:keys [db] :as cofx} go-to-view-id screen-params] + (rf/merge + cofx + {:db (all-screens-params db go-to-view-id screen-params) + :dispatch-n [[:hide-bottom-sheet]]} + (shell.events/shell-navigate-to go-to-view-id screen-params nil nil))) (rf/defn open-modal {:events [:open-modal]} @@ -34,13 +31,15 @@ (rf/defn navigate-back {:events [:navigate-back]} - [_] - {:navigate-back nil}) + [cofx] + (shell.events/shell-navigate-back cofx)) (rf/defn pop-to-root {:events [:pop-to-root]} - [_ tab] - {:pop-to-root-fx tab}) + [{:keys [db]} tab] + {:pop-to-root-fx tab + :db (dissoc db :shell/floating-screens) + :shell/pop-to-root-fx nil}) (rf/defn init-root {:events [:init-root]} @@ -54,8 +53,9 @@ (rf/defn change-tab {:events [:navigate-change-tab]} - [_ stack-id] - {:shell/change-tab-fx stack-id}) + [{:keys [db]} stack-id] + {:db (assoc db :view-id stack-id) + :shell/change-tab-fx stack-id}) (rf/defn navigate-replace {:events [:navigate-replace]} @@ -154,3 +154,10 @@ [:hide-select-acc-sheet] [:bottom-sheet/hide-old-navigation-overlay] [:toasts/close-all-toasts]]}) + +(rf/defn set-view-id + {:events [:set-view-id]} + [{:keys [db]} view-id] + (let [view-id (if (= view-id :shell-stack) (shell.utils/calculate-view-id) view-id)] + {:db (assoc db :view-id view-id) + :set-view-id-fx view-id})) diff --git a/src/status_im2/subs/root.cljs b/src/status_im2/subs/root.cljs index 07bca624fb..8140239a31 100644 --- a/src/status_im2/subs/root.cljs +++ b/src/status_im2/subs/root.cljs @@ -75,6 +75,8 @@ (reg-root-key-sub :visibility-status-updates :visibility-status-updates) (reg-root-key-sub :shell/switcher-cards :shell/switcher-cards) (reg-root-key-sub :password-authentication :password-authentication) +(reg-root-key-sub :shell/floating-screens :shell/floating-screens) +(reg-root-key-sub :shell/loaded-screens :shell/loaded-screens) ;;NOTE this one is not related to ethereum network ;; it is about cellular network/ wifi network diff --git a/src/status_im2/subs/shell.cljs b/src/status_im2/subs/shell.cljs index 52b9b7e41b..1b115531df 100644 --- a/src/status_im2/subs/shell.cljs +++ b/src/status_im2/subs/shell.cljs @@ -2,8 +2,11 @@ (:require [re-frame.core :as re-frame] [utils.datetime :as datetime] [status-im2.constants :as constants] - [status-im2.common.resources :as resources])) + [react-native.platform :as platform] + [status-im2.common.resources :as resources] + [status-im2.contexts.shell.constants :as shell.constants])) +;; Helper Functions (defn community-avatar [community] (let [images (:images community)] @@ -66,18 +69,16 @@ :profile-picture (when profile-picture (str profile-picture "&addRing=0"))} :customization-color (or (:customization-color contact) :primary) - :on-close #(re-frame/dispatch [:shell/close-switcher-card id]) - :on-press #(re-frame/dispatch [:chat/navigate-to-chat id]) - :content (get-card-content chat communities)})) + :content (get-card-content chat communities) + :id id})) (defn private-group-chat-card [chat id communities] {:title (:chat-name chat) :avatar-params {} :customization-color (or (:customization-color chat) :primary) - :on-close #(re-frame/dispatch [:shell/close-switcher-card id]) - :on-press #(re-frame/dispatch [:chat/navigate-to-chat id]) - :content (get-card-content chat communities)}) + :content (get-card-content chat communities) + :id id}) (defn community-card [community id] @@ -87,22 +88,18 @@ {:source profile-picture} {:name (:name community)}) :customization-color (or (:customization-color community) :primary) - :on-close #(re-frame/dispatch [:shell/close-switcher-card id]) - :on-press #(re-frame/dispatch [:navigate-to :community-overview id]) - :content {:community-info {:type :permission}}})) + :content {:community-info {:type :permission}} + :id id})) (defn community-channel-card [community community-id channel channel-id] (merge (community-card community community-id) - {:content {:community-channel {:emoji (:emoji channel) - :channel-name (str "# " (:name channel))}} - :on-press (fn [] - (re-frame/dispatch [:navigate-to :community-overview community-id]) - (js/setTimeout - #(re-frame/dispatch [:chat/navigate-to-chat channel-id]) - 100))})) + {:content {:community-channel {:emoji (:emoji channel) + :channel-name (str "# " (:name channel))}} + :channel-id channel-id})) +;;;; Subscriptions (def memo-shell-cards (atom nil)) (re-frame/reg-sub @@ -122,6 +119,7 @@ (fn [stacks] (> (count stacks) 6))) +;; Switcher Cards (re-frame/reg-sub :shell/one-to-one-chat-card (fn [[_ id] _] @@ -157,6 +155,7 @@ community (get communities (:community-id channel))] (community-channel-card community community-id channel channel-id)))) +;; Bottom tabs (re-frame/reg-sub :shell/bottom-tabs-notifications-data :<- [:chats/chats] @@ -191,3 +190,19 @@ {:new-notifications? (pos? (:unviewed-messages-count chats-stack)) :notification-indicator (if (pos? (:unviewed-mentions-count chats-stack)) :counter :unread-dot) :counter-label (:unviewed-mentions-count chats-stack)}}))) + +;; Floating screens +(re-frame/reg-sub + :shell/floating-screen + :<- [:shell/floating-screens] + (fn [screens [_ screen-id]] + (get screens screen-id))) + +(re-frame/reg-sub + :shell/animation-complete? + :<- [:shell/loaded-screens] + (fn [screens [_ chat-type]] + (or platform/ios? + (cond-> (get screens shell.constants/chat-screen) + (= chat-type constants/community-chat-type) + (and (get screens shell.constants/community-screen)))))) diff --git a/src/utils/worklets/shell.cljs b/src/utils/worklets/shell.cljs index c881d4b8d9..c21b2036ab 100644 --- a/src/utils/worklets/shell.cljs +++ b/src/utils/worklets/shell.cljs @@ -1,23 +1,21 @@ (ns utils.worklets.shell) -(def shell-worklets (js/require "../src/js/worklets/shell.js")) - -(defn stack-opacity - [id selected-stack-id] - (.stackOpacity ^js shell-worklets id selected-stack-id)) - -(defn stack-z-index - [id selected-stack-id] - (.stackZIndex ^js shell-worklets id selected-stack-id)) +(def bottom-tabs-worklets (js/require "../src/js/worklets/shell/bottom_tabs.js")) +(def home-stack-worklets (js/require "../src/js/worklets/shell/home_stack.js")) +(def floating-screen-worklets (js/require "../src/js/worklets/shell/floating_screen.js")) +;; Derived values for Bottom tabs (defn bottom-tabs-height [home-stack-state-sv container-height extended-container-height] - (.bottomTabsHeight ^js shell-worklets home-stack-state-sv container-height extended-container-height)) + (.bottomTabsHeight ^js bottom-tabs-worklets + home-stack-state-sv + container-height + extended-container-height)) (defn bottom-tab-icon-color [id selected-stack-id-sv home-stack-state-sv pass-through-sv selected-tab-color default-color pass-through-color] - (.bottomTabIconColor ^js shell-worklets + (.bottomTabIconColor ^js bottom-tabs-worklets id selected-stack-id-sv home-stack-state-sv @@ -26,22 +24,32 @@ default-color pass-through-color)) +;; Derived values for stacks (communities, chat, wallet, browser) +(defn stack-opacity + [id selected-stack-id] + (.stackOpacity ^js home-stack-worklets id selected-stack-id)) + +(defn stack-z-index + [id selected-stack-id] + (.stackZIndex ^js home-stack-worklets id selected-stack-id)) + +;; Derived values for Home stack (container) (defn home-stack-opacity [home-stack-state-sv] - (.homeStackOpacity ^js shell-worklets home-stack-state-sv)) + (.homeStackOpacity ^js home-stack-worklets home-stack-state-sv)) (defn home-stack-pointer [home-stack-state-sv] - (.homeStackPointer ^js shell-worklets home-stack-state-sv)) + (.homeStackPointer ^js home-stack-worklets home-stack-state-sv)) (defn home-stack-scale [home-stack-state-sv scale] - (.homeStackScale ^js shell-worklets home-stack-state-sv scale)) + (.homeStackScale ^js home-stack-worklets home-stack-state-sv scale)) (defn home-stack-left [selected-stack-id-sv animate-home-stack-left home-stack-state-sv left-home-stack-position] (.homeStackLeft - ^js shell-worklets + ^js home-stack-worklets selected-stack-id-sv animate-home-stack-left home-stack-state-sv @@ -49,4 +57,25 @@ (defn home-stack-top [home-stack-state-sv top-home-stack-position] - (.homeStackTop ^js shell-worklets home-stack-state-sv top-home-stack-position)) + (.homeStackTop ^js home-stack-worklets home-stack-state-sv top-home-stack-position)) + +;; Derived values for floating screen +(defn floating-screen-left + [screen-state screen-width switcher-card-left-position] + (.screenLeft ^js floating-screen-worklets screen-state screen-width switcher-card-left-position)) + +(defn floating-screen-top + [screen-state switcher-card-top-position] + (.screenTop ^js floating-screen-worklets screen-state switcher-card-top-position)) + +(defn floating-screen-width + [screen-state screen-width switcher-card-size] + (.screenWidth ^js floating-screen-worklets screen-state screen-width switcher-card-size)) + +(defn floating-screen-height + [screen-state screen-height switcher-card-size] + (.screenHeight ^js floating-screen-worklets screen-state screen-height switcher-card-size)) + +(defn floating-screen-z-index + [screen-state] + (.screenZIndex ^js floating-screen-worklets screen-state))