Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add floating button page component #17737

Merged
merged 5 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/status_im/ui/screens/profile/user/views.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@
:accessibility-label :appearance-settings-button
:chevron true
:on-press #(re-frame/dispatch [:navigate-to :quo-preview])}])
(when config/quo-preview-enabled?
[list.item/list-item
{:icon :main-icons/appearance
:title "Status IM Components"
:accessibility-label :status-im-common-components
:chevron true
:on-press #(re-frame/dispatch [:navigate-to :status-im-preview])}])
[list.item/list-item
{:icon :main-icons/appearance
:title (i18n/label :t/appearance)
Expand Down
32 changes: 32 additions & 0 deletions src/status_im2/common/floating_button_page/component_spec.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(ns status-im2.common.floating-button-page.component-spec
(:require [quo.core :as quo]
[status-im2.common.floating-button-page.view :as floating-button-page]
[test-helpers.component :as h]))

(h/describe "floating button page"
(h/test "renders with a header and standard button"
(h/render [floating-button-page/view
{:header [quo/page-nav
{:type :title-description
:title "floating button page"
:description "press right icon to swap button type"
:text-align :left
:background :blur
:icon-name :i/close}]
:footer [quo/button {} "continue"]}])
(h/is-truthy (h/get-by-text "continue"))
(h/is-truthy (h/get-by-text "floating button page")))

(h/test "renders with a header and a slide button"
(h/render [floating-button-page/view
{:header [quo/page-nav
{:type :title-description
:title "floating button page"
:description "press right icon to swap button type"
:text-align :left
:background :blur
:icon-name :i/close}]
:footer [quo/slide-button
{:track-text "We gotta slide"
:track-icon :face-id}]}])
(h/is-truthy (h/get-by-text "We gotta slide"))))
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
(ns status-im2.common.floating-button-page.floating-container.style
(:require [react-native.safe-area :as safe-area]))

(defn content-container
[blur? keyboard-shown?]
(let [margin-bottom (if keyboard-shown? 0 (safe-area/get-bottom))]
(cond-> {:margin-top :auto
:overflow :hidden
:margin-bottom margin-bottom
:padding-vertical 12
:padding-horizontal 20}
blur? (dissoc :padding-vertical :padding-horizontal))))

(def blur-inner-container
{:background-color :transparent ; required, otherwise blur-view will shrink
:padding-vertical 12
:padding-horizontal 20})
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
(ns status-im2.common.floating-button-page.floating-container.view
(:require [quo.theme :as quo.theme]
[react-native.blur :as blur]
[react-native.core :as rn]
[status-im2.common.floating-button-page.floating-container.style :as style]))

(defn- blur-container
[child theme]
[blur/view
{:blur-amount 12
:blur-radius 12
:blur-type (quo.theme/theme-value :light :dark theme)}
[rn/view {:style style/blur-inner-container}
child]])

(defn view-internal
[{:keys [theme on-layout keyboard-shown? blur?]} child]
[rn/view
{:style (style/content-container blur? keyboard-shown?)
:on-layout on-layout}
(if blur?
[blur-container child theme]
child)])

(def view (quo.theme/with-theme view-internal))
15 changes: 15 additions & 0 deletions src/status_im2/common/floating_button_page/style.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(ns status-im2.common.floating-button-page.style)

(def page-container
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})

(def keyboard-avoiding-view
ulisesmac marked this conversation as resolved.
Show resolved Hide resolved
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
102 changes: 102 additions & 0 deletions src/status_im2/common/floating_button_page/view.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
(ns status-im2.common.floating-button-page.view
(:require
[oops.core :as oops]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im2.common.floating-button-page.floating-container.view :as floating-container]
[status-im2.common.floating-button-page.style :as style]))

(defn- show-background
[{:keys [window-height keyboard-height footer-container-height content-scroll-y
content-container-height header-height keyboard-shown?]}]
(let [available-space (- window-height
(safe-area/get-top)
header-height
keyboard-height ; Already contains the bottom safe area value
footer-container-height)
scroll-view-height (- content-container-height content-scroll-y)
overlap? (< available-space scroll-view-height)]
(and keyboard-shown? overlap?)))

(defn- set-height-on-layout
[ratom]
(fn [event]
(let [height (oops/oget event "nativeEvent.layout.height")]
(reset! ratom height))))

(defn- init-keyboard-listeners
[{:keys [on-did-show]}]
(let [keyboard-will-show? (reagent/atom false)
keyboard-did-show? (reagent/atom false)
add-listener (fn [listener callback]
(oops/ocall rn/keyboard "addListener" listener callback))
will-show-listener (add-listener "keyboardWillShow"
#(reset! keyboard-will-show? true))
did-show-listener (add-listener "keyboardDidShow"
(fn [e]
(reset! keyboard-did-show? true)
(when on-did-show (on-did-show e))))
will-hide-listener (add-listener "keyboardWillHide"
#(reset! keyboard-will-show? false))
did-hide-listener (add-listener "keyboardDidHide"
#(reset! keyboard-did-show? false))
remove-listeners (fn []
(doseq [listener [will-show-listener will-hide-listener
did-show-listener did-hide-listener]]
(oops/ocall listener "remove")))]
{:keyboard-will-show? keyboard-will-show?
:keyboard-did-show? keyboard-did-show?
:remove-listeners remove-listeners}))

(defn view
[{:keys [header footer]} & children]
(reagent/with-let [window-height (:height (rn/get-window))
footer-container-height (reagent/atom 0)
header-height (reagent/atom 0)
content-container-height (reagent/atom 0)
content-scroll-y (reagent/atom 0)
keyboard-height (reagent/atom 0)
{:keys [keyboard-will-show?
keyboard-did-show?
remove-listeners]} (init-keyboard-listeners
{:on-did-show
(fn [e]
(reset! keyboard-height
(oops/oget e "endCoordinates.height")))})
set-header-height (set-height-on-layout header-height)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on setting up everything outside the renderer!

@J-Son89, @ulisesmac I think here we can learn from React hooks how to decouple views from stateful concepts, even if we're just using Reagent. The idea is simple, we extract to a function meaningful bundles of code and return a map of values/functions that operate as an interface of sorts that hides the inner complexities of the stateful code. I think this technique can be used much much more in our repo.

I suggest the following diff, which I very much enjoyed writing :) I wanted to see if it would be an improvement and if it actually works.

+(defn- init-keyboard-listeners
+  [{:keys [on-did-show]}]
+  (let [keyboard-will-show? (reagent/atom false)
+        keyboard-did-show?  (reagent/atom false)
+        will-show-listener  (oops/ocall rn/keyboard
+                                        :addListener
+                                        :keyboardWillShow
+                                        #(reset! keyboard-will-show? true))
+        did-show-listener   (oops/ocall rn/keyboard
+                                        :addListener
+                                        :keyboardDidShow
+                                        (fn [e]
+                                          (reset! keyboard-did-show? true)
+                                          (when on-did-show
+                                            (on-did-show e))))
+        will-hide-listener  (oops/ocall rn/keyboard
+                                        :addListener
+                                        :keyboardWillHide
+                                        #(reset! keyboard-will-show? false))
+        did-hide-listener   (oops/ocall rn/keyboard
+                                        :addListener
+                                        :keyboardDidHide
+                                        #(reset! keyboard-did-show? false))]
+    {:keyboard-will-show? keyboard-will-show?
+     :keyboard-did-show?  keyboard-did-show?
+     :remove-listeners    (fn []
+                            (doseq [listener [will-show-listener will-hide-listener did-show-listener
+                                              did-hide-listener]]
+                              (oops/ocall listener :remove)))}))
+
 (defn view
   [{:keys [header footer]} & children]
   (reagent/with-let [window-height                (:height (rn/get-window))
@@ -34,33 +64,19 @@
                      content-container-height     (reagent/atom 0)
                      content-scroll-y             (reagent/atom 0)
                      keyboard-height              (reagent/atom 0)
-                     keyboard-will-show?          (reagent/atom false)
-                     keyboard-did-show?           (reagent/atom false)
-                     will-show-listener           (oops/ocall rn/keyboard
-                                                              "addListener"
-                                                              "keyboardWillShow"
-                                                              #(reset! keyboard-will-show? true))
-                     did-show-listener            (oops/ocall rn/keyboard
-                                                              "addListener"
-                                                              "keyboardDidShow"
-                                                              (fn [e]
-                                                                (reset! keyboard-height
-                                                                  (oops/oget e "endCoordinates.height"))
-                                                                (reset! keyboard-did-show? true)))
-                     will-hide-listener           (oops/ocall rn/keyboard
-                                                              "addListener"
-                                                              "keyboardWillHide"
-                                                              #(reset! keyboard-will-show? false))
-                     did-hide-listener            (oops/ocall rn/keyboard
-                                                              "addListener"
-                                                              "keyboardDidHide"
-                                                              #(reset! keyboard-did-show? false))
+                     {:keys [keyboard-will-show?
+                             keyboard-did-show?
+                             remove-listeners]}   (init-keyboard-listeners
+                                                   {:on-did-show
+                                                    (fn [e]
+                                                      (reset! keyboard-height
+                                                        (oops/oget e :endCoordinates.height)))})
                      set-header-height            (set-height-on-layout header-height)
                      set-content-container-height (set-height-on-layout content-container-height)
                      set-footer-container-height  (set-height-on-layout footer-container-height)
                      set-content-y-scroll         (fn [event]
                                                     (reset! content-scroll-y
-                                                      (oops/oget event "nativeEvent.contentOffset.y")))]
+                                                      (oops/oget event :nativeEvent.contentOffset.y)))]
     (let [keyboard-shown?  (if platform/ios? @keyboard-will-show? @keyboard-did-show?)
           show-background? (show-background {:window-height            window-height
                                              :footer-container-height  @footer-container-height
@@ -89,5 +105,4 @@
           :blur?           show-background?}
          footer]]])
     (finally
-     (doseq [listener [will-show-listener will-hide-listener did-show-listener did-hide-listener]]
-       (oops/ocall listener "remove")))))
+     (remove-listeners))))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice suggestion! :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ilmotta it sounds good 👍
the reading of this component is hard, it has been definitely improved 👍

set-content-container-height (set-height-on-layout content-container-height)
set-footer-container-height (set-height-on-layout footer-container-height)
set-content-y-scroll (fn [event]
(reset! content-scroll-y
(oops/oget event "nativeEvent.contentOffset.y")))]
(let [keyboard-shown? (if platform/ios? @keyboard-will-show? @keyboard-did-show?)
show-background? (show-background {:window-height window-height
:footer-container-height @footer-container-height
:keyboard-height @keyboard-height
:content-scroll-y @content-scroll-y
:content-container-height @content-container-height
:header-height @header-height
:keyboard-shown? keyboard-shown?})]

[rn/view {:style style/page-container}
[rn/view {:on-layout set-header-height}
header]
[rn/scroll-view
{:on-scroll set-content-y-scroll
:scroll-event-throttle 64
:content-container-style {:flex-grow 1}}
(into [rn/view {:on-layout set-content-container-height}]
children)]
[rn/keyboard-avoiding-view
{:style style/keyboard-avoiding-view
:keyboard-vertical-offset (if platform/ios? (safe-area/get-top) 0)
:pointer-events :box-none}
[floating-container/view
{:on-layout set-footer-container-height
:keyboard-shown? keyboard-shown?
:blur? show-background?}
footer]]])
(finally
(remove-listeners))))
4 changes: 2 additions & 2 deletions src/status_im2/contexts/quo_preview/common.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@


(defn- view-internal
[{:keys [theme]}]
[{:keys [theme title]}]
(let [logged-in? (rf/sub [:multiaccount/logged-in?])
has-profiles? (boolean (rf/sub [:profile/profiles-overview]))
root (if has-profiles? :profiles :intro)
light? (= theme :light)]
[quo/page-nav
{:type :title
:title "quo components preview"
:title title
:text-align :left
:icon-name :i/close
:right-side [{:icon-name (if light? :i/dark :i/light)
Expand Down
2 changes: 1 addition & 1 deletion src/status_im2/contexts/quo_preview/main.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@
(defn- main-screen
[]
[:<>
[common/navigation-bar]
[common/navigation-bar {:title "Quo components preview"}]
[rn/scroll-view {:style (style/main)}
(for [category (sort screens-categories)]
^{:key (first category)}
Expand Down
6 changes: 3 additions & 3 deletions src/status_im2/contexts/quo_preview/preview.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,10 @@
children)])

(defn- f-preview-container
[{:keys [state descriptor blur? blur-dark-only?
[{:keys [title state descriptor blur? blur-dark-only?
component-container-style
blur-container-style blur-view-props blur-height show-blur-background?]
:or {blur-height 200}}
:or {blur-height 200 title "quo component"}}
& children]
(let [theme (quo.theme/use-theme-value)]
(rn/use-effect (fn []
Expand All @@ -332,7 +332,7 @@
[rn/view
{:style {:top (safe-area/get-top)
:flex 1}}
[common/navigation-bar]
[common/navigation-bar {:title title}]
[rn/scroll-view
{:style (style/panel-basic)
:shows-vertical-scroll-indicator false}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(ns status-im2.contexts.status-im-preview.common.floating-button-page.style
(:require [quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))

(defn container
[]
{:flex 1
:margin-top (safe-area/get-top)})

(def background-image
{:position :absolute
:top (- (safe-area/get-top))
:left 0
:right 0
:bottom 200})
ulisesmac marked this conversation as resolved.
Show resolved Hide resolved

(defn page-content
[height]
{:flex 1
:height height
:overflow :hidden
:background-color (colors/resolve-color :army 30)})
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
(ns status-im2.contexts.status-im-preview.common.floating-button-page.view
(:require [quo.core :as quo]
[re-frame.core :as rf]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.common.floating-button-page.view :as floating-button-page]
[status-im2.common.resources :as resources]
[status-im2.contexts.status-im-preview.common.floating-button-page.style :as style]))

(defn view
[]
(let [content-height (reagent/atom 450)
slide? (reagent/atom false)]
(fn []
[rn/view {:style (style/container)}
(when-not @slide?
[rn/image
{:style style/background-image
:source (resources/get-mock-image :dark-blur-bg)}])
[floating-button-page/view
{:header [quo/page-nav
{:type :title-description
:title "floating button page"
:description "press right icon to swap button type"
:text-align :left
:right-side [{:icon-name :i/swap
:on-press #(swap! slide? not)}]
:background :blur
:icon-name :i/close
:on-press #(rf/dispatch [:navigate-back])}]
:footer (if @slide?
[quo/slide-button
{:track-text "We gotta slide"
:track-icon :face-id
:container-style {:z-index 2}
:customization-color :blue
:on-complete #(js/alert "button slid")}
"Save address"]
[quo/button
{:container-style {:z-index 2}
:on-press #(js/alert "button pressed")}
"Save address"])}
[rn/view {:style (style/page-content @content-height)}
[quo/text {:size :heading-1} "Page Content"]
[quo/input
{:auto-focus true
:value ""}]
[quo/button
{:type :outline
:on-press #(swap! content-height (fn [v] (+ v 10)))}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partial is your friend here.

Suggested change
:on-press #(swap! content-height (fn [v] (+ v 10)))}
:on-press #(swap! content-height (partial + 10))}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on @smohamedjavid suggestion, we don't need an anonymous function.

(let [a (atom 1)]
  (swap! a + 10)
  @a) ; => 11

So just #(swap! content-height + 10) is enough. Same idea for the on-press that subtracts 10 a few lines below.

"increase height"]
[quo/button
{:type :outline
:on-press #(swap! content-height (fn [v] (- v 10)))}
"decrease height"]]]])))
59 changes: 59 additions & 0 deletions src/status_im2/contexts/status_im_preview/main.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
(ns status-im2.contexts.status-im-preview.main
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure we're trying to follow the naming scheme from quo preview, but it's more idiomatic to name the "main" namespace as core.

(:refer-clojure :exclude [filter])
(:require
[quo.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.contexts.quo-preview.common :as common]
[status-im2.contexts.status-im-preview.common.floating-button-page.view :as floating-button-page]
[status-im2.contexts.status-im-preview.style :as style]
[utils.re-frame :as rf]))

(def screens-categories
{:common [{:name :floating-button-page
:component floating-button-page/view}]})

(defn- category-view
[]
(let [open? (reagent/atom false)
on-press #(swap! open? not)]
(fn [category]
[rn/view {:style {:margin-vertical 8}}
[quo/dropdown
{:type :grey
:state (if @open? :active :default)
:on-press on-press}
(name (key category))]
(when @open?
(for [{category-name :name} (val category)]
^{:key category-name}
[quo/button
{:type :outline
:container-style {:margin-vertical 8}
:on-press #(rf/dispatch [:navigate-to category-name])}
(name category-name)]))])))

(defn- main-screen
[]
[:<>
[common/navigation-bar {:title "Status IM components"}]
[rn/scroll-view {:style (style/main)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We won't need a flat list for the screens of this type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as far as I know that's not the case, we can check if it's as simple as replacing this scroll-view here but if it starts adding complexity then it will be easier to add a separate component using many of the same sub components/approach as here.

(for [category (sort screens-categories)]
^{:key (first category)}
[category-view category])]])

(def screens
(->> screens-categories
(map val)
flatten
(map (fn [subcategory]
(update-in subcategory
[:options :topBar]
merge
{:visible false})))))

(def main-screens
[{:name :status-im-preview
:options {:topBar {:visible false}
:insets {:top? true}}
:component main-screen}])
Loading