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 ;; we need to disable notifications before starting the logout process
{:effects/push-notifications-disable nil {:effects/push-notifications-disable nil
:dispatch [:alert-banners/remove-all]
:dispatch-later [{:ms 100 :dispatch-later [{:ms 100
:dispatch [::logout-method :dispatch [::logout-method
{:auth-method keychain/auth-method-none {: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-count :peers-count)
(reg-root-key-sub :peers-summary :peers-summary) (reg-root-key-sub :peers-summary :peers-summary)
(reg-root-key-sub :web3-node-version :web3-node-version) (reg-root-key-sub :web3-node-version :web3-node-version)
(reg-root-key-sub :alert-banners :alert-banners)
;;keycard ;;keycard
(reg-root-key-sub :keycard :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 (:require
[quo.foundations.colors :as colors] [quo.foundations.colors :as colors]
[quo.theme :as theme] [quo.theme :as theme]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated])) [react-native.reanimated :as reanimated]))
(defn container (defn container
[{:keys [top] :as _insets}] [padding-top]
(let [padding-top (if platform/ios? top (+ top 10))]
{:flex 1 {:flex 1
:padding-top padding-top})) :padding-top padding-top})
(defn background (defn background
[opacity] [opacity]

View File

@ -49,7 +49,10 @@
set-animating-false (fn [ms] set-animating-false (fn [ms]
(js/setTimeout #(reset! animating? false) ms))] (js/setTimeout #(reset! animating? false) ms))]
(fn [{:keys [content skip-background? theme]}] (fn [{:keys [content skip-background? theme]}]
(let [insets (safe-area/get-insets) (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) {:keys [height]} (rn/get-window)
opacity (reanimated/use-shared-value 0) opacity (reanimated/use-shared-value 0)
translate-y (reanimated/use-shared-value height) translate-y (reanimated/use-shared-value height)
@ -71,7 +74,7 @@
(reanimated/animate opacity 1 300) (reanimated/animate opacity 1 300)
(set-animating-false 300) (set-animating-false 300)
#(rn/hw-back-remove-listener close))) #(rn/hw-back-remove-listener close)))
[rn/view {:style (style/container insets)} [rn/view {:style (style/container padding-top)}
(when-not skip-background? (when-not skip-background?
[reanimated/view {:style (style/background opacity)}]) [reanimated/view {:style (style/background opacity)}])
[gesture/gesture-detector [gesture/gesture-detector

View File

@ -465,3 +465,5 @@
(def ^:const bridge-name-transfer "Transfer") (def ^:const bridge-name-transfer "Transfer")
(def ^:const bridge-name-erc-721-transfer "ERC721Transfer") (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] [react-native.core :as rn]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.contexts.preview.quo.common :as common] [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 [status-im.contexts.preview.status-im.common.floating-button-page.view :as
floating-button-page] floating-button-page]
[status-im.contexts.preview.status-im.style :as style] [status-im.contexts.preview.status-im.style :as style]
@ -12,7 +13,10 @@
(def screens-categories (def screens-categories
{:common [{:name :floating-button-page {: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 (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.constants :as shell.constants]
[status-im.contexts.shell.jump-to.state :as state] [status-im.contexts.shell.jump-to.state :as state]
[status-im.contexts.shell.jump-to.utils :as utils] [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? (defn load-stack?
[stack-id] [stack-id]
@ -43,8 +44,15 @@
(defn f-home-stack (defn f-home-stack
[] []
(let [shared-values @state/shared-values-atom (let [shared-values @state/shared-values-atom
theme (quo.theme/use-theme-value)] theme (quo.theme/use-theme-value)
[reanimated/view {:style (style/home-stack shared-values (assoc (utils/dimensions) :theme theme))} {: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 :communities-stack shared-values]
[lazy-screen :chats-stack shared-values] [lazy-screen :chats-stack shared-values]
[lazy-screen :browser-stack shared-values] [lazy-screen :browser-stack shared-values]

View File

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

View File

@ -94,6 +94,13 @@
(fn [] (gesture/gesture-handler-root-hoc views/bottom-sheet)) (fn [] (gesture/gesture-handler-root-hoc views/bottom-sheet))
(fn [] 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) ;; LEGACY (should be removed in status 2.0)
(defonce (defonce

View File

@ -181,6 +181,13 @@
(rf/reg-fx :hide-bottom-sheet (rf/reg-fx :hide-bottom-sheet
(fn [] (navigation/dissmiss-overlay "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 ;;;; Merge options
(rf/reg-fx :merge-options (rf/reg-fx :merge-options

View File

@ -10,6 +10,7 @@
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
schema.view 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-screen.view :as bottom-sheet-screen]
[status-im.common.bottom-sheet.view :as bottom-sheet] [status-im.common.bottom-sheet.view :as bottom-sheet]
[status-im.common.toasts.view :as toasts] [status-im.common.toasts.view :as toasts]
@ -43,9 +44,10 @@
:z-index 999999999999999999}])) :z-index 999999999999999999}]))
(defn wrapped-screen-style (defn wrapped-screen-style
[{:keys [top? bottom?]} background-color] [{:keys [top? bottom? background-color alert-banners-top-margin]}]
(merge (merge
{:flex 1 {:flex 1
:margin-top alert-banners-top-margin
:background-color (or background-color (colors/theme-colors colors/white colors/neutral-100))} :background-color (or background-color (colors/theme-colors colors/white colors/neutral-100))}
(when bottom? (when bottom?
{:padding-bottom (safe-area/get-bottom)}) {:padding-bottom (safe-area/get-bottom)})
@ -62,11 +64,16 @@
(keyword screen-key)) (keyword screen-key))
{:keys [insets sheet? theme]} options {:keys [insets sheet? theme]} options
user-theme (theme/get-theme) user-theme (theme/get-theme)
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
background-color (or (get-in options [:layout :backgroundColor]) background-color (or (get-in options [:layout :backgroundColor])
(when sheet? :transparent))] (when sheet? :transparent))]
^{:key (str "root" screen-key @reloader/cnt)} ^{:key (str "root" screen-key @reloader/cnt)}
[theme/provider {:theme (or theme user-theme)} [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] [inactive]
(if sheet? (if sheet?
[bottom-sheet-screen/view {:content component}] [bottom-sheet-screen/view {:content component}]
@ -140,3 +147,11 @@
(when js/goog.DEBUG (when js/goog.DEBUG
[reloader/reload-view])]) [reloader/reload-view])])
functional-compiler)) 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 (:require
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
status-im.subs.activity-center status-im.subs.activity-center
status-im.subs.alert-banner
status-im.subs.chats status-im.subs.chats
status-im.subs.communities status-im.subs.communities
status-im.subs.contact status-im.subs.contact
@ -62,6 +63,7 @@
(reg-root-key-sub :waku/v2-peer-stats :peer-stats) (reg-root-key-sub :waku/v2-peer-stats :peer-stats)
(reg-root-key-sub :password-authentication :password-authentication) (reg-root-key-sub :password-authentication :password-authentication)
(reg-root-key-sub :initials-avatar-font-file :initials-avatar-font-file) (reg-root-key-sub :initials-avatar-font-file :initials-avatar-font-file)
(reg-root-key-sub :alert-banners :alert-banners)
;;onboarding ;;onboarding
(reg-root-key-sub :onboarding/generated-keys? :onboarding/generated-keys?) (reg-root-key-sub :onboarding/generated-keys? :onboarding/generated-keys?)