Implement alert banner (#19011)

This commit is contained in:
Parvesh Monu 2024-03-12 14:03:37 +05:30 committed by GitHub
parent e4997a2088
commit 604e232bda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 316 additions and 28 deletions

View File

@ -46,6 +46,7 @@
[_]
;; we need to disable notifications before starting the logout process
{:effects/push-notifications-disable nil
:dispatch [:alert-banners/remove-all]
:dispatch-later [{:ms 100
:dispatch [::logout-method
{:auth-method keychain/auth-method-none

View File

@ -22,6 +22,7 @@
(reg-root-key-sub :peers-count :peers-count)
(reg-root-key-sub :peers-summary :peers-summary)
(reg-root-key-sub :web3-node-version :web3-node-version)
(reg-root-key-sub :alert-banners :alert-banners)
;;keycard
(reg-root-key-sub :keycard :keycard)

View File

@ -0,0 +1,27 @@
(ns status-im.common.alert-banner.events
(:require [re-frame.core :as re-frame]))
(defn add-alert-banner
[{:keys [db]} [banner]]
(let [current-banners-count (count (get db :alert-banners))
db (assoc-in db [:alert-banners (:type banner)] banner)]
(cond-> {:db db}
(zero? current-banners-count)
(assoc :show-alert-banner nil))))
(defn remove-alert-banner
[{:keys [db]} [banner-type]]
(let [db (update-in db [:alert-banners] dissoc banner-type)
new-count (count (get db :alert-banners))]
(cond-> {:db db}
(zero? new-count)
(assoc :hide-alert-banner nil))))
(defn remove-all-alert-banners
[{:keys [db]}]
{:db (dissoc db :alert-banners)
:hide-alert-banner nil})
(re-frame/reg-event-fx :alert-banners/add add-alert-banner)
(re-frame/reg-event-fx :alert-banners/remove remove-alert-banner)
(re-frame/reg-event-fx :alert-banners/remove-all remove-all-alert-banners)

View File

@ -0,0 +1,42 @@
(ns status-im.common.alert-banner.events-test
(:require
[cljs.test :refer [deftest is testing]]
matcher-combinators.test
[status-im.common.alert-banner.events :as events]))
(deftest add-alert-banner
(testing "Alert banner is added"
(is (match? {:db {:alert-banners
{:alert {:text "Alert"
:type :alert}}}
:show-alert-banner nil}
(events/add-alert-banner {:db {}}
[{:text "Alert"
:type :alert}])))))
(deftest remove-alert-banner
(testing "Alert banner is removed"
(is (match? {:db {}
:hide-alert-banner nil}
(events/remove-alert-banner {:db {:alert-banners
{:alert {:text "Alert"
:type :alert}}}}
[:alert]))))
(testing "Alert banner is not removed"
(is (match? {:db {:alert-banners
{:alert {:text "Alert"
:type :alert}}}}
(events/remove-alert-banner {:db {:alert-banners
{:alert {:text "Alert"
:type :alert}}}}
[:error])))))
(deftest remove-all-alert-banners
(testing "All Alert banners are removed"
(is (match? {:db {}
:hide-alert-banner nil}
(events/remove-all-alert-banners {:db {:alert-banners
{:alert {:text "Alert"
:type :alert}
:error {:text "Error"
:type :error}}}})))))

View File

@ -0,0 +1,20 @@
(ns status-im.common.alert-banner.style)
(def border-radius 20)
(defn container
[background-color]
{:background-color background-color})
(def second-banner-wrapper
{:margin-top (- border-radius)
:overflow :hidden
:border-top-left-radius border-radius
:border-top-right-radius border-radius})
(defn hole-view
[background-color]
{:padding-top 11
:align-items :center
:height 60
:background-color background-color})

View File

@ -0,0 +1,68 @@
(ns status-im.common.alert-banner.view
(:require [quo.core :as quo]
[quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
[react-native.hole-view :as hole-view]
[react-native.safe-area :as safe-area]
[status-im.common.alert-banner.style :as style]
[status-im.constants :as constants]
[utils.re-frame :as rf]))
(defn get-colors-map
[theme]
{:alert
{:background-color (colors/resolve-color :warning theme 20)
:text-color (colors/resolve-color :warning theme)}
:error
{:background-color (colors/resolve-color :danger theme 20)
:text-color (colors/resolve-color :danger theme)}})
(defn- banner
[{:keys [text type second-banner? colors-map]}]
[rn/view (when second-banner? {:style style/second-banner-wrapper})
[hole-view/hole-view
{:style (style/hole-view (get-in colors-map [type :background-color]))
:holes (if second-banner?
[]
[{:x 0
:y constants/alert-banner-height
:width (:width (rn/get-window))
:height constants/alert-banner-height
:borderRadius style/border-radius}])}
[quo/text
{:size :paragraph-2
:weight :medium
:style {:color (get-in colors-map [type :text-color])}}
text]]])
(defn view
[]
(let [banners (rf/sub [:alert-banners])
theme (quo.theme/use-theme-value)
banners-count (count banners)
alert-banner (:alert banners)
error-banner (:error banners)
safe-area-top (safe-area/get-top)
colors-map (get-colors-map theme)]
[hole-view/hole-view
{:style {:background-color colors/neutral-100}
:holes [{:x 0
:y (+ safe-area-top (* constants/alert-banner-height banners-count))
:width (:width (rn/get-window))
:height constants/alert-banner-height
:borderRadius style/border-radius}]}
[rn/view
{:style {:height safe-area-top
:background-color (get-in colors-map
[(if error-banner :error :alert) :background-color])}}]
(when error-banner
[banner
(assoc error-banner
:colors-map colors-map
:second-banner? false)])
(when alert-banner
[banner
(assoc alert-banner
:colors-map colors-map
:second-banner? (= 2 banners-count))])]))

View File

@ -2,14 +2,12 @@
(:require
[quo.foundations.colors :as colors]
[quo.theme :as theme]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]))
(defn container
[{:keys [top] :as _insets}]
(let [padding-top (if platform/ios? top (+ top 10))]
{:flex 1
:padding-top padding-top}))
[padding-top]
{:flex 1
:padding-top padding-top})
(defn background
[opacity]

View File

@ -49,21 +49,24 @@
set-animating-false (fn [ms]
(js/setTimeout #(reset! animating? false) ms))]
(fn [{:keys [content skip-background? theme]}]
(let [insets (safe-area/get-insets)
{:keys [height]} (rn/get-window)
opacity (reanimated/use-shared-value 0)
translate-y (reanimated/use-shared-value height)
close (fn []
(set-animating-true)
(reanimated/animate translate-y height 300)
(reanimated/animate opacity 0 300)
(rf/dispatch [:navigate-back])
true)
reset-open-sheet (fn []
(reanimated/animate translate-y 0 300)
(reanimated/animate opacity 1 300)
(set-animating-false 300)
(reset! scroll-enabled? true))]
(let [{:keys [top] :as insets} (safe-area/get-insets)
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
padding-top (+ alert-banners-top-margin
(if platform/ios? top (+ top 10)))
{:keys [height]} (rn/get-window)
opacity (reanimated/use-shared-value 0)
translate-y (reanimated/use-shared-value height)
close (fn []
(set-animating-true)
(reanimated/animate translate-y height 300)
(reanimated/animate opacity 0 300)
(rf/dispatch [:navigate-back])
true)
reset-open-sheet (fn []
(reanimated/animate translate-y 0 300)
(reanimated/animate opacity 1 300)
(set-animating-false 300)
(reset! scroll-enabled? true))]
(rn/use-mount
(fn []
(rn/hw-back-add-listener close)
@ -71,7 +74,7 @@
(reanimated/animate opacity 1 300)
(set-animating-false 300)
#(rn/hw-back-remove-listener close)))
[rn/view {:style (style/container insets)}
[rn/view {:style (style/container padding-top)}
(when-not skip-background?
[reanimated/view {:style (style/background opacity)}])
[gesture/gesture-detector

View File

@ -465,3 +465,5 @@
(def ^:const bridge-name-transfer "Transfer")
(def ^:const bridge-name-erc-721-transfer "ERC721Transfer")
(def ^:const alert-banner-height 40)

View File

@ -0,0 +1,38 @@
(ns status-im.contexts.preview.status-im.banners.alert-banner
(:require
[reagent.core :as reagent]
[status-im.contexts.preview.quo.preview :as preview]
[utils.re-frame :as rf]))
(def descriptor
[{:key :alert-banner?
:type :boolean}
{:key :alert-message
:type :text}
{:key :error-banner?
:type :boolean}
{:key :error-message
:type :text}])
(defn view
[]
(let [state (reagent/atom {:alert-banner? false
:alert-message "Testnet mode enabled"
:error-banner? false
:error-message "Main and failover JSON RPC URLs offline"})
alert-banner? (reagent/cursor state [:alert-banner?])
alert-message (reagent/cursor state [:alert-message])
error-banner? (reagent/cursor state [:error-banner?])
error-message (reagent/cursor state [:error-message])]
(fn []
(if @alert-banner?
(rf/dispatch [:alert-banners/add
{:type :alert
:text @alert-message}])
(rf/dispatch [:alert-banners/remove :alert]))
(if @error-banner?
(rf/dispatch [:alert-banners/add
{:type :error
:text @error-message}])
(rf/dispatch [:alert-banners/remove :error]))
[preview/preview-container {:state state :descriptor descriptor}])))

View File

@ -5,6 +5,7 @@
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.preview.quo.common :as common]
[status-im.contexts.preview.status-im.banners.alert-banner :as alert-banner]
[status-im.contexts.preview.status-im.common.floating-button-page.view :as
floating-button-page]
[status-im.contexts.preview.status-im.style :as style]
@ -12,7 +13,10 @@
(def screens-categories
{:common [{:name :floating-button-page
:component floating-button-page/view}]})
:component floating-button-page/view}]
:banner
[{:name :alert-banner-preview
:component alert-banner/view}]})
(defn- category-view
[]

View File

@ -9,7 +9,8 @@
[status-im.contexts.shell.jump-to.constants :as shell.constants]
[status-im.contexts.shell.jump-to.state :as state]
[status-im.contexts.shell.jump-to.utils :as utils]
[status-im.contexts.wallet.home.view :as wallet-new]))
[status-im.contexts.wallet.home.view :as wallet-new]
[utils.re-frame :as rf]))
(defn load-stack?
[stack-id]
@ -42,9 +43,16 @@
(defn f-home-stack
[]
(let [shared-values @state/shared-values-atom
theme (quo.theme/use-theme-value)]
[reanimated/view {:style (style/home-stack shared-values (assoc (utils/dimensions) :theme theme))}
(let [shared-values @state/shared-values-atom
theme (quo.theme/use-theme-value)
{:keys [width height]} (utils/dimensions)
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])]
[reanimated/view
{:style (style/home-stack
shared-values
{:theme theme
:width width
:height (- height alert-banners-top-margin)})}
[lazy-screen :communities-stack shared-values]
[lazy-screen :chats-stack shared-values]
[lazy-screen :browser-stack shared-values]

View File

@ -2,6 +2,7 @@
(:require
[legacy.status-im.bottom-sheet.events]
[legacy.status-im.keycard.core :as keycard]
status-im.common.alert-banner.events
status-im.common.alert.effects
status-im.common.async-storage.effects
status-im.common.emoji-picker.events

View File

@ -94,6 +94,13 @@
(fn [] (gesture/gesture-handler-root-hoc views/bottom-sheet))
(fn [] views/bottom-sheet)))
;;;; Alert Banner
(navigation/register-component
"alert-banner"
(fn [] (gesture/gesture-handler-root-hoc views/alert-banner #js {:flex 0}))
(fn [] views/alert-banner))
;; LEGACY (should be removed in status 2.0)
(defonce

View File

@ -181,6 +181,13 @@
(rf/reg-fx :hide-bottom-sheet
(fn [] (navigation/dissmiss-overlay "bottom-sheet")))
;;;; Alert Banner
(rf/reg-fx :show-alert-banner
(fn [] (show-overlay "alert-banner" {:overlay {:interceptTouchOutside false}})))
(rf/reg-fx :hide-alert-banner
(fn [] (navigation/dissmiss-overlay "alert-banner")))
;;;; Merge options
(rf/reg-fx :merge-options

View File

@ -10,6 +10,7 @@
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
schema.view
[status-im.common.alert-banner.view :as alert-banner]
[status-im.common.bottom-sheet-screen.view :as bottom-sheet-screen]
[status-im.common.bottom-sheet.view :as bottom-sheet]
[status-im.common.toasts.view :as toasts]
@ -43,9 +44,10 @@
:z-index 999999999999999999}]))
(defn wrapped-screen-style
[{:keys [top? bottom?]} background-color]
[{:keys [top? bottom? background-color alert-banners-top-margin]}]
(merge
{:flex 1
:margin-top alert-banners-top-margin
:background-color (or background-color (colors/theme-colors colors/white colors/neutral-100))}
(when bottom?
{:padding-bottom (safe-area/get-bottom)})
@ -62,11 +64,16 @@
(keyword screen-key))
{:keys [insets sheet? theme]} options
user-theme (theme/get-theme)
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
background-color (or (get-in options [:layout :backgroundColor])
(when sheet? :transparent))]
^{:key (str "root" screen-key @reloader/cnt)}
[theme/provider {:theme (or theme user-theme)}
[rn/view {:style (wrapped-screen-style insets background-color)}
[rn/view
{:style (wrapped-screen-style (assoc
insets
:background-color background-color
:alert-banners-top-margin alert-banners-top-margin))}
[inactive]
(if sheet?
[bottom-sheet-screen/view {:content component}]
@ -140,3 +147,11 @@
(when js/goog.DEBUG
[reloader/reload-view])])
functional-compiler))
(def alert-banner
(reagent/reactify-component
(fn []
^{:key (str "alert-banner" @reloader/cnt)}
[theme/provider {:theme :dark}
[alert-banner/view]])
functional-compiler))

View File

@ -0,0 +1,12 @@
(ns status-im.subs.alert-banner
(:require
[re-frame.core :as re-frame]
[status-im.constants :as constants]))
(re-frame/reg-sub
:alert-banners/top-margin
:<- [:alert-banners]
(fn [banners]
(let [banners-count (count banners)]
(when (pos? banners-count)
(+ (* constants/alert-banner-height banners-count) 8)))))

View File

@ -0,0 +1,32 @@
(ns status-im.subs.alert-banner-test
(:require
[cljs.test :refer [is testing]]
[re-frame.db :as rf-db]
status-im.subs.activity-center
[test-helpers.unit :as h]
[utils.re-frame :as rf]))
(h/deftest-sub :alert-banners/top-margin
[sub-name]
(testing "returns 48 when only alert banner"
(swap! rf-db/app-db assoc
:alert-banners
{:alert {:text "Alert"
:type :alert}})
(is (= (rf/sub [sub-name]) 48)))
(testing "returns 48 when only error banner"
(swap! rf-db/app-db assoc
:alert-banners
{:error {:text "Error"
:type :error}})
(is (= (rf/sub [sub-name]) 48)))
(testing "returns 88 when both alert and error banner"
(swap! rf-db/app-db assoc
:alert-banners
{:alert {:text "Alert"
:type :alert}
:error {:text "Error"
:type :error}})
(is (= (rf/sub [sub-name]) 88))))

View File

@ -2,6 +2,7 @@
(:require
[re-frame.core :as re-frame]
status-im.subs.activity-center
status-im.subs.alert-banner
status-im.subs.chats
status-im.subs.communities
status-im.subs.contact
@ -62,6 +63,7 @@
(reg-root-key-sub :waku/v2-peer-stats :peer-stats)
(reg-root-key-sub :password-authentication :password-authentication)
(reg-root-key-sub :initials-avatar-font-file :initials-avatar-font-file)
(reg-root-key-sub :alert-banners :alert-banners)
;;onboarding
(reg-root-key-sub :onboarding/generated-keys? :onboarding/generated-keys?)