diff --git a/src/status_im/ui/components/bottom_sheet/styles.cljs b/src/status_im/ui/components/bottom_sheet/styles.cljs index 08ccaabf8e0..4de21dbfc0f 100644 --- a/src/status_im/ui/components/bottom_sheet/styles.cljs +++ b/src/status_im/ui/components/bottom_sheet/styles.cljs @@ -4,7 +4,8 @@ (def border-radius 16) (def bottom-padding (if platform/iphone-x? 34 8)) -(def bottom-view-height 1000) +(def top-padding 8) +(def bottom-view-height 1000) ; should be window-height (def container {:position :absolute @@ -33,9 +34,10 @@ :height (+ content-height bottom-view-height) :bottom (- bottom-view-height) :align-self :stretch - :transform [{:translateY bottom-value}] - :justify-content :flex-start - :padding-bottom bottom-padding}) + :transform [{:translateY bottom-value}]}) + +(def sheet-wrapper {:flex 1 + :justify-content :flex-end}) (def content-header {:height border-radius diff --git a/src/status_im/ui/components/bottom_sheet/view.cljs b/src/status_im/ui/components/bottom_sheet/view.cljs index 2a1d0e28b80..949bf8e56f8 100644 --- a/src/status_im/ui/components/bottom_sheet/view.cljs +++ b/src/status_im/ui/components/bottom_sheet/view.cljs @@ -9,7 +9,7 @@ (def release-animation-duration 150) (def cancellation-animation-duration 100) (def swipe-opacity-range 100) -(def cancellation-height 180) +(def cancellation-coefficient 0.3) (def min-opacity 0.05) (def min-velocity 0.1) @@ -31,13 +31,6 @@ :useNativeDriver true})]) (when (fn? callback) callback))) -(defn animate-panel-open [opacity-value bottom-value] - (animate {:bottom bottom-value - :new-bottom-value 0 - :opacity opacity-value - :new-opacity-value 1 - :duration initial-animation-duration})) - (defn- on-move [{:keys [height bottom-value opacity-value]}] (fn [_ state] @@ -49,35 +42,24 @@ (neg? dy) (animation/set-value bottom-value (/ dy 2)))))) -(defn cancelled? [height dy vy] +(defn- cancelled? [height dy vy] (or (<= min-velocity vy) - (> cancellation-height (- height dy)))) - -(defn- cancel - ([opts] (cancel opts nil)) - ([{:keys [height bottom-value show-sheet? opacity-value]} callback] - (animate {:bottom bottom-value - :new-bottom-value height - :opacity opacity-value - :new-opacity-value 0 - :duration cancellation-animation-duration - :callback #(do (reset! show-sheet? false) - (when (fn? callback) (callback)))}))) + (> (* cancellation-coefficient height) (- height dy)))) (defn- on-release - [{:keys [height bottom-value opacity-value on-cancel] :as opts}] + [{:keys [height bottom-value close-sheet opacity-value] :as opts}] (fn [_ state] (let [{:strs [dy vy]} (js->clj state)] (if (cancelled? height dy vy) - (cancel opts on-cancel) + (close-sheet) (animate {:bottom bottom-value :new-bottom-value 0 :opacity opacity-value :new-opacity-value 1 :duration release-animation-duration}))))) -(defn swipe-pan-responder [opts] +(defn- swipe-pan-responder [opts] (.create react/pan-responder (clj->js @@ -88,73 +70,84 @@ :onPanResponderRelease (on-release opts) :onPanResponderTerminate (on-release opts)}))) -(defn pan-handlers [pan-responder] +(defn- pan-handlers [pan-responder] (js->clj (.-panHandlers pan-responder))) -(defn- bottom-sheet-view - [{:keys [opacity-value bottom-value]}] - (reagent.core/create-class - {:component-did-mount - #(animate-panel-open opacity-value bottom-value) - :reagent-render - (fn [{:keys [opacity-value bottom-value height - content on-cancel disable-drag?] - :or {on-cancel #(re-frame/dispatch [:bottom-sheet/hide])} - :as opts}] - [react/keyboard-avoiding-view {:style styles/container} - [react/touchable-highlight - {:on-press #(cancel opts on-cancel) - :style styles/container} +(defn- on-open [{:keys [bottom-value opacity-value height internal-visible]}] + (reset! internal-visible true) + (animate {:bottom bottom-value + :new-bottom-value 0 + :opacity opacity-value + :new-opacity-value 1 + :duration initial-animation-duration})) - [react/animated-view (styles/shadow opacity-value)]] - [react/animated-view (merge - {:style (styles/content-container height bottom-value)} - (when-not disable-drag? - (pan-handlers (swipe-pan-responder opts)))) - [react/view {:style styles/content-header} - [react/view styles/handle]] - [react/view {:style {:flex 1 - :height height}} - [content]] - [react/view {:style styles/bottom-view}]]])})) +(defn- on-close + [{:keys [bottom-value opacity-value on-cancel height + internal-visible with-callback?]}] + (animate {:bottom bottom-value + :new-bottom-value height + :opacity opacity-value + :new-opacity-value 0 + :duration cancellation-animation-duration + :callback (fn [] + (reset! internal-visible false) + (reagent/flush) + (when (fn? on-cancel) + (on-cancel)))})) -(defn bottom-sheet - [{:keys [show? content-height on-cancel]}] - (let [show-sheet? (reagent/atom show?) - total-content-height (+ content-height styles/border-radius - styles/bottom-padding) - bottom-value (animation/create-value total-content-height) - opacity-value (animation/create-value 0) - opts {:height total-content-height - :bottom-value bottom-value - :opacity-value opacity-value - :show-sheet? show-sheet? - :on-cancel on-cancel}] - (reagent.core/create-class - {:component-will-update - (fn [this [_ new-args]] - (let [old-args (second (.-argv (.-props this))) - old-show? (:show? old-args) - new-show? (:show? new-args) - old-height (:content-height old-args) - new-height (:content-height new-args) - total-content-height (+ new-height - styles/border-radius - styles/bottom-padding) - opts' (assoc opts :height total-content-height)] - (when (and new-show? (not= old-height new-height)) - (animation/set-value bottom-value new-height)) - (cond (and (not old-show?) new-show?) - (reset! show-sheet? true) +;; TODO: Replace with modal, firtstly should convert also popover +;; NOTE: onRequestClose of modal should close sheet +;; TODO: animate content-height change +;; TODO: add max-height +(defn bottom-sheet [] + (let [opacity-value (animation/create-value 0) + bottom-value (animation/create-value 0) + content-height (reagent/atom 200) ; TODO: add a defualt value (half of screen height?) to have better init animation + internal-visible (reagent/atom nil) + external-visible (reagent/atom nil)] + (fn [{:keys [content on-cancel disable-drag? show?] + :or {on-cancel #(re-frame/dispatch [:bottom-sheet/hide])} + :as opts}] + (let [height (+ @content-height + styles/border-radius + styles/bottom-padding + styles/top-padding) + close-sheet (fn [] + (on-close {:opacity-value opacity-value + :bottom-value bottom-value + :height height + :internal-visible internal-visible + :on-cancel on-cancel}))] + (when-not (= @external-visible show?) + (reset! external-visible show?) + (if show? + (on-open {:bottom-value bottom-value + :opacity-value opacity-value + :height height + :internal-visible internal-visible}) + (close-sheet))) + (when @internal-visible + [react/view {:style styles/container} + [react/touchable-highlight {:style styles/container + :on-press #(close-sheet)} + [react/animated-view {:style (styles/shadow opacity-value)}]] - (and old-show? (false? new-show?) (true? @show-sheet?)) - (cancel opts')))) - :reagent-render - (fn [{:keys [content content-height]}] - (let [total-content-height (+ content-height - styles/border-radius - styles/bottom-padding)] - (when @show-sheet? - [bottom-sheet-view (assoc opts - :content content - :height total-content-height)])))}))) + [react/keyboard-avoiding-view {:pointer-events "box-none" + :style styles/sheet-wrapper} + [react/animated-view (merge + {:pointer-events "box-none" + :style (styles/content-container height bottom-value)} + (when-not disable-drag? + (pan-handlers + (swipe-pan-responder {:bottom-value bottom-value + :opacity-value opacity-value + :height height + :close-sheet #(close-sheet)})))) + [react/view {:style styles/content-header} + [react/view styles/handle]] + [react/view {:on-layout #(->> % + .-nativeEvent + .-layout + .-height + (reset! content-height))} + [content]]]]]))))) diff --git a/src/status_im/ui/screens/home/sheet/views.cljs b/src/status_im/ui/screens/home/sheet/views.cljs index 39fadd47f22..5d8c3382275 100644 --- a/src/status_im/ui/screens/home/sheet/views.cljs +++ b/src/status_im/ui/screens/home/sheet/views.cljs @@ -15,36 +15,34 @@ (re-frame/dispatch event)) (defn add-new-view [] - [react/view {:flex 1 :flex-direction :row} - [react/view {:flex 1} - [list-item/list-item - {:theme :action - :title :t/start-new-chat - :accessibility-label :start-1-1-chat-button - :icon :main-icons/one-on-one-chat - :on-press #(hide-sheet-and-dispatch [:navigate-to :new-chat])}] - (when config/group-chat-enabled? - [list-item/list-item - {:theme :action - :title :t/start-group-chat - :accessibility-label :start-group-chat-button - :icon :main-icons/group-chat - :on-press #(hide-sheet-and-dispatch [:contact.ui/start-group-chat-pressed])}]) - [list-item/list-item - {:theme :action - :title :t/new-public-group-chat - :accessibility-label :join-public-chat-button - :icon :main-icons/public-chat - :on-press #(hide-sheet-and-dispatch [:navigate-to :new-public-chat])}] - [list-item/list-item - {:theme :action - :title :t/invite-friends - :accessibility-label :chats-menu-invite-friends-button - :icon :main-icons/share - :on-press #(do - (re-frame/dispatch [:bottom-sheet/hide-sheet]) - (list-selection/open-share {:message (i18n/label :t/get-status-at)}))}]]]) + [react/view + [list-item/list-item + {:theme :action + :title :t/start-new-chat + :accessibility-label :start-1-1-chat-button + :icon :main-icons/one-on-one-chat + :on-press #(hide-sheet-and-dispatch [:navigate-to :new-chat])}] + (when config/group-chat-enabled? + [list-item/list-item + {:theme :action + :title :t/start-group-chat + :accessibility-label :start-group-chat-button + :icon :main-icons/group-chat + :on-press #(hide-sheet-and-dispatch [:contact.ui/start-group-chat-pressed])}]) + [list-item/list-item + {:theme :action + :title :t/new-public-group-chat + :accessibility-label :join-public-chat-button + :icon :main-icons/public-chat + :on-press #(hide-sheet-and-dispatch [:navigate-to :new-public-chat])}] + [list-item/list-item + {:theme :action + :title :t/invite-friends + :accessibility-label :chats-menu-invite-friends-button + :icon :main-icons/share + :on-press #(do + (re-frame/dispatch [:bottom-sheet/hide-sheet]) + (list-selection/open-share {:message (i18n/label :t/get-status-at)}))}]]) (def add-new - {:content add-new-view - :content-height (if config/group-chat-enabled? 256 192)}) + {:content add-new-view}) diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index 1503cdae428..cb545f239d4 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -31,47 +31,33 @@ (defonce initial-view-id (atom nil)) -(defn bottom-sheet-comp [opts height-atom] - ;; We compute bottom sheet height dynamically by rendering it - ;; on an invisible view; then, if height is already available - ;; (either because it is statically provided or computed), - ;; we render the sheet itself - (if (or (not @height-atom) (= 0 @height-atom)) - [react/view {:style {:position :absolute :opacity 0} - :on-layout (fn [e] - (let [h (-> e .-nativeEvent .-layout .-height)] - (reset! height-atom h)))} - (when (:content opts) - [(:content opts)])] - [bottom-sheet/bottom-sheet (assoc opts :content-height @height-atom)])) - -(views/defview bottom-sheet [] - (views/letsubs [{:keys [show? view]} [:bottom-sheet]] - (let [opts (cond-> {:show? show? - :on-cancel #(re-frame/dispatch [:bottom-sheet/hide])} - - (map? view) - (merge view) - - (= view :mobile-network) - (merge mobile-network-settings/settings-sheet) - - (= view :mobile-network-offline) - (merge mobile-network-settings/offline-sheet) - - (= view :add-new) - (merge home.sheet/add-new) - - (= view :keycard.login/more) - (merge keycard/more-sheet) - - (= view :learn-more) - (merge about-app/learn-more) - - (= view :recover-sheet) - (merge (recover.views/bottom-sheet))) - height-atom (reagent/atom (if (:content-height opts) (:content-height opts) nil))] - [bottom-sheet-comp opts height-atom]))) +(defn bottom-sheet [] + (fn [] + (let [{:keys [show? view]} @(re-frame/subscribe [:bottom-sheet]) + opts (cond-> {:show? show? + :on-cancel #(re-frame/dispatch [:bottom-sheet/hide])} + + (map? view) + (merge view) + + (= view :mobile-network) + (merge mobile-network-settings/settings-sheet) + + (= view :mobile-network-offline) + (merge mobile-network-settings/offline-sheet) + + (= view :add-new) + (merge home.sheet/add-new) + + (= view :keycard.login/more) + (merge keycard/more-sheet) + + (= view :learn-more) + (merge about-app/learn-more) + + (= view :recover-sheet) + (merge (recover.views/bottom-sheet)))] + [bottom-sheet/bottom-sheet opts]))) (defn reset-component-on-mount [view-id component two-pane?] (when (and @initial-view-id