Update PNs to use data-only messaging, and only encode/decode data values. Fixes #6772
Fix navigation to chat when PN is tapped while signed off. Fixes #3488 Anonymize PN pubkeys. Part of #6772
This commit is contained in:
parent
4af2073388
commit
5a69b4198e
|
@ -156,3 +156,4 @@ conan*.txt
|
|||
conanbuildinfo.*
|
||||
conan.cmake
|
||||
/yarn-error.log
|
||||
/default.realm/
|
||||
|
|
2
Makefile
2
Makefile
|
@ -211,6 +211,8 @@ android-ports: ##@other Add proxies to Android Device/Simulator
|
|||
adb reverse tcp:4567 tcp:4567
|
||||
adb forward tcp:5561 tcp:5561
|
||||
|
||||
android-logcat:
|
||||
adb logcat | grep -e StatusModule -e ReactNativeJS -e StatusNativeLogs
|
||||
|
||||
startdev-%:
|
||||
$(eval SYSTEM := $(word 2, $(subst -, , $@)))
|
||||
|
|
|
@ -1 +1 @@
|
|||
181221-204011-e80de6
|
||||
0.19.0-beta.1
|
||||
|
|
|
@ -68,3 +68,7 @@
|
|||
-dontwarn java.nio.file.*
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||
-dontwarn okio.**
|
||||
|
||||
# Firebase
|
||||
-keep class io.invertase.firebase.** { *; }
|
||||
-dontwarn io.invertase.firebase.**
|
||||
|
|
|
@ -13,7 +13,7 @@ pipeline {
|
|||
timestamps()
|
||||
disableConcurrentBuilds()
|
||||
/* Prevent Jenkins jobs from running forever */
|
||||
timeout(time: 35, unit: 'MINUTES')
|
||||
timeout(time: 45, unit: 'MINUTES')
|
||||
/* Limit builds retained */
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
|
|
|
@ -6,7 +6,7 @@ Each business logic module is managed in a module directory.
|
|||
|
||||
*There is no rigid structure on how to organize code inside modules outside of core and db namespaces*
|
||||
|
||||
```
|
||||
```txt
|
||||
- events.cljs
|
||||
- subs.cljs
|
||||
- notifications
|
||||
|
@ -37,13 +37,13 @@ Core namespace must only contain functions that can be called outside of the mod
|
|||
|
||||
- fx producing functions called by events and other modules
|
||||
|
||||
```clojure
|
||||
(def get-current-account
|
||||
```clojure
|
||||
(def get-current-account
|
||||
module.db/get-current-account)
|
||||
|
||||
(defn set-current-account [{db :db :as cofx}]
|
||||
(defn set-current-account [{db :db :as cofx}]
|
||||
{:db (module.db/set-current-account db)})
|
||||
```
|
||||
```
|
||||
|
||||
## db.cljs
|
||||
|
||||
|
@ -61,13 +61,14 @@ These guidelines make db.cljs namespaces the place to go when making changes to
|
|||
- events must always be declared with `register-handler-fx`, no `register-handler-db`
|
||||
- events must never use the `trim-v` interceptor
|
||||
- events must only contain a function call defined in a module
|
||||
```clojure
|
||||
(handlers/register-handler-fx
|
||||
:notifications/handle-push-notification
|
||||
```clojure
|
||||
(handlers/register-handler-fx
|
||||
:notifications/handle-push-notification-open
|
||||
(fn [cofx [_ event]]
|
||||
(notifications/handle-push-notification event cofx)))
|
||||
```
|
||||
(notifications/handle-push-notification-open event cofx)))
|
||||
```
|
||||
- events must use synthetic namespaces:
|
||||
|
||||
- `:module.ui/` for user triggered events
|
||||
- `:module.callback/` for callback events, which are events bringing back the result of an fx to the event loop, the name of the event should end with `-success` or `-error` most of the time. Other possibilities can be `-granted`, `-denied` for instance.
|
||||
- `:module/` for internal events, examples are time based events marked `-timed-out`, external changes marked `-changed` or reception of external events marked `-received`.
|
||||
|
|
|
@ -105,12 +105,12 @@ QList<ModuleMethod *> DesktopNotification::methodsToExport() {
|
|||
|
||||
QVariantMap DesktopNotification::constantsToExport() { return QVariantMap(); }
|
||||
|
||||
void DesktopNotification::sendNotification(QString title, QString body, bool prioritary) {
|
||||
void DesktopNotification::displayNotification(QString title, QString body, bool prioritary) {
|
||||
Q_D(DesktopNotification);
|
||||
qCDebug(NOTIFICATION) << "::sendNotification";
|
||||
qCDebug(NOTIFICATION) << "::displayNotification";
|
||||
|
||||
if (m_appHasFocus) {
|
||||
qCDebug(NOTIFICATION) << "Not sending notification since an application window is active";
|
||||
qCDebug(NOTIFICATION) << "Not displaying notification since an application window is active";
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
QList<ModuleMethod*> methodsToExport() override;
|
||||
QVariantMap constantsToExport() override;
|
||||
|
||||
Q_INVOKABLE void sendNotification(QString title, QString body, bool prioritary);
|
||||
Q_INVOKABLE void displayNotification(QString title, QString body, bool prioritary);
|
||||
Q_INVOKABLE void setDockBadgeLabel(const QString label);
|
||||
private:
|
||||
QScopedPointer<DesktopNotificationPrivate> d_ptr;
|
||||
|
|
|
@ -81,7 +81,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onHostResume() { // Actvity `onResume`
|
||||
public void onHostResume() { // Activity `onResume`
|
||||
module = this;
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity == null) {
|
||||
|
@ -459,7 +459,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
}
|
||||
|
||||
@ReactMethod
|
||||
public void notifyUsers(final String message, final String payloadJSON, final String tokensJSON, final Callback callback) {
|
||||
public void notifyUsers(final String dataPayloadJSON, final String tokensJSON, final Callback callback) {
|
||||
Log.d(TAG, "notifyUsers");
|
||||
if (!checkAvailability()) {
|
||||
callback.invoke(false);
|
||||
|
@ -469,7 +469,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String res = Statusgo.NotifyUsers(message, payloadJSON, tokensJSON);
|
||||
String res = Statusgo.NotifyUsers(dataPayloadJSON, tokensJSON);
|
||||
|
||||
callback.invoke(res);
|
||||
}
|
||||
|
|
|
@ -136,14 +136,14 @@ void RCTStatus::createAccount(QString password, double callbackId) {
|
|||
}
|
||||
|
||||
|
||||
void RCTStatus::notifyUsers(QString token, QString payloadJSON, QString tokensJSON, double callbackId) {
|
||||
void RCTStatus::notifyUsers(QString dataPayloadJSON, QString tokensJSON, double callbackId) {
|
||||
Q_D(RCTStatus);
|
||||
qCDebug(RCTSTATUS) << "::notifyUsers call - callbackId:" << callbackId;
|
||||
QtConcurrent::run([&](QString token, QString payloadJSON, QString tokensJSON, double callbackId) {
|
||||
const char* result = NotifyUsers(token.toUtf8().data(), payloadJSON.toUtf8().data(), tokensJSON.toUtf8().data());
|
||||
QtConcurrent::run([&](QString dataPayloadJSON, QString tokensJSON, double callbackId) {
|
||||
const char* result = NotifyUsers(dataPayloadJSON.toUtf8().data(), tokensJSON.toUtf8().data());
|
||||
logStatusGoResult("::notifyUsers Notify", result);
|
||||
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
||||
}, token, payloadJSON, tokensJSON, callbackId);
|
||||
}, dataPayloadJSON, tokensJSON, callbackId);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
Q_INVOKABLE void startNode(QString configString);
|
||||
Q_INVOKABLE void stopNode();
|
||||
Q_INVOKABLE void createAccount(QString password, double callbackId);
|
||||
Q_INVOKABLE void notifyUsers(QString token, QString payloadJSON, QString tokensJSON, double callbackId);
|
||||
Q_INVOKABLE void notifyUsers(QString dataPayloadJSON, QString tokensJSON, double callbackId);
|
||||
Q_INVOKABLE void sendLogs(QString dbJSON);
|
||||
Q_INVOKABLE void addPeer(QString enode, double callbackId);
|
||||
Q_INVOKABLE void recoverAccount(QString passphrase, QString password, double callbackId);
|
||||
|
|
|
@ -185,11 +185,10 @@ RCT_EXPORT_METHOD(createAccount:(NSString *)password
|
|||
////////////////////////////////////////////////////////////////////
|
||||
#pragma mark - NotifyUsers method
|
||||
//////////////////////////////////////////////////////////////////// notifyUsers
|
||||
RCT_EXPORT_METHOD(notifyUsers:(NSString *)message
|
||||
payloadJSON:(NSString *)payloadJSON
|
||||
RCT_EXPORT_METHOD(notifyUsers:(NSString *)dataPayloadJSON
|
||||
tokensJSON:(NSString *)tokensJSON
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
char * result = NotifyUsers((char *) [message UTF8String], (char *) [payloadJSON UTF8String], (char *) [tokensJSON UTF8String]);
|
||||
char * result = NotifyUsers((char *) [dataPayloadJSON UTF8String], (char *) [tokensJSON UTF8String]);
|
||||
callback(@[[NSString stringWithUTF8String: result]]);
|
||||
#if DEBUG
|
||||
NSLog(@"NotifyUsers() method called");
|
||||
|
|
|
@ -40,9 +40,7 @@
|
|||
(.addEventListener react/app-state "change" app-state-change-handler))
|
||||
:component-did-mount
|
||||
(fn [this]
|
||||
(dispatch [:set-initial-props (reagent/props this)])
|
||||
;; TODO(oskarth): Background click_action handler
|
||||
(notifications/init))
|
||||
(dispatch [:set-initial-props (reagent/props this)]))
|
||||
:component-will-unmount
|
||||
(fn []
|
||||
(.stop react/http-bridge)
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
(get-in db [:account/account :desktop-notifications?])
|
||||
(< (time/seconds-ago (time/to-date timestamp)) constants/one-earth-day))
|
||||
(let [{:keys [title body prioritary?]} (build-desktop-notification cofx message)]
|
||||
(.sendNotification react/desktop-notification title body prioritary?)))
|
||||
(.displayNotification react/desktop-notification title body prioritary?)))
|
||||
(fx/merge cofx
|
||||
{:db (cond->
|
||||
(-> db
|
||||
|
@ -159,19 +159,6 @@
|
|||
message
|
||||
(assoc message :clock-value (utils.clocks/send last-clock-value))))
|
||||
|
||||
(fx/defn display-notification
|
||||
[cofx chat-id]
|
||||
(when config/in-app-notifications-enabled?
|
||||
(let [view-id (get-in cofx [:db :view-id])
|
||||
from (accounts.db/current-public-key cofx)
|
||||
current-chat-id (get-in cofx [:db :current-chat-id])]
|
||||
(when-not (and (= :chat view-id)
|
||||
(= current-chat-id chat-id))
|
||||
{:notifications/display-notification {:title (i18n/label :notifications-new-message-title)
|
||||
:body (i18n/label :notifications-new-message-body)
|
||||
:to chat-id
|
||||
:from from}}))))
|
||||
|
||||
(defn check-response-to
|
||||
[{{:keys [response-to response-to-v2]} :content :as message}
|
||||
old-id->message]
|
||||
|
@ -210,7 +197,6 @@
|
|||
current-chat? :seen
|
||||
:else :received))
|
||||
(commands-receiving/receive message)
|
||||
(display-notification chat-id)
|
||||
(send-message-seen chat-id message-id (and (not group-chat)
|
||||
current-chat?
|
||||
(not (= constants/system from))
|
||||
|
@ -391,14 +377,14 @@
|
|||
(add-own-status chat-id message-id :sending)
|
||||
(send chat-id message-id wrapped-record))))
|
||||
|
||||
(fx/defn send-push-notification [cofx fcm-token status]
|
||||
(fx/defn send-push-notification [cofx message-id fcm-token status]
|
||||
(log/debug "#6772 - send-push-notification" message-id fcm-token)
|
||||
(when (and fcm-token (= status :sent))
|
||||
{:send-notification {:message (js/JSON.stringify #js {:from (accounts.db/current-public-key cofx)
|
||||
:to (get-in cofx [:db :current-chat-id])})
|
||||
:payload {:title (i18n/label :notifications-new-message-title)
|
||||
:body (i18n/label :notifications-new-message-body)
|
||||
:sound notifications/sound-name}
|
||||
:tokens [fcm-token]}}))
|
||||
(let [payload {:from (accounts.db/current-public-key cofx)
|
||||
:to (get-in cofx [:db :current-chat-id])
|
||||
:id message-id}]
|
||||
{:send-notification {:data-payload (notifications/encode-notification-payload payload)
|
||||
:tokens [fcm-token]}})))
|
||||
|
||||
(fx/defn update-message-status [{:keys [db]} chat-id message-id status]
|
||||
(let [from (get-in db [:chats chat-id :messages message-id :from])
|
||||
|
@ -484,8 +470,13 @@
|
|||
|
||||
(re-frame/reg-fx
|
||||
:send-notification
|
||||
(fn [{:keys [message payload tokens]}]
|
||||
(let [payload-json (types/clj->json payload)
|
||||
(fn [{:keys [data-payload tokens]}]
|
||||
"Sends a notification to another device. data-payload is a Clojure map of strings to strings"
|
||||
(let [data-payload-json (types/clj->json data-payload)
|
||||
tokens-json (types/clj->json tokens)]
|
||||
(log/debug "send-notification message: " message " payload-json: " payload-json " tokens-json: " tokens-json)
|
||||
(status/notify-users {:message message :payload payload-json :tokens tokens-json} #(log/debug "send-notification cb result: " %)))))
|
||||
(log/debug "send-notification data-payload-json:" data-payload-json "tokens-json:" tokens-json)
|
||||
;; NOTE: react-native-firebase doesn't have a good implementation of sendMessage
|
||||
;; (supporting e.g. priority or content_available properties),
|
||||
;; therefore we must use an implementation in status-go.
|
||||
(status/notify-users {:data-payload data-payload-json :tokens tokens-json}
|
||||
#(log/debug "send-notification cb result: " %)))))
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
(ns status-im.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.error-handler :as error-handler]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.notifications.background :as background-messaging]
|
||||
[reagent.core :as reagent]
|
||||
status-im.transport.impl.receive
|
||||
status-im.transport.impl.send
|
||||
|
@ -18,4 +20,6 @@
|
|||
(log/set-level! config/log-level)
|
||||
(error-handler/register-exception-handler!)
|
||||
(re-frame/dispatch [:init/app-started])
|
||||
(.registerComponent react/app-registry "StatusIm" #(reagent/reactify-component app-root)))
|
||||
(.registerComponent react/app-registry "StatusIm" #(reagent/reactify-component app-root))
|
||||
(when platform/android?
|
||||
(.registerHeadlessTask react/app-registry "RNFirebaseBackgroundMessage" background-messaging/message-handler-fn)))
|
||||
|
|
|
@ -830,9 +830,9 @@
|
|||
;; notifications module
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:notifications/notification-event-received
|
||||
(fn [cofx [_ event]]
|
||||
(notifications/handle-push-notification cofx event)))
|
||||
:notifications/notification-open-event-received
|
||||
(fn [cofx [_ decoded-payload ctx]]
|
||||
(notifications/handle-push-notification-open cofx decoded-payload ctx)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:notifications.callback/get-fcm-token-success
|
||||
|
@ -849,6 +849,11 @@
|
|||
(fn [cofx _]
|
||||
(accounts/show-mainnet-is-default-alert cofx)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:notifications.callback/on-message
|
||||
(fn [cofx [_ decoded-payload opts]]
|
||||
(notifications/handle-on-message cofx decoded-payload opts)))
|
||||
|
||||
;; hardwallet module
|
||||
|
||||
(handlers/register-handler-fx
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
(then #(re-frame/dispatch [:hardwallet.callback/check-nfc-support-success %])))))
|
||||
|
||||
(defn check-nfc-enabled []
|
||||
(when platform/android?
|
||||
(when (and platform/android?
|
||||
config/hardwallet-enabled?)
|
||||
(.. keycard
|
||||
nfcIsEnabled
|
||||
(then #(re-frame/dispatch [:hardwallet.callback/check-nfc-enabled-success %])))))
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
{:init/get-device-UUID nil
|
||||
:init/restore-native-settings nil
|
||||
:ui/listen-to-window-dimensions-change nil
|
||||
:notifications/handle-initial-push-notification nil
|
||||
:notifications/init nil
|
||||
:network/listen-to-network-status nil
|
||||
:network/listen-to-connection-status nil
|
||||
:hardwallet/check-nfc-support nil
|
||||
|
@ -90,7 +90,8 @@
|
|||
"Initialize db to initial state"
|
||||
[{{:keys [status-module-initialized? view-id hardwallet
|
||||
initial-props desktop/desktop
|
||||
network-status network peers-count peers-summary device-UUID]
|
||||
network-status network peers-count peers-summary device-UUID
|
||||
push-notifications/stored]
|
||||
:node/keys [status]
|
||||
:or {network (get app-db :network)}} :db}]
|
||||
{:db (assoc app-db
|
||||
|
@ -105,7 +106,8 @@
|
|||
:network network
|
||||
:hardwallet hardwallet
|
||||
:device-UUID device-UUID
|
||||
:view-id view-id)})
|
||||
:view-id view-id
|
||||
:push-notifications/stored stored)})
|
||||
|
||||
(fx/defn initialize-app
|
||||
[cofx encryption-key]
|
||||
|
@ -140,8 +142,13 @@
|
|||
(let [{{:accounts/keys [accounts] :as db} :db} cofx]
|
||||
(if (empty? accounts)
|
||||
(navigation/navigate-to-clean cofx :intro nil)
|
||||
(let [account-with-notification (first (keys (:push-notifications/stored db)))
|
||||
selection-fn (if (not-empty account-with-notification)
|
||||
(let [account-with-notification
|
||||
(when-not platform/desktop?
|
||||
(notifications/lookup-contact-pubkey-from-hash
|
||||
cofx
|
||||
(first (keys (:push-notifications/stored db)))))
|
||||
selection-fn
|
||||
(if (not-empty account-with-notification)
|
||||
#(filter (fn [account]
|
||||
(= account-with-notification
|
||||
(:public-key account)))
|
||||
|
@ -193,12 +200,12 @@
|
|||
(= view-id :create-account)
|
||||
(assoc-in [:accounts/create :step] :enter-name))}))
|
||||
|
||||
(defn login-only-events [cofx address]
|
||||
(defn login-only-events [cofx address stored-pns]
|
||||
(fx/merge cofx
|
||||
{:notifications/request-notifications-permissions nil}
|
||||
(navigation/navigate-to-cofx :home nil)
|
||||
(universal-links/process-stored-event)
|
||||
(notifications/process-stored-event address)
|
||||
(notifications/process-stored-event address stored-pns)
|
||||
(when platform/desktop?
|
||||
(chat-model/update-dock-badge-label))))
|
||||
|
||||
|
@ -213,7 +220,8 @@
|
|||
(= (get-in cofx [:db :view-id])
|
||||
:hardwallet-success))
|
||||
|
||||
(fx/defn initialize-account [cofx address]
|
||||
(fx/defn initialize-account [{:keys [db] :as cofx} address]
|
||||
(let [stored-pns (:push-notifications/stored db)]
|
||||
(fx/merge cofx
|
||||
{:notifications/get-fcm-token nil}
|
||||
(initialize-account-db address)
|
||||
|
@ -228,7 +236,7 @@
|
|||
(accounts.update/update-sign-in-time)
|
||||
#(when-not (or (creating-account? %)
|
||||
(finishing-hardwallet-setup? %))
|
||||
(login-only-events % address))))
|
||||
(login-only-events % address stored-pns)))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:init/init-store
|
||||
|
|
|
@ -35,8 +35,7 @@
|
|||
(.addEventListener react/app-state "change" app-state-change-handler))
|
||||
:component-did-mount
|
||||
(fn [this]
|
||||
(dispatch [:set-initial-props (reagent/props this)])
|
||||
(notifications/init))
|
||||
(dispatch [:set-initial-props (reagent/props this)]))
|
||||
:component-will-unmount
|
||||
(fn []
|
||||
(.stop react/http-bridge)
|
||||
|
|
|
@ -84,9 +84,9 @@
|
|||
true)
|
||||
false))))))
|
||||
|
||||
(defn notify-users [{:keys [message payload tokens] :as m} on-result]
|
||||
(defn notify-users [{:keys [data-payload tokens] :as m} on-result]
|
||||
(when status
|
||||
(call-module #(.notifyUsers status message payload tokens on-result))))
|
||||
(call-module #(.notifyUsers status data-payload tokens on-result))))
|
||||
|
||||
(defn send-logs [dbJson]
|
||||
(when status
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
(ns status-im.notifications.background
|
||||
(:require [goog.object :as object]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.react-native.js-dependencies :as rn]
|
||||
[status-im.notifications.core :as notifications]
|
||||
[status-im.i18n :as i18n]
|
||||
[cljs.core.async :as async]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(when-not platform/desktop?
|
||||
(def firebase (object/get rn/react-native-firebase "default")))
|
||||
|
||||
(defn message-handler-fn []
|
||||
;; message-js is firebase.messaging.RemoteMessage: https://github.com/invertase/react-native-firebase-docs/blob/master/docs/messaging/reference/RemoteMessage.md
|
||||
(fn [message-js]
|
||||
(js/Promise.
|
||||
(fn [on-success on-error]
|
||||
(try
|
||||
(when message-js
|
||||
(log/debug "message-handler-fn called" (js/JSON.stringify message-js))
|
||||
(let [decoded-payload (notifications/decode-notification-payload message-js)]
|
||||
(when decoded-payload
|
||||
(log/debug "dispatching :notifications.callback/on-message to display background message" decoded-payload)
|
||||
(re-frame/dispatch [:notifications.callback/on-message decoded-payload {:force true}]))))
|
||||
(on-success)
|
||||
(catch :default e
|
||||
(log/warn "failed to handle background message" e)
|
||||
(on-error e)))))))
|
|
@ -2,15 +2,22 @@
|
|||
(:require [goog.object :as object]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.react-native.js-dependencies :as rn]
|
||||
[status-im.js-dependencies :as dependencies]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.accounts.db :as accounts.db]
|
||||
[status-im.chat.models :as chat-model]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.fx :as fx]))
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.utils :as utils]))
|
||||
|
||||
;; Work in progress namespace responsible for push notifications and interacting
|
||||
;; with Firebase Cloud Messaging.
|
||||
|
||||
(def ^:private pn-message-id-hash-length 10)
|
||||
(def ^:private pn-pubkey-hash-length 10)
|
||||
(def ^:private pn-pubkey-length 132)
|
||||
|
||||
(when-not platform/desktop?
|
||||
|
||||
(def firebase (object/get rn/react-native-firebase "default")))
|
||||
|
@ -28,42 +35,157 @@
|
|||
(log/debug "notifications-denied")
|
||||
(re-frame/dispatch [:notifications.callback/request-notifications-permissions-denied {}]))))))
|
||||
|
||||
(defn valid-notification-payload?
|
||||
[{:keys [from to]}]
|
||||
(and from to
|
||||
(or
|
||||
;; is it full pubkey?
|
||||
(and (= (.-length from) pn-pubkey-length)
|
||||
(= (.-length to) pn-pubkey-length))
|
||||
;; partially deanonymized
|
||||
(and (= (.-length from) pn-pubkey-hash-length)
|
||||
(= (.-length to) pn-pubkey-length))
|
||||
;; or is it an anonymized pubkey hash (v2 payload)?
|
||||
(and (= (.-length from) pn-pubkey-hash-length)
|
||||
(= (.-length to) pn-pubkey-hash-length)))))
|
||||
|
||||
(defn sha3 [s]
|
||||
(.sha3 dependencies/Web3.prototype s))
|
||||
|
||||
(defn anonymize-pubkey
|
||||
[pubkey]
|
||||
"Anonymize a public key, if needed, by hashing it and taking the first 4 bytes"
|
||||
(if (= (count pubkey) pn-pubkey-hash-length)
|
||||
pubkey
|
||||
(apply str (take pn-pubkey-hash-length (sha3 pubkey)))))
|
||||
|
||||
(defn encode-notification-payload
|
||||
[{:keys [from to id] :as payload}]
|
||||
(if (valid-notification-payload? payload)
|
||||
{:msg-v2 (js/JSON.stringify #js {:from (anonymize-pubkey from)
|
||||
:to (anonymize-pubkey to)
|
||||
:id (apply str (take pn-message-id-hash-length id))})}
|
||||
(throw (str "Invalid push notification payload" payload))))
|
||||
|
||||
(when platform/desktop?
|
||||
(defn handle-initial-push-notification [] ())) ;; no-op
|
||||
|
||||
(when-not platform/desktop?
|
||||
|
||||
(defn get-fcm-token []
|
||||
(-> (.getToken (.messaging firebase))
|
||||
(.then (fn [x]
|
||||
(log/debug "get-fcm-token: " x)
|
||||
(re-frame/dispatch [:notifications.callback/get-fcm-token-success x])))))
|
||||
|
||||
(defn on-refresh-fcm-token []
|
||||
(.onTokenRefresh (.messaging firebase)
|
||||
(fn [x]
|
||||
(log/debug "on-refresh-fcm-token: " x)
|
||||
(re-frame/dispatch [:notifications.callback/get-fcm-token-success x]))))
|
||||
|
||||
;; TODO(oskarth): Only called in background on iOS right now.
|
||||
;; NOTE(oskarth): Hardcoded data keys :sum and :msg in status-go right now.
|
||||
(defn on-notification []
|
||||
(.onNotification (.notifications firebase)
|
||||
(fn [event-js]
|
||||
(let [event (js->clj event-js :keywordize-keys true)
|
||||
data (select-keys event [:sum :msg])
|
||||
aps (:aps event)]
|
||||
(log/debug "on-notification event: " (pr-str event))
|
||||
(log/debug "on-notification aps: " (pr-str aps))
|
||||
(log/debug "on-notification data: " (pr-str data))))))
|
||||
|
||||
(def channel-id "status-im")
|
||||
(def channel-name "Status")
|
||||
(def sound-name "message.wav")
|
||||
(def group-id "im.status.ethereum.MESSAGE")
|
||||
(def icon "ic_stat_status_notification")
|
||||
|
||||
(defn- hash->pubkey [hash accounts]
|
||||
(:public-key
|
||||
(first
|
||||
(filter #(= (anonymize-pubkey (:public-key %)) hash)
|
||||
(vals accounts)))))
|
||||
|
||||
(defn lookup-contact-pubkey-from-hash
|
||||
[{:keys [db] :as cofx} contact-pubkey-or-hash]
|
||||
"Tries to deanonymize a given contact pubkey hash by looking up the
|
||||
full pubkey (if db is unlocked) in :contacts/contacts.
|
||||
Returns original value if not a hash (e.g. already a public key)."
|
||||
(if (and contact-pubkey-or-hash
|
||||
(= (count contact-pubkey-or-hash) pn-pubkey-hash-length))
|
||||
(if-let
|
||||
[account-pubkey (hash->pubkey contact-pubkey-or-hash
|
||||
(:accounts/accounts db))]
|
||||
account-pubkey
|
||||
(if (accounts.db/logged-in? cofx)
|
||||
;; TODO: for simplicity we're doing a linear lookup of the contacts,
|
||||
;; but we might want to build a map of hashed pubkeys to pubkeys
|
||||
;; for this purpose
|
||||
(hash->pubkey contact-pubkey-or-hash (:contacts/contacts db))
|
||||
(do
|
||||
(log/warn "failed to lookup contact from hash, not logged in")
|
||||
contact-pubkey-or-hash)))
|
||||
contact-pubkey-or-hash))
|
||||
|
||||
(defn parse-notification-v1-payload [msg-json]
|
||||
(let [msg (js/JSON.parse msg-json)]
|
||||
{:from (object/get msg "from")
|
||||
:to (object/get msg "to")}))
|
||||
|
||||
(defn parse-notification-v2-payload [msg-v2-json]
|
||||
(let [msg (js/JSON.parse msg-v2-json)]
|
||||
{:from (object/get msg "from")
|
||||
:to (object/get msg "to")
|
||||
:id (object/get msg "id")}))
|
||||
|
||||
(defn decode-notification-payload [message-js]
|
||||
;; message-js.-data is Notification.data():
|
||||
;; https://github.com/invertase/react-native-firebase/blob/adcbeac3d11585dd63922ef178ff6fd886d5aa9b/src/modules/notifications/Notification.js#L79
|
||||
(let [data-js (.. message-js -data)
|
||||
msg-v2-json (object/get data-js "msg-v2")]
|
||||
(try
|
||||
(let [payload (if msg-v2-json
|
||||
(parse-notification-v2-payload msg-v2-json)
|
||||
(parse-notification-v1-payload (object/get data-js "msg")))]
|
||||
(if (valid-notification-payload? payload)
|
||||
payload
|
||||
(log/warn "failed to retrieve notification payload from"
|
||||
(js/JSON.stringify data-js))))
|
||||
(catch :default e
|
||||
(log/debug "failed to parse" (js/JSON.stringify data-js)
|
||||
"exception:" e)))))
|
||||
|
||||
(defn rehydrate-payload
|
||||
[cofx {:keys [from to id] :as decoded-payload}]
|
||||
"Takes a payload with hashed pubkeys and returns a payload with the real
|
||||
(matched) pubkeys"
|
||||
{:from (lookup-contact-pubkey-from-hash cofx from)
|
||||
:to (lookup-contact-pubkey-from-hash cofx to)
|
||||
;; TODO: Rehydrate message id
|
||||
:id id})
|
||||
|
||||
(defn- build-notification [{:keys [title body decoded-payload]}]
|
||||
(let [native-notification
|
||||
(clj->js
|
||||
(merge
|
||||
{:title title
|
||||
:body body
|
||||
:data (clj->js (encode-notification-payload decoded-payload))
|
||||
:sound sound-name}
|
||||
(when-let [msg-id (:id decoded-payload)]
|
||||
;; We must prefix the notification ID, otherwise it will
|
||||
;; cause a crash in iOS
|
||||
{:notificationId (str "hash:" msg-id)})))]
|
||||
(firebase.notifications.Notification.
|
||||
native-notification (.notifications firebase))))
|
||||
|
||||
(defn display-notification [{:keys [title body] :as params}]
|
||||
(let [notification (build-notification params)]
|
||||
(when platform/android?
|
||||
(.. notification
|
||||
(-android.setChannelId channel-id)
|
||||
(-android.setAutoCancel true)
|
||||
(-android.setPriority firebase.notifications.Android.Priority.High)
|
||||
(-android.setCategory firebase.notifications.Android.Category.Message)
|
||||
(-android.setGroup group-id)
|
||||
(-android.setSmallIcon icon)))
|
||||
(.. firebase
|
||||
notifications
|
||||
(displayNotification notification)
|
||||
(then #(log/debug "Display Notification" title body))
|
||||
(catch (fn [error]
|
||||
(log/debug "Display Notification error" title body error))))))
|
||||
|
||||
(defn get-fcm-token []
|
||||
(-> (.getToken (.messaging firebase))
|
||||
(.then (fn [x]
|
||||
(log/debug "get-fcm-token:" x)
|
||||
(re-frame/dispatch
|
||||
[:notifications.callback/get-fcm-token-success x])))))
|
||||
|
||||
(defn create-notification-channel []
|
||||
(let [channel (firebase.notifications.Android.Channel. channel-id
|
||||
(let [channel (firebase.notifications.Android.Channel.
|
||||
channel-id
|
||||
channel-name
|
||||
firebase.notifications.Android.Importance.Max)]
|
||||
firebase.notifications.Android.Importance.High)]
|
||||
(.setSound channel sound-name)
|
||||
(.setShowBadge channel true)
|
||||
(.enableVibration channel true)
|
||||
|
@ -74,88 +196,163 @@
|
|||
(then #(log/debug "Notification channel created:" channel-id)
|
||||
#(log/error "Notification channel creation error:" channel-id %)))))
|
||||
|
||||
(fx/defn handle-push-notification
|
||||
[{:keys [db] :as cofx} {:keys [from to] :as event}]
|
||||
(let [current-public-key (accounts.db/current-public-key cofx)]
|
||||
(defn- show-notification?
|
||||
"Ignore push notifications from unknown contacts or removed chats"
|
||||
[{:keys [db] :as cofx} {:keys [from] :as rehydrated-payload}]
|
||||
(and (valid-notification-payload? rehydrated-payload)
|
||||
(accounts.db/logged-in? cofx)
|
||||
(some #(= (:public-key %) from)
|
||||
(vals (:contacts/contacts db)))
|
||||
(some #(= (:chat-id %) from)
|
||||
(vals (:chats db)))))
|
||||
|
||||
(fx/defn handle-on-message
|
||||
[{:keys [db] :as cofx} decoded-payload {:keys [force]}]
|
||||
(let [view-id (:view-id db)
|
||||
current-chat-id (:current-chat-id db)
|
||||
app-state (:app-state db)
|
||||
rehydrated-payload (rehydrate-payload cofx decoded-payload)
|
||||
from (:from rehydrated-payload)]
|
||||
(log/debug "handle-on-message" "app-state:" app-state
|
||||
"view-id:" view-id "current-chat-id:" current-chat-id
|
||||
"from:" from "force:" force)
|
||||
(when (or force
|
||||
(and
|
||||
(not= app-state "active")
|
||||
(show-notification? cofx rehydrated-payload)))
|
||||
{:db
|
||||
(assoc-in db [:push-notifications/stored (:to rehydrated-payload)]
|
||||
(js/JSON.stringify (clj->js rehydrated-payload)))
|
||||
:notifications/display-notification
|
||||
{:title (i18n/label :notifications-new-message-title)
|
||||
:body (i18n/label :notifications-new-message-body)
|
||||
:decoded-payload rehydrated-payload}})))
|
||||
|
||||
(fx/defn handle-push-notification-open
|
||||
[{:keys [db] :as cofx} decoded-payload {:keys [stored?] :as ctx}]
|
||||
(let [current-public-key (accounts.db/current-public-key cofx)
|
||||
nav-opts (when stored? {:navigation-reset? true})
|
||||
rehydrated-payload (rehydrate-payload cofx decoded-payload)
|
||||
from (:from rehydrated-payload)
|
||||
to (:to rehydrated-payload)]
|
||||
(log/debug "handle-push-notification-open"
|
||||
"current-public-key:" current-public-key
|
||||
"rehydrated-payload:" rehydrated-payload "stored?:" stored?)
|
||||
(if (= to current-public-key)
|
||||
(fx/merge cofx
|
||||
{:db (update db :push-notifications/stored dissoc to)}
|
||||
(chat-model/navigate-to-chat from nil))
|
||||
{:db (assoc-in db [:push-notifications/stored to] from)})))
|
||||
(chat-model/navigate-to-chat from nav-opts))
|
||||
{:db (assoc-in db [:push-notifications/stored to]
|
||||
(js/JSON.stringify (clj->js rehydrated-payload)))})))
|
||||
|
||||
(defn parse-notification-payload [s]
|
||||
(try
|
||||
(js/JSON.parse s)
|
||||
(catch :default _
|
||||
#js {})))
|
||||
;; https://github.com/invertase/react-native-firebase/blob/adcbeac3d11585dd63922ef178ff6fd886d5aa9b/src/modules/notifications/Notification.js#L13
|
||||
(defn handle-notification-open-event [event]
|
||||
(log/debug "handle-notification-open-event" event)
|
||||
(let [decoded-payload (decode-notification-payload (.. event -notification))]
|
||||
(when decoded-payload
|
||||
(re-frame/dispatch
|
||||
[:notifications/notification-open-event-received decoded-payload nil]))))
|
||||
|
||||
(defn handle-notification-event [event]
|
||||
(let [msg (object/get (.. event -notification -data) "msg")
|
||||
data (parse-notification-payload msg)
|
||||
from (object/get data "from")
|
||||
to (object/get data "to")]
|
||||
(log/debug "on notification" (pr-str msg))
|
||||
(when (and from to)
|
||||
(re-frame/dispatch [:notifications/notification-event-received {:from from
|
||||
:to to}]))))
|
||||
|
||||
(defn handle-initial-push-notification
|
||||
[]
|
||||
(defn handle-initial-push-notification []
|
||||
"This method handles pending push notifications.
|
||||
It is only needed to handle PNs from legacy clients
|
||||
(which use firebase.notifications API)"
|
||||
(log/debug "Handle initial push notifications")
|
||||
(.. firebase
|
||||
notifications
|
||||
getInitialNotification
|
||||
(then (fn [event]
|
||||
(log/debug "getInitialNotification" event)
|
||||
(when event
|
||||
(handle-notification-event event))))))
|
||||
(handle-notification-open-event event))))))
|
||||
|
||||
(defn on-notification-opened []
|
||||
(defn setup-token-refresh-callback []
|
||||
(.onTokenRefresh
|
||||
(.messaging firebase)
|
||||
(fn [x]
|
||||
(log/debug "onTokenRefresh:" x)
|
||||
(re-frame/dispatch [:notifications.callback/get-fcm-token-success x]))))
|
||||
|
||||
(defn setup-on-notification-callback []
|
||||
"Calling onNotification is only needed so that we're able to receive PNs"
|
||||
"while in foreground from older clients who are still relying"
|
||||
"on the notifications API. Once that is no longer a consideration"
|
||||
"we can remove this method"
|
||||
(log/debug "calling onNotification")
|
||||
(.onNotification
|
||||
(.notifications firebase)
|
||||
(fn [message-js]
|
||||
(log/debug "handle-on-notification-callback called")
|
||||
(let [decoded-payload (decode-notification-payload message-js)]
|
||||
(log/debug "handle-on-notification-callback payload:" decoded-payload)
|
||||
(when decoded-payload
|
||||
(re-frame/dispatch
|
||||
[:notifications.callback/on-message decoded-payload]))))))
|
||||
|
||||
(defn setup-on-message-callback []
|
||||
(log/debug "calling onMessage")
|
||||
(.onMessage
|
||||
(.messaging firebase)
|
||||
(fn [message-js]
|
||||
(log/debug "handle-on-message-callback called")
|
||||
(let [decoded-payload (decode-notification-payload message-js)]
|
||||
(log/debug "handle-on-message-callback decoded-payload:"
|
||||
decoded-payload)
|
||||
(when decoded-payload
|
||||
(re-frame/dispatch
|
||||
[:notifications.callback/on-message decoded-payload]))))))
|
||||
|
||||
(defn setup-on-notification-opened-callback []
|
||||
(log/debug "setup-on-notification-opened-callback")
|
||||
(.. firebase
|
||||
notifications
|
||||
(onNotificationOpened handle-notification-event)))
|
||||
(onNotificationOpened handle-notification-open-event)))
|
||||
|
||||
(defn init []
|
||||
(on-refresh-fcm-token)
|
||||
(on-notification)
|
||||
(on-notification-opened)
|
||||
(log/debug "Init notifications")
|
||||
(setup-token-refresh-callback)
|
||||
(setup-on-message-callback)
|
||||
(setup-on-notification-callback)
|
||||
(setup-on-notification-opened-callback)
|
||||
(when platform/android?
|
||||
(create-notification-channel)))
|
||||
(create-notification-channel))
|
||||
(handle-initial-push-notification)))
|
||||
|
||||
(defn display-notification [{:keys [title body from to]}]
|
||||
(let [notification (firebase.notifications.Notification.)]
|
||||
(.. notification
|
||||
(setTitle title)
|
||||
(setBody body)
|
||||
(setData (js/JSON.stringify #js {:from from
|
||||
:to to}))
|
||||
(setSound sound-name)
|
||||
(-android.setChannelId channel-id)
|
||||
(-android.setAutoCancel true)
|
||||
(-android.setPriority firebase.notifications.Android.Priority.Max)
|
||||
(-android.setGroup group-id)
|
||||
(-android.setGroupSummary true)
|
||||
(-android.setSmallIcon icon))
|
||||
(.. firebase
|
||||
notifications
|
||||
(displayNotification notification)
|
||||
(then #(log/debug "Display Notification" title body))
|
||||
(then #(log/debug "Display Notification error" title body))))))
|
||||
|
||||
(fx/defn process-stored-event [cofx address]
|
||||
(fx/defn process-stored-event [cofx address stored-pns]
|
||||
(when-not platform/desktop?
|
||||
(let [to (get-in cofx [:db :accounts/accounts address :public-key])
|
||||
from (get-in cofx [:db :push-notifications/stored to])]
|
||||
(when from
|
||||
(handle-push-notification cofx
|
||||
{:from from
|
||||
:to to})))))
|
||||
(if (accounts.db/logged-in? cofx)
|
||||
(let [current-account (get-in cofx [:db :account/account])
|
||||
current-address (:address current-account)
|
||||
current-account-pubkey (:public-key current-account)
|
||||
stored-pn-val-json (or (get stored-pns current-account-pubkey)
|
||||
(get stored-pns (anonymize-pubkey current-account-pubkey)))
|
||||
stored-pn-payload (if (= (first stored-pn-val-json) \{)
|
||||
(js->clj (js/JSON.parse stored-pn-val-json) :keywordize-keys true)
|
||||
{:from stored-pn-val-json
|
||||
:to current-account-pubkey})
|
||||
from (lookup-contact-pubkey-from-hash cofx (:from stored-pn-payload))
|
||||
to (lookup-contact-pubkey-from-hash cofx (:to stored-pn-payload))]
|
||||
(when (and from
|
||||
(= address current-address))
|
||||
(log/debug "process-stored-event" "address" address "from" from "to" to)
|
||||
(handle-push-notification-open cofx
|
||||
stored-pn-payload
|
||||
{:stored? true})))
|
||||
(log/error "process-stored-event called without user being logged in!"))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:notifications/display-notification
|
||||
display-notification)
|
||||
|
||||
(re-frame/reg-fx
|
||||
:notifications/handle-initial-push-notification
|
||||
handle-initial-push-notification)
|
||||
:notifications/init
|
||||
(fn []
|
||||
(cond
|
||||
platform/android?
|
||||
(init)
|
||||
|
||||
platform/ios?
|
||||
(utils/set-timeout init 100))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:notifications/get-fcm-token
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
(fx/merge cofx
|
||||
(remove-hash envelope-hash)
|
||||
(check-confirmations status chat-id message-id)
|
||||
(models.message/send-push-notification fcm-token status)))))))
|
||||
(models.message/send-push-notification message-id fcm-token status)))))))
|
||||
|
||||
(fx/defn set-contact-message-envelope-hash
|
||||
[{:keys [db] :as cofx} chat-id envelope-hash]
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
(def mailserver-confirmations-enabled? (enabled? (get-config :MAILSERVER_CONFIRMATIONS_ENABLED)))
|
||||
(def mainnet-warning-enabled? (enabled? (get-config :MAINNET_WARNING_ENABLED 0)))
|
||||
(def pfs-encryption-enabled? (enabled? (get-config :PFS_ENCRYPTION_ENABLED "0")))
|
||||
(def in-app-notifications-enabled? (enabled? (get-config :IN_APP_NOTIFICATIONS_ENABLED 0)))
|
||||
(def cached-webviews-enabled? (enabled? (get-config :CACHED_WEBVIEWS_ENABLED 0)))
|
||||
(def rn-bridge-threshold-warnings-enabled? (enabled? (get-config :RN_BRIDGE_THRESHOLD_WARNINGS 0)))
|
||||
(def extensions-enabled? (enabled? (get-config :EXTENSIONS 0)))
|
||||
|
|
|
@ -55,11 +55,11 @@
|
|||
(re-frame/dispatch [:handle-universal-link url])))
|
||||
|
||||
(fx/defn handle-browse [cofx url]
|
||||
(log/info "universal-links: handling browse " url)
|
||||
(log/info "universal-links: handling browse" url)
|
||||
{:browser/show-browser-selection url})
|
||||
|
||||
(fx/defn handle-public-chat [cofx public-chat]
|
||||
(log/info "universal-links: handling public chat " public-chat)
|
||||
(log/info "universal-links: handling public chat" public-chat)
|
||||
(chat/start-public-chat cofx public-chat {:navigation-reset? true}))
|
||||
|
||||
(fx/defn handle-view-profile [{:keys [db] :as cofx} public-key]
|
||||
|
|
|
@ -2,18 +2,27 @@
|
|||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im.notifications.core :as notifications]))
|
||||
|
||||
(deftest test-handle-push-notification
|
||||
(deftest test-handle-push-notification-open
|
||||
(testing "user's signing in having opened PN while signed out"
|
||||
(is (= {:db {:account/account {:public-key "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}
|
||||
:push-notifications/stored {}}
|
||||
:dispatch [:navigate-to-chat "0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de" {:navigation-reset? true}]}
|
||||
(notifications/handle-push-notification-open {:db {:push-notifications/stored {}
|
||||
:account/account {:public-key "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}}}
|
||||
[:push-notification-opened {:from "0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de"
|
||||
:to "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"
|
||||
:stored? true}]))))
|
||||
(testing "user's signed in"
|
||||
(is (= {:db {:account/account {:public-key "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}
|
||||
:push-notifications/stored {}}
|
||||
:dispatch [:navigate-to-chat "0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de"]}
|
||||
(notifications/handle-push-notification {:db {:push-notifications/stored {}
|
||||
:account/account {:publi-key "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}}}
|
||||
(notifications/handle-push-notification-open {:db {:push-notifications/stored {}
|
||||
:account/account {:public-key "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}}}
|
||||
[:push-notification-opened {:from "0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de"
|
||||
:to "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}]))))
|
||||
(testing "user's signed in into another account"
|
||||
(is (= {}
|
||||
(notifications/handle-push-notification {:db {:account/account {:public-key "0x04bc8bf4a91ab726bd98f2c54b3036caacaeea527867945ab839e9ad4e62696856d7f7fa485f68304de357e38a1553eac5592706a16fcf71fd821bbd6c796f9ab3"}}}
|
||||
(notifications/handle-push-notification-open {:db {:account/account {:public-key "0x04bc8bf4a91ab726bd98f2c54b3036caacaeea527867945ab839e9ad4e62696856d7f7fa485f68304de357e38a1553eac5592706a16fcf71fd821bbd6c796f9ab3"}}}
|
||||
[:push-notification-opened {:from "0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de"
|
||||
:to "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}]))))
|
||||
(testing "user's not signed in"
|
||||
|
@ -25,7 +34,7 @@
|
|||
:push-notifications/stored {"0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"
|
||||
"0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de"}}
|
||||
:dispatch [:ui/open-login "bd36cd64e2621b054a3b7464ff1b3c4c304880e7" "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX" "Bob"]}
|
||||
(notifications/handle-push-notification {:db {:accounts/accounts {"bd36cd64e2621b054a3b7464ff1b3c4c304880e7" {:address "bd36cd64e2621b054a3b7464ff1b3c4c304880e7"
|
||||
(notifications/handle-push-notification-open {:db {:accounts/accounts {"bd36cd64e2621b054a3b7464ff1b3c4c304880e7" {:address "bd36cd64e2621b054a3b7464ff1b3c4c304880e7"
|
||||
:photo-path "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX"
|
||||
:name "Bob"
|
||||
:public-key "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}}
|
||||
|
|
Loading…
Reference in New Issue