diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index 1de086edb7..7deec79a6b 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -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) diff --git a/src/status_im2/common/floating_button_page/component_spec.cljs b/src/status_im2/common/floating_button_page/component_spec.cljs new file mode 100644 index 0000000000..16e3d80b41 --- /dev/null +++ b/src/status_im2/common/floating_button_page/component_spec.cljs @@ -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")))) diff --git a/src/status_im2/common/floating_button_page/floating_container/style.cljs b/src/status_im2/common/floating_button_page/floating_container/style.cljs new file mode 100644 index 0000000000..d82b63c6cc --- /dev/null +++ b/src/status_im2/common/floating_button_page/floating_container/style.cljs @@ -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}) diff --git a/src/status_im2/common/floating_button_page/floating_container/view.cljs b/src/status_im2/common/floating_button_page/floating_container/view.cljs new file mode 100644 index 0000000000..e176c721ec --- /dev/null +++ b/src/status_im2/common/floating_button_page/floating_container/view.cljs @@ -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)) diff --git a/src/status_im2/common/floating_button_page/style.cljs b/src/status_im2/common/floating_button_page/style.cljs new file mode 100644 index 0000000000..d6e530676f --- /dev/null +++ b/src/status_im2/common/floating_button_page/style.cljs @@ -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 + {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0}) diff --git a/src/status_im2/common/floating_button_page/view.cljs b/src/status_im2/common/floating_button_page/view.cljs new file mode 100644 index 0000000000..19ebe8285d --- /dev/null +++ b/src/status_im2/common/floating_button_page/view.cljs @@ -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) + 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)))) diff --git a/src/status_im2/contexts/quo_preview/common.cljs b/src/status_im2/contexts/quo_preview/common.cljs index 0ea3615d13..6fb22ce59f 100644 --- a/src/status_im2/contexts/quo_preview/common.cljs +++ b/src/status_im2/contexts/quo_preview/common.cljs @@ -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) diff --git a/src/status_im2/contexts/quo_preview/main.cljs b/src/status_im2/contexts/quo_preview/main.cljs index c495ec8ead..8d50101cef 100644 --- a/src/status_im2/contexts/quo_preview/main.cljs +++ b/src/status_im2/contexts/quo_preview/main.cljs @@ -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)} diff --git a/src/status_im2/contexts/quo_preview/preview.cljs b/src/status_im2/contexts/quo_preview/preview.cljs index 21b10f36ae..72aab421d4 100644 --- a/src/status_im2/contexts/quo_preview/preview.cljs +++ b/src/status_im2/contexts/quo_preview/preview.cljs @@ -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 [] @@ -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} diff --git a/src/status_im2/contexts/status_im_preview/common/floating_button_page/style.cljs b/src/status_im2/contexts/status_im_preview/common/floating_button_page/style.cljs new file mode 100644 index 0000000000..020fe2fcda --- /dev/null +++ b/src/status_im2/contexts/status_im_preview/common/floating_button_page/style.cljs @@ -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}) + +(defn page-content + [height] + {:flex 1 + :height height + :overflow :hidden + :background-color (colors/resolve-color :army 30)}) diff --git a/src/status_im2/contexts/status_im_preview/common/floating_button_page/view.cljs b/src/status_im2/contexts/status_im_preview/common/floating_button_page/view.cljs new file mode 100644 index 0000000000..b7f5cf6054 --- /dev/null +++ b/src/status_im2/contexts/status_im_preview/common/floating_button_page/view.cljs @@ -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)))} + "increase height"] + [quo/button + {:type :outline + :on-press #(swap! content-height (fn [v] (- v 10)))} + "decrease height"]]]]))) diff --git a/src/status_im2/contexts/status_im_preview/main.cljs b/src/status_im2/contexts/status_im_preview/main.cljs new file mode 100644 index 0000000000..b41830ba00 --- /dev/null +++ b/src/status_im2/contexts/status_im_preview/main.cljs @@ -0,0 +1,59 @@ +(ns status-im2.contexts.status-im-preview.main + (: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)} + (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}]) diff --git a/src/status_im2/contexts/status_im_preview/style.cljs b/src/status_im2/contexts/status_im_preview/style.cljs new file mode 100644 index 0000000000..5dbc217b62 --- /dev/null +++ b/src/status_im2/contexts/status_im_preview/style.cljs @@ -0,0 +1,10 @@ +(ns status-im2.contexts.status-im-preview.style + (:require + [quo.foundations.colors :as colors])) + +(defn main + [] + {:flex 1 + :padding-bottom 8 + :padding-horizontal 16 + :background-color (colors/theme-colors colors/white colors/neutral-90)}) diff --git a/src/status_im2/core_spec.cljs b/src/status_im2/core_spec.cljs index 46ba6b2c24..a400d3b73f 100644 --- a/src/status_im2/core_spec.cljs +++ b/src/status_im2/core_spec.cljs @@ -1,5 +1,6 @@ (ns status-im2.core-spec (:require + [status-im2.common.floating-button-page.component-spec] [status-im2.contexts.chat.messages.content.audio.component-spec] [status-im2.contexts.communities.actions.community-options.component-spec] [status-im2.contexts.wallet.create-account.edit-derivation-path.component-spec])) diff --git a/src/status_im2/navigation/screens.cljs b/src/status_im2/navigation/screens.cljs index ae26a93c6f..8757bed1e7 100644 --- a/src/status_im2/navigation/screens.cljs +++ b/src/status_im2/navigation/screens.cljs @@ -32,6 +32,7 @@ [status-im2.contexts.shell.activity-center.view :as activity-center] [status-im2.contexts.shell.jump-to.view :as shell] [status-im2.contexts.shell.share.view :as share] + [status-im2.contexts.status-im-preview.main :as status-im-preview] [status-im2.contexts.syncing.find-sync-code.view :as find-sync-code] [status-im2.contexts.syncing.how-to-pair.view :as how-to-pair] [status-im2.contexts.syncing.scan-sync-code-page.view :as scan-sync-code-page] @@ -46,7 +47,7 @@ [status-im2.contexts.wallet.create-account.select-keypair.view :as wallet-select-keypair] [status-im2.contexts.wallet.create-account.view :as wallet-create-account] [status-im2.contexts.wallet.edit-account.view :as wallet-edit-account] - [status-im2.contexts.wallet.saved-address.view :as wallet-saved-address] + [status-im2.contexts.wallet.saved-addresses.view :as wallet-saved-addresses] [status-im2.contexts.wallet.scan-account.view :as scan-address] [status-im2.contexts.wallet.send.select-address.view :as wallet-select-address] [status-im2.navigation.options :as options] @@ -283,8 +284,8 @@ :options {:insets {:top? true}} :component wallet-create-account/view} - {:name :wallet-saved-address - :component wallet-saved-address/view} + {:name :wallet-saved-addresses + :component wallet-saved-addresses/view} {:name :wallet-select-address :options {:modalPresentationStyle :overCurrentContext} @@ -305,4 +306,11 @@ quo.preview/screens) (when config/quo-preview-enabled? - quo.preview/main-screens))) + quo.preview/main-screens) + + (when config/quo-preview-enabled? + status-im-preview/screens) + + (when config/quo-preview-enabled? + status-im-preview/main-screens))) + diff --git a/test/jest/jestSetup.js b/test/jest/jestSetup.js index 5c15317265..872561033d 100644 --- a/test/jest/jestSetup.js +++ b/test/jest/jestSetup.js @@ -25,6 +25,13 @@ jest.mock('react-native-languages', () => ({ }, })); +jest.mock('react-native-static-safe-area-insets', () => ({ + default: { + safeAreaInsetsTop: 0, + safeAreaInsetsBottom: 0, + }, +})); + jest.mock('react-native-permissions', () => require('react-native-permissions/mock')); jest.mock('@react-native-community/audio-toolkit', () => ({