diff --git a/src/js/bottom_sheet.js b/src/js/worklets/bottom_sheet.js similarity index 100% rename from src/js/bottom_sheet.js rename to src/js/worklets/bottom_sheet.js diff --git a/src/js/worklet_factory.js b/src/js/worklets/core.js similarity index 100% rename from src/js/worklet_factory.js rename to src/js/worklets/core.js diff --git a/src/js/record_audio_worklets.js b/src/js/worklets/record_audio.js similarity index 100% rename from src/js/record_audio_worklets.js rename to src/js/worklets/record_audio.js diff --git a/src/js/worklets/scroll_view.js b/src/js/worklets/scroll_view.js new file mode 100644 index 0000000000..60b6dff90c --- /dev/null +++ b/src/js/worklets/scroll_view.js @@ -0,0 +1,7 @@ +import {useAnimatedScrollHandler} from "react-native-reanimated"; + +export function useAnimatedScrollHandlerWorklet(scrollY) { + return useAnimatedScrollHandler((event) => { + scrollY.value = event.contentOffset.y; + }) +} diff --git a/src/js/shell_worklets.js b/src/js/worklets/shell.js similarity index 100% rename from src/js/shell_worklets.js rename to src/js/worklets/shell.js diff --git a/src/mocks/js_dependencies.cljs b/src/mocks/js_dependencies.cljs index 9924918900..734b5ee23e 100644 --- a/src/mocks/js_dependencies.cljs +++ b/src/mocks/js_dependencies.cljs @@ -355,11 +355,6 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( (def worklet-factory #js {:applyAnimationsToStyle (fn [])}) -(def shell-worklets #js {}) - -(def bottom-sheet #js {}) -(def record-audio-worklets #js {}) - ;; Update i18n_resources.cljs (defn mock [module] @@ -407,10 +402,11 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( "@react-native-async-storage/async-storage" async-storage "react-native-svg" react-native-svg "react-native-orientation-locker" react-native-orientation-locker - "../src/js/worklet_factory.js" worklet-factory - "../src/js/shell_worklets.js" shell-worklets - "../src/js/bottom_sheet.js" bottom-sheet - "../src/js/record_audio_worklets.js" record-audio-worklets + "../src/js/worklets/core.js" worklet-factory + "../src/js/worklets/shell.js" #js {} + "../src/js/worklets/bottom_sheet.js" #js {} + "../src/js/worklets/record_audio.js" #js {} + "../src/js/worklets/scroll_view.js" #js {} "./fleets.js" default-fleets "@walletconnect/client" wallet-connect-client "../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json")) diff --git a/src/quo2/components/community/community_list_view.cljs b/src/quo2/components/community/community_list_view.cljs index 4defe1b3fc..1ef185e7ef 100644 --- a/src/quo2/components/community/community_list_view.cljs +++ b/src/quo2/components/community/community_list_view.cljs @@ -78,42 +78,42 @@ [props {:keys [name muted? - unread-messages? - unread-mentions-count + unviewed-messages-count + unviewed-mentions-count status - community-icon + images tokens - locked?]}] + locked? + style]}] [rn/touchable-highlight (merge {:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-95) :style {:border-radius 12}} props) - [rn/view {:flex 1} - [rn/view (style/membership-info-container) - [community-icon/community-icon - {:images community-icon} 32] - [rn/view - {:flex 1 - :margin-left 12 - :justify-content :center} - [text/text - {:accessibility-label :chat-name-text - :number-of-lines 1 - :ellipsize-mode :tail - :weight :semi-bold - :size :paragraph-1} - name]] + [rn/view (merge (style/membership-info-container) style) + [community-icon/community-icon + {:images images} 32] + [rn/view + {:flex 1 + :margin-left 12 + :justify-content :center} + [text/text + {:accessibility-label :chat-name-text + :number-of-lines 1 + :ellipsize-mode :tail + :weight :semi-bold + :size :paragraph-1} + name]] - [rn/view - {:justify-content :center - :margin-right 16} - (if (= status :gated) - [community-view/permission-tag-container - {:locked? locked? - :tokens tokens}] - [notification-view - {:muted? muted? - :unread-mentions-count unread-mentions-count - :unread-messages? unread-messages?}])]]]]) + [rn/view + {:justify-content :center + :margin-right 16} + (if (= status :gated) + [community-view/permission-tag-container + {:locked? locked? + :tokens tokens}] + [notification-view + {:muted? muted? + :unread-mentions-count unviewed-mentions-count + :unread-messages? (pos? unviewed-messages-count)}])]]]) diff --git a/src/quo2/components/community/discover_card.cljs b/src/quo2/components/community/discover_card.cljs index 10149faa0e..21b821e7c3 100644 --- a/src/quo2/components/community/discover_card.cljs +++ b/src/quo2/components/community/discover_card.cljs @@ -92,6 +92,7 @@ colors/neutral-90)} {:flex-direction :row :margin-horizontal 20 + :margin-vertical 8 :height 56 :padding-right 12}) [card-title-and-description title description] diff --git a/src/quo2/components/community/style.cljs b/src/quo2/components/community/style.cljs index f773ac73a4..5dc31f0118 100644 --- a/src/quo2/components/community/style.cljs +++ b/src/quo2/components/community/style.cljs @@ -15,9 +15,9 @@ :shadow-radius radius :shadow-opacity 1 :shadow-color colors/shadow + :elevation 1 :border-radius radius :justify-content :space-between - :elevation 2 :background-color (colors/theme-colors colors/white colors/neutral-90)}) diff --git a/src/quo2/components/record_audio/record_audio/buttons/record_button_big.cljs b/src/quo2/components/record_audio/record_audio/buttons/record_button_big.cljs index 43f4243f04..6f6244e6c5 100644 --- a/src/quo2/components/record_audio/record_audio/buttons/record_button_big.cljs +++ b/src/quo2/components/record_audio/record_audio/buttons/record_button_big.cljs @@ -8,7 +8,8 @@ [taoensso.timbre :as log] [cljs-bean.core :as bean] [reagent.core :as reagent] - [quo2.components.record-audio.record-audio.helpers :as helpers])) + [quo2.components.record-audio.record-audio.helpers :as helpers] + [utils.worklets.record-audio :as worklets.record-audio])) (def ^:private scale-to-each 1.8) (def ^:private scale-to-total 2.6) @@ -18,14 +19,6 @@ (def ^:private signal-anim-duration 3900) (def ^:private signal-anim-duration-2 1950) -(def ^:private record-audio-worklets (js/require "../src/js/record_audio_worklets.js")) - -(defn- ring-scale - [scale substract] - (.ringScale ^js record-audio-worklets - scale - substract)) - (def ^:private animated-ring (reagent/adapt-react-class (rn/memo @@ -45,7 +38,9 @@ opacity-from (if @ready-to-lock? opacity-from-lock opacity-from-default) animations (map (fn [index] - (let [ring-scale (ring-scale scale (* scale-padding index))] + (let [ring-scale (worklets.record-audio/ring-scale scale + (* scale-padding + index))] {:scale ring-scale :opacity (reanimated/interpolate ring-scale [1 scale-to-each] diff --git a/src/react_native/core.cljs b/src/react_native/core.cljs index c1e37652ef..ae83a6a4c4 100644 --- a/src/react_native/core.cljs +++ b/src/react_native/core.cljs @@ -98,6 +98,10 @@ (def use-ref react/useRef) +(defn current-ref + [ref] + (oops/oget ref "current")) + (defn use-effect ([effect-fn] (use-effect effect-fn [])) diff --git a/src/react_native/reanimated.cljs b/src/react_native/reanimated.cljs index 78dc29b4f6..7d33b627c7 100644 --- a/src/react_native/reanimated.cljs +++ b/src/react_native/reanimated.cljs @@ -18,7 +18,8 @@ LinearTransition)] [reagent.core :as reagent] [react-native.flat-list :as rn-flat-list] - [utils.collection])) + [utils.collection] + [utils.worklets.core :as worklets.core])) ;; Animations (def slide-in-up-animation SlideInUp) @@ -29,14 +30,14 @@ (def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated))) (def view (reagent/adapt-react-class (.-View reanimated))) +(def scroll-view (reagent/adapt-react-class (.-ScrollView reanimated))) (def image (reagent/adapt-react-class (.-Image reanimated))) -(def reanimated-flat-list (reagent/adapt-react-class (.-FlatList ^js rn))) +(def reanimated-flat-list (create-animated-component (.-FlatList ^js rn))) (defn flat-list [props] [reanimated-flat-list (rn-flat-list/base-list-props props)]) (def touchable-opacity (create-animated-component (.-TouchableOpacity ^js rn))) - (def linear-gradient (create-animated-component LinearGradient)) (def fast-image (create-animated-component FastImage)) (def blur-view (create-animated-component (.-BlurView blur))) @@ -77,24 +78,20 @@ (when (and anim (some? val)) (set! (.-value anim) val))) -;; Worklets -(def worklet-factory (js/require "../src/js/worklet_factory.js")) - (defn interpolate ([shared-value input-range output-range] (interpolate shared-value input-range output-range nil)) ([shared-value input-range output-range extrapolation] - (.interpolateValue ^js worklet-factory - shared-value - (clj->js input-range) - (clj->js output-range) - (clj->js extrapolation)))) + (worklets.core/interpolate-value + shared-value + (clj->js input-range) + (clj->js output-range) + (clj->js extrapolation)))) -;;;; Component Animations (defn apply-animations-to-style [animations style] (use-animated-style - (.applyAnimationsToStyle ^js worklet-factory (clj->js animations) (clj->js style)))) + (worklets.core/apply-animations-to-style (clj->js animations) (clj->js style)))) ;; Animators (defn animate-shared-value-with-timing diff --git a/src/status_im2/common/bottom_sheet/view.cljs b/src/status_im2/common/bottom_sheet/view.cljs index bbffd898ba..96c1e3f272 100644 --- a/src/status_im2/common/bottom_sheet/view.cljs +++ b/src/status_im2/common/bottom_sheet/view.cljs @@ -10,9 +10,8 @@ [react-native.platform :as platform] [react-native.reanimated :as reanimated] [react-native.safe-area :as safe-area] - [reagent.core :as reagent])) - -(def bottom-sheet-js (js/require "../src/js/bottom_sheet.js")) + [reagent.core :as reagent] + [utils.worklets.bottom-sheet :as worklets.bottom-sheet])) (def animation-delay 450) @@ -129,9 +128,9 @@ bg-height (max (min @content-height bg-height-expanded) 109) bottom-sheet-dy (reanimated/use-shared-value 0) pan-y (reanimated/use-shared-value 0) - translate-y (.useTranslateY ^js bottom-sheet-js window-height bottom-sheet-dy pan-y) + translate-y (worklets.bottom-sheet/use-translate-y window-height bottom-sheet-dy pan-y) bg-opacity - (.useBackgroundOpacity ^js bottom-sheet-js translate-y bg-height window-height) + (worklets.bottom-sheet/use-background-opacity translate-y bg-height window-height) on-content-layout (fn [evt] (let [height (oget evt "nativeEvent" "layout" "height")] (reset! content-height height))) diff --git a/src/status_im2/common/home/view.cljs b/src/status_im2/common/home/view.cljs index 91ddbef424..6c83cf01f2 100644 --- a/src/status_im2/common/home/view.cljs +++ b/src/status_im2/common/home/view.cljs @@ -46,7 +46,7 @@ :style override-style :avatar user-avatar} " - [{:keys [type style avatar hide-search]}] + [{:keys [type style avatar search?] :or {type :default}}] (let [button-common-props (get-button-common-props type) notif-count (rf/sub [:activity-center/unread-count]) new-notifications? (pos? notif-count) @@ -71,7 +71,7 @@ :right 20 :top 12 :flex-direction :row}} - (when-not hide-search + (when search? [base-button :i/search #() :open-search-button button-common-props]) [base-button :i/scan #() :open-scanner-button button-common-props] [base-button :i/qr-code #() :show-qr-button button-common-props] diff --git a/src/status_im2/common/scroll_page/view.cljs b/src/status_im2/common/scroll_page/view.cljs index cea0164c90..fed3bd8126 100644 --- a/src/status_im2/common/scroll_page/view.cljs +++ b/src/status_im2/common/scroll_page/view.cljs @@ -11,9 +11,7 @@ (defn icon-color [] - (colors/theme-colors - colors/white-opa-40 - colors/neutral-80-opa-40)) + (colors/theme-colors colors/white-opa-40 colors/neutral-80-opa-40)) (def negative-scroll-position-0 (if platform/ios? -44 0)) (def scroll-position-0 (if platform/ios? 44 0)) @@ -27,15 +25,7 @@ (min maximum))) (defn scroll-page-header - [scroll-height - height - name - page-nav - logo - sticky-header - top-nav - title-colum - navigate-back?] + [scroll-height height name page-nav logo sticky-header top-nav title-colum navigate-back?] (let [input-range (if platform/ios? [-47 10] [0 10]) output-range (if platform/ios? [-208 0] [-208 -45]) y (reanimated/use-shared-value scroll-height) @@ -120,19 +110,10 @@ (defn scroll-page [_ _ _] (let [scroll-height (reagent/atom negative-scroll-position-0)] - (fn - [{:keys [cover-image - logo - page-nav-right-section-buttons - name - on-scroll - height - top-nav - title-colum - background-color - navigate-back?]} - sticky-header - children] + (fn [{:keys [name cover-image logo page-nav-right-section-buttons on-scroll + height top-nav title-colum background-color navigate-back?]} + sticky-header + children] [:<> [:f> scroll-page-header @scroll-height height name page-nav-right-section-buttons logo sticky-header top-nav title-colum navigate-back?] diff --git a/src/status_im2/common/sticky_scroll_view/view.cljs b/src/status_im2/common/sticky_scroll_view/view.cljs new file mode 100644 index 0000000000..d82828c254 --- /dev/null +++ b/src/status_im2/common/sticky_scroll_view/view.cljs @@ -0,0 +1,100 @@ +(ns status-im2.common.sticky-scroll-view.view + (:require [react-native.reanimated :as reanimated] + [react-native.core :as rn] + [react-native.platform :as platform] + [utils.worklets.scroll-view :as worklets.scroll-view] + [quo2.foundations.colors :as colors])) + +(defn sticky-item + [{:keys [height translation-y background-color]} content] + [:f> + (fn [] + [rn/view (merge {:height height} (when platform/ios? {:z-index 1})) + [reanimated/view + {:style (reanimated/apply-animations-to-style + {:transform [{:translateY translation-y}]} + {:position :absolute + :z-index (when platform/android? 1) + :top 0 + :left 0 + :right 0 + :height height + :background-color background-color})} + content]])]) + +(defn scroll-view + [{:keys [ref scroll-y blur]} & content] + [:f> + (fn [] + (let [scroll-handler (worklets.scroll-view/use-animated-scroll-handler scroll-y) + opacity (when blur + (reanimated/interpolate scroll-y + [0 (:delta blur)] + [0 1] + {:extrapolateLeft "clamp" + :extrapolateRight "extend"}))] + (into [reanimated/scroll-view + {:ref ref + :scroll-event-throttle 1 + :shows-vertical-scroll-indicator false + :contentInsetAdjustmentBehavior :never + :on-scroll scroll-handler}] + (concat + (when blur + ;bug on Android + ;https://github.com/Kureev/react-native-blur/issues/520} + [[reanimated/view + {:style (reanimated/apply-animations-to-style + {:transform [{:translateY scroll-y}] + :opacity opacity} + {:overflow (if platform/ios? :visible :hidden) + :position :absolute + :z-index 1 + :top 0 + :height (:height blur) + :right 0 + :left 0})} + [reanimated/blur-view + {:blur-amount 10 + :blur-type (if (colors/dark?) :dark :xlight) + :style {:position :absolute + :top 0 + :height (:height blur) + :right 0 + :left 0}}]]]) + content))))]) + +(defn flat-list + [{:keys [scroll-y blur header render-fn data]}] + [:f> + (fn [] + (let [scroll-handler (worklets.scroll-view/use-animated-scroll-handler scroll-y) + opacity (when blur + (reanimated/interpolate scroll-y + [0 (:delta blur)] + [0 1] + {:extrapolateLeft "clamp" + :extrapolateRight "extend"}))] + [reanimated/flat-list + {:scroll-event-throttle 1 + :shows-vertical-scroll-indicator false + :contentInsetAdjustmentBehavior :never + :data data + :render-fn render-fn + :header (if blur + [:<> + [reanimated/blur-view + {:blur-amount 10 + :blur-type :xlight + :style (reanimated/apply-animations-to-style + {:transform [{:translateY scroll-y}] + :opacity opacity} + {:z-index 1 + :position :absolute + :top 0 + :height (:height blur) + :right 0 + :left 0})}] + header] + header) + :on-scroll scroll-handler}]))]) diff --git a/src/status_im2/contexts/chat/home/style.cljs b/src/status_im2/contexts/chat/home/style.cljs new file mode 100644 index 0000000000..66c192fa40 --- /dev/null +++ b/src/status_im2/contexts/chat/home/style.cljs @@ -0,0 +1,24 @@ +(ns status-im2.contexts.chat.home.style + (:require [react-native.platform :as platform])) + +(def tabs + {:padding-horizontal 20 + :padding-top 16 + :padding-bottom 12}) + +(def blur + {:position :absolute + :top 0 + :right 0 + :left 0 + :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}) diff --git a/src/status_im2/contexts/chat/home/view.cljs b/src/status_im2/contexts/chat/home/view.cljs index 50418610c2..693601788d 100644 --- a/src/status_im2/contexts/chat/home/view.cljs +++ b/src/status_im2/contexts/chat/home/view.cljs @@ -10,11 +10,16 @@ [status-im2.contexts.chat.home.contact-request.view :as contact-request] [utils.re-frame :as rf] [status-im2.common.contact-list-item.view :as contact-list-item] - [status-im2.common.home.actions.view :as actions])) + [status-im2.common.home.actions.view :as actions] + [react-native.safe-area :as safe-area] + [quo2.foundations.colors :as colors] + [react-native.blur :as blur] + [status-im2.contexts.chat.home.style :as style] + [react-native.platform :as platform])) (defn get-item-layout [_ index] - #js {:length 64 :offset (* 64 index) :index index}) + #js {:length 56 :offset (* 56 index) :index index}) (defn filter-items-by-tab [tab items] @@ -30,20 +35,21 @@ [quo/text (i18n/label :t/blank-messages-text)]]) (defn chats - [selected-tab] + [selected-tab top] (let [{:keys [items search-filter]} (rf/sub [:home-items]) items (filter-items-by-tab selected-tab items)] (if (and (empty? items) (empty? search-filter)) [welcome-blank-chats] [rn/flat-list - {:key-fn #(or (:chat-id %) (:public-key %) (:id %)) - :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 - :content-container-style {:padding-bottom 30}}]))) + {:key-fn #(or (:chat-id %) (:public-key %) (:id %)) + :content-inset-adjustment-behavior :never + :header [rn/view {:height (+ 245 top)}] + :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}]))) (defn welcome-blank-contacts [] @@ -65,63 +71,63 @@ :on-press show-profile-actions}}) item])) -(defn contacts-section-list - [sections] - [rn/section-list - {:key-fn :title - :sticky-section-headers-enabled false - :sections sections - :render-section-header-fn contact-list/contacts-section-header - :content-container-style {:padding-bottom 20} - :render-fn contact-item-render}]) - (defn contacts - [pending-contact-requests] + [pending-contact-requests top] (let [items (rf/sub [:contacts/active-sections])] (if (and (empty? items) (empty? pending-contact-requests)) [welcome-blank-contacts] - [:<> - (when (seq pending-contact-requests) - [contact-request/contact-requests pending-contact-requests]) - (when (seq items) - [contacts-section-list items])]))) + [rn/section-list + {:key-fn :public-key + :get-item-layout get-item-layout + :content-inset-adjustment-behavior :never + :header [:<> + [rn/view {:height (+ 245 top)}] + (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}]))) -(defn tabs +(defn get-tabs-data + [dot?] + [{:id :recent :label (i18n/label :t/recent) :accessibility-label :tab-recent} + {:id :groups :label (i18n/label :t/groups) :accessibility-label :tab-groups} + {:id :contacts + :label (i18n/label :t/contacts) + :accessibility-label :tab-contacts + :notification-dot? dot?}]) + +(defn home [] (let [selected-tab (reagent/atom :recent)] (fn [] (let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests])] - [:<> - [quo/discover-card - {:title (i18n/label :t/invite-friends-to-status) - :description (i18n/label :t/share-invite-link)}] - [quo/tabs - {:style {:margin-left 20 - :margin-bottom 20 - :margin-top 24} - :size 32 - :on-change #(reset! selected-tab %) - :default-active @selected-tab - :data [{:id :recent - :label (i18n/label :t/recent) - :accessibility-label :tab-recent} - {:id :groups - :label (i18n/label :t/groups) - :accessibility-label :tab-groups} - {:id :contacts - :label (i18n/label :t/contacts) - :accessibility-label :tab-contacts - :notification-dot? (pos? (count pending-contact-requests))}]}] - (if (= @selected-tab :contacts) - [contacts pending-contact-requests] - [chats @selected-tab])])))) - -(defn home - [] - [:<> - [common.home/top-nav {:type :default :hide-search true}] - [common.home/title-column - {:label (i18n/label :t/messages) - :handler #(rf/dispatch [:bottom-sheet/show-sheet :new-chat-bottom-sheet {}]) - :accessibility-label :new-chat-button}] - [tabs]]) + [safe-area/consumer + (fn [{:keys [top]}] + [:<> + (if (= @selected-tab :contacts) + [contacts pending-contact-requests top] + [chats @selected-tab top]) + [rn/view + {:style (style/blur-container top)} + [blur/view + {:blur-amount (if platform/ios? 20 10) + :blur-type (if (colors/dark?) :dark (if platform/ios? :light :xlight)) + :style style/blur}] + [common.home/top-nav] + [common.home/title-column + {:label (i18n/label :t/messages) + :handler #(rf/dispatch [:bottom-sheet/show-sheet :new-chat-bottom-sheet + {}]) + :accessibility-label :new-chat-button}] + [quo/discover-card + {:title (i18n/label :t/invite-friends-to-status) + :description (i18n/label :t/share-invite-link)}] + [quo/tabs + {:style style/tabs + :size 32 + :on-change #(reset! selected-tab %) + :default-active @selected-tab + :data (get-tabs-data (pos? (count pending-contact-requests)))}]]])])))) diff --git a/src/status_im2/contexts/communities/discover/view.cljs b/src/status_im2/contexts/communities/discover/view.cljs index 5ca726abab..13e9c6fd87 100644 --- a/src/status_im2/contexts/communities/discover/view.cljs +++ b/src/status_im2/contexts/communities/discover/view.cljs @@ -153,31 +153,24 @@ (get mock-community-item-data :data))])])) (if communities communities communities-ids))]) - (defn communities-lists [selected-tab view-type] - (let [ids-by-user-involvement (rf/sub [:communities/community-ids-by-user-involvement]) - all-communities (rf/sub [:communities/sorted-communities]) - tab @selected-tab] - [rn/view {:style {:flex 1}} - (case tab - :all - (other-communities-list {:communities all-communities - :view-type view-type}) + [rn/view {:style {:flex 1}} + (case @selected-tab + :all + (other-communities-list {:communities (rf/sub [:communities/sorted-communities]) + :view-type view-type}) - :open - (other-communities-list {:communities-ids (:open ids-by-user-involvement) - :view-type view-type}) + :open + [:<>] - :gated - (other-communities-list {:communities-ids (:gated ids-by-user-involvement) - :view-type view-type}) - - [quo/information-box - {:type :error - :icon :i/info} - (i18n/label :t/error)])])) + :gated + [:<>] + [quo/information-box + {:type :error + :icon :i/info} + (i18n/label :t/error)])]) (defn render-communities [selected-tab diff --git a/src/status_im2/contexts/communities/home/style.cljs b/src/status_im2/contexts/communities/home/style.cljs index 2be6305c32..c69e1f3abc 100644 --- a/src/status_im2/contexts/communities/home/style.cljs +++ b/src/status_im2/contexts/communities/home/style.cljs @@ -1,38 +1,25 @@ (ns status-im2.contexts.communities.home.style (:require [react-native.platform :as platform])) -(defn community-segments - [padding-top] - {:padding-bottom 12 - :padding-top padding-top - :padding-horizontal 20 - :background-color :transparent}) +(def tabs + {:padding-horizontal 20 + :padding-top 16 + :padding-bottom 12}) -(def communities-header-style - {:padding-vertical 8 - :margin-top (if platform/ios? 208 112)}) +(def blur + {:position :absolute + :top 0 + :right 0 + :left 0 + :bottom 0}) -(def render-segments-container - {:flex 1 - :padding-horizontal 20 - :padding-vertical 12}) +(defn blur-container + [top] + {:overflow (if platform/ios? :visible :hidden) + :position :absolute + :z-index 1 + :top 0 + :right 0 + :left 0 + :padding-top top}) -(defn home-communities-container - [background-color] - {:flex 1 - :background-color background-color - :position :absolute - :top (if platform/ios? -44 0) - :bottom 0 - :left 0 - :right 0}) - -(def blur-tabs-header - {:flex 1 - :position :absolute - :top (if platform/ios? 156 112) - :height 52 - :left 0 - :right 0 - :justify-content :center - :background-color :transparent}) diff --git a/src/status_im2/contexts/communities/home/view.cljs b/src/status_im2/contexts/communities/home/view.cljs index c35b86c39d..8cb22a8e49 100644 --- a/src/status_im2/contexts/communities/home/view.cljs +++ b/src/status_im2/contexts/communities/home/view.cljs @@ -1,136 +1,74 @@ (ns status-im2.contexts.communities.home.view (:require [utils.i18n :as i18n] [quo2.core :as quo] - [quo2.foundations.colors :as colors] [reagent.core :as reagent] [react-native.core :as rn] [status-im2.common.home.view :as common.home] - [status-im2.common.scroll-page.view :as scroll-page] [status-im2.contexts.communities.menus.community-options.view :as options] + [utils.re-frame :as rf] + [react-native.safe-area :as safe-area] + [react-native.blur :as blur] + [quo2.foundations.colors :as colors] [status-im2.contexts.communities.home.style :as style] - [utils.re-frame :as rf])) + [react-native.platform :as platform])) -(defn community-segments - [selected-tab padding-top] - [rn/view - {:style (style/community-segments padding-top)} - [quo/tabs - {:size 32 - :on-change #(reset! selected-tab %) - :default-active :joined - :data [{:id :joined :label (i18n/label :chats/joined) :accessibility-label :joined-tab} - {:id :pending :label (i18n/label :t/pending) :accessibility-label :pending-tab} - {:id :opened :label (i18n/label :t/opened) :accessibility-label :opened-tab}]}]]) +(defn item-render + [{:keys [id] :as item}] + (let [unviewed-counts (rf/sub [:communities/unviewed-counts id]) + item (merge item unviewed-counts)] + [quo/communities-membership-list-item + {:style {:padding-horizontal 18} + :on-press #(rf/dispatch [:navigate-to-nav2 :community-overview id]) + :on-long-press #(rf/dispatch + [:bottom-sheet/show-sheet + {:content (fn [] + [options/community-options-bottom-sheet id]) + :selected-item (fn [] + [quo/communities-membership-list-item {} item])}])} + item])) -(defn communities-list - [communities-ids] - [rn/view - (map-indexed - (fn [index id] - (let [community (rf/sub [:communities/home-item id])] - ^{:key index} - [quo/communities-membership-list-item - {:on-press #(rf/dispatch [:navigate-to-nav2 :community-overview id]) - :on-long-press #(rf/dispatch - [:bottom-sheet/show-sheet - {:content (fn [] - [options/community-options-bottom-sheet id]) - :selected-item (fn [] - [quo/communities-membership-list-item {} community])}])} - community])) - communities-ids)]) - - -(defn render-communities-segments - [selected-tab] - (let [ids-by-user-involvement (rf/sub [:communities/community-ids-by-user-involvement]) - tab @selected-tab] - [rn/view - {:style style/render-segments-container} - (case tab - :joined - [communities-list (:joined ids-by-user-involvement)] - - :pending - [communities-list (:pending ids-by-user-involvement)] - - :opened - [communities-list (:opened ids-by-user-involvement)] - - [quo/information-box - {:type :error - :icon :i/info} - (i18n/label :t/error)])])) - -(defn communities-header - [selected-tab padding-top] - [:<> - [rn/view - {:style style/communities-header-style} - [quo/discover-card - {:on-press #(rf/dispatch [:navigate-to :discover-communities]) - :title (i18n/label :t/discover) - :description (i18n/label :t/whats-trending) - :accessibility-label :communities-home-discover-card}]] - [community-segments selected-tab padding-top]]) - -(defn home-page-comunity-lists - [{:keys [selected-tab padding-top]}] - [rn/view {:style {:flex 1}} - [communities-header selected-tab padding-top] - [render-communities-segments selected-tab]]) - -(defn home-sticky-header - [{:keys [selected-tab scroll-height padding-top]}] - (when (> @scroll-height 80) - [rn/view - {:style style/blur-tabs-header} - [community-segments selected-tab padding-top]])) - -(defn home-nav - [] - [common.home/top-nav - {:type :default - :hide-search true - :style {:background-color :transparent}}]) - -(defn title-column - [] - [common.home/title-column - {:label (i18n/label :t/communities) - :handler #(rf/dispatch [:bottom-sheet/show-sheet :add-new {}]) - :accessibility-label :new-chat-button}]) - -(defn communities-screen-content - [] - (let [scroll-height (reagent/atom 0) - selected-tab (reagent/atom :joined)] - (fn [] - [scroll-page/scroll-page - {:name (i18n/label :t/communities) - :on-scroll #(reset! scroll-height %) - :top-nav [home-nav] - :title-colum [title-column] - :background-color (colors/theme-colors - colors/white - colors/neutral-95) - :navigate-back? :false - :height (if (> @scroll-height 80) - 208 - 156)} - [home-sticky-header - {:selected-tab selected-tab - :scroll-height scroll-height - :padding-top 8}] - [home-page-comunity-lists - {:selected-tab selected-tab - :padding-top 16 - :height 60}]]))) +(def tabs-data + [{:id :joined :label (i18n/label :chats/joined) :accessibility-label :joined-tab} + {:id :pending :label (i18n/label :t/pending) :accessibility-label :pending-tab} + {:id :opened :label (i18n/label :t/opened) :accessibility-label :opened-tab}]) (defn home [] - [rn/view - {:style (style/home-communities-container (colors/theme-colors - colors/white - colors/neutral-95))} - [communities-screen-content]]) + (let [selected-tab (reagent/atom :joined)] + (fn [] + (let [{:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status]) + selected-items (case @selected-tab + :joined joined + :pending pending + :opened opened)] + [safe-area/consumer + (fn [{:keys [top]}] + [:<> + [rn/flat-list + {:key-fn :id + :content-inset-adjustment-behavior :never + :header [rn/view {:height (+ 245 top)}] + :render-fn item-render + :data selected-items}] + [rn/view + {:style (style/blur-container top)} + [blur/view + {:blur-amount (if platform/ios? 20 10) + :blur-type (if (colors/dark?) :dark (if platform/ios? :light :xlight)) + :style style/blur}] + [common.home/top-nav] + [common.home/title-column + {:label (i18n/label :t/communities) + :handler #(rf/dispatch [:bottom-sheet/show-sheet :add-new {}]) + :accessibility-label :new-chat-button}] + [quo/discover-card + {:on-press #(rf/dispatch [:navigate-to :discover-communities]) + :title (i18n/label :t/discover) + :description (i18n/label :t/whats-trending) + :accessibility-label :communities-home-discover-card}] + [quo/tabs + {:size 32 + :style style/tabs + :on-change #(reset! selected-tab %) + :default-active @selected-tab + :data tabs-data}]]])])))) diff --git a/src/status_im2/contexts/shell/animation.cljs b/src/status_im2/contexts/shell/animation.cljs index 286626677a..7a2e7b1313 100644 --- a/src/status_im2/contexts/shell/animation.cljs +++ b/src/status_im2/contexts/shell/animation.cljs @@ -4,7 +4,8 @@ [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])) + [status-im2.contexts.shell.constants :as shell.constants] + [utils.worklets.shell :as worklets.shell])) ;; Atoms (def selected-stack-id (atom nil)) @@ -66,8 +67,6 @@ :top (+ top-empty-space (shell.constants/bottom-tabs-container-height)) :scale minimize-scale})) -(def shell-worklets (js/require "../src/js/shell_worklets.js")) - ;; Shared Values (defn calculate-shared-values [] @@ -87,18 +86,11 @@ (assoc acc stack-opacity-keyword - (.stackOpacity - ^js shell-worklets - (name id) - selected-stack-id-sv) + (worklets.shell/stack-opacity (name id) selected-stack-id-sv) stack-pointer-keyword - (.stackPointer - ^js shell-worklets - (name id) - selected-stack-id-sv) + (worklets.shell/stack-pointer (name id) selected-stack-id-sv) tabs-icon-color-keyword - (.bottomTabIconColor - ^js shell-worklets + (worklets.shell/bottom-tab-icon-color (name id) selected-stack-id-sv home-stack-state-sv @@ -110,28 +102,19 @@ :pass-through? pass-through-sv :home-stack-state home-stack-state-sv :animate-home-stack-left animate-home-stack-left - :home-stack-left (.homeStackLeft - ^js shell-worklets + :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 (.homeStackTop - ^js shell-worklets + :home-stack-top (worklets.shell/home-stack-top home-stack-state-sv (:top home-stack-position)) - :home-stack-opacity (.homeStackOpacity - ^js shell-worklets - home-stack-state-sv) - :home-stack-pointer (.homeStackPointer - ^js shell-worklets - home-stack-state-sv) - :home-stack-scale (.homeStackScale - ^js shell-worklets - home-stack-state-sv - (:scale home-stack-position)) - :bottom-tabs-height (.bottomTabsHeight - ^js shell-worklets + :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))} diff --git a/src/status_im2/contexts/shell/home_stack.cljs b/src/status_im2/contexts/shell/home_stack.cljs index 4426a2c0e2..bd18685f35 100644 --- a/src/status_im2/contexts/shell/home_stack.cljs +++ b/src/status_im2/contexts/shell/home_stack.cljs @@ -1,9 +1,7 @@ (ns status-im2.contexts.shell.home-stack - (:require [react-native.core :as rn] - [react-native.reanimated :as reanimated] - [react-native.safe-area :as safe-area] + (:require [react-native.reanimated :as reanimated] [status-im.ui.screens.wallet.accounts.views :as wallet.accounts] - [status-im2.contexts.chat.home.view :as chat] ;; TODO move to status-im2 + [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] @@ -43,22 +41,19 @@ (defn home-stack [] - [safe-area/consumer - (fn [insets] - [:f> - (fn [] - (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} - [rn/view {:margin-top (:top insets) :flex 1} - [stack-view :communities-stack shared-values] - [stack-view :chats-stack shared-values] - [stack-view :browser-stack shared-values] - [stack-view :wallet-stack shared-values]]]))])]) + [:f> + (fn [] + (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} + [stack-view :communities-stack shared-values] + [stack-view :chats-stack shared-values] + [stack-view :browser-stack shared-values] + [stack-view :wallet-stack shared-values]]))]) diff --git a/src/status_im2/contexts/shell/style.cljs b/src/status_im2/contexts/shell/style.cljs index 4589308130..b36d9d5137 100644 --- a/src/status_im2/contexts/shell/style.cljs +++ b/src/status_im2/contexts/shell/style.cljs @@ -40,7 +40,7 @@ height (or screen-height height)] {:border-bottom-left-radius 20 :border-bottom-right-radius 20 - :background-color (colors/theme-colors colors/neutral-5 colors/neutral-95) + :background-color (colors/theme-colors colors/white colors/neutral-95) :overflow :hidden :position :absolute :width width diff --git a/src/status_im2/contexts/shell/view.cljs b/src/status_im2/contexts/shell/view.cljs index 8799738f5e..e18af5b679 100644 --- a/src/status_im2/contexts/shell/view.cljs +++ b/src/status_im2/contexts/shell/view.cljs @@ -109,10 +109,9 @@ [jump-to-list switcher-cards shell-margin] [top-nav-blur-overlay (:top insets)] [common.home/top-nav - {:type :shell - :hide-search true - :style {:margin-top (:top insets) - :z-index 2}}]])])) + {:type :shell + :style {:margin-top (:top insets) + :z-index 2}}]])])) (defn on-layout [evt] diff --git a/src/status_im2/subs/communities.cljs b/src/status_im2/subs/communities.cljs index 56fd99d535..e80ee75c13 100644 --- a/src/status_im2/subs/communities.cljs +++ b/src/status_im2/subs/communities.cljs @@ -121,21 +121,16 @@ (map :id communities))) (re-frame/reg-sub - :communities/community-ids-by-user-involvement + :communities/grouped-by-status :<- [:communities/communities] - ;; Return communities splitted by level of user participation. Some communities user - ;; already joined, to some of them join request sent and others were opened one day - ;; and their data remained in app-db. - ;; Result map has form: {:joined [id1, id2] :pending [id3, id5] :opened [id4]}" (fn [communities] (reduce (fn [acc community] (let [joined? (:joined community) - requested? (pos? (:requested-to-join-at community)) - id (:id community)] + requested? (pos? (:requested-to-join-at community))] ;; this looks suspicious (cond - joined? (update acc :joined conj id) - requested? (update acc :pending conj id) - :else (update acc :opened conj id)))) + joined? (update acc :joined conj community) + requested? (update acc :pending conj community) + :else (update acc :opened conj community)))) {:joined [] :pending [] :opened []} communities))) diff --git a/src/status_im2/subs/communities_test.cljs b/src/status_im2/subs/communities_test.cljs index c6c81f0907..00ed8c7966 100644 --- a/src/status_im2/subs/communities_test.cljs +++ b/src/status_im2/subs/communities_test.cljs @@ -81,42 +81,6 @@ :unviewed-mentions-count 0} (rf/sub [sub-name community-id]))))) -(h/deftest-sub :communities/community-ids-by-user-involvement - [sub-name] - (testing "Empty communities list" - (swap! rf-db/app-db assoc - :communities - {}) - (is (= {:joined [] :pending [] :opened []} - (rf/sub [sub-name])))) - (testing "Only opened communities" - (swap! rf-db/app-db assoc - :communities/enabled? true - :communities - {"0x1" {:id "0x1" :name "civilized monkeys"} - "0x2" {:id "0x2" :name "Civilized rats"} - "0x3" {:id "0x3" :name "Civilized dolphins"}}) - (is (= {:joined [] :pending [] :opened ["0x1" "0x2" "0x3"]} - (rf/sub [sub-name])))) - (testing "One joined community and two opened ones" - (swap! rf-db/app-db assoc - :communities/enabled? true - :communities - {"0x1" {:id "0x1" :name "civilized monkeys" :joined true} - "0x2" {:id "0x2" :name "Civilized rats"} - "0x3" {:id "0x3" :name "Civilized dolphins"}}) - (is (= {:joined ["0x1"] :pending [] :opened ["0x2" "0x3"]} - (rf/sub [sub-name])))) - (testing "One joined community, one open and one pending" - (swap! rf-db/app-db assoc - :communities/enabled? true - :communities - {"0x1" {:id "0x1" :name "civilized monkeys" :joined true} - "0x2" {:id "0x2" :name "Civilized rats" :requested-to-join-at 1000} - "0x3" {:id "0x3" :name "Civilized dolphins"}}) - (is (= {:joined ["0x1"] :pending ["0x2"] :opened ["0x3"]} - (rf/sub [sub-name]))))) - (h/deftest-sub :communities/sorted-communities [sub-name] (testing "Empty communities list" diff --git a/src/utils/worklets/bottom_sheet.cljs b/src/utils/worklets/bottom_sheet.cljs new file mode 100644 index 0000000000..a8948cafa1 --- /dev/null +++ b/src/utils/worklets/bottom_sheet.cljs @@ -0,0 +1,11 @@ +(ns utils.worklets.bottom-sheet) + +(def bottom-sheet-js (js/require "../src/js/worklets/bottom_sheet.js")) + +(defn use-translate-y + [window-height bottom-sheet-dy pan-y] + (.useTranslateY ^js bottom-sheet-js window-height bottom-sheet-dy pan-y)) + +(defn use-background-opacity + [translate-y bg-height window-height] + (.useBackgroundOpacity ^js bottom-sheet-js translate-y bg-height window-height)) diff --git a/src/utils/worklets/core.cljs b/src/utils/worklets/core.cljs new file mode 100644 index 0000000000..dcb75edd35 --- /dev/null +++ b/src/utils/worklets/core.cljs @@ -0,0 +1,18 @@ +(ns utils.worklets.core) + +(def core-js (js/require "../src/js/worklets/core.js")) + +(defn interpolate-value + [shared-value + input-range + output-range + extrapolation] + (.interpolateValue ^js core-js + shared-value + (clj->js input-range) + (clj->js output-range) + (clj->js extrapolation))) + +(defn apply-animations-to-style + [animations style] + (.applyAnimationsToStyle ^js core-js (clj->js animations) (clj->js style))) diff --git a/src/utils/worklets/record_audio.cljs b/src/utils/worklets/record_audio.cljs new file mode 100644 index 0000000000..f83ae8e1f6 --- /dev/null +++ b/src/utils/worklets/record_audio.cljs @@ -0,0 +1,7 @@ +(ns utils.worklets.record-audio) + +(def ^:private record-audio-worklets (js/require "../src/js/worklets/record_audio.js")) + +(defn ring-scale + [scale substract] + (.ringScale ^js record-audio-worklets scale substract)) diff --git a/src/utils/worklets/scroll_view.cljs b/src/utils/worklets/scroll_view.cljs new file mode 100644 index 0000000000..dc98271802 --- /dev/null +++ b/src/utils/worklets/scroll_view.cljs @@ -0,0 +1,7 @@ +(ns utils.worklets.scroll-view) + +(def scroll-worklet-js (js/require "../src/js/worklets/scroll_view.js")) + +(defn use-animated-scroll-handler + [scroll-y] + (.useAnimatedScrollHandlerWorklet ^js scroll-worklet-js scroll-y)) diff --git a/src/utils/worklets/shell.cljs b/src/utils/worklets/shell.cljs new file mode 100644 index 0000000000..29a46e1588 --- /dev/null +++ b/src/utils/worklets/shell.cljs @@ -0,0 +1,52 @@ +(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-pointer + [id selected-stack-id] + (.stackPointer ^js shell-worklets id selected-stack-id)) + +(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)) + +(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 + id + selected-stack-id-sv + home-stack-state-sv + pass-through-sv + selected-tab-color + default-color + pass-through-color)) + +(defn home-stack-opacity + [home-stack-state-sv] + (.homeStackOpacity ^js shell-worklets home-stack-state-sv)) + +(defn home-stack-pointer + [home-stack-state-sv] + (.homeStackPointer ^js shell-worklets home-stack-state-sv)) + +(defn home-stack-scale + [home-stack-state-sv scale] + (.homeStackScale ^js shell-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 + selected-stack-id-sv + animate-home-stack-left + home-stack-state-sv + left-home-stack-position)) + +(defn home-stack-top + [home-stack-state-sv top-home-stack-position] + (.homeStackTop ^js shell-worklets home-stack-state-sv top-home-stack-position))