diff --git a/src/quo2/components/community/community_view.cljs b/src/quo2/components/community/community_view.cljs index fd6d75fb79..105c986b42 100644 --- a/src/quo2/components/community/community_view.cljs +++ b/src/quo2/components/community/community_view.cljs @@ -28,13 +28,9 @@ [community-stats {:icon :i/group :members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from outside this component :icon-color icon-color}] - [community-stats {:icon :i/lightning + [community-stats {:icon :i/lightning :members-count "112.1K" - :icon-color icon-color}] - (when (= type :card-view) - [community-stats {:icon :i/placeholder - :count 4 - :icon-color icon-color}])])) + :icon-color icon-color}]])) (defn community-tags [tags] [rn/view (style/community-tags-container) diff --git a/src/quo2/core.cljs b/src/quo2/core.cljs index 947a4e6dd7..e079c3a8c2 100644 --- a/src/quo2/core.cljs +++ b/src/quo2/core.cljs @@ -15,6 +15,7 @@ quo2.components.community.community-list-view quo2.components.community.community-view quo2.components.community.discover-card + quo2.components.community.token-gating quo2.components.counter.counter quo2.components.dividers.divider-label quo2.components.dividers.new-messages @@ -85,8 +86,12 @@ (def community-title quo2.components.community.community-view/community-title) (def permission-tag-container quo2.components.community.community-view/permission-tag-container) (def discover-card quo2.components.community.discover-card/discover-card) +(def token-gating quo2.components.community.token-gating/token-gating) + ;;;; DIVIDERS + + (def divider-label quo2.components.dividers.divider-label/divider-label) (def new-messages quo2.components.dividers.new-messages/new-messages) diff --git a/src/status_im2/common/scroll_page/style.cljs b/src/status_im2/common/scroll_page/style.cljs new file mode 100644 index 0000000000..91394cf764 --- /dev/null +++ b/src/status_im2/common/scroll_page/style.cljs @@ -0,0 +1,32 @@ +(ns status-im2.common.scroll-page.style + (:require + [quo2.foundations.colors :as colors] + [react-native.platform :as platform])) + +(defn image-slider [height] + {:top (if platform/ios? 0 -64) + ;; -64 is needed on android as the scroll doesn't + ;; bounce so this slider won't disapear otherwise + :height height + :z-index 4 + :flex 1}) + +(defn blur-slider [height] + {:blur-amount 32 + :blur-type :xlight + :overlay-color (if platform/ios? colors/white-opa-70 :transparent) + :style {:z-index 5 + :top (if platform/ios? 0 -64) + ;; -64 is needed on android as the scroll doesn't + ;; bounce so this slider won't disapear otherwise + :position :absolute + :height height + :width "100%" + :flex 1}}) + +(defn scroll-view-container [border-radius] + {:position :absolute + :top -48 + :overflow :scroll + :border-radius border-radius + :height "100%"}) \ No newline at end of file diff --git a/src/status_im2/common/scroll_page/view.cljs b/src/status_im2/common/scroll_page/view.cljs new file mode 100644 index 0000000000..d5cb8636d9 --- /dev/null +++ b/src/status_im2/common/scroll_page/view.cljs @@ -0,0 +1,98 @@ +(ns status-im2.common.scroll-page.view + (:require + [react-native.core :as rn] + [quo2.core :as quo] + [utils.re-frame :as rf] + [quo2.foundations.colors :as colors] + [status-im2.common.scroll-page.style :as style] + [oops.core :as oops] + [reagent.core :as reagent] + [react-native.platform :as platform])) + +(defn icon-color [] + (colors/theme-colors + colors/white-opa-40 + colors/neutral-80-opa-40)) + +(defn get-platform-value [value] (if platform/ios? (+ value 44) value)) +(def negative-scroll-position-0 (if platform/ios? -44 0)) +(def scroll-position-0 (if platform/ios? 44 0)) +(def scroll-position-1 (if platform/ios? 86 134)) +(def scroll-position-2 (if platform/ios? -26 18)) + +(defn get-header-size [scroll-height] + (if (<= scroll-height scroll-position-2) + 0 + (->> + (+ (get-platform-value -17) scroll-height) + (* (if platform/ios? 3 1)) + (max 0) + (min (if platform/ios? 100 124))))) + +(def max-image-size 80) +(def min-image-size 32) + +(defn diff-with-max-min [value maximum minimum] + (->> + (+ value scroll-position-0) + (- maximum) + (max minimum) + (min maximum))) + +(defn icon-top-fn [scroll-height] + (if (<= scroll-height negative-scroll-position-0) + -40 + (->> (+ scroll-position-0 scroll-height) + (* (if platform/ios? 3 1)) + (+ -40) + (min 8)))) + +(defn icon-size-fn [scroll-height] + (->> (+ scroll-position-0 scroll-height) + (* (if platform/ios? 3 1)) + (- max-image-size) + (max min-image-size) + (min max-image-size))) + +(defn scroll-page [icon cover page-nav name] + (let [scroll-height (reagent/atom negative-scroll-position-0)] + (fn [sticky-header children] + [:<> + [:<> + [rn/image + {:source cover + :position :absolute + :style (style/image-slider (get-header-size @scroll-height))}] + [rn/blur-view (style/blur-slider (get-header-size @scroll-height))]] + [rn/view {:style {:z-index 6 :margin-top (if platform/ios? 44 0)}} + [quo/page-nav + {:horizontal-description? true + :one-icon-align-left? true + :align-mid? false + :page-nav-color :transparent + :page-nav-background-uri "" + :mid-section {:type :text-with-description + :main-text (when (>= @scroll-height scroll-position-1) name) + :description-img (when (>= @scroll-height scroll-position-1) icon)} + :right-section-buttons (:right-section-buttons page-nav) + :left-section {:icon :i/close + :icon-background-color (icon-color) + :on-press #(rf/dispatch [:navigate-back])}}] + (when sticky-header [sticky-header @scroll-height])] + [rn/scroll-view {:style (style/scroll-view-container (diff-with-max-min @scroll-height 16 0)) + :shows-vertical-scroll-indicator false + :scroll-event-throttle 4 + :on-scroll #(swap! scroll-height (fn [] (int (oops/oget % "nativeEvent.contentOffset.y"))))} + [rn/view {:style {:height 151}} + [rn/image + {:source cover + :style {:overflow :visible + :flex 1}}]] + (when children + [rn/view {:flex 1 + :border-radius (diff-with-max-min @scroll-height 16 0) + :background-color (colors/theme-colors + colors/white + colors/neutral-90)} + [children @scroll-height icon-top-fn icon-size-fn]])]]))) + diff --git a/src/status_im2/contexts/communities/overview/style.cljs b/src/status_im2/contexts/communities/overview/style.cljs index 57cfef4b64..a4c62a83f3 100644 --- a/src/status_im2/contexts/communities/overview/style.cljs +++ b/src/status_im2/contexts/communities/overview/style.cljs @@ -1,5 +1,5 @@ (ns status-im2.contexts.communities.overview.style - (:require [quo.platform :as platform] + (:require [react-native.platform :as platform] [quo2.foundations.colors :as colors])) (def preview-user @@ -7,32 +7,11 @@ :align-items :center :margin-top 20}) -(defn image-slider [height] {:top (if platform/ios? 0 -64) ; -44 -20 (the 20 is needed on android as the scroll doesn't bounce so this won't disapear otherwise) - :height height - :z-index 4 - :flex 1}) - -(defn blur-slider [height] {:blur-amount 32 - :blur-type :xlight - :overlay-color (if platform/ios? colors/white-opa-70 "transparent") - :style {:z-index 5 - :top (if platform/ios? 0 -64) ; -44 -20 (the 20 is needed on android as the scroll doesn't bounce so this won't disapear otherwise) - :position :absolute - :height height - :width "100%" - :flex 1}}) - (def blur-channel-header {:blur-amount 32 :blur-type :xlight - :overlay-color (if platform/ios? colors/white-opa-70 "transparent") + :overlay-color (if platform/ios? colors/white-opa-70 :transparent) :style {:position :absolute - :top (if platform/ios? 44 48) + :top (if platform/ios? 56 60) :height 34 :width "100%" - :flex 1}}) - -(defn scroll-view-container [border-radius] {:position :absolute - :top -48 - :overflow :scroll - :border-radius border-radius - :height "100%"}) \ No newline at end of file + :flex 1}}) \ No newline at end of file diff --git a/src/status_im2/contexts/communities/overview/view.cljs b/src/status_im2/contexts/communities/overview/view.cljs index 3c643dda5a..f6e3434650 100644 --- a/src/status_im2/contexts/communities/overview/view.cljs +++ b/src/status_im2/contexts/communities/overview/view.cljs @@ -8,12 +8,12 @@ [status-im2.contexts.communities.overview.style :as style] ;; TODO move to status-im2 when reimplemented [status-im.ui.screens.communities.icon :as communities.icon] + [status-im2.common.scroll-page.view :as scroll-page] [oops.core :as oops] [reagent.core :as reagent] - [quo.platform :as platform] + [react-native.platform :as platform] [status-im2.contexts.communities.requests.actions.view :as requests.actions] [status-im2.contexts.communities.home.actions.view :as home.actions] - [quo2.components.community.token-gating :as token-gating] [status-im.constants :as constants] [status-im.react-native.resources :as resources] [status-im.utils.utils :as utils])) @@ -44,7 +44,7 @@ #(rf/dispatch [:bottom-sheet/show-sheet {:content - (constantly [token-gating/token-gating + (constantly [quo/token-gating {:channel {:name name :community-color (colors/custom-color :pink 50) :emoji emoji @@ -78,55 +78,58 @@ :is-sufficient? false}]}}}]) :content-height 210}])) -(def list-of-channels {:Welcome [{:name "welcome" - :emoji "🤝"} - {:name "onboarding" - :emoji "🍑" - :locked? true - :on-press #((open-token-gating-mocked - "onboarding" - "🍑" - (colors/custom-color :pink 50)))} - {:name "intro" - :emoji "🦄" - :locked? true - :on-press #((open-token-gating-mocked - "intro" - "🦄" - (colors/custom-color :pink 50)))}] - :General [{:name "general" - :emoji "🐷"} - {:name "people-ops" - :emoji "🌏" - :locked? true - :on-press #((open-token-gating-mocked - "onboarding" - "🌏" - (colors/custom-color :blue 50)))} - {:name "announcements" - :emoji "🎺"}] - :Mobile [{:name "mobile" - :emoji "👽"} - {:name "mobile-ui" - :emoji "👽"} - {:name "mobile-ui-reviews" - :emoji "👽"}] - :Desktop [{:name "desktop" - :emoji "👽"} - {:name "desktop-ui" - :emoji "👽"} - {:name "desktop-ui-reviews" - :emoji "👽"} - {:name "desktop2" - :emoji "👽"} - {:name "desktop-ui2" - :emoji "👽"} - {:name "desktop-ui2-reviews" - :emoji "👽"}]}) +(def mock-list-of-channels {:Welcome [{:name "welcome" + :emoji "🤝"} + {:name "onboarding" + :emoji "🍑" + :locked? true + :on-press #((open-token-gating-mocked + "onboarding" + "🍑" + (colors/custom-color :pink 50)))} + {:name "intro" + :emoji "🦄" + :locked? true + :on-press #((open-token-gating-mocked + "intro" + "🦄" + (colors/custom-color :pink 50)))}] + :General [{:name "general" + :emoji "🐷"} + {:name "people-ops" + :emoji "🌏" + :locked? true + :on-press #((open-token-gating-mocked + "onboarding" + "🌏" + (colors/custom-color :blue 50)))} + {:name "announcements" + :emoji "🎺"}] + :Mobile [{:name "mobile" + :emoji "👽"} + {:name "mobile-ui" + :emoji "👽"} + {:name "mobile-ui-reviews" + :emoji "👽"}] + :Desktop [{:name "desktop" + :emoji "👽"} + {:name "desktop-ui" + :emoji "👽"} + {:name "desktop-ui-reviews" + :emoji "👽"} + {:name "desktop2" + :emoji "👽"} + {:name "desktop-ui2" + :emoji "👽"} + {:name "desktop-ui2-reviews" + :emoji "👽"}]}) -(defn channel-list-component [channel-heights first-channel-height] +(defn channel-list-component-fn [channel-heights first-channel-height] [rn/view {:on-layout #(swap! first-channel-height - (fn [] (+ (if platform/ios? 0 38) (int (Math/ceil (oops/oget % "nativeEvent.layout.y")))))) + (fn [] (+ (if platform/ios? + 0 + 38) + (int (Math/ceil (oops/oget % "nativeEvent.layout.y")))))) :style {:margin-top 20 :flex 1}} (map-indexed (fn [index category] (let [first-category (first category)] @@ -139,7 +142,7 @@ (sort-by :height (conj @channel-heights {:height (int (oops/oget % "nativeEvent.layout.y")) - :label first-category}))))} + :label first-category}))))} [quo/divider-label {:label first-category @@ -148,200 +151,142 @@ {:margin-left 8 :margin-top 10 :margin-bottom 8} - (map-indexed (fn [inner-index channel-data] [rn/view {:key (str inner-index (:name channel-data)) :margin-top 4} - [quo/channel-list-item channel-data]]) (first-category list-of-channels))]])) - list-of-channels)]) + (map-indexed (fn [inner-index channel-data] + [rn/view {:key (str inner-index (:name channel-data)) + :margin-top 4} + [quo/channel-list-item channel-data]]) (first-category mock-list-of-channels))]])) + mock-list-of-channels)]) -(defn icon-color [] - (colors/theme-colors - colors/white-opa-40 - colors/neutral-80-opa-40)) +(def channel-list-component (memoize channel-list-component-fn)) -(defn get-platform-value [value] (if platform/ios? (+ value 44) value)) - -(def scroll-0 (if platform/ios? -44 0)) -(def scroll0 (if platform/ios? 44 0)) -(def scroll1 (if platform/ios? 86 134)) - -(def scroll2 (if platform/ios? -26 18)) - -(def max-image-size 80) -(def min-image-size 32) - -(defn diff-with-max-min [value maximum minimum] - (->> - (+ value scroll0) - (- maximum) - (max minimum) - (min maximum))) - -(defn get-header-size [scroll-height] - (if (<= scroll-height scroll2) - 0 - (->> - (+ (get-platform-value -17) scroll-height) - (* (if platform/ios? 3 1)) - (max 0) - (min (if platform/ios? 100 124))))) - -(defn community-card-page-view [{:keys [name id description locked joined - status tokens cover images tags community-color] :as community}] - (let [community-icon (memoize (fn [] [communities.icon/community-icon-redesign community 24])) - thumbnail-image (get-in images [:thumbnail]) - scroll-height (reagent/atom scroll-0) - channel-heights (reagent/atom []) - first-channel-height (reagent/atom 0)] - - (fn [] - [:<> - [:<> - [rn/image - {:source cover - :position :absolute - :style (style/image-slider (get-header-size @scroll-height))}] - [rn/blur-view (style/blur-slider (get-header-size @scroll-height))]] - [rn/view {:style {:z-index 6 :margin-top (if platform/ios? 56 12)}} - [quo/page-nav - {:horizontal-description? true - :one-icon-align-left? true - :align-mid? false - :page-nav-color :transparent - :page-nav-background-uri "" - :mid-section {:type :text-with-description - :main-text (when (>= @scroll-height scroll1) name) - :description-img (when (>= @scroll-height scroll1) community-icon)} - :right-section-buttons [{:icon :i/search - :background-color (icon-color)} - {:icon :i/options - :background-color (icon-color) - :on-press #(rf/dispatch [:bottom-sheet/show-sheet - {:content (constantly [home.actions/actions community]) - :content-height 400}])}] - :left-section {:icon :i/close - :icon-background-color (icon-color) - :on-press #(rf/dispatch [:navigate-back])}}] - (when (>= @scroll-height @first-channel-height) - [rn/blur-view style/blur-channel-header - [quo/divider-label - {:label (:label (last (filter (fn [{:keys [height]}] - (>= @scroll-height (+ height @first-channel-height))) - @channel-heights))) - :chevron-position :left}]])] - [rn/scroll-view {:style (style/scroll-view-container (diff-with-max-min @scroll-height 16 0)) - :shows-vertical-scroll-indicator false - :scroll-event-throttle 1 - :on-scroll #(swap! scroll-height (fn [] (int (oops/oget % "nativeEvent.contentOffset.y"))))} - [rn/view {:style {:height 151}} - [rn/image - {:source cover - :style {:overflow :visible - :flex 1}}]] - [rn/view {:flex 1 - :border-radius (diff-with-max-min @scroll-height 16 0) +(defn render-page-content [{:keys [name description locked joined id images + status tokens tags community-color] + :as community} + channel-heights first-channel-height] + (let [thumbnail-image (get-in images [:thumbnail])] + (fn [scroll-height icon-top icon-size] + [rn/view + [rn/view {:padding-horizontal 20} + [rn/view {:border-radius 40 + :border-width 1 + :border-color colors/white + :position :absolute + :top (icon-top scroll-height) + :left 17 + :padding 2 :background-color (colors/theme-colors colors/white colors/neutral-90)} - [rn/view - [rn/view {:padding-horizontal 20} - [rn/view {:border-radius 40 - :border-width 1 - :border-color colors/white - :position :absolute - :top (if (<= @scroll-height scroll-0) - -40 - (->> (+ scroll0 @scroll-height) - (* (if platform/ios? 3 1)) - (+ -40) - (min 8))) + [communities.icon/community-icon-redesign community + (icon-size scroll-height)]] + (when (and (not joined) + (= status :gated)) + [rn/view {:position :absolute + :top 8 + :right 8} + [quo/permission-tag-container + {:locked locked + :status status + :tokens tokens + :on-press #(rf/dispatch + [:bottom-sheet/show-sheet + {:content-height 210 + :content + (constantly [quo/token-gating + {:community {:name name + :community-color colors/primary-50 + :community-avatar (cond + (= id constants/status-community-id) + (resources/get-image :status-logo) + (seq thumbnail-image) + thumbnail-image) + :gates {:join [{:token "KNC" + :token-img-src knc-token-img + :amount 200 + :is-sufficient? true} + {:token "MANA" + :token-img-src mana-token-img + :amount 10 + :is-sufficient? false + :is-purchasable true} + {:token "RARE" + :token-img-src rare-token-img + :amount 10 + :is-sufficient? false}]}}}])}])}]]) + (when joined + [rn/view {:position :absolute + :top 12 + :right 12} + [quo/status-tag {:status {:type :positive} :label (i18n/label :t/joined)}]]) + [rn/view {:margin-top 56} + [quo/text + {:accessibility-label :chat-name-text + :number-of-lines 1 + :ellipsize-mode :tail + :weight :semi-bold + :size :heading-1} name]] - :left 17 - :padding 2 - :background-color (colors/theme-colors - colors/white - colors/neutral-90)} - [communities.icon/community-icon-redesign community - (->> (+ scroll0 @scroll-height) - (* (if platform/ios? 3 1)) - (- max-image-size) - (max min-image-size) - (min max-image-size))]] - (when (and (not joined) - (= status :gated)) - [rn/view {:position :absolute - :top 8 - :right 8} - [quo/permission-tag-container - {:locked locked - :status status - :tokens tokens - :on-press #(rf/dispatch - [:bottom-sheet/show-sheet - {:content - (constantly [token-gating/token-gating - {:community {:name name - :community-color colors/primary-50 - :community-avatar (cond - (= id constants/status-community-id) - (resources/get-image :status-logo) - (seq thumbnail-image) - thumbnail-image) - :gates {:join [{:token "KNC" - :token-img-src knc-token-img - :amount 200 - :is-sufficient? true} - {:token "MANA" - :token-img-src mana-token-img - :amount 10 - :is-sufficient? false - :is-purchasable true} - {:token "RARE" - :token-img-src rare-token-img - :amount 10 - :is-sufficient? false}]}}}]) - :content-height 210}])}]]) + [quo/text + {:accessibility-label :community-description-text + :number-of-lines 2 + :ellipsize-mode :tail + :weight :regular + :size :paragraph-1 + :style {:margin-top 8 :margin-bottom 12}} + description] + [quo/community-stats-column :card-view] + [rn/view {:margin-top 12}] + [quo/community-tags tags] + [preview-user-list] + (when-not joined + [quo/button + {:on-press #(rf/dispatch [:bottom-sheet/show-sheet + {:content (constantly [requests.actions/request-to-join community]) + :content-height 300}]) + :override-background-color community-color + :style + {:width "100%" + :margin-top 20 + :margin-left :auto + :margin-right :auto} + :before :i/communities} + (i18n/label :t/join-open-community)])] + [channel-list-component channel-heights first-channel-height]]))) - (when joined - [rn/view {:position :absolute - :top 12 - :right 12} - [quo/status-tag {:status {:type :positive} :label (i18n/label :joined)}]]) - [rn/view {:margin-top 56} - [quo/text - {:accessibility-label :chat-name-text - :number-of-lines 1 - :ellipsize-mode :tail - :weight :semi-bold - :size :heading-1} name]] +(defn render-sticky-header [channel-heights first-channel-height] + (fn [scroll-height] + (when (> scroll-height @first-channel-height) + [rn/blur-view style/blur-channel-header + [quo/divider-label + {:label (:label (last (filter (fn [{:keys [height]}] + (>= scroll-height (+ height @first-channel-height))) + @channel-heights))) + :chevron-position :left}]]))) - [quo/text - {:accessibility-label :community-description-text - :number-of-lines 2 - :ellipsize-mode :tail - :weight :regular - :size :paragraph-1 - :style {:margin-top 8 :margin-bottom 12}} - description] - [quo/community-stats-column :card-view] - [rn/view {:margin-top 12}] - [quo/community-tags tags] - [preview-user-list] - (when-not joined - [quo/button - {:on-press #(rf/dispatch [:bottom-sheet/show-sheet - {:content (constantly [requests.actions/request-to-join community]) - :content-height 300}]) - :override-background-color community-color - :style - {:width "100%" - :margin-top 20 - :margin-left :auto - :margin-right :auto} - :before :i/communities} - (i18n/label :join-open-community)])] - [channel-list-component channel-heights first-channel-height]]]]]))) +(defn community-card-page-view [{:keys [name cover] :as community}] + (let [channel-heights (reagent/atom []) + first-channel-height (reagent/atom 0) + scroll-component (scroll-page/scroll-page + (fn [] [communities.icon/community-icon-redesign community 24]) + cover + {:right-section-buttons [{:icon :i/search + :background-color (scroll-page/icon-color)} + {:icon :i/options + :background-color (scroll-page/icon-color) + :on-press #(rf/dispatch [:bottom-sheet/show-sheet + {:content (constantly [home.actions/actions community]) + :content-height 400}])}]} + name)] + (fn [] + (let [page-component (memoize (render-page-content community channel-heights first-channel-height)) + sticky-header (memoize (render-sticky-header channel-heights first-channel-height))] + (fn [] + (scroll-component + sticky-header + page-component)))))) (defn overview [] - (let [community-mock (rf/sub [:get-screen-params :community-overview]) ;;TODO stop using mock data and only pass community id + (let [community-mock (rf/sub [:get-screen-params :community-overview]) ;TODO stop using mock data and only pass community id community (rf/sub [:communities/community (:id community-mock)])] [rn/view {:style