feat: add scrolling in community (#14281)

This commit is contained in:
Jamie Caprani 2022-11-28 10:24:31 +00:00 committed by GitHub
parent 42de2a6384
commit 0e614d51ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 383 additions and 193 deletions

View File

@ -41,7 +41,8 @@
[icons/icon icon (icon-props icon-color :big)]]])
(defn- mid-section-comp
[{:keys [description-user-icon horizontal-description? text-secondary-color align-mid? text-color icon main-text type description]}]
[{:keys [description-img description-user-icon horizontal-description?
text-secondary-color align-mid? text-color icon main-text type description]}]
[rn/view {:style (assoc
centrify-style
:flex-direction :row
@ -49,13 +50,16 @@
(when (or (and (not horizontal-description?)
align-mid?
(not= :text-with-description type))
(and description-user-icon
(and (or description-img description-user-icon)
(not icon)))
[rn/image {:source {:uri description-user-icon}
:style {:width 32
:height 32
:border-radius 32
:margin-right 8}}])
(if description-img
[rn/view {:margin-right 8}
[description-img]]
[rn/image {:source {:uri description-user-icon}
:style {:width 32
:height 32
:border-radius 32
:margin-right 8}}]))
[rn/view {:style {:flex-direction (if horizontal-description?
:row
:column)}}
@ -99,7 +103,6 @@
:style {:padding-horizontal 4
:color text-color}}
main-text]
[icons/icon right-icon
(icon-props main-text-icon-color :big)]]
:text-with-one-icon [rn/view {:style {:flex-direction :row}}
@ -163,6 +166,7 @@
:description-color color
:description-icon icon
:description-user-icon icon
:description-img a render prop which will be used in place of :description-user-icon
:main-text-icon-color color
}
:left-section
@ -189,6 +193,7 @@
mid-section-props
{:type (:type mid-section)
:horizontal-description? horizontal-description?
:description-img (:description-img mid-section)
:main-text (:main-text mid-section)
:main-text-icon-color (:main-text-icon-color mid-section)
:one-icon-align-left? one-icon-align-left?

View File

@ -1,10 +1,12 @@
(ns react-native.core
(:require [reagent.core :as reagent]
["react-native" :as react-native]
["@react-native-community/blur" :as blur]
[react-native.flat-list :as flat-list]
[react-native.section-list :as section-list]))
(def app-state ^js (.-AppState ^js react-native))
(def blur-view (reagent/adapt-react-class (.-BlurView blur)))
(def view (reagent/adapt-react-class (.-View ^js react-native)))
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js react-native)))

View File

@ -11,7 +11,7 @@
thumbnail-image (get-in images [:thumbnail :uri])]
(cond
(= id constants/status-community-id)
[react/image {:source (resources/get-image :status-logo)
[react/image {:source (resources/get-image :status-logo) ;; TODO replace with real data (or remove this code)
:style {:width 40
:height 40}}]
(seq thumbnail-image)
@ -27,7 +27,7 @@
thumbnail-image (get-in images [:thumbnail :uri])]
(cond
(= id constants/status-community-id)
[react/image {:source (resources/get-image :status-logo)
[react/image {:source (resources/get-image :status-logo) ;; TODO replace with real data
:style {:width size
:height size}}]
(seq thumbnail-image)

View File

@ -1,23 +1,38 @@
(ns status-im2.contexts.communities.overview.style
(:require [quo2.foundations.colors :as colors]))
(defn container1 []
{:flex 1
:height 20
:border-radius 16
:background-color (colors/theme-colors colors/white colors/neutral-90)})
(defn container2 []
{:border-radius 40
:border-width 1
:border-color colors/white
:position :absolute
:top (- (/ 80 2))
:left (/ 70 4)
:padding 2
:background-color (colors/theme-colors colors/white colors/neutral-90)})
(:require [quo.platform :as platform]
[quo2.foundations.colors :as colors]))
(def preview-user
{:flex-direction :row
: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")
:style {:position :absolute
:top (if platform/ios? 44 48)
:height 34
:width "100%"
:flex 1}})
(defn scroll-view-container [border-radius] {:position :absolute
:top -48
:overflow :scroll
:border-radius border-radius
:height "100%"})

View File

@ -1,18 +1,22 @@
(ns status-im2.contexts.communities.overview.view
(:require [i18n.i18n :as i18n]
[react-native.core :as rn]
[quo2.core :as quo]
[utils.re-frame :as rf]
[quo2.components.community.style :as styles]
[quo2.foundations.colors :as colors]
[status-im2.contexts.communities.home.actions.view :as home.actions]
[status-im2.contexts.communities.requests.actions.view :as requests.actions]
[status-im2.contexts.communities.overview.style :as style]
;; TODO move to status-im2 when reimplemented
[status-im.ui.screens.communities.icon :as communities.icon]))
(:require
[i18n.i18n :as i18n]
[react-native.core :as rn]
[quo2.core :as quo]
[utils.re-frame :as rf]
[quo2.foundations.colors :as colors]
[status-im2.contexts.communities.overview.style :as style]
;; TODO move to status-im2 when reimplemented
[status-im.ui.screens.communities.icon :as communities.icon]
[oops.core :as oops]
[reagent.core :as reagent]
[quo.platform :as platform]
[status-im2.contexts.communities.requests.actions.view :as requests.actions]
[status-im2.contexts.communities.home.actions.view :as home.actions]))
;; Mocked list items
(def user-list
[{:full-name "Alicia K"}
{:full-name "Marcus C"}
@ -21,114 +25,243 @@
(defn preview-user-list []
[rn/view style/preview-user
[quo/preview-list {:type :user
:more-than-99-label (i18n/label :counter-99-plus)
:user user-list :list-size 4 :size 24}]
[quo/preview-list {:type :user
:user user-list :list-size 4 :size 24}]
[quo/text {:accessibility-label :communities-screen-title
:style {:margin-left 8}
:style {:margin-left 8}
:size :label}
"Join Alicia, Marcus and 2 more"]]) ;; TODO remove mocked data and use from contacts list/communities members
"Join Alicia, Marcus and 2 more"]]) ;; TODO remove mocked data and use from contacts list/communities members
(def list-of-channels {:Welcome [{:name "welcome"
(def list-of-channels {:Welcome [{:name "welcome"
:emoji "🤝"}
{:name "onboarding"
:emoji "🍑"}
{:name "intro"
{:name "intro"
:emoji "🦄"}]
:General [{:name "general"
:emoji "🐷"}
{:name "people-ops"
:emoji "🌏"}
{:name "announcements"
{:name "announcements"
:emoji "🎺"}]
:Mobile [{:name "mobile"
: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 []
[rn/scroll-view {:style {:margin-top 20}}
[:<>
(map (fn [category]
^{:key (get category 0)}
[rn/view {:flex 1}
[quo/divider-label
{:label (first category)
:chevron-position :left}]
[rn/view
{:margin-left 8
:margin-top 10
:margin-bottom 8}
[rn/flat-list
{:shows-horizontal-scroll-indicator false
:separator [rn/view {:margin-top 4}]
:data ((first category) list-of-channels)
:render-fn quo/channel-list-item}]]])
list-of-channels)]])
(defn channel-list-component [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"))))))
:style {:margin-top 20 :flex 1}}
(map-indexed (fn [index category]
(let [first-category (first category)]
^{:key first-category}
[rn/view
{:flex 1
:key (str index first-category)
:on-layout #(swap! channel-heights
(fn []
(sort-by :height
(conj @channel-heights
{:height (int (oops/oget % "nativeEvent.layout.y"))
:label first-category}))))}
[quo/divider-label
{:label first-category
:chevron-position :left}]
[rn/view
{: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)])
(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 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 description locked joined
status tokens cover tags community-color] :as community}]
(let [community-icon (memoize (fn [] [communities.icon/community-icon-redesign community 24]))
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)
: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)))
: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}]])
(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]]
[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 overview []
(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)])
{:keys [name description locked joined
status tokens cover tags community-color]} (merge community-mock {:joined (:joined community)})
icon-color (colors/theme-colors colors/white-opa-40 colors/neutral-80-opa-40)]
[rn/view {:flex 1 :border-radius 20}
[rn/view (styles/community-cover-container 148) ;; TODO why component's style is used here ?
[rn/image
{:source cover
:style {:position :absolute
:flex 1}}]
[rn/view {:style {:margin-top 26}}
[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}
: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])}}]]]
[rn/view (style/container1)
[rn/view {:padding-horizontal 20}
[rn/view (style/container2)
[communities.icon/community-icon-redesign community 80]]
(when (and (not joined)
(= status :gated))
[rn/view (styles/permission-tag-styles)
[quo/permission-tag-container
{:locked locked
:status status
:tokens tokens}]])
(when joined
[rn/view {:position :absolute :top 12 :right 12}
[quo/status-tag {:status {:type :positive} :label (i18n/label :joined)}]])
[quo/community-title
{:title name
:size :large
:description description}]
[rn/view {:margin-top 12}
[quo/community-stats-column :card-view]]
[rn/view {:margin-top 16}
[quo/community-tags tags]]
[preview-user-list]
(when (not joined)
;; TODO (flexsurfer) we shouldn't have custom buttons, this should be a component
[quo/button
{:on-press #(rf/dispatch [:bottom-sheet/show-sheet
{:content (constantly [requests.actions/actions 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]]]))
community (rf/sub [:communities/community (:id community-mock)])]
[rn/view {:style
{:position :absolute
:top (if platform/ios? 0 44)
:width "100%"
:height "110%"}}
[community-card-page-view
(merge community-mock {:joined (:joined community)})]]))

View File

@ -1,99 +1,132 @@
(ns status-im2.contexts.communities.requests.actions.view
(:require [i18n.i18n :as i18n]
(:require [status-im.i18n.i18n :as i18n]
[reagent.core :as reagent]
[react-native.core :as rn]
[quo2.core :as quo]
[utils.re-frame :as rf]
[status-im2.contexts.communities.requests.actions.style :as style]
;;TODO remove when not needed anymore
[status-im.react-native.resources :as resources]))
[status-im.react-native.resources :as resources]
[status-im.ui.components.list.views :as list]
[quo.react-native :as rn]
[quo2.components.markdown.text :as text]
[quo2.components.buttons.button :as button]
[quo2.components.selectors.disclaimer :as disclaimer]
[quo2.components.icon :as icon]
[status-im.utils.handlers :refer [>evt]]
[quo2.components.tags.context-tags :as context-tags]
[status-im.communities.core :as communities]
[quo2.foundations.colors :as colors]))
;; TODO: update with real data
(def community-rules [{:index 1
:title "Be respectful"
(def community-rules [{:index 1
:title "Be respectful"
:content "You must respect all users, regardless of your liking towards them. Treat others the way you want to be treated."}
{:index 2
:title "No Inappropriate Language"
{:index 2
:title "No Inappropriate Language"
:content "The use of profanity should be kept to a minimum. However, any derogatory language towards any user is prohibited."}
{:index 3
:title "No spamming"
{:index 3
:title "No spamming"
:content "Don't send a lot of small messages right after each other. Do not disrupt chat by spamming."}
{:index 4
:title "No pornographic, adult or NSFW material"
{:index 4
:title "No pornographic, adult or NSFW material"
:content "This is a community server and not meant to share this kind of material."}
{:index 5
:title "No advertisements"
{:index 5
:title "No advertisements"
:content "We do not tolerate any kind of advertisements, whether it be for other communities or streams."}
{:index 6
:title "No offensive names and profile pictures"
{:index 6
:title "No offensive names and profile pictures"
:content "You will be asked to change your name or picture if the staff deems them inappropriate."}])
(defn community-rule-item [{:keys [title content index]}]
[rn/view {:flex 1 :margin-top 16}
[rn/view {:flex 1 :flex-direction :row :align-items :center}
[rn/view style/community-rule
[quo/text {:style style/community-rule-text
:accessibility-label :communities-rule-index
:weight :medium
:size :label}
[rn/view
{:style {:flex 1 :margin-top 16}}
[rn/view
{:style
{:flex 1
:flex-direction :row
:align-items :center}}
[rn/view
{:style
{:height 18
:width 18
:margin-left 1
:margin-right 9
:background-color colors/white
:border-color colors/neutral-20
:border-width 1
:border-radius 6}}
[text/text {:style
{:margin-left :auto
:margin-right :auto
:margin-top :auto
:margin-bottom :auto}
:accessibility-label :communities-rule-index
:weight :medium
:size :label}
(str index)]]
[quo/text
[text/text
{:accessibility-label :communities-rule-title
:weight :semi-bold
:size :paragraph-2}
title]]
[quo/text
{:style {:margin-left 28 :margin-top 1}
[text/text
{:style {:margin-left 28 :margin-top 1}
:accessibility-label :communities-rule-content
:size :paragraph-2}
:size :paragraph-2}
content]])
(defn community-rules-list [rules]
[rn/flat-list
[list/flat-list
{:shows-horizontal-scroll-indicator false
:data rules
:separator [rn/view {:margin-top 1}]
:separator [rn/view {:margin-top 1}]
:render-fn community-rule-item}])
(defn actions [community]
(defn request-to-join [community]
(let [agreed-to-rules? (reagent/atom false)]
(fn []
;; TODO shouldn't this be a drawer from quo2 ?
[rn/view style/request-container1
[rn/view style/request-container2
[quo/text {:accessibility-label :communities-join-community
:weight :semi-bold
:size :heading-1}
[rn/scroll-view {:style {:margin-left 20 :margin-right 20 :margin-bottom 20}}
[rn/view {:style {:flex 1 :flex-direction :row :align-items :center :justify-content :space-between}}
[text/text {:accessibility-label :communities-join-community
:weight :semi-bold
:size :heading-1}
(i18n/label :t/join-open-community)]
[rn/view style/request-icon
[quo/icon :i/info]]]
[rn/view {:style {:height 32
:width 32
:align-items :center
:background-color colors/white
:border-color colors/neutral-20
:border-width 1
:border-radius 8
:display :flex
:justify-content :center}}
[icon/icon :i/info]]]
;; TODO get tag image from community data
[quo/context-tag
[context-tags/context-tag
{:style
{:margin-right :auto
:margin-top 8}}
:margin-top 8}}
(resources/get-image :status-logo) (:name community)]
[quo/text {:style {:margin-top 24}
:accessibility-label :communities-rules-title
:weight :semi-bold
:size :paragraph-1}
[text/text {:style {:margin-top 24}
:accessibility-label :communities-rules-title
:weight :semi-bold
:size :paragraph-1}
(i18n/label :t/community-rules)]
[community-rules-list community-rules]
[quo/disclaimer
[disclaimer/disclaimer
{:container-style {:margin-top 20}
:on-change #(swap! agreed-to-rules? not)}
:on-change #(swap! agreed-to-rules? not)}
(i18n/label :t/accept-community-rules)]
[rn/view style/request-button
[quo/button {:on-press #(rf/dispatch [:bottom-sheet/hide])
:type :grey :style {:flex 1 :margin-right 12}}
(i18n/label :t/cancel)]
[quo/button
[rn/view {:style {:width "100%"
:margin-top 32 :margin-bottom 16
:flex 1
:flex-direction :row
:align-items :center
:justify-content :space-evenly}}
[button/button {:on-press #(>evt [:bottom-sheet/hide])
:type :grey :style {:flex 1 :margin-right 12}} (i18n/label :t/cancel)]
[button/button
{:on-press (fn []
(rf/dispatch [:communities/join (:id community)])
(rf/dispatch [:bottom-sheet/hide]))
:disabled (not @agreed-to-rules?) :style {:flex 1}}
(i18n/label :t/join-open-community)]]])))
(>evt [::communities/join (:id community)])
(>evt [:bottom-sheet/hide]))
:disabled (not @agreed-to-rules?) :style {:flex 1}} (i18n/label :t/join-open-community)]]])))

View File

@ -35,7 +35,9 @@
(defn status-bar-options []
(if platform/android?
{:navigationBar {:backgroundColor (colors/theme-colors colors/white colors/neutral-100)}
:statusBar {:backgroundColor (colors/theme-colors colors/white colors/neutral-100)
:statusBar {:translucent true
:backgroundColor :transparent
:drawBehind true
:style (if (colors/dark?) :light :dark)}}
{:statusBar {:style (if (colors/dark?) :light :dark)}}))