refactor by separating sp address view from service
This commit is contained in:
parent
46622e2a7a
commit
4a59424d05
@ -193,7 +193,7 @@ body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.process-container {
|
.process-container {
|
||||||
grid-column: 3 / 5;
|
grid-column: 3 / 6;
|
||||||
grid-row: 3 ;
|
grid-row: 3 ;
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { currentRoute } from '../../router';
|
import { INotification } from '~/models/notification.model';
|
||||||
|
import { currentRoute, navigate } from '../../router';
|
||||||
import Services from '../../services/service';
|
import Services from '../../services/service';
|
||||||
|
|
||||||
let notifications = [];
|
let notifications = [];
|
||||||
@ -6,6 +7,7 @@ let notifications = [];
|
|||||||
export async function unpair() {
|
export async function unpair() {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
await service.unpairDevice();
|
await service.unpairDevice();
|
||||||
|
navigate('home')
|
||||||
}
|
}
|
||||||
|
|
||||||
(window as any).unpair = unpair;
|
(window as any).unpair = unpair;
|
||||||
@ -25,8 +27,7 @@ function toggleMenu() {
|
|||||||
async function getNotifications() {
|
async function getNotifications() {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
notifications = service.getNotifications();
|
notifications = service.getNotifications();
|
||||||
const badge = document.querySelector('.notification-badge') as HTMLDivElement;
|
return notifications
|
||||||
if (badge) badge.innerHTML = notifications.length.toString();
|
|
||||||
}
|
}
|
||||||
function openCloseNotifications() {
|
function openCloseNotifications() {
|
||||||
const notifications = document.querySelector('.notification-board') as HTMLDivElement;
|
const notifications = document.querySelector('.notification-board') as HTMLDivElement;
|
||||||
@ -39,7 +40,7 @@ export async function initHeader() {
|
|||||||
if (currentRoute === 'home') {
|
if (currentRoute === 'home') {
|
||||||
hideSomeFunctionnalities();
|
hideSomeFunctionnalities();
|
||||||
} else {
|
} else {
|
||||||
getNotifications();
|
launchNotificationWorker()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,3 +56,43 @@ function hideSomeFunctionnalities() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setNotification(notifications: INotification[]): Promise<void> {
|
||||||
|
const badge = document.querySelector('.notification-badge') as HTMLDivElement;
|
||||||
|
const noNotifications = document.querySelector('.no-notification') as HTMLDivElement;
|
||||||
|
if (notifications?.length) {
|
||||||
|
badge.innerText = notifications.length.toString();
|
||||||
|
const notificationBoard = document.querySelector('.notification-board') as HTMLDivElement;
|
||||||
|
noNotifications.style.display = 'none';
|
||||||
|
for (const notif of notifications) {
|
||||||
|
const notifElement = document.createElement('div');
|
||||||
|
notifElement.className = 'notification-element';
|
||||||
|
notifElement.setAttribute('notif-id', notif.id.toString());
|
||||||
|
notifElement.innerHTML = `
|
||||||
|
<div>${notif.title}</div>
|
||||||
|
<div>${notif.description}</div>
|
||||||
|
`;
|
||||||
|
// this.addSubscription(notifElement, 'click', 'goToProcessPage')
|
||||||
|
notificationBoard.appendChild(notifElement);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
noNotifications.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function launchNotificationWorker() {
|
||||||
|
if (window.Worker) {
|
||||||
|
// Initialize the worker
|
||||||
|
const worker = new Worker('/src/workers/notification.worker.ts', { type: 'module' });
|
||||||
|
|
||||||
|
// Listen for messages from the worker
|
||||||
|
worker.addEventListener('message', (event) => {
|
||||||
|
const data = event.data;
|
||||||
|
console.log('Received data from worker:', data);
|
||||||
|
setNotification(data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Your browser doesn\'t support web workers.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { Html5QrcodeScanner } from 'html5-qrcode';
|
import { Html5QrcodeScanner } from 'html5-qrcode';
|
||||||
import Routing from '../../services/routing.service';
|
import Routing from '../../services/modal.service';
|
||||||
import Services from '../../services/service';
|
import Services from '../../services/service';
|
||||||
|
import { addSubscription } from '../../utils/subscription.utils';
|
||||||
|
import { displayEmojis } from '../../utils/sp-address.utils';
|
||||||
|
|
||||||
let resultContainer = document.getElementById('qr-reader-results');
|
let resultContainer = document.getElementById('qr-reader-results');
|
||||||
let lastResult: any,
|
let lastResult: any,
|
||||||
@ -21,9 +23,10 @@ export async function initHomePage(): Promise<void> {
|
|||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
const spAddress = await service.getDeviceAddress()
|
const spAddress = await service.getDeviceAddress()
|
||||||
service.generateQRCode(spAddress)
|
service.generateQRCode(spAddress)
|
||||||
service.displayEmojis(spAddress)
|
displayEmojis(spAddress)
|
||||||
|
|
||||||
document.getElementById('notification-bell')?.addEventListener('click', openCloseNotifications);
|
const notifBell = document.getElementById('notification-bell')
|
||||||
|
if(notifBell) addSubscription(notifBell, 'click', openCloseNotifications)
|
||||||
|
|
||||||
var html5QrcodeScanner = new Html5QrcodeScanner('qr-reader', { fps: 10, qrbox: 250 }, undefined);
|
var html5QrcodeScanner = new Html5QrcodeScanner('qr-reader', { fps: 10, qrbox: 250 }, undefined);
|
||||||
html5QrcodeScanner.render(onScanSuccess, undefined);
|
html5QrcodeScanner.render(onScanSuccess, undefined);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { addSubscription } from '../../utils/subscription.utils';
|
||||||
import { navigate } from '../../router';
|
import { navigate } from '../../router';
|
||||||
import Services from '../../services/service';
|
import Services from '../../services/service';
|
||||||
|
|
||||||
@ -6,7 +7,7 @@ export async function init() {
|
|||||||
const element = document.querySelector('select') as HTMLSelectElement;
|
const element = document.querySelector('select') as HTMLSelectElement;
|
||||||
// Create div that wroaps all the elements inside (select, elements selected, search div) to put select inside
|
// Create div that wroaps all the elements inside (select, elements selected, search div) to put select inside
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
wrapper.addEventListener('click', clickOnWrapper);
|
if(wrapper) addSubscription(wrapper, 'click', clickOnWrapper)
|
||||||
wrapper.classList.add('multi-select-component');
|
wrapper.classList.add('multi-select-component');
|
||||||
wrapper.classList.add('input-field');
|
wrapper.classList.add('input-field');
|
||||||
|
|
||||||
@ -17,14 +18,16 @@ export async function init() {
|
|||||||
input.classList.add('selected-input');
|
input.classList.add('selected-input');
|
||||||
input.setAttribute('autocomplete', 'off');
|
input.setAttribute('autocomplete', 'off');
|
||||||
input.setAttribute('tabindex', '0');
|
input.setAttribute('tabindex', '0');
|
||||||
input.addEventListener('keyup', inputChange);
|
if(input) {
|
||||||
input.addEventListener('keydown', deletePressed);
|
addSubscription(input, 'keyup', inputChange)
|
||||||
input.addEventListener('click', openOptions);
|
addSubscription(input, 'keydown', deletePressed)
|
||||||
|
addSubscription(input, 'click', openOptions)
|
||||||
|
}
|
||||||
|
|
||||||
const dropdown_icon = document.createElement('a');
|
const dropdown_icon = document.createElement('a');
|
||||||
dropdown_icon.classList.add('dropdown-icon');
|
dropdown_icon.classList.add('dropdown-icon');
|
||||||
|
|
||||||
dropdown_icon.addEventListener('click', clickDropdown);
|
if(dropdown_icon) addSubscription(dropdown_icon, 'click', clickDropdown)
|
||||||
const autocomplete_list = document.createElement('ul');
|
const autocomplete_list = document.createElement('ul');
|
||||||
autocomplete_list.classList.add('autocomplete-list');
|
autocomplete_list.classList.add('autocomplete-list');
|
||||||
search_div.appendChild(input);
|
search_div.appendChild(input);
|
||||||
@ -128,7 +131,7 @@ function createToken(wrapper: HTMLElement, value: any) {
|
|||||||
close.setAttribute('data-option', value);
|
close.setAttribute('data-option', value);
|
||||||
close.setAttribute('data-hits', '0');
|
close.setAttribute('data-hits', '0');
|
||||||
close.innerText = 'x';
|
close.innerText = 'x';
|
||||||
close.addEventListener('click', removeToken);
|
if(close) addSubscription(close, 'click', removeToken)
|
||||||
token.appendChild(token_span);
|
token.appendChild(token_span);
|
||||||
token.appendChild(close);
|
token.appendChild(close);
|
||||||
inputInderline?.appendChild(token);
|
inputInderline?.appendChild(token);
|
||||||
@ -185,7 +188,7 @@ function populateAutocompleteList(select: HTMLSelectElement, query: string, drop
|
|||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.innerText = options_to_show[0];
|
li.innerText = options_to_show[0];
|
||||||
li.setAttribute('data-value', options_to_show[0]);
|
li.setAttribute('data-value', options_to_show[0]);
|
||||||
li.addEventListener('click', selectOption);
|
if(li) addSubscription(li, 'click', selectOption)
|
||||||
autocomplete_list?.appendChild(li);
|
autocomplete_list?.appendChild(li);
|
||||||
if (query.length == options_to_show[0].length) {
|
if (query.length == options_to_show[0].length) {
|
||||||
const event = new Event('click');
|
const event = new Event('click');
|
||||||
@ -196,7 +199,7 @@ function populateAutocompleteList(select: HTMLSelectElement, query: string, drop
|
|||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.innerText = options_to_show[i];
|
li.innerText = options_to_show[i];
|
||||||
li.setAttribute('data-value', options_to_show[i]);
|
li.setAttribute('data-value', options_to_show[i]);
|
||||||
li.addEventListener('click', selectOption);
|
if(li) addSubscription(li, 'click', selectOption)
|
||||||
autocomplete_list?.appendChild(li);
|
autocomplete_list?.appendChild(li);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -299,10 +302,10 @@ function removeToken(e: Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Listen for 2 sequence of hits on the delete key, if this happens delete the last token if exist
|
// Listen for 2 sequence of hits on the delete key, if this happens delete the last token if exist
|
||||||
function deletePressed(e: KeyboardEvent) {
|
function deletePressed(e: Event) {
|
||||||
const input_search = e.target as HTMLInputElement;
|
const input_search = e.target as HTMLInputElement;
|
||||||
const wrapper = input_search?.parentNode?.parentNode;
|
const wrapper = input_search?.parentNode?.parentNode;
|
||||||
const key = e.keyCode || e.charCode;
|
const key = (e as KeyboardEvent).keyCode || (e as KeyboardEvent).charCode;
|
||||||
const tokens = wrapper?.querySelectorAll('.selected-wrapper');
|
const tokens = wrapper?.querySelectorAll('.selected-wrapper');
|
||||||
|
|
||||||
if (tokens?.length) {
|
if (tokens?.length) {
|
||||||
@ -327,7 +330,7 @@ function deletePressed(e: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dismiss on outside click
|
// Dismiss on outside click
|
||||||
document.addEventListener('click', () => {
|
addSubscription(document, 'click', () => {
|
||||||
// get select that has the options available
|
// get select that has the options available
|
||||||
const select = document.querySelectorAll('[data-multi-select-plugin]');
|
const select = document.querySelectorAll('[data-multi-select-plugin]');
|
||||||
for (let i = 0; i < select.length; i++) {
|
for (let i = 0; i < select.length; i++) {
|
||||||
@ -345,7 +348,7 @@ document.addEventListener('click', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
async function showSelectedProcess(elem: MouseEvent) {
|
async function showSelectedProcess(elem: MouseEvent) {
|
||||||
if (elem) {
|
if (elem) {
|
||||||
@ -369,9 +372,7 @@ async function showSelectedProcess(elem: MouseEvent) {
|
|||||||
zoneElement.setAttribute('process-title', process[1].title);
|
zoneElement.setAttribute('process-title', process[1].title);
|
||||||
zoneElement.setAttribute('process-id', `${process[0]}_${zone.id}`);
|
zoneElement.setAttribute('process-id', `${process[0]}_${zone.id}`);
|
||||||
zoneElement.innerHTML = `${zone.title}: ${zone.description}`;
|
zoneElement.innerHTML = `${zone.title}: ${zone.description}`;
|
||||||
const service = await Services.getInstance();
|
addSubscription(zoneElement, 'click', select);
|
||||||
// service.addSubscription(zoneElement, 'click', 'goToProcessPage');
|
|
||||||
zoneElement.addEventListener('click', select);
|
|
||||||
processDiv.appendChild(zoneElement);
|
processDiv.appendChild(zoneElement);
|
||||||
}
|
}
|
||||||
if (cardContent) cardContent.appendChild(processDiv);
|
if (cardContent) cardContent.appendChild(processDiv);
|
||||||
@ -379,7 +380,7 @@ async function showSelectedProcess(elem: MouseEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(event: MouseEvent) {
|
function select(event: Event) {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
const oldSelectedProcess = document.querySelector('.selected-process-zone');
|
const oldSelectedProcess = document.querySelector('.selected-process-zone');
|
||||||
oldSelectedProcess?.classList.remove('selected-process-zone');
|
oldSelectedProcess?.classList.remove('selected-process-zone');
|
||||||
@ -406,7 +407,6 @@ function goToProcessPage() {
|
|||||||
async function getProcesses(): Promise<any[]> {
|
async function getProcesses(): Promise<any[]> {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
const processes = await service.getProcesses();
|
const processes = await service.getProcesses();
|
||||||
console.log('🚀 ~ Services ~ getProcesses ~ processes:', processes);
|
|
||||||
|
|
||||||
return processes;
|
return processes;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import '../public/style/4nk.css';
|
import '../public/style/4nk.css';
|
||||||
import { initHeader } from './components/header/header';
|
import { initHeader } from './components/header/header';
|
||||||
import Services from './services/service';
|
import Services from './services/service';
|
||||||
|
import { cleanSubscriptions } from './utils/subscription.utils';
|
||||||
|
|
||||||
const routes: { [key: string]: string } = {
|
const routes: { [key: string]: string } = {
|
||||||
home: '/src/pages/home/home.html',
|
home: '/src/pages/home/home.html',
|
||||||
@ -11,6 +12,7 @@ const routes: { [key: string]: string } = {
|
|||||||
export let currentRoute = '';
|
export let currentRoute = '';
|
||||||
|
|
||||||
export async function navigate(path: string) {
|
export async function navigate(path: string) {
|
||||||
|
cleanSubscriptions()
|
||||||
path = path.replace(/^\//, '');
|
path = path.replace(/^\//, '');
|
||||||
if (path.includes('/')) {
|
if (path.includes('/')) {
|
||||||
const parsedPath = path.split('/')[0];
|
const parsedPath = path.split('/')[0];
|
||||||
@ -86,7 +88,7 @@ async function init(): Promise<void> {
|
|||||||
if (pairingAddress) {
|
if (pairingAddress) {
|
||||||
setTimeout(async () => await services.sendPairingTx(pairingAddress), 2000);
|
setTimeout(async () => await services.sendPairingTx(pairingAddress), 2000);
|
||||||
}
|
}
|
||||||
await navigate('home');
|
await navigate('process');
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -5,9 +5,10 @@ import confirmationModalScript from '../html/confirmation-modal.js?raw';
|
|||||||
import Services from './service';
|
import Services from './service';
|
||||||
import { U32_MAX } from './service';
|
import { U32_MAX } from './service';
|
||||||
import { navigate } from '../router';
|
import { navigate } from '../router';
|
||||||
|
import { addressToEmoji } from '../utils/sp-address.utils';
|
||||||
|
|
||||||
export default class Routing {
|
export default class ModalService {
|
||||||
private static instance: Routing;
|
private static instance: ModalService;
|
||||||
private sdkClient: any;
|
private sdkClient: any;
|
||||||
private currentPrd: any;
|
private currentPrd: any;
|
||||||
private currentOutpoint?: string;
|
private currentOutpoint?: string;
|
||||||
@ -15,16 +16,11 @@ export default class Routing {
|
|||||||
private paired_addresses: string[] = [];
|
private paired_addresses: string[] = [];
|
||||||
|
|
||||||
// Method to access the singleton instance of Services
|
// Method to access the singleton instance of Services
|
||||||
public static async getInstance(): Promise<Routing> {
|
public static async getInstance(): Promise<ModalService> {
|
||||||
if (!Routing.instance) {
|
if (!ModalService.instance) {
|
||||||
Routing.instance = new Routing();
|
ModalService.instance = new ModalService();
|
||||||
await Routing.instance.init();
|
|
||||||
}
|
}
|
||||||
return Routing.instance;
|
return ModalService.instance;
|
||||||
}
|
|
||||||
|
|
||||||
public async init(): Promise<void> {
|
|
||||||
this.sdkClient = await import('../../dist/pkg/sdk_client');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public openLoginModal(myAddress: string, receiverAddress: string) {
|
public openLoginModal(myAddress: string, receiverAddress: string) {
|
||||||
@ -43,23 +39,22 @@ export default class Routing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async openConfirmationModal(pcd: any, outpointCommitment: string) {
|
public async openConfirmationModal(pcd: any, outpointCommitment: string) {
|
||||||
console.log('');
|
|
||||||
let roles = JSON.parse(pcd['roles']); // ['members'][0];
|
let roles = JSON.parse(pcd['roles']); // ['members'][0];
|
||||||
console.log(roles);
|
console.log(roles);
|
||||||
let members = roles['owner']['members'];
|
let members = roles['owner']['members'];
|
||||||
console.log(members);
|
console.log(members);
|
||||||
// We take all the addresses except our own
|
// We take all the addresses except our own
|
||||||
const local_address = this.sdkClient.get_address();
|
const service = await Services.getInstance()
|
||||||
|
const localAddress = await service.getDeviceAddress();
|
||||||
console.log('🚀 ~ Routing ~ openConfirmationModal ~ pcd:', pcd);
|
console.log('🚀 ~ Routing ~ openConfirmationModal ~ pcd:', pcd);
|
||||||
for (const address of members[0]['sp_addresses']) {
|
for (const address of members[0]['sp_addresses']) {
|
||||||
if (address !== local_address) {
|
if (address !== localAddress) {
|
||||||
this.paired_addresses.push(address);
|
this.paired_addresses.push(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let html = confirmationModalHtml;
|
let html = confirmationModalHtml;
|
||||||
let services = await Services.getInstance();
|
html = html.replace('{{device1}}', await addressToEmoji(members[0]['sp_addresses'][0]));
|
||||||
html = html.replace('{{device1}}', await services.addressToEmoji(members[0]['sp_addresses'][0]));
|
html = html.replace('{{device2}}', await addressToEmoji(members[0]['sp_addresses'][1]));
|
||||||
html = html.replace('{{device2}}', await services.addressToEmoji(members[0]['sp_addresses'][1]));
|
|
||||||
this.currentOutpoint = outpointCommitment as string;
|
this.currentOutpoint = outpointCommitment as string;
|
||||||
const container = document.querySelector('.page-container');
|
const container = document.querySelector('.page-container');
|
||||||
|
|
||||||
@ -99,7 +94,7 @@ export default class Routing {
|
|||||||
const commitmentOutpoint = `${emptyTxid}:${U32_MAX}`;
|
const commitmentOutpoint = `${emptyTxid}:${U32_MAX}`;
|
||||||
|
|
||||||
// We take the paired device(s) from the contract
|
// We take the paired device(s) from the contract
|
||||||
await this.sdkClient.pair_device(commitmentOutpoint, this.paired_addresses);
|
await service.pairDevice(commitmentOutpoint, this.paired_addresses);
|
||||||
this.paired_addresses = [];
|
this.paired_addresses = [];
|
||||||
const newDevice = await service.dumpDevice();
|
const newDevice = await service.dumpDevice();
|
||||||
await service.saveDevice(newDevice);
|
await service.saveDevice(newDevice);
|
@ -5,8 +5,9 @@ import { IProcess } from '~/models/process.model';
|
|||||||
import { WebSocketClient } from '../websockets';
|
import { WebSocketClient } from '../websockets';
|
||||||
import QRCode from 'qrcode';
|
import QRCode from 'qrcode';
|
||||||
import { ApiReturn, Member } from '../../dist/pkg/sdk_client';
|
import { ApiReturn, Member } from '../../dist/pkg/sdk_client';
|
||||||
import Routing from './routing.service';
|
import ModalService from './modal.service';
|
||||||
import { currentRoute, navigate } from '../router';
|
import { navigate } from '../router';
|
||||||
|
import { copyToClipboard } from '../utils/sp-address.utils';
|
||||||
|
|
||||||
type ProcessesCache = {
|
type ProcessesCache = {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
@ -25,6 +26,7 @@ export default class Services {
|
|||||||
private notifications: INotification[] | null = null;
|
private notifications: INotification[] | null = null;
|
||||||
private subscriptions: { element: Element; event: string; eventHandler: string }[] = [];
|
private subscriptions: { element: Element; event: string; eventHandler: string }[] = [];
|
||||||
private database: any;
|
private database: any;
|
||||||
|
private routingInstance!: ModalService;
|
||||||
// Private constructor to prevent direct instantiation from outside
|
// Private constructor to prevent direct instantiation from outside
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
@ -38,6 +40,7 @@ export default class Services {
|
|||||||
Services.initializing = (async () => {
|
Services.initializing = (async () => {
|
||||||
const instance = new Services();
|
const instance = new Services();
|
||||||
await instance.init();
|
await instance.init();
|
||||||
|
instance.routingInstance = await ModalService.getInstance()
|
||||||
return instance;
|
return instance;
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
@ -79,162 +82,13 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
const copyBtn = document.getElementById('copyBtn');
|
const copyBtn = document.getElementById('copyBtn');
|
||||||
if (copyBtn) {
|
if (copyBtn) {
|
||||||
copyBtn.addEventListener('click', () => this.copyToClipboard(currentUrl + '?sp_address=' + text));
|
copyBtn.addEventListener('click', () => copyToClipboard(currentUrl + '?sp_address=' + text));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//Copy Address
|
|
||||||
public async copyToClipboard(fullAddress: string) {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(fullAddress);
|
|
||||||
alert('Adresse copiée dans le presse-papiers !');
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to copy the address: ', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Generate emojis list
|
|
||||||
public generateEmojiList(): string[] {
|
|
||||||
const emojiRanges = [
|
|
||||||
[0x1f600, 0x1f64f],
|
|
||||||
[0x1f300, 0x1f5ff],
|
|
||||||
[0x1f680, 0x1f6ff],
|
|
||||||
[0x1f700, 0x1f77f],
|
|
||||||
];
|
|
||||||
|
|
||||||
const emojiList: string[] = [];
|
|
||||||
for (const range of emojiRanges) {
|
|
||||||
const [start, end] = range;
|
|
||||||
for (let i = start; i <= end && emojiList.length < 256; i++) {
|
|
||||||
emojiList.push(String.fromCodePoint(i));
|
|
||||||
}
|
|
||||||
if (emojiList.length >= 256) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return emojiList.slice(0, 256);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Adress to emojis
|
|
||||||
public addressToEmoji = async (text: string): Promise<string> => {
|
|
||||||
//Adress to Hash
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
const data = encoder.encode(text);
|
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
||||||
|
|
||||||
const hash = new Uint8Array(hashBuffer);
|
|
||||||
const bytes = hash.slice(-4);
|
|
||||||
|
|
||||||
//Hash slice to emojis
|
|
||||||
const emojiList = this.generateEmojiList();
|
|
||||||
const emojis = Array.from(bytes)
|
|
||||||
.map((byte) => emojiList[byte])
|
|
||||||
.join('');
|
|
||||||
return emojis;
|
|
||||||
};
|
|
||||||
|
|
||||||
//Get emojis from other device
|
|
||||||
public async emojisPairingRequest() {
|
|
||||||
try {
|
|
||||||
const container = document.getElementById('containerId');
|
|
||||||
|
|
||||||
const urlParams: URLSearchParams = new URLSearchParams(window.location.search);
|
|
||||||
const sp_adress: string | null = urlParams.get('sp_address');
|
|
||||||
|
|
||||||
if (!sp_adress) {
|
|
||||||
// console.error("No 'sp_adress' parameter found in the URL.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis = await this.addressToEmoji(sp_adress);
|
|
||||||
const emojiDisplay = container?.querySelector('.pairing-request');
|
|
||||||
|
|
||||||
if (emojiDisplay) {
|
|
||||||
emojiDisplay.textContent = '(Request from: ' + emojis + ')';
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display address emojis and other device emojis
|
|
||||||
displayEmojis = async (text: string) => {
|
|
||||||
console.log('🚀 ~ Services ~ adressToEmoji');
|
|
||||||
try {
|
|
||||||
const container = document.getElementById('containerId');
|
|
||||||
const emojis = await this.addressToEmoji(text);
|
|
||||||
const emojiDisplay = container?.querySelector('.emoji-display');
|
|
||||||
|
|
||||||
if (emojiDisplay) {
|
|
||||||
emojiDisplay.textContent = emojis;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emojisPairingRequest();
|
|
||||||
|
|
||||||
this.initAddressInput();
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Verify Other address
|
|
||||||
public initAddressInput() {
|
|
||||||
const addressInput = document.getElementById('addressInput') as HTMLInputElement;
|
|
||||||
const emojiDisplay = document.getElementById('emoji-display-2');
|
|
||||||
const okButton = document.getElementById('okButton');
|
|
||||||
|
|
||||||
addressInput.addEventListener('input', async () => {
|
|
||||||
let address = addressInput.value;
|
|
||||||
|
|
||||||
// Vérifie si l'adresse est une URL
|
|
||||||
try {
|
|
||||||
const url = new URL(address);
|
|
||||||
// Si c'est une URL valide, extraire le paramètre sp_address
|
|
||||||
const urlParams = new URLSearchParams(url.search);
|
|
||||||
const extractedAddress = urlParams.get('sp_address') || ''; // Prend sp_address ou une chaîne vide
|
|
||||||
|
|
||||||
if (extractedAddress) {
|
|
||||||
address = extractedAddress;
|
|
||||||
addressInput.value = address; // Met à jour l'input pour afficher uniquement l'adresse extraite
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Si ce n'est pas une URL valide, on garde l'adresse originale
|
|
||||||
console.log("Ce n'est pas une URL valide, on garde l'adresse originale.");
|
|
||||||
}
|
|
||||||
if (address) {
|
|
||||||
const emojis = await this.addressToEmoji(address);
|
|
||||||
if (emojiDisplay) {
|
|
||||||
emojiDisplay.innerHTML = emojis;
|
|
||||||
}
|
|
||||||
if (okButton) {
|
|
||||||
okButton.style.display = 'inline-block';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (emojiDisplay) {
|
|
||||||
emojiDisplay.innerHTML = '';
|
|
||||||
}
|
|
||||||
if (okButton) {
|
|
||||||
okButton.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (okButton) {
|
|
||||||
okButton.addEventListener('click', () => {
|
|
||||||
this.onOkButtonClick();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async onOkButtonClick() {
|
|
||||||
const addressInput = (document.getElementById('addressInput') as HTMLInputElement).value;
|
|
||||||
await this.sendPairingTx(addressInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public isPaired(): boolean | undefined {
|
public isPaired(): boolean | undefined {
|
||||||
try {
|
try {
|
||||||
return this.sdkClient.is_linking();
|
return this.sdkClient.is_linking();
|
||||||
@ -382,8 +236,7 @@ export default class Services {
|
|||||||
if (proposals && proposals.length != 0) {
|
if (proposals && proposals.length != 0) {
|
||||||
const actual_proposal = JSON.parse(proposals[0]); // We just don't acknowledge concurrent proposals for now
|
const actual_proposal = JSON.parse(proposals[0]); // We just don't acknowledge concurrent proposals for now
|
||||||
console.info(actual_proposal);
|
console.info(actual_proposal);
|
||||||
let router = await Routing.getInstance();
|
await this.routingInstance.openConfirmationModal(actual_proposal, processCommitment);
|
||||||
await router.openConfirmationModal(actual_proposal, processCommitment);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -409,15 +262,18 @@ export default class Services {
|
|||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async pairDevice(prd: any, outpointCommitment: string) {
|
async pairDevice(commitmentTx: string, spAddressList: string[]) {
|
||||||
|
await this.sdkClient.pair_device(commitmentTx, spAddressList);
|
||||||
|
}
|
||||||
|
|
||||||
|
async validatePairingDevice(prd: any, outpointCommitment: string) {
|
||||||
console.log('🚀 ~ Services ~ pairDevice ~ prd:', prd);
|
console.log('🚀 ~ Services ~ pairDevice ~ prd:', prd);
|
||||||
const service = await Services.getInstance();
|
|
||||||
const spAddress = (await this.getDeviceAddress()) as any;
|
const spAddress = (await this.getDeviceAddress()) as any;
|
||||||
const sender = JSON.parse(prd?.sender);
|
const sender = JSON.parse(prd?.sender);
|
||||||
const senderAddress = sender?.sp_addresses?.find((address: string) => address !== spAddress);
|
const senderAddress = sender?.sp_addresses?.find((address: string) => address !== spAddress);
|
||||||
console.log('🚀 ~ Services ~ pairDevice ~ senderAddress:', senderAddress);
|
console.log('🚀 ~ Services ~ pairDevice ~ senderAddress:', senderAddress);
|
||||||
if (senderAddress) {
|
if (senderAddress) {
|
||||||
const proposal = service.sdkClient.get_update_proposals(outpointCommitment);
|
const proposal = this.sdkClient.get_update_proposals(outpointCommitment);
|
||||||
console.log('🚀 ~ Services ~ pairDevice ~ proposal:', proposal);
|
console.log('🚀 ~ Services ~ pairDevice ~ proposal:', proposal);
|
||||||
// const pairingTx = proposal.pairing_tx.replace(/^\"+|\"+$/g, '')
|
// const pairingTx = proposal.pairing_tx.replace(/^\"+|\"+$/g, '')
|
||||||
const parsedProposal = JSON.parse(proposal[0]);
|
const parsedProposal = JSON.parse(proposal[0]);
|
||||||
@ -436,7 +292,7 @@ export default class Services {
|
|||||||
let txid = '0'.repeat(64);
|
let txid = '0'.repeat(64);
|
||||||
console.log('🚀 ~ Services ~ pairDevice ~ pairingTx:', pairingTx, `${txid}:4294967295`);
|
console.log('🚀 ~ Services ~ pairDevice ~ pairingTx:', pairingTx, `${txid}:4294967295`);
|
||||||
|
|
||||||
const pairing = await service.sdkClient.pair_device(`${txid}:4294967295`, [senderAddress]);
|
const pairing = await this.sdkClient.pair_device(`${txid}:4294967295`, [senderAddress]);
|
||||||
const device = this.dumpDevice();
|
const device = this.dumpDevice();
|
||||||
console.log('🚀 ~ Services ~ pairDevice ~ device:', device);
|
console.log('🚀 ~ Services ~ pairDevice ~ device:', device);
|
||||||
this.saveDevice(device);
|
this.saveDevice(device);
|
||||||
@ -446,10 +302,10 @@ export default class Services {
|
|||||||
console.log('🚀 ~ Services ~ pairDevice ~ process:', outpointCommitment, prd, prd.payload);
|
console.log('🚀 ~ Services ~ pairDevice ~ process:', outpointCommitment, prd, prd.payload);
|
||||||
const prdString = JSON.stringify(prd).trim();
|
const prdString = JSON.stringify(prd).trim();
|
||||||
console.log('🚀 ~ Services ~ pairDevice ~ prdString:', prdString);
|
console.log('🚀 ~ Services ~ pairDevice ~ prdString:', prdString);
|
||||||
let tx = await service.sdkClient.response_prd(outpointCommitment, prdString, true);
|
let tx = await this.sdkClient.response_prd(outpointCommitment, prdString, true);
|
||||||
console.log('🚀 ~ Services ~ pairDevice ~ tx:', tx);
|
console.log('🚀 ~ Services ~ pairDevice ~ tx:', tx);
|
||||||
if (tx.ciphers_to_send) {
|
if (tx.ciphers_to_send) {
|
||||||
tx.ciphers_to_send.forEach((cipher: string) => service.websocketConnection?.sendMessage('Cipher', cipher));
|
tx.ciphers_to_send.forEach((cipher: string) => this.websocketConnection?.sendMessage('Cipher', cipher));
|
||||||
}
|
}
|
||||||
navigate('process');
|
navigate('process');
|
||||||
}
|
}
|
||||||
@ -506,11 +362,6 @@ export default class Services {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
addSubscription(element: Element, event: string, eventHandler: string): void {
|
|
||||||
this.subscriptions.push({ element, event, eventHandler });
|
|
||||||
element.addEventListener(event, (this as any)[eventHandler]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async createNewDevice() {
|
async createNewDevice() {
|
||||||
let spAddress = '';
|
let spAddress = '';
|
||||||
try {
|
try {
|
||||||
@ -696,48 +547,6 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private cleanSubscriptions(): void {
|
|
||||||
for (const sub of this.subscriptions) {
|
|
||||||
const el = sub.element;
|
|
||||||
const eventHandler = sub.eventHandler;
|
|
||||||
el.removeEventListener(sub.event, (this as any)[eventHandler].bind(this));
|
|
||||||
}
|
|
||||||
this.subscriptions = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public async setProcessesInSelectElement(processList: any[]) {
|
|
||||||
console.log('🚀 ~ Services ~ setProcessesInSelectElement ~ processList:', processList);
|
|
||||||
const select = document.querySelector('.select-field');
|
|
||||||
if (select) {
|
|
||||||
for (const process of processList) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.setAttribute('value', process.name);
|
|
||||||
option.innerText = process.name;
|
|
||||||
select.appendChild(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const optionList = document.querySelector('.autocomplete-list');
|
|
||||||
if (optionList) {
|
|
||||||
const observer = new MutationObserver((mutations, observer) => {
|
|
||||||
const options = optionList.querySelectorAll('li');
|
|
||||||
if (options) {
|
|
||||||
for (const option of options) {
|
|
||||||
this.addSubscription(option, 'click', 'showSelectedProcess');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
observer.observe(document, {
|
|
||||||
subtree: true,
|
|
||||||
attributes: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async listenToOptionListPopulating(event: Event) {
|
|
||||||
const target = event.target as HTMLUListElement;
|
|
||||||
const options = target?.querySelectorAll('li');
|
|
||||||
}
|
|
||||||
|
|
||||||
getNotifications(): INotification[] {
|
getNotifications(): INotification[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -763,28 +572,4 @@ export default class Services {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async setNotification(): Promise<void> {
|
|
||||||
const badge = document.querySelector('.notification-badge') as HTMLDivElement;
|
|
||||||
const notifications = this.notifications;
|
|
||||||
const noNotifications = document.querySelector('.no-notification') as HTMLDivElement;
|
|
||||||
if (notifications?.length) {
|
|
||||||
badge.innerText = notifications.length.toString();
|
|
||||||
const notificationBoard = document.querySelector('.notification-board') as HTMLDivElement;
|
|
||||||
noNotifications.style.display = 'none';
|
|
||||||
for (const notif of notifications) {
|
|
||||||
const notifElement = document.createElement('div');
|
|
||||||
notifElement.className = 'notification-element';
|
|
||||||
notifElement.setAttribute('notif-id', notif.id.toString());
|
|
||||||
notifElement.innerHTML = `
|
|
||||||
<div>${notif.title}</div>
|
|
||||||
<div>${notif.description}</div>
|
|
||||||
`;
|
|
||||||
// this.addSubscription(notifElement, 'click', 'goToProcessPage')
|
|
||||||
notificationBoard.appendChild(notifElement);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
noNotifications.style.display = 'block';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
152
src/utils/sp-address.utils.ts
Normal file
152
src/utils/sp-address.utils.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import Services from "../services/service";
|
||||||
|
import { addSubscription } from "./subscription.utils";
|
||||||
|
|
||||||
|
//Copy Address
|
||||||
|
export async function copyToClipboard(fullAddress: string) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(fullAddress);
|
||||||
|
alert('Adresse copiée dans le presse-papiers !');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to copy the address: ', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generate emojis list
|
||||||
|
export function generateEmojiList(): string[] {
|
||||||
|
const emojiRanges = [
|
||||||
|
[0x1f600, 0x1f64f],
|
||||||
|
[0x1f300, 0x1f5ff],
|
||||||
|
[0x1f680, 0x1f6ff],
|
||||||
|
[0x1f700, 0x1f77f],
|
||||||
|
];
|
||||||
|
|
||||||
|
const emojiList: string[] = [];
|
||||||
|
for (const range of emojiRanges) {
|
||||||
|
const [start, end] = range;
|
||||||
|
for (let i = start; i <= end && emojiList.length < 256; i++) {
|
||||||
|
emojiList.push(String.fromCodePoint(i));
|
||||||
|
}
|
||||||
|
if (emojiList.length >= 256) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return emojiList.slice(0, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Adress to emojis
|
||||||
|
export async function addressToEmoji(text: string): Promise<string> {
|
||||||
|
//Adress to Hash
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const data = encoder.encode(text);
|
||||||
|
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
||||||
|
|
||||||
|
const hash = new Uint8Array(hashBuffer);
|
||||||
|
const bytes = hash.slice(-4);
|
||||||
|
|
||||||
|
//Hash slice to emojis
|
||||||
|
const emojiList = generateEmojiList();
|
||||||
|
const emojis = Array.from(bytes)
|
||||||
|
.map((byte) => emojiList[byte])
|
||||||
|
.join('');
|
||||||
|
return emojis;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//Get emojis from other device
|
||||||
|
async function emojisPairingRequest() {
|
||||||
|
try {
|
||||||
|
const container = document.getElementById('containerId');
|
||||||
|
|
||||||
|
const urlParams: URLSearchParams = new URLSearchParams(window.location.search);
|
||||||
|
const sp_adress: string | null = urlParams.get('sp_address');
|
||||||
|
|
||||||
|
if (!sp_adress) {
|
||||||
|
// console.error("No 'sp_adress' parameter found in the URL.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojis = await addressToEmoji(sp_adress);
|
||||||
|
const emojiDisplay = container?.querySelector('.pairing-request');
|
||||||
|
|
||||||
|
if (emojiDisplay) {
|
||||||
|
emojiDisplay.textContent = '(Request from: ' + emojis + ')';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display address emojis and other device emojis
|
||||||
|
export async function displayEmojis(text: string) {
|
||||||
|
console.log('🚀 ~ Services ~ adressToEmoji');
|
||||||
|
try {
|
||||||
|
const container = document.getElementById('containerId');
|
||||||
|
const emojis = await addressToEmoji(text);
|
||||||
|
const emojiDisplay = container?.querySelector('.emoji-display');
|
||||||
|
|
||||||
|
if (emojiDisplay) {
|
||||||
|
emojiDisplay.textContent = emojis;
|
||||||
|
}
|
||||||
|
|
||||||
|
emojisPairingRequest();
|
||||||
|
|
||||||
|
initAddressInput();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify Other address
|
||||||
|
export function initAddressInput() {
|
||||||
|
const addressInput = document.getElementById('addressInput') as HTMLInputElement;
|
||||||
|
const emojiDisplay = document.getElementById('emoji-display-2');
|
||||||
|
const okButton = document.getElementById('okButton');
|
||||||
|
addSubscription(addressInput, 'input', async () => {
|
||||||
|
let address = addressInput.value;
|
||||||
|
|
||||||
|
// Vérifie si l'adresse est une URL
|
||||||
|
try {
|
||||||
|
const url = new URL(address);
|
||||||
|
// Si c'est une URL valide, extraire le paramètre sp_address
|
||||||
|
const urlParams = new URLSearchParams(url.search);
|
||||||
|
const extractedAddress = urlParams.get('sp_address') || ''; // Prend sp_address ou une chaîne vide
|
||||||
|
|
||||||
|
if (extractedAddress) {
|
||||||
|
address = extractedAddress;
|
||||||
|
addressInput.value = address; // Met à jour l'input pour afficher uniquement l'adresse extraite
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Si ce n'est pas une URL valide, on garde l'adresse originale
|
||||||
|
console.log("Ce n'est pas une URL valide, on garde l'adresse originale.");
|
||||||
|
}
|
||||||
|
if (address) {
|
||||||
|
const emojis = await addressToEmoji(address);
|
||||||
|
if (emojiDisplay) {
|
||||||
|
emojiDisplay.innerHTML = emojis;
|
||||||
|
}
|
||||||
|
if (okButton) {
|
||||||
|
okButton.style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (emojiDisplay) {
|
||||||
|
emojiDisplay.innerHTML = '';
|
||||||
|
}
|
||||||
|
if (okButton) {
|
||||||
|
okButton.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (okButton) {
|
||||||
|
okButton.addEventListener('click', () => {
|
||||||
|
onOkButtonClick();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onOkButtonClick() {
|
||||||
|
const service = await Services.getInstance()
|
||||||
|
const addressInput = (document.getElementById('addressInput') as HTMLInputElement).value;
|
||||||
|
await service.sendPairingTx(addressInput);
|
||||||
|
}
|
16
src/utils/subscription.utils.ts
Normal file
16
src/utils/subscription.utils.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
let subscriptions: { element: Element | Document, event: any, eventHandler: EventListenerOrEventListenerObject }[] = [];
|
||||||
|
|
||||||
|
export function cleanSubscriptions(): void {
|
||||||
|
console.log("🚀 ~ cleanSubscriptions ~ sub:", subscriptions)
|
||||||
|
for (const sub of subscriptions) {
|
||||||
|
const el = sub.element;
|
||||||
|
const eventHandler = sub.eventHandler;
|
||||||
|
el.removeEventListener(sub.event, eventHandler);
|
||||||
|
}
|
||||||
|
subscriptions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addSubscription(element: Element | Document, event: any, eventHandler: EventListenerOrEventListenerObject): void {
|
||||||
|
subscriptions.push({ element, event, eventHandler });
|
||||||
|
element.addEventListener(event, eventHandler);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user