Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing ADAMANT Notification Service #658

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,686 changes: 1,778 additions & 908 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"ed2curve": "^0.3.0",
"emoji-mart": "^5.5.2",
"file-saver": "^2.0.5",
"firebase": "^10.12.5",
"hdkey": "^2.1.0",
"https-browserify": "^1.0.0",
"i": "^0.3.7",
Expand Down Expand Up @@ -110,7 +111,8 @@
"web3-eth-accounts": "^4.1.1",
"web3-eth-contract": "^4.2.0",
"web3-providers-http": "^4.1.0",
"web3-utils": "^4.2.1"
"web3-utils": "^4.2.1",
"workbox-precaching": "^7.1.0"
},
"devDependencies": {
"@capacitor/assets": "^3.0.5",
Expand Down Expand Up @@ -162,7 +164,7 @@
"uuid": "^9.0.1",
"vite": "^5.1.6",
"vite-plugin-electron": "^0.28.4",
"vite-plugin-pwa": "^0.19.8",
"vite-plugin-pwa": "^0.20.1",
"vite-plugin-top-level-await": "^1.4.1",
"vite-plugin-wasm": "^3.3.0",
"vitest": "^1.4.0",
Expand Down
42 changes: 42 additions & 0 deletions public/firebase-messaging-sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* eslint-disable */

importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.5.4/workbox-sw.js')
importScripts('https://www.gstatic.com/firebasejs/9.23.0/firebase-app-compat.js')
importScripts('https://www.gstatic.com/firebasejs/9.23.0/firebase-messaging-compat.js')

// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
const firebaseApp = firebase.initializeApp({
apiKey: 'AIzaSyDgtB_hqwL1SS_YMYepRMmXYhmc7154wmU',
authDomain: 'adamant-messenger.firebaseapp.com',
databaseURL: 'https://adamant-messenger.firebaseio.com',
projectId: 'adamant-messenger',
storageBucket: 'adamant-messenger.appspot.com',
messagingSenderId: '987518845753',
appId: '1:987518845753:web:6585b11ca36bac4c251ee8'
})

const messaging = firebase.messaging()

messaging.onBackgroundMessage((payload) => {
console.log('[SW] BACKGROUND MESSAGE', payload)
// Customize notification here
const notificationTitle = payload.notification?.title
const notificationOptions = {
body: payload.notification?.body
}

self.registration.showNotification(notificationTitle, notificationOptions)
})

messaging.onMessage((payload) => {
console.log('[SW] MESSAGE', payload)
// Customize notification here
const notificationTitle = payload.notification?.title
const notificationOptions = {
body: payload.notification?.body
}

self.registration.showNotification(notificationTitle, notificationOptions)
})
28 changes: 28 additions & 0 deletions src/firebase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { initializeApp } from 'firebase/app'
import { getMessaging } from 'firebase/messaging'
import { getId, getInstallations } from 'firebase/installations'

const firebaseConfig = {
apiKey: 'AIzaSyDgtB_hqwL1SS_YMYepRMmXYhmc7154wmU',
authDomain: 'adamant-messenger.firebaseapp.com',
databaseURL: 'https://adamant-messenger.firebaseio.com',
projectId: 'adamant-messenger',
storageBucket: 'adamant-messenger.appspot.com',
messagingSenderId: '987518845753',
appId: '1:987518845753:web:6585b11ca36bac4c251ee8'
}

const firebaseApp = initializeApp(firebaseConfig)
const fcm = getMessaging(firebaseApp)

console.log('Firebase app initialized', firebaseApp)
console.log('FCM instance initialized', fcm)

window.firebaseApp = firebaseApp
window.fcm = fcm

export function getDeviceId() {
return getId(getInstallations(firebaseApp))
}

export { firebaseApp, fcm }
7 changes: 7 additions & 0 deletions src/lib/adamant-api/asset.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,10 @@ export interface ReactionAsset {
}

export function reactionAsset(reactToId: string, reactMessage: string): ReactionAsset

export function signalAsset(
deviceId: string,
token: string,
provider: 'APNS' | 'FCM',
action: 'add' | 'remove'
): SignalMessagePayload
9 changes: 9 additions & 0 deletions src/lib/adamant-api/asset.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,12 @@ export function reactionAsset(reactToId, reactMessage) {
react_message: reactMessage
}
}

export function signalAsset(deviceId, token, provider, action) {
return {
deviceId,
token,
provider,
action
}
}
16 changes: 15 additions & 1 deletion src/lib/adamant-api/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import Queue from 'promise-queue'
import { Base64 } from 'js-base64'

import { Transactions, Delegates, MessageType } from '@/lib/constants'
import {
Transactions,
Delegates,
MessageType,
ADAMANT_NOTIFICATION_SERVICE_ADDRESS
} from '@/lib/constants'
import utils from '@/lib/adamant'
import client from '@/lib/nodes/adm'
import { encryptPassword } from '@/lib/idb/crypto'
Expand Down Expand Up @@ -173,6 +178,15 @@ export function sendMessage(params) {
})
}

// https://aips.adamant.im/AIPS/aip-6
export function sendSignalMessage(asset) {
return sendMessage({
type: MessageType.SIGNAL_MESSAGE,
message: asset,
to: ADAMANT_NOTIFICATION_SERVICE_ADDRESS
})
}

/**
* Sends special message with the specified payload
* @param {string} to recipient address
Expand Down
3 changes: 3 additions & 0 deletions src/lib/constants/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export declare const Transactions: {
export declare const MessageType: {
BASIC_ENCRYPTED_MESSAGE: 1
RICH_CONTENT_MESSAGE: 2
SIGNAL_MESSAGE: 3
cryptoTransferMessage: (cryptoSymbol: string) => string
}

Expand Down Expand Up @@ -127,3 +128,5 @@ export declare const AnimationReactionType = {
Incoming: 0,
Outgoing: 1
}

export declare const ADAMANT_NOTIFICATION_SERVICE_ADDRESS: string
4 changes: 4 additions & 0 deletions src/lib/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const Transactions = {
export const MessageType = {
BASIC_ENCRYPTED_MESSAGE: 1,
RICH_CONTENT_MESSAGE: 2,
SIGNAL_MESSAGE: 3,
cryptoTransferMessage: (cryptoSymbol) => `${cryptoSymbol.toLowerCase()}_transaction`
}

Expand Down Expand Up @@ -231,3 +232,6 @@ export const AnimationReactionType = {
Incoming: 0,
Outgoing: 1
}

// https://github.com/Adamant-im/adamant-ns
export const ADAMANT_NOTIFICATION_SERVICE_ADDRESS = 'U922832474468487910'
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createApp } from 'vue'
import { VueQueryPlugin } from '@tanstack/vue-query'

import App from './App.vue'
import './firebase'
import { router } from './router'
import store from './store/index.js'
import { i18n } from './i18n'
Expand Down
48 changes: 48 additions & 0 deletions src/notifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getToken, deleteToken } from 'firebase/messaging'
import { fcm } from './firebase'

export async function requestToken() {
let permission: NotificationPermission
try {
permission = await Notification.requestPermission()
} catch (err) {
console.log('An error occurred while requesting notifications permission', err)
return null
}

if (permission !== 'granted') {
console.log('Notification permission not granted.')
return null
}
console.log('Notification permission granted.')

try {
// Get the registration token
const currentToken = await getToken(fcm)
if (currentToken) {
console.log('FCM registration token:', currentToken)
return currentToken
// Send the token to your server and save it for later
} else {
console.log('No registration token available. Request permission to generate one.')

return null
}
} catch (err) {
console.log('An error occurred while retrieving the FCM token', err)

return null
}
}

export async function revokeToken() {
try {
await deleteToken(fcm)
console.log('FCM registration token deleted.')

return true
} catch (err) {
console.log('An error occurred while deleting the FCM token', err)
return false
}
}
76 changes: 75 additions & 1 deletion src/views/Options.vue
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@
</v-col>
<v-col cols="12" class="mt-6">
<v-checkbox
v-model="allowPushNotifications"
:model-value="allowPushNotifications"
@update:model-value="handlePushNotificationsCheckbox"
:label="$t('options.enable_push')"
color="grey darken-1"
density="comfortable"
Expand Down Expand Up @@ -211,8 +212,12 @@ import LanguageSwitcher from '@/components/LanguageSwitcher.vue'
import CurrencySwitcher from '@/components/CurrencySwitcher.vue'
import AppToolbarCentered from '@/components/AppToolbarCentered.vue'
import PasswordSetDialog from '@/components/PasswordSetDialog.vue'
import { getDeviceId } from '@/firebase'
import { sendSignalMessage } from '@/lib/adamant-api'
import { signalAsset } from '@/lib/adamant-api/asset'
import { clearDb, db as isIDBSupported } from '@/lib/idb'
import scrollPosition from '@/mixins/scrollPosition'
import { requestToken, revokeToken } from '@/notifications'

export default {
components: {
Expand Down Expand Up @@ -314,6 +319,75 @@ export default {
}
},
methods: {
async handlePushNotificationsCheckbox(checked) {
const deviceId = await getDeviceId()

if (checked) {
const token = await requestToken()

if (!token) {
this.$store.dispatch('snackbar/show', {
message: 'Unable to retrieve FCM token',
timeout: 5000
})
return
}

const result = await sendSignalMessage(signalAsset(deviceId, token, 'FCM', 'add'))
console.log('Sent signal message transaction (action: add)', result)

if (!result.success) {
this.$store.dispatch('snackbar/show', {
message: 'Send signal message transaction failed',
timeout: 5000
})
return
}

this.$store.dispatch('snackbar/show', {
message: 'Successfully subscribed to push notifications',
timeout: 5000
})

this.allowPushNotifications = checked
} else {
const token = await requestToken()
if (!token) {
this.$store.dispatch('snackbar/show', {
message: 'Unable to retrieve FCM token',
timeout: 5000
})
return
}

const result = await sendSignalMessage(signalAsset(deviceId, token, 'FCM', 'remove'))
console.log('Sent signal message transaction (action: remove)', result)

if (!result.success) {
this.$store.dispatch('snackbar/show', {
message: 'Send signal message transaction failed',
timeout: 5000
})
return
}

const revoked = await revokeToken()
if (!revoked) {
this.$store.dispatch('snackbar/show', {
message: 'Unable to revoke FCM token',
timeout: 5000
})
return
}

this.$store.dispatch('snackbar/show', {
message: 'Successfully unsubscribed from push notifications',
timeout: 5000
})

this.allowPushNotifications = checked
}
},
onSetPassword() {
this.$store.commit('options/updateOption', {
key: 'stayLoggedIn',
Expand Down
4 changes: 4 additions & 0 deletions src/window.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { App } from 'vue'
import { Store } from 'vuex'
import { FirebaseApp } from 'firebase/app'
import { Messaging } from 'firebase/messaging'

declare global {
interface Window {
ep: App<Element>
store: Store<any>
firebaseApp: FirebaseApp
fcm: Messaging
}
}
11 changes: 11 additions & 0 deletions vite-pwa.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ export default mergeConfig(
workbox: {
maximumFileSizeToCacheInBytes: 5000000 // 5 MiB
}
}),
VitePWA({
registerType: 'autoUpdate',
strategies: 'injectManifest',
filename: 'firebase-messaging-sw.js',
injectManifest: {
injectionPoint: undefined
},
devOptions: {
enabled: true
}
})
]
})
Expand Down