* 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
This commit is contained in:
parent
f6ce63734c
commit
7ad378e9c8
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
(defn section-list
|
(defn section-list
|
||||||
"A wrapper for SectionList.
|
"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
|
render on empty data
|
||||||
See https://facebook.github.io/react-native/docs/sectionlist.html"
|
See https://facebook.github.io/react-native/docs/sectionlist.html"
|
||||||
[{:keys [sections render-section-header-fn render-section-footer-fn style] :as props}]
|
[{:keys [sections render-section-header-fn render-section-footer-fn style] :as props}]
|
||||||
|
|
|
@ -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})
|
|
@ -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))
|
|
@ -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
|
(def title-column
|
||||||
{:flex-direction :row
|
{:flex-direction :row
|
||||||
|
@ -44,3 +45,16 @@
|
||||||
|
|
||||||
(def top-nav-container
|
(def top-nav-container
|
||||||
{:height 56})
|
{: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})
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
[quo2.core :as quo]
|
[quo2.core :as quo]
|
||||||
[quo2.foundations.colors :as colors]
|
[quo2.foundations.colors :as colors]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[status-im2.common.home.style :as style]
|
|
||||||
[status-im.multiaccounts.core :as multiaccounts]
|
[status-im.multiaccounts.core :as multiaccounts]
|
||||||
|
[status-im2.common.home.style :as style]
|
||||||
[status-im2.common.plus-button.view :as plus-button]
|
[status-im2.common.plus-button.view :as plus-button]
|
||||||
[status-im2.constants :as constants]
|
[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
|
(defn title-column
|
||||||
[{:keys [label handler accessibility-label customization-color]}]
|
[{:keys [label handler accessibility-label customization-color]}]
|
||||||
|
@ -130,3 +130,18 @@
|
||||||
[rn/view {:style (merge style/top-nav-container style)}
|
[rn/view {:style (merge style/top-nav-container style)}
|
||||||
[left-section {:avatar avatar}]
|
[left-section {:avatar avatar}]
|
||||||
[right-section {:button-type type :button-background background :search? search?}]]))
|
[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}]]))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
(ns status-im2.contexts.chat.home.style
|
(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
|
(def tabs
|
||||||
{:padding-horizontal 20
|
{:padding-horizontal 20
|
||||||
|
@ -14,24 +15,11 @@
|
||||||
:bottom 0})
|
:bottom 0})
|
||||||
|
|
||||||
(defn blur-container
|
(defn blur-container
|
||||||
[top]
|
[]
|
||||||
{:overflow (if platform/ios? :visible :hidden)
|
{:overflow (if platform/ios? :visible :hidden)
|
||||||
:position :absolute
|
:position :absolute
|
||||||
:z-index 1
|
:z-index 1
|
||||||
:top 0
|
:top 0
|
||||||
:right 0
|
:right 0
|
||||||
:left 0
|
:left 0
|
||||||
:padding-top top})
|
:padding-top (safe-area/get-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})
|
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
(ns status-im2.contexts.chat.home.view
|
(ns status-im2.contexts.chat.home.view
|
||||||
(:require [quo2.core :as quo]
|
(:require [oops.core :as oops]
|
||||||
[quo2.foundations.colors :as colors]
|
|
||||||
[quo2.theme :as theme]
|
[quo2.theme :as theme]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[react-native.blur :as blur]
|
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.platform :as platform]
|
[react-native.reanimated :as reanimated]
|
||||||
[react-native.safe-area :as safe-area]
|
|
||||||
[status-im2.common.contact-list-item.view :as contact-list-item]
|
[status-im2.common.contact-list-item.view :as contact-list-item]
|
||||||
[status-im2.common.contact-list.view :as contact-list]
|
[status-im2.common.contact-list.view :as contact-list]
|
||||||
[status-im2.common.home.actions.view :as actions]
|
[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.home.view :as common.home]
|
||||||
[status-im2.common.resources :as resources]
|
[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.chat-list-item.view :as chat-list-item]
|
||||||
[status-im2.contexts.chat.home.contact-request.view :as contact-request]
|
[status-im2.contexts.chat.home.contact-request.view :as contact-request]
|
||||||
[status-im2.contexts.chat.home.style :as style]
|
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
|
@ -30,50 +27,45 @@
|
||||||
(filter k)
|
(filter k)
|
||||||
(sort-by :timestamp >))))
|
(sort-by :timestamp >))))
|
||||||
|
|
||||||
(defn empty-state-content
|
(def empty-state-content
|
||||||
[selected-tab]
|
#:tab{:contacts
|
||||||
(case selected-tab
|
{:title (i18n/label :t/no-contacts)
|
||||||
:tab/contacts
|
:description (i18n/label :t/no-contacts-description)
|
||||||
{:title (i18n/label :t/no-contacts)
|
:image (resources/get-image
|
||||||
:description (i18n/label :t/no-contacts-description)
|
(theme/theme-value :no-contacts-light :no-contacts-dark))}
|
||||||
:image (resources/get-image
|
:groups
|
||||||
(theme/theme-value :no-contacts-light :no-contacts-dark))}
|
{:title (i18n/label :t/no-group-chats)
|
||||||
:tab/groups
|
:description (i18n/label :t/no-group-chats-description)
|
||||||
{:title (i18n/label :t/no-group-chats)
|
:image (resources/get-image
|
||||||
:description (i18n/label :t/no-group-chats-description)
|
(theme/theme-value :no-group-chats-light :no-group-chats-dark))}
|
||||||
:image (resources/get-image
|
:recent
|
||||||
(theme/theme-value :no-group-chats-light :no-group-chats-dark))}
|
{:title (i18n/label :t/no-messages)
|
||||||
:tab/recent
|
:description (i18n/label :t/no-messages-description)
|
||||||
{:title (i18n/label :t/no-messages)
|
:image (resources/get-image
|
||||||
:description (i18n/label :t/no-messages-description)
|
(theme/theme-value :no-messages-light :no-messages-dark))}})
|
||||||
: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}]]))
|
|
||||||
|
|
||||||
(defn chats
|
(defn chats
|
||||||
[selected-tab top]
|
[{:keys [selected-tab set-scroll-ref scroll-shared-value]}]
|
||||||
(let [unfiltered-items (rf/sub [:chats-stack-items])
|
(let [unfiltered-items (rf/sub [:chats-stack-items])
|
||||||
items (filter-and-sort-items-by-tab selected-tab unfiltered-items)]
|
items (filter-and-sort-items-by-tab selected-tab unfiltered-items)]
|
||||||
(if (empty? items)
|
(if (empty? items)
|
||||||
[empty-state {:top top :selected-tab selected-tab}]
|
[common.home/empty-state-image
|
||||||
[rn/flat-list
|
{:selected-tab selected-tab
|
||||||
{:key-fn #(or (:chat-id %) (:public-key %) (:id %))
|
: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
|
:content-inset-adjustment-behavior :never
|
||||||
:header [rn/view {:style (style/header-space top)}]
|
:header [common.home/header-spacing]
|
||||||
:get-item-layout get-item-layout
|
:get-item-layout get-item-layout
|
||||||
:on-end-reached #(re-frame/dispatch [:chat/show-more-chats])
|
:on-end-reached #(re-frame/dispatch [:chat/show-more-chats])
|
||||||
:keyboard-should-persist-taps :always
|
:keyboard-should-persist-taps :always
|
||||||
:data items
|
: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
|
(defn contact-item-render
|
||||||
[{:keys [public-key] :as item}]
|
[{:keys [public-key] :as item}]
|
||||||
|
@ -89,23 +81,30 @@
|
||||||
item]))
|
item]))
|
||||||
|
|
||||||
(defn contacts
|
(defn contacts
|
||||||
[pending-contact-requests top]
|
[{:keys [pending-contact-requests set-scroll-ref scroll-shared-value]}]
|
||||||
(let [items (rf/sub [:contacts/active-sections])]
|
(let [items (rf/sub [:contacts/active-sections])]
|
||||||
(if (and (empty? items) (empty? pending-contact-requests))
|
(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
|
[rn/section-list
|
||||||
{:key-fn :public-key
|
{:ref set-scroll-ref
|
||||||
|
:key-fn :public-key
|
||||||
:get-item-layout get-item-layout
|
:get-item-layout get-item-layout
|
||||||
:content-inset-adjustment-behavior :never
|
:content-inset-adjustment-behavior :never
|
||||||
:header [:<>
|
:header [:<>
|
||||||
[rn/view {:style (style/header-space top)}]
|
[common.home/header-spacing]
|
||||||
(when (seq pending-contact-requests)
|
(when (seq pending-contact-requests)
|
||||||
[contact-request/contact-requests
|
[contact-request/contact-requests
|
||||||
pending-contact-requests])]
|
pending-contact-requests])]
|
||||||
:sections items
|
:sections items
|
||||||
:sticky-section-headers-enabled false
|
:sticky-section-headers-enabled false
|
||||||
:render-section-header-fn contact-list/contacts-section-header
|
: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
|
(defn get-tabs-data
|
||||||
[dot?]
|
[dot?]
|
||||||
|
@ -116,41 +115,39 @@
|
||||||
:accessibility-label :tab-contacts
|
:accessibility-label :tab-contacts
|
||||||
:notification-dot? dot?}])
|
: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
|
(defn home
|
||||||
[]
|
[]
|
||||||
(let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests])
|
(let [scroll-ref (atom nil)
|
||||||
selected-tab (or (rf/sub [:messages-home/selected-tab]) :tab/recent)
|
set-scroll-ref #(reset! scroll-ref %)]
|
||||||
customization-color (rf/sub [:profile/customization-color])
|
(fn []
|
||||||
top (safe-area/get-top)]
|
(let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests])
|
||||||
[:<>
|
selected-tab (or (rf/sub [:messages-home/selected-tab]) :tab/recent)
|
||||||
(if (= selected-tab :tab/contacts)
|
scroll-shared-value (reanimated/use-shared-value 0)]
|
||||||
[contacts pending-contact-requests top]
|
[:<>
|
||||||
[chats selected-tab top])
|
(if (= selected-tab :tab/contacts)
|
||||||
[rn/view {:style (style/blur-container top)}
|
[contacts
|
||||||
(let [{:keys [sheets]} (rf/sub [:bottom-sheet])]
|
{:pending-contact-requests pending-contact-requests
|
||||||
[blur/view
|
:set-scroll-ref set-scroll-ref
|
||||||
{:blur-amount (if platform/ios? 20 10)
|
:scroll-shared-value scroll-shared-value}]
|
||||||
:blur-type (if (colors/dark?) :dark (if platform/ios? :light :xlight))
|
[chats
|
||||||
:style style/blur
|
{:selected-tab selected-tab
|
||||||
:overlay-color (if (seq sheets)
|
:set-scroll-ref set-scroll-ref
|
||||||
(theme/theme-value colors/white colors/neutral-95-opa-70)
|
:scroll-shared-value scroll-shared-value}])
|
||||||
(when (colors/dark?)
|
[:f> common.home.banner/animated-banner
|
||||||
colors/neutral-95-opa-70))}])
|
{:content banner-data
|
||||||
[common.home/top-nav {:type :grey}]
|
:scroll-ref scroll-ref
|
||||||
[common.home/title-column
|
:tabs (get-tabs-data (pos? (count pending-contact-requests)))
|
||||||
{:label (i18n/label :t/messages)
|
:selected-tab selected-tab
|
||||||
:handler #(rf/dispatch [:show-bottom-sheet {:content home.sheet/new-chat}])
|
:on-tab-change (fn [tab] (rf/dispatch [:messages-home/select-tab tab]))
|
||||||
:accessibility-label :new-chat-button
|
:scroll-shared-value scroll-shared-value}]]))))
|
||||||
: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)))}]]]))
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
(ns status-im2.contexts.communities.home.style
|
(ns status-im2.contexts.communities.home.style
|
||||||
(:require [quo2.foundations.colors :as colors]
|
(:require [quo2.foundations.colors :as colors]
|
||||||
[react-native.platform :as platform]
|
[react-native.platform :as platform]
|
||||||
[react-native.safe-area :as safe-area]
|
[react-native.reanimated :as reanimated]
|
||||||
[react-native.reanimated :as reanimated]))
|
[react-native.safe-area :as safe-area]))
|
||||||
|
|
||||||
(def header-height 245)
|
|
||||||
|
|
||||||
(def tabs
|
(def tabs
|
||||||
{:padding-horizontal 20
|
{:padding-horizontal 20
|
||||||
|
@ -18,22 +16,11 @@
|
||||||
:left 0
|
:left 0
|
||||||
:bottom 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
|
(def empty-state-placeholder
|
||||||
{:height 120
|
{:height 120
|
||||||
:width 120
|
:width 120
|
||||||
:background-color colors/danger-50})
|
:background-color colors/danger-50})
|
||||||
|
|
||||||
(defn header-spacing
|
|
||||||
[]
|
|
||||||
{:height (+ header-height (safe-area/get-top))})
|
|
||||||
|
|
||||||
(defn blur-banner-layer
|
(defn blur-banner-layer
|
||||||
[animated-translation-y]
|
[animated-translation-y]
|
||||||
(let [fixed-height (+ (safe-area/get-top) 244)]
|
(let [fixed-height (+ (safe-area/get-top) 244)]
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
(ns status-im2.contexts.communities.home.view
|
(ns status-im2.contexts.communities.home.view
|
||||||
(:require [oops.core :as oops]
|
(:require [oops.core :as oops]
|
||||||
[quo2.core :as quo]
|
[quo2.core :as quo]
|
||||||
[quo2.foundations.colors :as colors]
|
|
||||||
[quo2.theme :as theme]
|
[quo2.theme :as theme]
|
||||||
[react-native.blur :as blur]
|
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.platform :as platform]
|
|
||||||
[react-native.reanimated :as reanimated]
|
[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.home.view :as common.home]
|
||||||
[status-im2.common.resources :as resources]
|
[status-im2.common.resources :as resources]
|
||||||
[status-im2.contexts.communities.actions.community-options.view :as options]
|
[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.actions.home-plus.view :as actions.home-plus]
|
||||||
[status-im2.contexts.communities.home.style :as style]
|
|
||||||
[utils.debounce :as debounce]
|
[utils.debounce :as debounce]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.number]
|
[utils.number]
|
||||||
|
@ -40,122 +37,43 @@
|
||||||
{:id :pending :label (i18n/label :t/pending) :accessibility-label :pending-tab}
|
{:id :pending :label (i18n/label :t/pending) :accessibility-label :pending-tab}
|
||||||
{:id :opened :label (i18n/label :t/opened) :accessibility-label :opened-tab}])
|
{:id :opened :label (i18n/label :t/opened) :accessibility-label :opened-tab}])
|
||||||
|
|
||||||
(defn empty-state-content
|
(def empty-state-content
|
||||||
[selected-tab]
|
{:joined
|
||||||
(case selected-tab
|
{:title (i18n/label :t/no-communities)
|
||||||
:joined
|
:description [:<>
|
||||||
{:title (i18n/label :t/no-communities)
|
[rn/text {:style {:text-decoration-line :line-through}}
|
||||||
:description [:<>
|
(i18n/label :t/no-communities-description-strikethrough)]
|
||||||
[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
|
||||||
(i18n/label :t/no-communities-description)]
|
:no-communities-dark))}
|
||||||
:image (resources/get-image (theme/theme-value :no-communities-light
|
:pending
|
||||||
:no-communities-dark))}
|
{:title (i18n/label :t/no-pending-communities)
|
||||||
:pending
|
:description (i18n/label :t/no-pending-communities-description)
|
||||||
{:title (i18n/label :t/no-pending-communities)
|
:image (resources/get-image (theme/theme-value :no-pending-communities-light
|
||||||
:description (i18n/label :t/no-pending-communities-description)
|
:no-pending-communities-dark))}
|
||||||
:image (resources/get-image (theme/theme-value :no-pending-communities-light
|
:opened
|
||||||
:no-pending-communities-dark))}
|
{:title (i18n/label :t/no-opened-communities)
|
||||||
:opened
|
:description (i18n/label :t/no-opened-communities-description)
|
||||||
{:title (i18n/label :t/no-opened-communities)
|
:image (resources/get-image (theme/theme-value :no-opened-communities-light
|
||||||
:description (i18n/label :t/no-opened-communities-description)
|
:no-opened-communities-dark))}})
|
||||||
:image (resources/get-image (theme/theme-value :no-opened-communities-light
|
|
||||||
:no-opened-communities-dark))}
|
|
||||||
nil))
|
|
||||||
|
|
||||||
(defn- empty-state
|
(def ^:private banner-data
|
||||||
[{:keys [style selected-tab]}]
|
{:title-props
|
||||||
(let [{:keys [image title description]} (empty-state-content selected-tab)
|
{:label (i18n/label :t/communities)
|
||||||
customization-color (rf/sub [:profile/customization-color])]
|
:handler #(rf/dispatch [:show-bottom-sheet {:content actions.home-plus/view}])
|
||||||
[rn/view {:style style}
|
:accessibility-label :new-communities-button}
|
||||||
[quo/empty-state
|
:card-props
|
||||||
{:customization-color customization-color
|
{:on-press #(rf/dispatch [:navigate-to :discover-communities])
|
||||||
:image image
|
:title (i18n/label :t/discover)
|
||||||
:title title
|
:description (i18n/label :t/favorite-communities)
|
||||||
:description description}]]))
|
:banner (resources/get-image :discover)
|
||||||
|
:accessibility-label :communities-home-discover-card}})
|
||||||
(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)))
|
|
||||||
|
|
||||||
(defn home
|
(defn home
|
||||||
[]
|
[]
|
||||||
(let [flat-list-ref (atom nil)]
|
(let [flat-list-ref (atom nil)
|
||||||
|
set-flat-list-ref #(reset! flat-list-ref %)]
|
||||||
(fn []
|
(fn []
|
||||||
(let [selected-tab (or (rf/sub [:communities/selected-tab]) :joined)
|
(let [selected-tab (or (rf/sub [:communities/selected-tab]) :joined)
|
||||||
{:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status])
|
{:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status])
|
||||||
|
@ -163,28 +81,29 @@
|
||||||
:joined joined
|
:joined joined
|
||||||
:pending pending
|
:pending pending
|
||||||
:opened opened)
|
:opened opened)
|
||||||
animated-opacity (reanimated/use-shared-value 1)
|
scroll-shared-value (reanimated/use-shared-value 0)]
|
||||||
animated-translation-y (reanimated/use-shared-value 0)]
|
|
||||||
[:<>
|
[:<>
|
||||||
(if (empty? selected-items)
|
(if (empty? selected-items)
|
||||||
[empty-state
|
[common.home/empty-state-image
|
||||||
{:style (style/empty-state-container)
|
{:selected-tab selected-tab
|
||||||
:selected-tab selected-tab}]
|
:tab->content empty-state-content}]
|
||||||
[rn/flat-list
|
[reanimated/flat-list
|
||||||
{:ref #(reset! flat-list-ref %)
|
{:ref set-flat-list-ref
|
||||||
:key-fn :id
|
:key-fn :id
|
||||||
:content-inset-adjustment-behavior :never
|
:content-inset-adjustment-behavior :never
|
||||||
:header [rn/view {:style (style/header-spacing)}]
|
:header [common.home/header-spacing]
|
||||||
:render-fn item-render
|
:render-fn item-render
|
||||||
:data selected-items
|
:data selected-items
|
||||||
:on-scroll #(set-animated-banner-values
|
:scroll-event-throttle 8
|
||||||
{:scroll-offset (oops/oget
|
:on-scroll #(common.home.banner/set-scroll-shared-value
|
||||||
%
|
{:scroll-input (oops/oget
|
||||||
"nativeEvent.contentOffset.y")
|
%
|
||||||
:translation-y animated-translation-y
|
"nativeEvent.contentOffset.y")
|
||||||
:opacity animated-opacity})}])
|
:shared-value scroll-shared-value})}])
|
||||||
[:f> animated-banner
|
[:f> common.home.banner/animated-banner
|
||||||
{:selected-tab selected-tab
|
{:content banner-data
|
||||||
:animated-translation-y animated-translation-y
|
:scroll-ref flat-list-ref
|
||||||
:animated-opacity animated-opacity
|
:tabs tabs-data
|
||||||
:flat-list-ref flat-list-ref}]]))))
|
:selected-tab selected-tab
|
||||||
|
:on-tab-change (fn [tab] (rf/dispatch [:communities/select-tab tab]))
|
||||||
|
:scroll-shared-value scroll-shared-value}]]))))
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
(get shell.constants/stacks-z-index-keywords stack-id))})}
|
(get shell.constants/stacks-z-index-keywords stack-id))})}
|
||||||
(case stack-id
|
(case stack-id
|
||||||
:communities-stack [:f> communities/home]
|
:communities-stack [:f> communities/home]
|
||||||
:chats-stack [chat/home]
|
:chats-stack [:f> chat/home]
|
||||||
:wallet-stack [wallet.accounts/accounts-overview-old]
|
:wallet-stack [wallet.accounts/accounts-overview-old]
|
||||||
:browser-stack [browser.stack/browser-stack]
|
:browser-stack [browser.stack/browser-stack]
|
||||||
[:<>])])
|
[:<>])])
|
||||||
|
|
Loading…
Reference in New Issue