[#17435] migrate status-im.notifications (#17603)

This commit is contained in:
flexsurfer 2023-10-13 12:40:50 +02:00 committed by GitHub
parent edd6b8d62c
commit ca88de162a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 478 additions and 696 deletions

View File

@ -323,7 +323,11 @@
(def react-native-permissions #js {:default #js {}})
(def push-notification-ios #js {:default #js {:abandonPermissions identity}})
(def push-notification-ios
#js
{:default #js
{:abandonPermissions identity
:removeAllDeliveredNotifications identity}})
(def rn-emoji-keyboard
#js {:EmojiKeyboard #js {}})

View File

@ -3,7 +3,6 @@
[utils.validators :as validators]
[taoensso.timbre :as log]
[react-native.platform :as platform]
[react-native.core :as rn]
[utils.transforms :as types]
[clojure.string :as string]))
@ -14,7 +13,7 @@
(defn init
[handler]
(.addListener ^js rn/device-event-emitter "gethEvent" #(handler (.-jsonEvent ^js %))))
(.addListener ^js (.-DeviceEventEmitter ^js react-native) "gethEvent" #(handler (.-jsonEvent ^js %))))
(defn clear-web-data
[]

View File

@ -0,0 +1,42 @@
(ns native-module.push-notifications
(:require ["react-native" :as react-native]
[taoensso.timbre :as log]))
(defn push-notification
[]
(when (exists? (.-NativeModules react-native))
(.-PushNotification ^js (.-NativeModules react-native))))
(defn present-local-notification
[opts]
(.presentLocalNotification ^js (push-notification) (clj->js opts)))
(defn clear-message-notifications
[chat-id]
(.clearMessageNotifications ^js (push-notification) chat-id))
(defn clear-all-message-notifications
[]
(.clearAllMessageNotifications ^js (push-notification)))
(defn create-channel
[{:keys [channel-id channel-name]}]
(.createChannel ^js (push-notification)
#js {:channelId channel-id :channelName channel-name}
#(log/info "Notifications create channel:" %)))
(defn enable-notifications
[]
(.enableNotifications ^js (push-notification)))
(defn disable-notifications
[]
(.disableNotifications ^js (push-notification)))
(defn add-listener
[event callback]
(.addListener ^js (.-DeviceEventEmitter ^js react-native)
event
(fn [^js data]
(when (and data (.-dataJSON data) callback)
(callback (.-dataJSON data))))))

View File

@ -0,0 +1,24 @@
(ns react-native.push-notification-ios
(:require ["@react-native-community/push-notification-ios" :default pn-ios]))
(defn present-local-notification
[title message user-info]
(.presentLocalNotification ^js pn-ios #js {:alertBody message :alertTitle title :userInfo user-info}))
(defn add-listener
[event callback]
(.addEventListener ^js pn-ios event callback))
(defn request-permissions
[]
(-> (.requestPermissions ^js pn-ios)
(.then #())
(.catch #())))
(defn abandon-permissions
[]
(.abandonPermissions ^js pn-ios))
(defn remove-all-delivered-notifications
[]
(.removeAllDeliveredNotifications ^js pn-ios))

View File

@ -83,24 +83,26 @@
(rf/defn handle-mark-all-read
{:events [:chat.ui/mark-all-read-pressed :chat/mark-all-as-read]}
[{db :db} chat-id]
{:db (mark-chat-all-read db chat-id)
:clear-message-notifications [[chat-id]
(get-in db [:profile/profile :remote-push-notifications-enabled?])]
:json-rpc/call [{:method "wakuext_markAllRead"
:params [chat-id]
:on-success #(re-frame/dispatch [::mark-all-read-successful])}]})
{:db (mark-chat-all-read db chat-id)
:effects/push-notifications-clear-message-notifications [chat-id]
:json-rpc/call [{:method "wakuext_markAllRead"
:params [chat-id]
:on-success
#(re-frame/dispatch
[::mark-all-read-successful])}]})
(rf/defn handle-mark-mark-all-read-in-community
{:events [:chat.ui/mark-all-read-in-community-pressed]}
[{db :db} community-id]
(let [community-chat-ids (map #(str community-id %)
(keys (get-in db [:communities community-id :chats])))]
{:clear-message-notifications [community-chat-ids
(get-in db [:profile/profile :remote-push-notifications-enabled?])]
:json-rpc/call [{:method "wakuext_markAllReadInCommunity"
:params [community-id]
:on-success #(re-frame/dispatch
[::mark-all-read-in-community-successful %])}]}))
{:effects/push-notifications-clear-message-notifications community-chat-ids
:json-rpc/call [{:method "wakuext_markAllReadInCommunity"
:params [community-id]
:on-success
#(re-frame/dispatch
[::mark-all-read-in-community-successful
%])}]}))
(rf/defn messages-loaded
"Loads more messages for current chat"

View File

@ -271,18 +271,20 @@
[{:keys [db]} community-id]
(let [community-chat-ids (map #(str community-id %)
(keys (get-in db [:communities community-id :chats])))]
{:clear-message-notifications [community-chat-ids
(get-in db [:profile/profile :remote-push-notifications-enabled?])]
:dispatch [:shell/close-switcher-card community-id]
:json-rpc/call [{:method "wakuext_leaveCommunity"
:params [community-id]
:js-response true
:on-success #(re-frame/dispatch [::left %])
:on-error (fn [response]
(log/error "failed to leave community"
community-id
response)
(re-frame/dispatch [::failed-to-leave]))}]}))
{:effects/push-notifications-clear-message-notifications community-chat-ids
:dispatch [:shell/close-switcher-card community-id]
:json-rpc/call [{:method "wakuext_leaveCommunity"
:params [community-id]
:js-response true
:on-success #(re-frame/dispatch [::left
%])
:on-error (fn [response]
(log/error
"failed to leave community"
community-id
response)
(re-frame/dispatch
[::failed-to-leave]))}]}))
(rf/defn status-tag-pressed
{:events [:communities/status-tag-pressed]}

View File

@ -41,17 +41,20 @@
(map #(->> (chats-store/<-rpc %)
(clean-up-chat public-key))
(types/js->clj chats-js)))]
(apply rf/merge
cofx
{:db (-> db
(update :chats dissoc public-key)
(update :chats-home-list disj public-key)
(assoc-in [:contacts/contacts public-key :added?] false))
:dispatch [:shell/close-switcher-card public-key]
:clear-message-notifications
[[public-key] (get-in db [:profile/profile :remote-push-notifications-enabled?])]}
(activity-center/notifications-fetch-unread-count)
fxs)))
(apply
rf/merge
cofx
{:db (->
db
(update :chats dissoc public-key)
(update :chats-home-list disj public-key)
(assoc-in [:contacts/contacts public-key
:added?]
false))
:dispatch [:shell/close-switcher-card public-key]
:effects/push-notifications-clear-message-notifications [public-key]}
(activity-center/notifications-fetch-unread-count)
fxs)))
(rf/defn block-contact
{:events [:contact.ui/block-contact-confirmed]}

View File

@ -60,7 +60,8 @@
status-im2.contexts.chat.home.events
status-im2.contexts.communities.home.events
status-im.ui.components.invite.events
[status-im2.common.biometric.events :as biometric]))
[status-im2.common.biometric.events :as biometric]
status-im.ui.screens.notifications-settings.events))
(re-frame/reg-fx
:dismiss-keyboard

View File

@ -165,7 +165,6 @@
:dapps-address (:address wallet-account)
:latest-derived-path 0
:signing-phrase signing-phrase
:send-push-notifications? true
:backup-enabled? true
:installation-id (random-guid-generator)
;; default mailserver (history node) setting

View File

@ -2,7 +2,6 @@
(:require [native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.notifications.core :as notifications]
[status-im.wallet.core :as wallet]
[status-im2.common.keychain.events :as keychain]
[status-im2.db :as db]
@ -46,14 +45,13 @@
(rf/defn logout
{:events [:logout :multiaccounts.logout.ui/logout-confirmed
:multiaccounts.update.callback/save-settings-success]}
[cofx]
[_]
;; we need to disable notifications before starting the logout process
(rf/merge cofx
{:dispatch-later [{:ms 100
:dispatch [::logout-method
{:auth-method keychain/auth-method-none
:logout? true}]}]}
(notifications/logout-disable)))
{:effects/push-notifications-disable nil
:dispatch-later [{:ms 100
:dispatch [::logout-method
{:auth-method keychain/auth-method-none
:logout? true}]}]})
(rf/defn show-logout-confirmation
{:events [:multiaccounts.logout.ui/logout-pressed]}

View File

@ -1,37 +0,0 @@
(ns status-im.notifications.android
(:require ["react-native" :as react-native]
[quo.platform :as platform]
[taoensso.timbre :as log]))
(defn pn-android
[]
(when platform/android?
(.-PushNotification ^js (.-NativeModules react-native))))
(defn present-local-notification
[opts]
(.presentLocalNotification ^js (pn-android) (clj->js opts)))
(defn clear-message-notifications
[chat-id]
(.clearMessageNotifications ^js (pn-android) chat-id))
(defn clear-all-message-notifications
[]
(.clearAllMessageNotifications ^js (pn-android)))
(defn create-channel
[{:keys [channel-id channel-name]}]
(.createChannel ^js (pn-android)
#js
{:channelId channel-id
:channelName channel-name}
#(log/info "Notifications create channel:" %)))
(defn enable-notifications
[]
(.enableNotifications ^js (pn-android)))
(defn disable-notifications
[]
(.disableNotifications ^js (pn-android)))

View File

@ -1,313 +0,0 @@
(ns status-im.notifications.core
(:require ["@react-native-community/push-notification-ios" :default pn-ios]
[quo.platform :as platform]
[re-frame.core :as re-frame]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.notifications.android :as pn-android]
[status-im.notifications.local :as local]
[status-im2.config :as config]
[utils.re-frame :as rf]
[taoensso.timbre :as log]))
(def server-type-default 1)
(def server-type-custom 2)
(def apn-token-type 1)
(def firebase-token-type 2)
(def listeners-added? (atom nil))
(defn server<-rpc
[{:keys [type publicKey registered]}]
{:public-key publicKey
:type type
:registered registered})
(defn add-event-listeners
[]
(when-not @listeners-added?
(reset! listeners-added? true)
(.addEventListener
^js pn-ios
"register"
(fn [token]
(re-frame/dispatch [:notifications/registered-for-push-notifications token])))
(.addEventListener
^js pn-ios
"registrationError"
(fn [error]
(re-frame/dispatch [:notifications/switch-error true error])))))
(defn enable-ios-notifications
[]
(add-event-listeners)
(-> (.requestPermissions ^js pn-ios)
(.then #())
(.catch #())))
(defn disable-ios-notifications
[]
(.abandonPermissions ^js pn-ios)
(re-frame/dispatch [:notifications/unregistered-from-push-notifications]))
(defn enable-android-notifications
[]
(pn-android/create-channel
{:channel-id "status-im-notifications"
:channel-name "Status push notifications"})
(pn-android/enable-notifications))
(defn disable-android-notifications
[]
(pn-android/disable-notifications))
;; FIXME: Repalce with request permission from audio messages PR lib
(re-frame/reg-fx
::request-permission
identity)
(rf/defn request-permission
{:events [::request-permission]}
[_]
{::request-permission true})
(re-frame/reg-fx
::local-notification
(fn [props]
(if platform/ios?
(local/local-push-ios props)
(local/local-push-android props))))
(re-frame/reg-fx
::enable
(fn []
(if platform/android?
(enable-android-notifications)
(enable-ios-notifications))))
(re-frame/reg-fx
::disable
(fn [_]
(if platform/android?
(disable-android-notifications)
(disable-ios-notifications))))
(re-frame/reg-fx
::logout-disable
(fn [_]
(if platform/android?
(pn-android/disable-notifications)
(.abandonPermissions ^js pn-ios))))
(re-frame/reg-fx
:clear-message-notifications
(fn [[chat-ids] remote-push-notifications-enabled?]
(if remote-push-notifications-enabled?
(if platform/android?
(pn-android/clear-all-message-notifications)
(.removeAllDeliveredNotifications ^js pn-ios))
(when platform/android?
(doseq [chat-id chat-ids]
(pn-android/clear-message-notifications chat-id))))))
(rf/defn handle-enable-notifications-event
{:events [:notifications/registered-for-push-notifications]}
[cofx token]
{:json-rpc/call [{:method "wakuext_registerForPushNotifications"
:params [token (if platform/ios? config/apn-topic)
(if platform/ios? apn-token-type firebase-token-type)]
:on-success #(log/info "[push-notifications] register-success" %)
:on-error #(re-frame/dispatch [:notifications/switch-error true %])}]})
(rf/defn handle-disable-notifications-event
{:events [:notifications/unregistered-from-push-notifications]}
[cofx]
{:json-rpc/call [{:method "wakuext_unregisterFromPushNotifications"
:params []
:on-success #(log/info "[push-notifications] unregister-success" %)
:on-error #(re-frame/dispatch [:notifications/switch-error false %])}]})
(rf/defn logout-disable
[cofx]
(merge {::logout-disable nil}
{:json-rpc/call [{:method "wakuext_unregisterFromPushNotifications"
:params []
:on-success #(log/info "[push-notifications] unregister-success" %)
:on-error #(log/info "[push-notifications] unregister-error" %)}]}))
(rf/defn notification-switch-error
{:events [:notifications/switch-error]}
[cofx enabled?]
(multiaccounts.update/multiaccount-update
cofx
:remote-push-notifications-enabled?
(not enabled?)
{}))
(rf/defn notification-switch
{:events [::switch]}
[{:keys [db] :as cofx} enabled? remote-push-notifications?]
(rf/merge cofx
(if enabled?
{::enable remote-push-notifications?}
{::disable nil})
(multiaccounts.update/multiaccount-update
:remote-push-notifications-enabled?
(and remote-push-notifications? enabled?)
{})
(multiaccounts.update/multiaccount-update
:notifications-enabled?
(and (not remote-push-notifications?) enabled?)
{})))
(rf/defn notification-non-contacts-error
{:events [::non-contacts-update-error]}
[cofx enabled?]
(multiaccounts.update/optimistic cofx
:push-notifications-from-contacts-only?
(not (boolean enabled?))))
(rf/defn notification-block-mentions-error
{:events [::block-mentions-update-error]}
[cofx enabled?]
(multiaccounts.update/optimistic cofx :push-notifications-block-mentions? (not (boolean enabled?))))
(rf/defn notification-non-contacts
{:events [::switch-non-contacts]}
[{:keys [db] :as cofx} enabled?]
(let [method (if enabled?
"wakuext_enablePushNotificationsFromContactsOnly"
"wakuext_disablePushNotificationsFromContactsOnly")]
(rf/merge
cofx
{:json-rpc/call [{:method method
:params []
:on-success #(log/info "[push-notifications] contacts-notification-success" %)
:on-error #(re-frame/dispatch [::non-contacts-update-error enabled? %])}]}
(multiaccounts.update/optimistic :push-notifications-from-contacts-only? (boolean enabled?)))))
(rf/defn notification-block-mentions
{:events [::switch-block-mentions]}
[{:keys [db] :as cofx} enabled?]
(let [method (if enabled?
"wakuext_enablePushNotificationsBlockMentions"
"wakuext_disablePushNotificationsBlockMentions")]
(log/info "USING METHOD" method enabled?)
(rf/merge cofx
{:json-rpc/call [{:method method
:params []
:on-success #(log/info "[push-notifications] block-mentions-success" %)
:on-error #(re-frame/dispatch [::block-mentions-update-error enabled?
%])}]}
(multiaccounts.update/optimistic :push-notifications-block-mentions? (boolean enabled?)))))
(rf/defn switch-push-notifications-server-enabled
{:events [::switch-push-notifications-server-enabled]}
[{:keys [db] :as cofx} enabled?]
(let [method (if enabled?
"wakuext_startPushNotificationsServer"
"wakuext_stopPushNotificationsServer")]
(rf/merge
cofx
{:json-rpc/call [{:method method
:params []
:on-success #(log/info "[push-notifications] switch-server-enabled successful" %)
:on-error #(re-frame/dispatch [::push-notifications-server-update-error
enabled? %])}]}
(multiaccounts.update/optimistic :push-notifications-server-enabled? (boolean enabled?)))))
(rf/defn switch-send-notifications
{:events [::switch-send-push-notifications]}
[{:keys [db] :as cofx} enabled?]
(let [method (if enabled?
"wakuext_enableSendingNotifications"
"wakuext_disableSendingNotifications")]
(rf/merge cofx
{:json-rpc/call [{:method method
:params []
:on-success
#(log/info "[push-notifications] switch-send-notifications successful"
%)
:on-error #(re-frame/dispatch [::push-notifications-send-update-error
enabled? %])}]}
(multiaccounts.update/optimistic :send-push-notifications? (boolean enabled?)))))
(rf/defn handle-add-server-error
{:events [::push-notifications-add-server-error]}
[_ public-key error]
(log/error "failed to add server" public-key error))
(rf/defn add-server
{:events [::add-server]}
[{:keys [db] :as cofx} public-key]
(rf/merge cofx
{:json-rpc/call [{:method "wakuext_addPushNotificationsServer"
:params [public-key]
:on-success
#(do
(log/info "[push-notifications] switch-send-notifications successful"
%)
(re-frame/dispatch [::fetch-servers]))
:on-error #(re-frame/dispatch [::push-notifications-add-server-error
public-key %])}]}))
(rf/defn handle-servers-fetched
{:events [::servers-fetched]}
[{:keys [db]} servers]
{:db (assoc db :push-notifications/servers (map server<-rpc servers))})
(rf/defn fetch-push-notifications-servers
{:events [::fetch-servers]}
[cofx]
{:json-rpc/call [{:method "wakuext_getPushNotificationsServers"
:params []
:on-success #(do
(log/info "[push-notifications] servers fetched" %)
(re-frame/dispatch [::servers-fetched %]))}]})
;; Wallet transactions
(rf/defn handle-preferences-load
{:events [::preferences-loaded]}
[{:keys [db]} preferences]
{:db (assoc db :push-notifications/preferences preferences)})
(rf/defn load-notification-preferences
{:events [::load-notification-preferences]}
[_]
{:json-rpc/call [{:method "localnotifications_notificationPreferences"
:params []
:on-success #(re-frame/dispatch [::preferences-loaded %])}]})
(defn preference=
[x y]
(and (= (:service x) (:service y))
(= (:event x) (:event y))
(= (:identifier x) (:identifier y))))
(defn- update-preference
[all new-preference]
(conj (filter (comp not (partial preference= new-preference))
all)
new-preference))
(rf/defn switch-transaction-notifications
{:events [::switch-transaction-notifications]}
[{:keys [db] :as cofx} enabled?]
{:db (update db
:push-notifications/preferences
update-preference
{:enabled (not enabled?)
:service "wallet"
:event "transaction"
:identifier "all"})
:json-rpc/call [{:method "localnotifications_switchWalletNotifications"
:params [(not enabled?)]
:on-success #(log/info
"[push-notifications] switch-transaction-notifications successful"
%)
:on-error #(log/error
"[push-notifications] switch-transaction-notifications error"
%)}]})

View File

@ -1,150 +0,0 @@
(ns status-im.notifications.local
(:require ["@react-native-community/push-notification-ios" :default pn-ios]
[cljs-bean.core :as bean]
[clojure.string :as string]
[quo.platform :as platform]
[re-frame.core :as re-frame]
[react-native.async-storage :as async-storage]
[status-im.ethereum.decode :as decode]
[status-im.ethereum.tokens :as tokens]
[utils.i18n :as i18n]
[status-im.notifications.android :as pn-android]
[utils.re-frame :as rf]
[utils.money :as money]
[status-im.utils.deprecated-types :as types]
[status-im.utils.utils :as utils]
[react-native.core :as rn]))
(def default-erc20-token
{:symbol :ERC20
:decimals 18
:name "ERC20"})
(def notification-event-ios "localNotification")
(def notification-event-android "remoteNotificationReceived")
(defn local-push-ios
[{:keys [title message user-info body-type]}]
(when (not= body-type "message")
(.presentLocalNotification
pn-ios
#js
{:alertBody message
:alertTitle title
;; NOTE: Use a special type to hide in Obj-C code other notifications
:userInfo (bean/->js (merge user-info
{:notificationType "local-notification"}))})))
(defn local-push-android
[notification]
(pn-android/present-local-notification notification))
(defn handle-notification-press
[{{deep-link :deepLink} :userInfo
interaction :userInteraction}]
(async-storage/set-item! (str :chat-id) nil)
(when (and deep-link
(or platform/ios?
(and platform/android? interaction)))
(re-frame/dispatch [:universal-links/handle-url deep-link])))
(defn listen-notifications
[]
(if platform/ios?
(.addEventListener ^js pn-ios
notification-event-ios
(fn [notification]
(handle-notification-press {:userInfo (bean/bean (.getData ^js
notification))})))
(.addListener ^js rn/device-event-emitter
notification-event-android
(fn [^js data]
(when (and data (.-dataJSON data))
(handle-notification-press (types/json->clj (.-dataJSON data))))))))
(defn create-transfer-notification
[{db :db}
{{:keys [state from to fromAccount toAccount value erc20 contract network]}
:body
:as notification}]
(let [token (if erc20
(get-in db
[:wallet/all-tokens (string/lower-case contract)]
default-erc20-token)
(tokens/native-currency network))
amount (money/wei->ether (decode/uint value))
to (or (:name toAccount) (utils/get-shortened-address to))
from (or (:name fromAccount) (utils/get-shortened-address from))
title (case state
"inbound" (i18n/label :t/push-inbound-transaction
{:value amount
:currency (:symbol token)})
"outbound" (i18n/label :t/push-outbound-transaction
{:value amount
:currency (:symbol token)})
"failed" (i18n/label :t/push-failed-transaction
{:value amount
:currency (:symbol token)})
nil)
description (case state
"inbound" (i18n/label :t/push-inbound-transaction-body
{:from from
:to to})
"outbound" (i18n/label :t/push-outbound-transaction-body
{:from from
:to to})
"failed" (i18n/label :t/push-failed-transaction-body
{:value amount
:currency (:symbol token)
:to to})
nil)]
{:title title
:icon (get-in token [:icon :source])
:deepLink (:deepLink notification)
:user-info notification
:message description}))
(defn foreground-chat?
[{{:keys [current-chat-id view-id]} :db} chat-id]
(and (= current-chat-id chat-id)
(= view-id :chat)))
(defn show-message-pn?
[{{:keys [app-state profile/profile]} :db :as cofx}
notification]
(let [chat-id (get-in notification [:body :chat :id])
notification-author (get-in notification [:notificationAuthor :id])]
(and
(not= notification-author (:public-key profile))
(or (= app-state "background")
(not (foreground-chat? cofx chat-id))))))
(defn create-notification
([notification]
(create-notification nil notification))
([cofx {:keys [bodyType] :as notification}]
(assoc
(case bodyType
"message" (when (show-message-pn? cofx notification) notification)
"transaction" (create-transfer-notification cofx notification)
nil)
:body-type
bodyType)))
(re-frame/reg-fx
::local-push-ios
(fn [evt]
(-> evt create-notification local-push-ios)))
(rf/defn local-notification-android
{:events [::local-notification-android]}
[cofx event]
(some->> event
(create-notification cofx)
local-push-android))
(rf/defn process
[cofx evt]
(if platform/ios?
{::local-push-ios evt}
(local-notification-android cofx evt)))

View File

@ -0,0 +1,83 @@
(ns status-im.notifications.wallet
(:require [utils.re-frame :as rf]
[taoensso.timbre :as log]
[clojure.string :as string]
[status-im.ethereum.tokens :as tokens]
[utils.money :as money]
[status-im.ethereum.decode :as decode]
[status-im.utils.utils :as utils]
[utils.i18n :as i18n]))
(def default-erc20-token
{:symbol :ERC20
:decimals 18
:name "ERC20"})
(defn preference=
[x y]
(and (= (:service x) (:service y))
(= (:event x) (:event y))
(= (:identifier x) (:identifier y))))
(defn- update-preference
[all new-preference]
(conj (filter (comp not (partial preference= new-preference))
all)
new-preference))
(rf/defn switch-transaction-notifications
{:events [:push-notifications.wallet/switch-transactions]}
[{:keys [db]} enabled?]
{:db (update db
:push-notifications/preferences
update-preference
{:enabled? enabled?
:service "wallet"
:event "transaction"
:identifier "all"})
:json-rpc/call [{:method "localnotifications_switchWalletNotifications"
:params [enabled?]
:on-success #(log/info "[push-notifications] switch-transaction successful" %)
:on-error #(log/error "[push-notifications] switch-transaction error" %)}]})
(defn create-transfer-notification
[{db :db}
{{:keys [state from to fromAccount toAccount value erc20 contract network]}
:body
:as notification}]
(let [token (if erc20
(get-in db
[:wallet/all-tokens (string/lower-case contract)]
default-erc20-token)
(tokens/native-currency network))
amount (money/wei->ether (decode/uint value))
to (or (:name toAccount) (utils/get-shortened-address to))
from (or (:name fromAccount) (utils/get-shortened-address from))
title (case state
"inbound" (i18n/label :t/push-inbound-transaction
{:value amount
:currency (:symbol token)})
"outbound" (i18n/label :t/push-outbound-transaction
{:value amount
:currency (:symbol token)})
"failed" (i18n/label :t/push-failed-transaction
{:value amount
:currency (:symbol token)})
nil)
description (case state
"inbound" (i18n/label :t/push-inbound-transaction-body
{:from from
:to to})
"outbound" (i18n/label :t/push-outbound-transaction-body
{:from from
:to to})
"failed" (i18n/label :t/push-failed-transaction-body
{:value amount
:currency (:symbol token)
:to to})
nil)]
{:title title
:icon (get-in token [:icon :source])
:deepLink (:deepLink notification)
:user-info notification
:message description}))

View File

@ -2,7 +2,7 @@
(:require [status-im.chat.models.message :as models.message]
[status-im.ethereum.subscriptions :as ethereum.subscriptions]
[status-im.mailserver.core :as mailserver]
[status-im.notifications.local :as local-notifications]
[status-im2.contexts.push-notifications.local.events :as local-notifications]
[status-im.transport.message.core :as transport.message]
[status-im.visibility-status-updates.core :as visibility-status-updates]
[utils.re-frame :as rf]

View File

@ -0,0 +1,55 @@
(ns status-im.ui.screens.notifications-settings.events
(:require [status-im.multiaccounts.update.core :as multiaccounts.update]
[utils.re-frame :as rf]
[taoensso.timbre :as log]))
(rf/defn notification-non-contacts-error
{:events [:push-notifications/non-contacts-update-error]}
[cofx enabled?]
(multiaccounts.update/optimistic cofx
:push-notifications-from-contacts-only?
(not (boolean enabled?))))
(rf/defn notification-block-mentions-error
{:events [:push-notifications/block-mentions-update-error]}
[cofx enabled?]
(multiaccounts.update/optimistic cofx :push-notifications-block-mentions? (not (boolean enabled?))))
(rf/defn notification-non-contacts
{:events [:push-notifications/switch-non-contacts]}
[{:keys [db] :as cofx} enabled?]
(let [method (if enabled?
"wakuext_enablePushNotificationsFromContactsOnly"
"wakuext_disablePushNotificationsFromContactsOnly")]
(rf/merge
cofx
{:json-rpc/call [{:method method
:params []
:on-success #(log/info "[push-notifications] contacts-notification-success" %)
:on-error #(log/info "[push-notifications] contacts-notification-error" %)}]}
(multiaccounts.update/optimistic :push-notifications-from-contacts-only? (boolean enabled?)))))
(rf/defn notification-block-mentions
{:events [:push-notifications/switch-block-mentions]}
[{:keys [db] :as cofx} enabled?]
(let [method (if enabled?
"wakuext_enablePushNotificationsBlockMentions"
"wakuext_disablePushNotificationsBlockMentions")]
(rf/merge cofx
{:json-rpc/call [{:method method
:params []
:on-success #(log/info "[push-notifications] block-mentions-success" %)
:on-error #(rf/dispatch
[:push-notifications/block-mentions-update-error enabled?
%])}]}
(multiaccounts.update/optimistic :push-notifications-block-mentions? (boolean enabled?)))))
(rf/defn notification-switch
{:events [:push-notifications/switch]}
[{:keys [db] :as cofx} enabled?]
(rf/merge cofx
(if enabled?
{:effects/push-notifications-enable nil}
{:effects/push-notifications-disable nil})
(multiaccounts.update/multiaccount-update :notifications-enabled? enabled? {})))

View File

@ -1,19 +1,15 @@
(ns status-im.ui.screens.notifications-settings.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [quo.core :as quo]
[quo.design-system.colors :as quo-colors]
[quo.platform :as platform]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[utils.i18n :as i18n]
[status-im.notifications.core :as notifications]
[status-im.ui.components.react :as react]))
(defonce server (reagent/atom ""))
[status-im.ui.components.react :as react]
[utils.re-frame :as rf]))
(defn local-notifications
[]
(let [{:keys [enabled]} @(re-frame/subscribe [:notifications/wallet-transactions])]
(let [{:keys [enabled?]} (rf/sub [:push-notifications/wallet-transactions])
{:keys [notifications-enabled?]} (rf/sub [:profile/profile])]
[:<>
[quo/separator
{:color (:ui-02 @quo-colors/theme)
@ -23,25 +19,24 @@
{:size :small
:title (i18n/label :t/notifications-transactions)
:accessibility-label :notifications-button
:active enabled
:on-press #(re-frame/dispatch
[::notifications/switch-transaction-notifications enabled])
:active (and notifications-enabled? enabled?)
:on-press #(rf/dispatch [:push-notifications.wallet/switch-transactions
(not enabled?)])
:accessory :switch}]]))
(defn notifications-settings-ios
[]
(let [{:keys [remote-push-notifications-enabled?
(let [{:keys [notifications-enabled?
push-notifications-block-mentions?
push-notifications-from-contacts-only?]}
@(re-frame/subscribe [:profile/profile])]
(rf/sub [:profile/profile])]
[:<>
[quo/list-item
{:size :small
:title (i18n/label :t/show-notifications)
:accessibility-label :notifications-button
:active remote-push-notifications-enabled?
:on-press #(re-frame/dispatch [::notifications/switch
(not remote-push-notifications-enabled?) true])
:active notifications-enabled?
:on-press #(rf/dispatch [:push-notifications/switch (not notifications-enabled?)])
:accessory :switch}]
[quo/separator
{:color (:ui-02 @quo-colors/theme)
@ -51,35 +46,34 @@
{:size :small
:title (i18n/label :t/notifications-non-contacts)
:accessibility-label :notifications-button
:active (and remote-push-notifications-enabled?
:active (and notifications-enabled?
(not push-notifications-from-contacts-only?))
:on-press #(re-frame/dispatch
[::notifications/switch-non-contacts
:on-press #(rf/dispatch
[:push-notifications/switch-non-contacts
(not push-notifications-from-contacts-only?)])
:accessory :switch}]
[quo/list-item
{:size :small
:title (i18n/label :t/allow-mention-notifications)
:accessibility-label :notifications-button
:active (and remote-push-notifications-enabled?
:active (and notifications-enabled?
(not push-notifications-block-mentions?))
:on-press #(re-frame/dispatch
[::notifications/switch-block-mentions
:on-press #(rf/dispatch
[:push-notifications/switch-block-mentions
(not push-notifications-block-mentions?)])
:accessory :switch}]
[local-notifications]]))
(defn notifications-settings-android
[]
(let [{:keys [notifications-enabled?]} @(re-frame/subscribe [:profile/profile])]
(let [{:keys [notifications-enabled?]} (rf/sub [:profile/profile])]
[:<>
[quo/list-item
{:title (i18n/label :t/local-notifications)
:accessibility-label :local-notifications-settings-button
:subtitle (i18n/label :t/local-notifications-subtitle)
:active notifications-enabled?
:on-press #(re-frame/dispatch
[::notifications/switch (not notifications-enabled?) false])
:on-press #(rf/dispatch [:push-notifications/switch (not notifications-enabled?)])
:accessory :switch}]
[local-notifications]]))
@ -91,81 +85,3 @@
(if platform/ios?
[notifications-settings-ios]
[notifications-settings-android])])
(defn notifications-advanced-settings
[]
(let [{:keys [remote-push-notifications-enabled?
send-push-notifications?
push-notifications-server-enabled?]}
@(re-frame/subscribe [:profile/profile])]
[react/scroll-view
{:style {:flex 1}
:content-container-style {:padding-vertical 8}}
[quo/list-item
{:size :small
:title (i18n/label :t/send-push-notifications)
:accessibility-label :send-push-notifications-button
:active send-push-notifications?
:on-press #(re-frame/dispatch
[::notifications/switch-send-push-notifications
(not send-push-notifications?)])
:accessory :switch}]
[quo/list-footer
(i18n/label :t/send-push-notifications-description)]
[quo/separator {:style {:margin-vertical 8}}]
[quo/list-item
{:size :small
:title (i18n/label :t/push-notifications-server-enabled)
:accessibility-label :send-push-notifications-button
:active (and remote-push-notifications-enabled?
push-notifications-server-enabled?)
:on-press #(re-frame/dispatch
[::notifications/switch-push-notifications-server-enabled
(not push-notifications-server-enabled?)])
:accessory :switch}]
[quo/list-item
{:size :small
:title (i18n/label :t/push-notifications-servers)
:accessibility-label :send-push-notifications-button
:chevron true
:on-press #(re-frame/dispatch
[:navigate-to :notifications-servers])}]]))
(defn server-view
[{:keys [public-key type registered]}]
[quo/list-item
{:size :small
:title (str (subs public-key 0 8)
" "
(if (= type notifications/server-type-custom)
(i18n/label :t/custom)
(i18n/label :t/default))
" "
(if registered
(i18n/label :t/registered)
(i18n/label :t/not-registered)))}])
(defview notifications-servers
[]
(letsubs [servers [:push-notifications/servers]]
{:component-did-mount #(re-frame/dispatch [::notifications/fetch-servers])}
[react/scroll-view
{:style {:flex 1}
:content-container-style {:padding-vertical 8}}
(map server-view servers)
[react/keyboard-avoiding-view {}
[react/view {:style {:padding-horizontal 20}}
[quo/text-input
{:label (i18n/label :t/server)
:placeholder (i18n/label :t/specify-server-public-key)
:value @server
:on-change-text #(reset! server %)
:auto-focus true}]]
[quo/button
{:type :secondary
:after :main-icon/next
:disabled (empty? @server)
:on-press #(do
(re-frame/dispatch [::notifications/add-server @server])
(reset! server ""))}
(i18n/label :t/save)]]]))

View File

@ -312,10 +312,6 @@
:options {:topBar {:title {:text (i18n/label :t/notification-settings)}}
:insets {:top? true}}
:component notifications-settings/notifications-settings}
{:name :notifications-servers
:options {:topBar {:title {:text (i18n/label :t/notifications-servers)}}
:insets {:top? true}}
:component notifications-settings/notifications-servers}
{:name :sync-settings
:options {:topBar {:title {:text (i18n/label :t/sync-settings)}}
:insets {:top? true}}
@ -475,17 +471,6 @@
:top? true}}
:component notifications-settings/notifications-settings}
;;TODO WHY MODAL?
;[Profile] Notifications Advanced settings
{:name :notifications-advanced-settings
:options {:topBar {:title {:text (i18n/label :t/notification-settings)}}
:popGesture false
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false}
:insets {:bottom? true
:top? true}}
:component notifications-settings/notifications-advanced-settings}
;[Wallet] Prepare Transaction
{:name :prepare-send-transaction
:on-dissmiss [:wallet/cancel-transaction-command]

View File

@ -109,9 +109,7 @@
(update :chats #(apply dissoc % removed-chats))
(update :chats-home-list set/difference removed-chats))
:fx [(when (not-empty removed-chats)
[:clear-message-notifications
[removed-chats
(get-in db [:profile/profile :remote-push-notifications-enabled?])]])
[:effects/push-notifications-clear-message-notifications removed-chats])
[:dispatch [:chat/leave-removed-chat]]]}))
(re-frame/reg-event-fx :chat/ensure-chats ensure-chats)
@ -286,9 +284,9 @@
{:events [:chat.ui/remove-chat]}
[{:keys [db now] :as cofx} chat-id]
(rf/merge cofx
{:clear-message-notifications
[[chat-id] (get-in db [:profile/profile :remote-push-notifications-enabled?])]
:dispatch [:shell/close-switcher-card chat-id]}
{:effects/push-notifications-clear-message-notifications [chat-id]
:dispatch [:shell/close-switcher-card
chat-id]}
(deactivate-chat chat-id)
(offload-messages chat-id)))

View File

@ -3,7 +3,6 @@
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[status-im.notifications.core :as notifications]
[status-im2.contexts.onboarding.enable-notifications.style :as style]
[status-im2.contexts.shell.jump-to.utils :as shell.utils]
[utils.i18n :as i18n]
@ -25,7 +24,7 @@
[quo/button
{:on-press (fn []
(shell.utils/change-selected-stack-id :communities-stack true nil)
(rf/dispatch [::notifications/switch true platform/ios?])
(rf/dispatch [:push-notifications/switch true platform/ios?])
(rf/dispatch [:navigate-to-within-stack
[:welcome :enable-notifications]]))
:type :primary

View File

@ -9,7 +9,6 @@
[status-im2.common.biometric.events :as biometric]
[status-im2.contexts.profile.config :as profile.config]
[taoensso.timbre :as log]
[status-im.notifications.core :as notifications]
[status-im2.config :as config]
[status-im.data-store.settings :as data-store.settings]
[status-im.communities.core :as communities]
@ -25,7 +24,8 @@
[status-im.data-store.visibility-status-updates :as visibility-status-updates-store]
[status-im.data-store.switcher-cards :as switcher-cards-store]
[status-im.browser.core :as browser]
[status-im.group-chats.core :as group-chats]))
[status-im.group-chats.core :as group-chats]
[status-im2.contexts.push-notifications.events :as notifications]))
(re-frame/reg-fx
::login
@ -77,7 +77,7 @@
:networks/current-network current-network
:networks/networks (merge networks config/default-networks-by-id)
:profile/profile (merge profile settings))}
(notifications/load-notification-preferences)
(notifications/load-preferences)
(data-store.chats/fetch-chats-preview
{:on-success
#(do (re-frame/dispatch [:chats-list/load-success %])
@ -98,12 +98,10 @@
{:events [:profile.login/get-chats-callback]}
[{:keys [db] :as cofx}]
(let [{:networks/keys [current-network networks]} db
notifications-enabled? (get-in db [:profile/profile :notifications-enabled?])
current-network-config (get networks current-network)
network-id (str (get-in networks
[current-network :config :NetworkId]))
remote-push-notifications-enabled?
(get-in db [:profile/profile :remote-push-notifications-enabled?])]
{:keys [notifications-enabled?]} (:profile/profile db)
current-network-config (get networks current-network)
network-id (str (get-in networks
[current-network :config :NetworkId]))]
(rf/merge cofx
(cond-> {:wallet/initialize-transactions-management-enabled nil
:wallet/initialize-wallet
@ -114,8 +112,8 @@
accounts tokens custom-tokens favourites]))]
:check-eip1559-activation {:network-id network-id}
:chat/open-last-chat (get-in db [:profile/profile :key-uid])}
(or notifications-enabled? remote-push-notifications-enabled?)
(assoc ::notifications/enable remote-push-notifications-enabled?))
notifications-enabled?
(assoc :effects/push-notifications-enable nil))
(transport/start-messenger)
(contacts/initialize-contacts)
(browser/initialize-browser)

View File

@ -0,0 +1,59 @@
(ns status-im2.contexts.push-notifications.effects
(:require [react-native.push-notification-ios :as pn-ios]
[utils.re-frame :as rf]
[native-module.push-notifications :as native-module.pn]
[react-native.platform :as platform]))
(def ios-listeners-added? (atom nil))
(defn enable-ios-notifications
[]
(when-not @ios-listeners-added?
(reset! ios-listeners-added? true)
(pn-ios/add-listener
"register"
(fn [token]
(rf/dispatch [:push-notifications/registered-for-push-notifications token])))
(pn-ios/add-listener
"registrationError"
(fn [error]
(rf/dispatch [:push-notifications/switch-error true error]))))
(pn-ios/request-permissions))
(defn disable-ios-notifications
[]
(pn-ios/abandon-permissions)
(rf/dispatch [:push-notifications/unregistered-from-push-notifications]))
(defn enable-android-notifications
[]
(native-module.pn/create-channel
{:channel-id "status-im-notifications"
:channel-name "Status push notifications"})
(native-module.pn/enable-notifications))
(defn disable-android-notifications
[]
(native-module.pn/disable-notifications))
(rf/reg-fx
:effects/push-notifications-enable
(fn []
(if platform/android?
(enable-android-notifications)
(enable-ios-notifications))))
(rf/reg-fx
:effects/push-notifications-disable
(fn []
(if platform/android?
(disable-android-notifications)
(disable-ios-notifications))))
(rf/reg-fx
:effects/push-notifications-clear-message-notifications
(fn [chat-ids]
(if platform/android?
(doseq [chat-id chat-ids]
(native-module.pn/clear-message-notifications chat-id))
(pn-ios/remove-all-delivered-notifications))))

View File

@ -0,0 +1,60 @@
(ns status-im2.contexts.push-notifications.events
(:require [react-native.push-notification-ios :as pn-ios]
[native-module.push-notifications :as native-module.pn]
[status-im2.config :as config]
[utils.re-frame :as rf]
[taoensso.timbre :as log]
[react-native.platform :as platform]
[react-native.async-storage :as async-storage]
[utils.transforms :as transforms]
[cljs-bean.core :as bean]
status-im2.contexts.push-notifications.effects))
(def server-type-default 1)
(def server-type-custom 2)
(def apn-token-type 1)
(def firebase-token-type 2)
(defn handle-notification-press
[{{deep-link :deepLink} :userInfo
interaction :userInteraction}]
(async-storage/set-item! (str :chat-id) nil)
(when (and deep-link (or platform/ios? (and platform/android? interaction)))
(rf/dispatch [:universal-links/handle-url deep-link])))
(defn listen-notifications
[]
(if platform/ios?
(pn-ios/add-listener "localNotification"
#(handle-notification-press {:userInfo (bean/bean (.getData ^js %))}))
(native-module.pn/add-listener "remoteNotificationReceived"
#(handle-notification-press (transforms/json->clj %)))))
(rf/defn handle-enable-notifications-event
{:events [:push-notifications/registered-for-push-notifications]}
[_ token]
{:json-rpc/call [{:method "wakuext_registerForPushNotifications"
:params [token (if platform/ios? config/apn-topic)
(if platform/ios? apn-token-type firebase-token-type)]
:on-success #(log/info "[push-notifications] register-success" %)
:on-error #(log/info "[push-notifications] register-error" %)}]})
(rf/defn handle-disable-notifications-event
{:events [:push-notifications/unregistered-from-push-notifications]}
[_]
{:json-rpc/call [{:method "wakuext_unregisterFromPushNotifications"
:params []
:on-success #(log/info "[push-notifications] unregister-success" %)
:on-error #(log/info "[push-notifications] unregister-error" %)}]})
(rf/defn handle-preferences-load
{:events [:push-notifications/preferences-loaded]}
[{:keys [db]} preferences]
{:db (assoc db :push-notifications/preferences preferences)})
(rf/defn load-preferences
[_]
{:json-rpc/call [{:method "localnotifications_notificationPreferences"
:params []
:on-success #(rf/dispatch [:push-notifications/preferences-loaded %])}]})

View File

@ -0,0 +1,18 @@
(ns status-im2.contexts.push-notifications.local.effects
(:require [react-native.push-notification-ios :as pn-ios]
[cljs-bean.core :as bean]
[native-module.push-notifications :as native-module.pn]
[utils.re-frame :as rf]))
(rf/reg-fx :effects/push-notifications-local-present-ios
(fn [{:keys [title message user-info body-type]}]
(when (not= body-type "message")
(pn-ios/present-local-notification title
message
(bean/->js (merge user-info
{:notificationType
"local-notification"}))))))
(rf/reg-fx :effects/push-notifications-local-present-android
(fn [notification]
(native-module.pn/present-local-notification notification)))

View File

@ -0,0 +1,36 @@
(ns status-im2.contexts.push-notifications.local.events
(:require [quo.platform :as platform]
[utils.re-frame :as rf]
[status-im.notifications.wallet :as notifications.wallet]
status-im2.contexts.push-notifications.local.effects))
(defn foreground-chat?
[{{:keys [current-chat-id view-id]} :db} chat-id]
(and (= current-chat-id chat-id)
(= view-id :chat)))
(defn show-message-pn?
[{{:keys [app-state profile/profile]} :db :as cofx}
notification]
(let [chat-id (get-in notification [:body :chat :id])
notification-author (get-in notification [:notificationAuthor :id])]
(and
(not= notification-author (:public-key profile))
(or (= app-state "background")
(not (foreground-chat? cofx chat-id))))))
(defn create-notification
[cofx {:keys [bodyType] :as notification}]
(assoc
(case bodyType
"message" (when (show-message-pn? cofx notification) notification)
"transaction" (notifications.wallet/create-transfer-notification cofx notification)
nil)
:body-type
bodyType))
(rf/defn process
[cofx event]
(if platform/ios?
{:effects/push-notifications-local-present-ios (create-notification nil event)}
{:effects/push-notifications-local-present-android (create-notification cofx event)}))

View File

@ -18,12 +18,12 @@
[status-im2.setup.interceptors :as interceptors]
[react-native.async-storage :as async-storage]
[native-module.core :as native-module]
[status-im.notifications.local :as notifications]
[status-im.utils.universal-links.core :as utils.universal-links]
status-im.events
status-im2.events
status-im2.navigation.core
status-im2.subs.root))
status-im2.subs.root
[status-im2.contexts.push-notifications.events :as notifications]))
;;;; re-frame RN setup
(set! interop/next-tick js/setTimeout)

View File

@ -236,7 +236,6 @@
(reg-root-key-sub :delete-profile/keep-keys-on-keycard? :delete-profile/keep-keys-on-keycard?)
;; push notifications
(reg-root-key-sub :push-notifications/servers :push-notifications/servers)
(reg-root-key-sub :push-notifications/preferences :push-notifications/preferences)
(reg-root-key-sub :buy-crypto/on-ramps :buy-crypto/on-ramps)

View File

@ -2,11 +2,11 @@
(:require [re-frame.core :as re-frame]
[status-im.ethereum.transactions.core :as transactions]
[utils.i18n :as i18n]
[status-im.notifications.core :as notifications]
[utils.datetime :as datetime]
[utils.money :as money]
[status-im.wallet.db :as wallet.db]
[status-im.wallet.utils :as wallet.utils]))
[status-im.wallet.utils :as wallet.utils]
[status-im.notifications.wallet :as notifications.wallet]))
(re-frame/reg-sub
:wallet/accounts
@ -236,11 +236,11 @@
(* 100 (/ confirmations transactions/confirmations-count-threshold)))))))
(re-frame/reg-sub
:notifications/wallet-transactions
:push-notifications/wallet-transactions
:<- [:push-notifications/preferences]
(fn [pref]
(first (filter #(notifications/preference= %
{:service "wallet"
:event "transaction"
:identifier "all"})
(first (filter #(notifications.wallet/preference= %
{:service "wallet"
:event "transaction"
:identifier "all"})
pref))))

View File

@ -78,3 +78,5 @@
(def sub (comp deref re-frame/subscribe))
(def dispatch re-frame/dispatch)
(def reg-fx re-frame/reg-fx)