From 7ad378e9c835915b71d5f1d97c512d3cc147876c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Manuel=20C=C3=A1rdenas?= <90291778+ulisesmac@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:29:44 -0600 Subject: [PATCH] [#16709] chat home banner animation (#16823) * Small typo fix * Move header-spacing & empty-state to common.home namespace * Create common animated banner component for chats and communities * Add animated banner to chats tab --- src/react_native/section_list.cljs | 2 +- src/status_im2/common/home/banner/style.cljs | 63 ++++++ src/status_im2/common/home/banner/view.cljs | 78 ++++++++ src/status_im2/common/home/style.cljs | 16 +- src/status_im2/common/home/view.cljs | 21 +- src/status_im2/contexts/chat/home/style.cljs | 20 +- src/status_im2/contexts/chat/home/view.cljs | 161 ++++++++------- .../contexts/communities/home/style.cljs | 17 +- .../contexts/communities/home/view.cljs | 189 +++++------------- .../jump_to/components/home_stack/view.cljs | 2 +- 10 files changed, 315 insertions(+), 254 deletions(-) create mode 100644 src/status_im2/common/home/banner/style.cljs create mode 100644 src/status_im2/common/home/banner/view.cljs diff --git a/src/react_native/section_list.cljs b/src/react_native/section_list.cljs index f8293b117d..54e16dedcf 100644 --- a/src/react_native/section_list.cljs +++ b/src/react_native/section_list.cljs @@ -32,7 +32,7 @@ (defn section-list "A wrapper for SectionList. - To render something on empty sections, use renderSectionFooter and conditionaly + To render something on empty sections, use renderSectionFooter and conditionally render on empty data See https://facebook.github.io/react-native/docs/sectionlist.html" [{:keys [sections render-section-header-fn render-section-footer-fn style] :as props}] diff --git a/src/status_im2/common/home/banner/style.cljs b/src/status_im2/common/home/banner/style.cljs new file mode 100644 index 0000000000..5a61303611 --- /dev/null +++ b/src/status_im2/common/home/banner/style.cljs @@ -0,0 +1,63 @@ +(ns status-im2.common.home.banner.style + (:require [react-native.platform :as platform] + [react-native.reanimated :as reanimated] + [react-native.safe-area :as safe-area])) + +(def ^:private card-height (+ 56 16)) +(def ^:private max-scroll (+ card-height 8)) + +(def fill-space + {:position :absolute + :top 0 + :right 0 + :left 0 + :bottom 0}) + +(defn- animated-card-translation-y + [scroll-shared-value] + (reanimated/interpolate scroll-shared-value [0 max-scroll] [0 (- max-scroll)] :clamp)) + +(defn banner-card-blur-layer + [scroll-shared-value] + (reanimated/apply-animations-to-style + {:transform [{:translate-y (animated-card-translation-y scroll-shared-value)}]} + {:overflow (if platform/ios? :visible :hidden) + :z-index 1 + :position :absolute + :top 0 + :right 0 + :left 0 + :height (+ (safe-area/get-top) 244)})) + +(defn banner-card-hiding-layer + [] + {:z-index 2 + :position :absolute + :top 0 + :right 0 + :left 0 + :padding-top (safe-area/get-top)}) + +(def animated-banner-card-container {:overflow :hidden}) + +(defn animated-banner-card + [scroll-shared-value] + (reanimated/apply-animations-to-style + {:opacity (reanimated/interpolate scroll-shared-value [0 card-height] [1 0] :clamp) + :transform [{:translate-y (animated-card-translation-y scroll-shared-value)}]} + {})) + +(defn banner-card-tabs-layer + [scroll-shared-value] + (reanimated/apply-animations-to-style + {:transform [{:translate-y (animated-card-translation-y scroll-shared-value)}]} + {:z-index 3 + :position :absolute + :top (+ (safe-area/get-top) 192) + :right 0 + :left 0})) + +(def banner-card-tabs + {:padding-horizontal 20 + :padding-top 8 + :padding-bottom 12}) diff --git a/src/status_im2/common/home/banner/view.cljs b/src/status_im2/common/home/banner/view.cljs new file mode 100644 index 0000000000..0427fdd5b8 --- /dev/null +++ b/src/status_im2/common/home/banner/view.cljs @@ -0,0 +1,78 @@ +(ns status-im2.common.home.banner.view + (:require [oops.core :as oops] + [quo2.core :as quo] + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.blur :as blur] + [react-native.core :as rn] + [react-native.platform :as platform] + [react-native.reanimated :as reanimated] + [status-im2.common.home.banner.style :as style] + [status-im2.common.home.view :as common.home] + [utils.re-frame :as rf])) + +(defn- reset-banner-animation + [scroll-shared-value] + (reanimated/animate-shared-value-with-timing scroll-shared-value 0 200 :easing3)) + +(defn- reset-scroll + [scroll-ref] + (cond + (.-scrollToLocation scroll-ref) + (oops/ocall! scroll-ref "scrollToLocation" #js {:itemIndex 0 :sectionIndex 0 :viewOffset 0}) + (.-scrollToOffset scroll-ref) + (oops/ocall! scroll-ref "scrollToOffset" #js {:offset 0}))) + +(defn- banner-card-blur-layer + [scroll-shared-value] + (let [open-sheet? (-> (rf/sub [:bottom-sheet]) :sheets seq)] + [reanimated/view {:style (style/banner-card-blur-layer scroll-shared-value)} + [blur/view + {:style style/fill-space + :blur-amount (if platform/ios? 20 10) + :blur-type (theme/theme-value (if platform/ios? :light :xlight) :dark) + :overlay-color (if open-sheet? + (colors/theme-colors colors/white colors/neutral-95-opa-70) + (theme/theme-value nil colors/neutral-95-opa-70))}]])) + +(defn- banner-card-hiding-layer + [{:keys [title-props card-props scroll-shared-value]}] + (let [customization-color (rf/sub [:profile/customization-color])] + [rn/view {:style (style/banner-card-hiding-layer)} + [common.home/top-nav {:type :grey}] + [common.home/title-column (assoc title-props :customization-color customization-color)] + [rn/view {:style style/animated-banner-card-container} + [reanimated/view {:style (style/animated-banner-card scroll-shared-value)} + [quo/discover-card card-props]]]])) + +(defn- banner-card-tabs-layer + [{:keys [selected-tab tabs on-tab-change scroll-ref scroll-shared-value]}] + [reanimated/view {:style (style/banner-card-tabs-layer scroll-shared-value)} + ^{:key (str "tabs-" selected-tab)} + [quo/tabs + {:style style/banner-card-tabs + :size 32 + :default-active selected-tab + :data tabs + :on-change (fn [tab] + (reset-banner-animation scroll-shared-value) + (some-> scroll-ref + deref + reset-scroll) + (on-tab-change tab))}]]) + +(defn animated-banner + [{:keys [scroll-ref tabs selected-tab on-tab-change scroll-shared-value content]}] + [:<> + [:f> banner-card-blur-layer scroll-shared-value] + [:f> banner-card-hiding-layer (assoc content :scroll-shared-value scroll-shared-value)] + [:f> banner-card-tabs-layer + {:scroll-shared-value scroll-shared-value + :selected-tab selected-tab + :tabs tabs + :on-tab-change on-tab-change + :scroll-ref scroll-ref}]]) + +(defn set-scroll-shared-value + [{:keys [shared-value scroll-input]}] + (reanimated/set-shared-value shared-value scroll-input)) diff --git a/src/status_im2/common/home/style.cljs b/src/status_im2/common/home/style.cljs index 9f555d820f..75610e9afa 100644 --- a/src/status_im2/common/home/style.cljs +++ b/src/status_im2/common/home/style.cljs @@ -1,4 +1,5 @@ -(ns status-im2.common.home.style) +(ns status-im2.common.home.style + (:require [react-native.safe-area :as safe-area])) (def title-column {:flex-direction :row @@ -44,3 +45,16 @@ (def top-nav-container {:height 56}) + +(def header-height 245) + +(defn header-spacing + [] + {:height (+ header-height (safe-area/get-top))}) + +(defn empty-state-container + [] + {:flex 1 + :margin-top (+ header-height (safe-area/get-top)) + :margin-bottom 44 + :justify-content :center}) diff --git a/src/status_im2/common/home/view.cljs b/src/status_im2/common/home/view.cljs index 4e7b7426f8..910c039088 100644 --- a/src/status_im2/common/home/view.cljs +++ b/src/status_im2/common/home/view.cljs @@ -3,12 +3,12 @@ [quo2.core :as quo] [quo2.foundations.colors :as colors] [react-native.core :as rn] - [status-im2.common.home.style :as style] [status-im.multiaccounts.core :as multiaccounts] + [status-im2.common.home.style :as style] [status-im2.common.plus-button.view :as plus-button] [status-im2.constants :as constants] - [utils.re-frame :as rf] - [utils.debounce :refer [dispatch-and-chill]])) + [utils.debounce :refer [dispatch-and-chill]] + [utils.re-frame :as rf])) (defn title-column [{:keys [label handler accessibility-label customization-color]}] @@ -130,3 +130,18 @@ [rn/view {:style (merge style/top-nav-container style)} [left-section {:avatar avatar}] [right-section {:button-type type :button-background background :search? search?}]])) + +(defn header-spacing + [] + [rn/view {:style (style/header-spacing)}]) + +(defn empty-state-image + [{:keys [selected-tab tab->content]}] + (let [{:keys [image title description]} (tab->content selected-tab) + customization-color (rf/sub [:profile/customization-color])] + [rn/view {:style (style/empty-state-container)} + [quo/empty-state + {:customization-color customization-color + :image image + :title title + :description description}]])) diff --git a/src/status_im2/contexts/chat/home/style.cljs b/src/status_im2/contexts/chat/home/style.cljs index a42dcf90ba..6856d6d658 100644 --- a/src/status_im2/contexts/chat/home/style.cljs +++ b/src/status_im2/contexts/chat/home/style.cljs @@ -1,5 +1,6 @@ (ns status-im2.contexts.chat.home.style - (:require [react-native.platform :as platform])) + (:require [react-native.platform :as platform] + [react-native.safe-area :as safe-area])) (def tabs {:padding-horizontal 20 @@ -14,24 +15,11 @@ :bottom 0}) (defn blur-container - [top] + [] {:overflow (if platform/ios? :visible :hidden) :position :absolute :z-index 1 :top 0 :right 0 :left 0 - :padding-top top}) - -(def header-height 245) - -(defn header-space - [top] - {:height (+ header-height top)}) - -(defn empty-content-container - [top] - {:flex 1 - :margin-top (+ header-height top) - :margin-bottom 44 - :justify-content :center}) + :padding-top (safe-area/get-top)}) diff --git a/src/status_im2/contexts/chat/home/view.cljs b/src/status_im2/contexts/chat/home/view.cljs index 37879bb0d5..a518ab8eba 100644 --- a/src/status_im2/contexts/chat/home/view.cljs +++ b/src/status_im2/contexts/chat/home/view.cljs @@ -1,21 +1,18 @@ (ns status-im2.contexts.chat.home.view - (:require [quo2.core :as quo] - [quo2.foundations.colors :as colors] + (:require [oops.core :as oops] [quo2.theme :as theme] [re-frame.core :as re-frame] - [react-native.blur :as blur] [react-native.core :as rn] - [react-native.platform :as platform] - [react-native.safe-area :as safe-area] + [react-native.reanimated :as reanimated] [status-im2.common.contact-list-item.view :as contact-list-item] [status-im2.common.contact-list.view :as contact-list] [status-im2.common.home.actions.view :as actions] + [status-im2.common.home.banner.view :as common.home.banner] [status-im2.common.home.view :as common.home] [status-im2.common.resources :as resources] - [status-im2.contexts.chat.actions.view :as home.sheet] + [status-im2.contexts.chat.actions.view :as chat.actions.view] [status-im2.contexts.chat.home.chat-list-item.view :as chat-list-item] [status-im2.contexts.chat.home.contact-request.view :as contact-request] - [status-im2.contexts.chat.home.style :as style] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -30,50 +27,45 @@ (filter k) (sort-by :timestamp >)))) -(defn empty-state-content - [selected-tab] - (case selected-tab - :tab/contacts - {:title (i18n/label :t/no-contacts) - :description (i18n/label :t/no-contacts-description) - :image (resources/get-image - (theme/theme-value :no-contacts-light :no-contacts-dark))} - :tab/groups - {:title (i18n/label :t/no-group-chats) - :description (i18n/label :t/no-group-chats-description) - :image (resources/get-image - (theme/theme-value :no-group-chats-light :no-group-chats-dark))} - :tab/recent - {:title (i18n/label :t/no-messages) - :description (i18n/label :t/no-messages-description) - :image (resources/get-image - (theme/theme-value :no-messages-light :no-messages-dark))} - nil)) - -(defn empty-state - [{:keys [selected-tab top]}] - (let [{:keys [image title description]} (empty-state-content selected-tab)] - [rn/view {:style (style/empty-content-container top)} - [quo/empty-state - {:image image - :title title - :description description}]])) +(def empty-state-content + #:tab{:contacts + {:title (i18n/label :t/no-contacts) + :description (i18n/label :t/no-contacts-description) + :image (resources/get-image + (theme/theme-value :no-contacts-light :no-contacts-dark))} + :groups + {:title (i18n/label :t/no-group-chats) + :description (i18n/label :t/no-group-chats-description) + :image (resources/get-image + (theme/theme-value :no-group-chats-light :no-group-chats-dark))} + :recent + {:title (i18n/label :t/no-messages) + :description (i18n/label :t/no-messages-description) + :image (resources/get-image + (theme/theme-value :no-messages-light :no-messages-dark))}}) (defn chats - [selected-tab top] + [{:keys [selected-tab set-scroll-ref scroll-shared-value]}] (let [unfiltered-items (rf/sub [:chats-stack-items]) items (filter-and-sort-items-by-tab selected-tab unfiltered-items)] (if (empty? items) - [empty-state {:top top :selected-tab selected-tab}] - [rn/flat-list - {:key-fn #(or (:chat-id %) (:public-key %) (:id %)) + [common.home/empty-state-image + {:selected-tab selected-tab + :tab->content empty-state-content}] + [reanimated/flat-list + {:ref set-scroll-ref + :key-fn #(or (:chat-id %) (:public-key %) (:id %)) :content-inset-adjustment-behavior :never - :header [rn/view {:style (style/header-space top)}] + :header [common.home/header-spacing] :get-item-layout get-item-layout :on-end-reached #(re-frame/dispatch [:chat/show-more-chats]) :keyboard-should-persist-taps :always :data items - :render-fn chat-list-item/chat-list-item}]))) + :render-fn chat-list-item/chat-list-item + :scroll-event-throttle 8 + :on-scroll #(common.home.banner/set-scroll-shared-value + {:scroll-input (oops/oget % "nativeEvent.contentOffset.y") + :shared-value scroll-shared-value})}]))) (defn contact-item-render [{:keys [public-key] :as item}] @@ -89,23 +81,30 @@ item])) (defn contacts - [pending-contact-requests top] + [{:keys [pending-contact-requests set-scroll-ref scroll-shared-value]}] (let [items (rf/sub [:contacts/active-sections])] (if (and (empty? items) (empty? pending-contact-requests)) - [empty-state {:top top :selected-tab :tab/contacts}] + [common.home/empty-state-image + {:selected-tab :tab/contacts + :tab->content empty-state-content}] [rn/section-list - {:key-fn :public-key + {:ref set-scroll-ref + :key-fn :public-key :get-item-layout get-item-layout :content-inset-adjustment-behavior :never :header [:<> - [rn/view {:style (style/header-space top)}] + [common.home/header-spacing] (when (seq pending-contact-requests) [contact-request/contact-requests pending-contact-requests])] :sections items :sticky-section-headers-enabled false :render-section-header-fn contact-list/contacts-section-header - :render-fn contact-item-render}]))) + :render-fn contact-item-render + :scroll-event-throttle 8 + :on-scroll #(common.home.banner/set-scroll-shared-value + {:scroll-input (oops/oget % "nativeEvent.contentOffset.y") + :shared-value scroll-shared-value})}]))) (defn get-tabs-data [dot?] @@ -116,41 +115,39 @@ :accessibility-label :tab-contacts :notification-dot? dot?}]) +(def ^:private banner-data + {:title-props + {:label (i18n/label :t/messages) + :handler #(rf/dispatch + [:show-bottom-sheet {:content chat.actions.view/new-chat}]) + :accessibility-label :new-chat-button} + :card-props + {:banner (resources/get-image :invite-friends) + :title (i18n/label :t/invite-friends-to-status) + :description (i18n/label :t/share-invite-link)}}) + (defn home [] - (let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests]) - selected-tab (or (rf/sub [:messages-home/selected-tab]) :tab/recent) - customization-color (rf/sub [:profile/customization-color]) - top (safe-area/get-top)] - [:<> - (if (= selected-tab :tab/contacts) - [contacts pending-contact-requests top] - [chats selected-tab top]) - [rn/view {:style (style/blur-container top)} - (let [{:keys [sheets]} (rf/sub [:bottom-sheet])] - [blur/view - {:blur-amount (if platform/ios? 20 10) - :blur-type (if (colors/dark?) :dark (if platform/ios? :light :xlight)) - :style style/blur - :overlay-color (if (seq sheets) - (theme/theme-value colors/white colors/neutral-95-opa-70) - (when (colors/dark?) - colors/neutral-95-opa-70))}]) - [common.home/top-nav {:type :grey}] - [common.home/title-column - {:label (i18n/label :t/messages) - :handler #(rf/dispatch [:show-bottom-sheet {:content home.sheet/new-chat}]) - :accessibility-label :new-chat-button - :customization-color customization-color}] - [quo/discover-card - {:banner (resources/get-image :invite-friends) - :title (i18n/label :t/invite-friends-to-status) - :description (i18n/label :t/share-invite-link)}] - ^{:key (str "tabs-" selected-tab)} - [quo/tabs - {:style style/tabs - :size 32 - :on-change (fn [tab] - (rf/dispatch [:messages-home/select-tab tab])) - :default-active selected-tab - :data (get-tabs-data (pos? (count pending-contact-requests)))}]]])) + (let [scroll-ref (atom nil) + set-scroll-ref #(reset! scroll-ref %)] + (fn [] + (let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests]) + selected-tab (or (rf/sub [:messages-home/selected-tab]) :tab/recent) + scroll-shared-value (reanimated/use-shared-value 0)] + [:<> + (if (= selected-tab :tab/contacts) + [contacts + {:pending-contact-requests pending-contact-requests + :set-scroll-ref set-scroll-ref + :scroll-shared-value scroll-shared-value}] + [chats + {:selected-tab selected-tab + :set-scroll-ref set-scroll-ref + :scroll-shared-value scroll-shared-value}]) + [:f> common.home.banner/animated-banner + {:content banner-data + :scroll-ref scroll-ref + :tabs (get-tabs-data (pos? (count pending-contact-requests))) + :selected-tab selected-tab + :on-tab-change (fn [tab] (rf/dispatch [:messages-home/select-tab tab])) + :scroll-shared-value scroll-shared-value}]])))) diff --git a/src/status_im2/contexts/communities/home/style.cljs b/src/status_im2/contexts/communities/home/style.cljs index cc74242fab..b07ca19030 100644 --- a/src/status_im2/contexts/communities/home/style.cljs +++ b/src/status_im2/contexts/communities/home/style.cljs @@ -1,10 +1,8 @@ (ns status-im2.contexts.communities.home.style (:require [quo2.foundations.colors :as colors] [react-native.platform :as platform] - [react-native.safe-area :as safe-area] - [react-native.reanimated :as reanimated])) - -(def header-height 245) + [react-native.reanimated :as reanimated] + [react-native.safe-area :as safe-area])) (def tabs {:padding-horizontal 20 @@ -18,22 +16,11 @@ :left 0 :bottom 0}) -(defn empty-state-container - [] - {:margin-top (+ header-height (safe-area/get-top)) - :margin-bottom 44 - :flex 1 - :justify-content :center}) - (def empty-state-placeholder {:height 120 :width 120 :background-color colors/danger-50}) -(defn header-spacing - [] - {:height (+ header-height (safe-area/get-top))}) - (defn blur-banner-layer [animated-translation-y] (let [fixed-height (+ (safe-area/get-top) 244)] diff --git a/src/status_im2/contexts/communities/home/view.cljs b/src/status_im2/contexts/communities/home/view.cljs index 6d34db097b..a2a1c55a28 100644 --- a/src/status_im2/contexts/communities/home/view.cljs +++ b/src/status_im2/contexts/communities/home/view.cljs @@ -1,17 +1,14 @@ (ns status-im2.contexts.communities.home.view (:require [oops.core :as oops] [quo2.core :as quo] - [quo2.foundations.colors :as colors] [quo2.theme :as theme] - [react-native.blur :as blur] [react-native.core :as rn] - [react-native.platform :as platform] [react-native.reanimated :as reanimated] + [status-im2.common.home.banner.view :as common.home.banner] [status-im2.common.home.view :as common.home] [status-im2.common.resources :as resources] [status-im2.contexts.communities.actions.community-options.view :as options] [status-im2.contexts.communities.actions.home-plus.view :as actions.home-plus] - [status-im2.contexts.communities.home.style :as style] [utils.debounce :as debounce] [utils.i18n :as i18n] [utils.number] @@ -40,122 +37,43 @@ {:id :pending :label (i18n/label :t/pending) :accessibility-label :pending-tab} {:id :opened :label (i18n/label :t/opened) :accessibility-label :opened-tab}]) -(defn empty-state-content - [selected-tab] - (case selected-tab - :joined - {:title (i18n/label :t/no-communities) - :description [:<> - [rn/text {:style {:text-decoration-line :line-through}} - (i18n/label :t/no-communities-description-strikethrough)] - " " - (i18n/label :t/no-communities-description)] - :image (resources/get-image (theme/theme-value :no-communities-light - :no-communities-dark))} - :pending - {:title (i18n/label :t/no-pending-communities) - :description (i18n/label :t/no-pending-communities-description) - :image (resources/get-image (theme/theme-value :no-pending-communities-light - :no-pending-communities-dark))} - :opened - {:title (i18n/label :t/no-opened-communities) - :description (i18n/label :t/no-opened-communities-description) - :image (resources/get-image (theme/theme-value :no-opened-communities-light - :no-opened-communities-dark))} - nil)) +(def empty-state-content + {:joined + {:title (i18n/label :t/no-communities) + :description [:<> + [rn/text {:style {:text-decoration-line :line-through}} + (i18n/label :t/no-communities-description-strikethrough)] + " " + (i18n/label :t/no-communities-description)] + :image (resources/get-image (theme/theme-value :no-communities-light + :no-communities-dark))} + :pending + {:title (i18n/label :t/no-pending-communities) + :description (i18n/label :t/no-pending-communities-description) + :image (resources/get-image (theme/theme-value :no-pending-communities-light + :no-pending-communities-dark))} + :opened + {:title (i18n/label :t/no-opened-communities) + :description (i18n/label :t/no-opened-communities-description) + :image (resources/get-image (theme/theme-value :no-opened-communities-light + :no-opened-communities-dark))}}) -(defn- empty-state - [{:keys [style selected-tab]}] - (let [{:keys [image title description]} (empty-state-content selected-tab) - customization-color (rf/sub [:profile/customization-color])] - [rn/view {:style style} - [quo/empty-state - {:customization-color customization-color - :image image - :title title - :description description}]])) - -(defn- blur-banner-layer - [animated-translation-y] - (let [open-sheet? (-> (rf/sub [:bottom-sheet]) :sheets seq)] - [reanimated/view {:style (style/blur-banner-layer animated-translation-y)} - [blur/view - {:blur-amount (if platform/ios? 20 10) - :blur-type (theme/theme-value (if platform/ios? :light :xlight) :dark) - :style style/blur - :overlay-color (if open-sheet? - (colors/theme-colors colors/white colors/neutral-95-opa-70) - (theme/theme-value nil colors/neutral-95-opa-70))}]])) - -(defn- hiding-banner-layer - [animated-translation-y animated-opacity] - (let [customization-color (rf/sub [:profile/customization-color])] - [rn/view {:style (style/hiding-banner-layer)} - [common.home/top-nav {:type :grey}] - [common.home/title-column - {:label (i18n/label :t/communities) - :handler #(rf/dispatch - [:show-bottom-sheet {:content actions.home-plus/view}]) - :accessibility-label :new-communities-button - :customization-color customization-color}] - [rn/view {:style style/animated-card-container} - [reanimated/view {:style (style/animated-card animated-opacity animated-translation-y)} - [quo/discover-card - {:on-press #(rf/dispatch [:navigate-to :discover-communities]) - :title (i18n/label :t/discover) - :description (i18n/label :t/favorite-communities) - :banner (resources/get-image :discover) - :accessibility-label :communities-home-discover-card}]]]])) - -(defn- reset-banner-animation - [animated-opacity animated-translation-y] - (reanimated/animate-shared-value-with-timing animated-opacity 1 200 :easing3) - (reanimated/animate-shared-value-with-timing animated-translation-y 0 200 :easing3)) - -(defn- reset-scroll - [flat-list-ref] - (some-> flat-list-ref - (.scrollToOffset #js {:offset 0 :animated? true}))) - -(defn- tabs-banner-layer - [animated-translation-y animated-opacity selected-tab flat-list-ref] - (let [on-tab-change (fn [tab] - (if (empty? (get (rf/sub [:communities/grouped-by-status]) tab)) - (reset-banner-animation animated-opacity animated-translation-y) - (reset-scroll @flat-list-ref)) - (rf/dispatch [:communities/select-tab tab]))] - [reanimated/view {:style (style/tabs-banner-layer animated-translation-y)} - ^{:key (str "tabs-" selected-tab)} - [quo/tabs - {:size 32 - :style style/tabs - :on-change on-tab-change - :default-active selected-tab - :data tabs-data}]])) - -(defn- animated-banner - [{:keys [selected-tab animated-translation-y animated-opacity flat-list-ref]}] - [:<> - [:f> blur-banner-layer animated-translation-y] - [:f> hiding-banner-layer animated-translation-y animated-opacity] - [:f> tabs-banner-layer animated-translation-y animated-opacity selected-tab flat-list-ref]]) - -(def ^:private card-height (+ 56 16)) ; Card height + its vertical margins -(def ^:private card-opacity-factor (/ 100 card-height 100)) -(def ^:private max-scroll (- (+ card-height 8))) ; added 8 from tabs top padding - -(defn- set-animated-banner-values - [{:keys [scroll-offset translation-y opacity]}] - (let [new-opacity (-> (* (- card-height scroll-offset) card-opacity-factor) - (utils.number/value-in-range 0 1)) - new-translation-y (-> (- scroll-offset) - (utils.number/value-in-range max-scroll 0))] - (reanimated/animate-shared-value-with-timing opacity new-opacity 80 :easing4) - (reanimated/animate-shared-value-with-timing translation-y new-translation-y 80 :easing4))) +(def ^:private banner-data + {:title-props + {:label (i18n/label :t/communities) + :handler #(rf/dispatch [:show-bottom-sheet {:content actions.home-plus/view}]) + :accessibility-label :new-communities-button} + :card-props + {:on-press #(rf/dispatch [:navigate-to :discover-communities]) + :title (i18n/label :t/discover) + :description (i18n/label :t/favorite-communities) + :banner (resources/get-image :discover) + :accessibility-label :communities-home-discover-card}}) (defn home [] - (let [flat-list-ref (atom nil)] + (let [flat-list-ref (atom nil) + set-flat-list-ref #(reset! flat-list-ref %)] (fn [] (let [selected-tab (or (rf/sub [:communities/selected-tab]) :joined) {:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status]) @@ -163,28 +81,29 @@ :joined joined :pending pending :opened opened) - animated-opacity (reanimated/use-shared-value 1) - animated-translation-y (reanimated/use-shared-value 0)] + scroll-shared-value (reanimated/use-shared-value 0)] [:<> (if (empty? selected-items) - [empty-state - {:style (style/empty-state-container) - :selected-tab selected-tab}] - [rn/flat-list - {:ref #(reset! flat-list-ref %) + [common.home/empty-state-image + {:selected-tab selected-tab + :tab->content empty-state-content}] + [reanimated/flat-list + {:ref set-flat-list-ref :key-fn :id :content-inset-adjustment-behavior :never - :header [rn/view {:style (style/header-spacing)}] + :header [common.home/header-spacing] :render-fn item-render :data selected-items - :on-scroll #(set-animated-banner-values - {:scroll-offset (oops/oget - % - "nativeEvent.contentOffset.y") - :translation-y animated-translation-y - :opacity animated-opacity})}]) - [:f> animated-banner - {:selected-tab selected-tab - :animated-translation-y animated-translation-y - :animated-opacity animated-opacity - :flat-list-ref flat-list-ref}]])))) + :scroll-event-throttle 8 + :on-scroll #(common.home.banner/set-scroll-shared-value + {:scroll-input (oops/oget + % + "nativeEvent.contentOffset.y") + :shared-value scroll-shared-value})}]) + [:f> common.home.banner/animated-banner + {:content banner-data + :scroll-ref flat-list-ref + :tabs tabs-data + :selected-tab selected-tab + :on-tab-change (fn [tab] (rf/dispatch [:communities/select-tab tab])) + :scroll-shared-value scroll-shared-value}]])))) diff --git a/src/status_im2/contexts/shell/jump_to/components/home_stack/view.cljs b/src/status_im2/contexts/shell/jump_to/components/home_stack/view.cljs index 2aa4ad81a3..43787fded3 100644 --- a/src/status_im2/contexts/shell/jump_to/components/home_stack/view.cljs +++ b/src/status_im2/contexts/shell/jump_to/components/home_stack/view.cljs @@ -28,7 +28,7 @@ (get shell.constants/stacks-z-index-keywords stack-id))})} (case stack-id :communities-stack [:f> communities/home] - :chats-stack [chat/home] + :chats-stack [:f> chat/home] :wallet-stack [wallet.accounts/accounts-overview-old] :browser-stack [browser.stack/browser-stack] [:<>])])