Compare commits
87 Commits
request
...
create-acc
Author | SHA1 | Date | |
---|---|---|---|
afe45ad960 | |||
ed0c11b527 | |||
0cfb02b06a | |||
f00faa1841 | |||
1952ade39c | |||
e62a04ea57 | |||
b46a9aa13a | |||
2aa846b8e5 | |||
57bd945599 | |||
c82c219a28 | |||
3a851d1b62 | |||
4ff2965b49 | |||
4d0c3e3f56 | |||
b9c4dfbfd9 | |||
cc0d823125 | |||
090bdbedaa | |||
722e08ea82 | |||
3785285e4d | |||
3d09a20512 | |||
de6213b1b7 | |||
dc231ba982 | |||
570f98c51c | |||
b6c2d211e5 | |||
bde2ed0e44 | |||
15626201a8 | |||
bb0d998c9f | |||
1747908d20 | |||
7ad8063e87 | |||
c32a7f9e86 | |||
7ebc5a75ea | |||
38b5567b68 | |||
e566c17a9a | |||
d243b58101 | |||
6e57225529 | |||
7f6eabaa90 | |||
8f34f2bcbe | |||
1bdb66b583 | |||
837ae35be9 | |||
4eb24d9678 | |||
b4d35b5dc8 | |||
9f05099338 | |||
f2991dd130 | |||
c8d049ce85 | |||
5bda1736e1 | |||
db07abaa8a | |||
a1aec7cec3 | |||
7c3e263b8a | |||
ed578be468 | |||
d950ce0a2b | |||
b7a2f3a058 | |||
aa22b78121 | |||
85a2296d56 | |||
6180a66405 | |||
8ce490eb7b | |||
0fe9a6f371 | |||
a16e984ecd | |||
a3542a48e9 | |||
512a9025b1 | |||
3be5efd60c | |||
7bf0fb2b4f | |||
a2a7022c08 | |||
682ab03861 | |||
57684eba6b | |||
422fa2469b | |||
208da9b819 | |||
d6de96910a | |||
9dda743ba0 | |||
091a8d4bd2 | |||
5a94888a78 | |||
8d267f5b12 | |||
8fbcb769d6 | |||
239ddae893 | |||
a380ee4f29 | |||
4225f41ca0 | |||
939c640e8f | |||
1cdf61f8fe | |||
7403fb7a5e | |||
1a6e6d595e | |||
5eef7c6981 | |||
d5c5224a9f | |||
463f4d952c | |||
cb2fea029e | |||
369c83af3a | |||
0f3d9b4920 | |||
3813f542f2 | |||
51664e62ec | |||
cc2f960af0 |
@ -5,7 +5,7 @@
|
|||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build_wasm": "wasm-pack build --out-dir ../ihm_client_dev2/pkg ../sdk_client --target bundler ",
|
"build_wasm": "wasm-pack build --out-dir ../ihm_client_dev1/pkg ../sdk_client --target bundler --dev",
|
||||||
"start": "vite --host 0.0.0.0",
|
"start": "vite --host 0.0.0.0",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"deploy": "sudo cp -r dist/* /var/www/html/",
|
"deploy": "sudo cp -r dist/* /var/www/html/",
|
||||||
|
249
patch.patch
249
patch.patch
@ -1,249 +0,0 @@
|
|||||||
diff --git a/package.json b/package.json
|
|
||||||
index f0515ae..4e52ec3 100755
|
|
||||||
--- a/package.json
|
|
||||||
+++ b/package.json
|
|
||||||
@@ -5,7 +5,7 @@
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
- "build_wasm": "wasm-pack build --out-dir ../ihm_client/pkg ../sdk_client --target bundler --dev",
|
|
||||||
+ "build_wasm": "wasm-pack build --out-dir ../ihm_client_dev2/pkg ../sdk_client --target bundler ",
|
|
||||||
"start": "vite --host 0.0.0.0",
|
|
||||||
"build": "tsc && vite build",
|
|
||||||
"deploy": "sudo cp -r dist/* /var/www/html/",
|
|
||||||
diff --git a/src/pages/chat/chat.ts b/src/pages/chat/chat.ts
|
|
||||||
index 80493a0..2bd753a 100755
|
|
||||||
--- a/src/pages/chat/chat.ts
|
|
||||||
+++ b/src/pages/chat/chat.ts
|
|
||||||
@@ -932,6 +932,8 @@ class ChatElement extends HTMLElement {
|
|
||||||
switch (tabType) {
|
|
||||||
case 'processes':
|
|
||||||
const processSet = await this.getProcessesWhereTheCurrentMemberIs();
|
|
||||||
+ const db = await Database.getInstance();
|
|
||||||
+ await db.updateMyProcesses(processSet);
|
|
||||||
await this.loadAllProcesses(processSet);
|
|
||||||
break;
|
|
||||||
case 'members':
|
|
||||||
diff --git a/src/service-workers/database.worker.js b/src/service-workers/database.worker.js
|
|
||||||
index 5753d1b..5c15bce 100755
|
|
||||||
--- a/src/service-workers/database.worker.js
|
|
||||||
+++ b/src/service-workers/database.worker.js
|
|
||||||
@@ -27,9 +27,23 @@ self.addEventListener('message', async (event) => {
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const scanMissingData = async () => {
|
|
||||||
+ console.log('Scanning for missing data...');
|
|
||||||
const myProcesses = getProcesses(myProcessesId);
|
|
||||||
|
|
||||||
-
|
|
||||||
+ let toDownload = [];
|
|
||||||
+ // Iterate on each process
|
|
||||||
+ for (const process of myProcesses) {
|
|
||||||
+ console.log(process);
|
|
||||||
+ // Iterate on states
|
|
||||||
+ for (const state of process.states) {
|
|
||||||
+ console.log(state);
|
|
||||||
+ // iterate on pcd_commitment
|
|
||||||
+ for (const hash of state.pcd_commitment) {
|
|
||||||
+ console.log(hash);
|
|
||||||
+ toDownload.push(hash);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
|
|
||||||
event.ports[0].postMessage({
|
|
||||||
type: 'TO_DOWNLOAD',
|
|
||||||
diff --git a/src/services/database.service.ts b/src/services/database.service.ts
|
|
||||||
index 0544ccd..6e0d3c2 100755
|
|
||||||
--- a/src/services/database.service.ts
|
|
||||||
+++ b/src/services/database.service.ts
|
|
||||||
@@ -220,7 +220,10 @@ export class Database {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
+<<<<<<< Updated upstream
|
|
||||||
|
|
||||||
+=======
|
|
||||||
+>>>>>>> Stashed changes
|
|
||||||
public updateMyProcesses(payload: { myProcessesId: string[] }): Promise<void> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Check if the service worker is active
|
|
||||||
diff --git a/src/services/service.ts b/src/services/service.ts
|
|
||||||
index 7a2549d..01efad6 100755
|
|
||||||
--- a/src/services/service.ts
|
|
||||||
+++ b/src/services/service.ts
|
|
||||||
@@ -302,25 +302,31 @@ export default class Services {
|
|
||||||
}
|
|
||||||
const myAddress: string = this.sdkClient.get_address();
|
|
||||||
pairWith.push(myAddress);
|
|
||||||
+ const roles: Record<string, RoleDefinition> = {
|
|
||||||
+ pairing: {
|
|
||||||
+ members: [{ sp_addresses: pairWith }],
|
|
||||||
+ validation_rules: [
|
|
||||||
+ {
|
|
||||||
+ quorum: 1.0,
|
|
||||||
+ fields: ['description', 'counter'],
|
|
||||||
+ min_sig_member: 1.0,
|
|
||||||
+ },
|
|
||||||
+ ],
|
|
||||||
+ storages: [storageUrl]
|
|
||||||
+ },
|
|
||||||
+ };
|
|
||||||
const pairingTemplate = {
|
|
||||||
description: 'pairing',
|
|
||||||
counter: 0,
|
|
||||||
- roles: {
|
|
||||||
- pairing: {
|
|
||||||
- members: [{ sp_addresses: pairWith }],
|
|
||||||
- validation_rules: [
|
|
||||||
- {
|
|
||||||
- quorum: 1.0,
|
|
||||||
- fields: ['description', 'roles', 'counter'],
|
|
||||||
- min_sig_member: 1.0,
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
- storages: [storageUrl]
|
|
||||||
- },
|
|
||||||
- },
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
- return this.sdkClient.create_new_process(JSON.stringify(pairingTemplate), null, relayAddress, feeRate);
|
|
||||||
+ return this.sdkClient.create_new_process(
|
|
||||||
+ JSON.stringify(pairingTemplate),
|
|
||||||
+ JSON.stringify(roles),
|
|
||||||
+ null,
|
|
||||||
+ relayAddress,
|
|
||||||
+ feeRate
|
|
||||||
+ );
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(`Creating process failed:, ${e}`);
|
|
||||||
}
|
|
||||||
@@ -605,6 +611,7 @@ export default class Services {
|
|
||||||
}
|
|
||||||
|
|
||||||
public async handleApiReturn(apiReturn: ApiReturn) {
|
|
||||||
+ console.log(apiReturn);
|
|
||||||
if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.transaction.length != 0) {
|
|
||||||
await this.sendNewTxMessage(JSON.stringify(apiReturn.new_tx_to_send));
|
|
||||||
await new Promise(r => setTimeout(r, 500));
|
|
||||||
@@ -639,71 +646,51 @@ export default class Services {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- setTimeout(async () => {
|
|
||||||
- if (apiReturn.updated_process) {
|
|
||||||
- const updatedProcess = apiReturn.updated_process;
|
|
||||||
+ if (apiReturn.updated_process) {
|
|
||||||
+ const updatedProcess = apiReturn.updated_process;
|
|
||||||
+
|
|
||||||
+ const processId: string = updatedProcess.process_id;
|
|
||||||
|
|
||||||
- const processId: string = updatedProcess.process_id;
|
|
||||||
+ // Save process to db
|
|
||||||
+ try {
|
|
||||||
+ await this.saveProcessToDb(processId, updatedProcess.current_process);
|
|
||||||
+ } catch (e) {
|
|
||||||
+ throw e;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- // Save process to db
|
|
||||||
+ const isPaired = this.isPaired();
|
|
||||||
+
|
|
||||||
+ if (updatedProcess.diffs && updatedProcess.diffs.length != 0) {
|
|
||||||
try {
|
|
||||||
- await this.saveProcessToDb(processId, updatedProcess.current_process);
|
|
||||||
+ await this.saveDiffsToDb(updatedProcess.diffs);
|
|
||||||
} catch (e) {
|
|
||||||
- throw e;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- const isPaired = this.isPaired();
|
|
||||||
-
|
|
||||||
- if (updatedProcess.diffs && updatedProcess.diffs.length != 0) {
|
|
||||||
- const [updatedDiffs, retrievedValues] = await this.tryFetchDiffValue(updatedProcess.diffs);
|
|
||||||
- if (Object.entries(retrievedValues).length != 0) {
|
|
||||||
- const stateId = updatedDiffs[0].state_id;
|
|
||||||
- const processId = updatedDiffs[0].process_id;
|
|
||||||
- // We update the process with the value we retrieved
|
|
||||||
- const hashToValues = JSON.stringify(retrievedValues);
|
|
||||||
- const apiReturn = this.sdkClient.update_process_state(processId, stateId, hashToValues);
|
|
||||||
- await this.handleApiReturn(apiReturn);
|
|
||||||
- } else {
|
|
||||||
- try {
|
|
||||||
- await this.saveDiffsToDb(updatedDiffs);
|
|
||||||
- } catch (e) {
|
|
||||||
- throw e;
|
|
||||||
- }
|
|
||||||
- if (!isPaired) {
|
|
||||||
- await this.openPairingConfirmationModal(updatedDiffs);
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
-
|
|
||||||
- if (updatedProcess.validated_state) {
|
|
||||||
- const responsePrdReturn = this.sdkClient.create_response_prd(processId, updatedProcess.validated_state);
|
|
||||||
- await this.handleApiReturn(responsePrdReturn);
|
|
||||||
+ console.error('Failed to save diffs to db:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-
|
|
||||||
- if (apiReturn.commit_to_send) {
|
|
||||||
- const commit = apiReturn.commit_to_send;
|
|
||||||
- await this.sendCommitMessage(JSON.stringify(commit));
|
|
||||||
+ if (!isPaired) {
|
|
||||||
+ console.log(updatedProcess);
|
|
||||||
+ await this.openPairingConfirmationModal(updatedProcess.current_process.states[0]);
|
|
||||||
}
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if (apiReturn.ciphers_to_send && apiReturn.ciphers_to_send.length != 0) {
|
|
||||||
- await this.sendCipherMessages(apiReturn.ciphers_to_send);
|
|
||||||
- }
|
|
||||||
- }, 0);
|
|
||||||
- }
|
|
||||||
+ if (apiReturn.commit_to_send) {
|
|
||||||
+ const commit = apiReturn.commit_to_send;
|
|
||||||
+ await this.sendCommitMessage(JSON.stringify(commit));
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- public async openPairingConfirmationModal(diffs: UserDiff[]) {
|
|
||||||
- const rolesDiff = diffs.find((diff) => diff.field === 'roles');
|
|
||||||
- if (!rolesDiff) {
|
|
||||||
- throw new Error('Pairing process must have roles');
|
|
||||||
+ if (apiReturn.ciphers_to_send && apiReturn.ciphers_to_send.length != 0) {
|
|
||||||
+ await this.sendCipherMessages(apiReturn.ciphers_to_send);
|
|
||||||
}
|
|
||||||
- const processId = rolesDiff.process_id;
|
|
||||||
- const stateId = rolesDiff.state_id;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public async openPairingConfirmationModal(firstState: ProcessState) {
|
|
||||||
+ const roles = firstState.roles;
|
|
||||||
+ const processId = firstState.commited_in;
|
|
||||||
+ const stateId = firstState.state_id;
|
|
||||||
try {
|
|
||||||
- await this.routingInstance.openPairingConfirmationModal(rolesDiff.new_value, processId, stateId);
|
|
||||||
+ await this.routingInstance.openPairingConfirmationModal(roles, processId, stateId);
|
|
||||||
} catch (e) {
|
|
||||||
- throw new Error(`${e}`);
|
|
||||||
+ console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -804,7 +791,6 @@ export default class Services {
|
|
||||||
try {
|
|
||||||
this.sdkClient.roles_contains_us(JSON.stringify(roles));
|
|
||||||
} catch (e) {
|
|
||||||
- console.error(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -815,7 +801,6 @@ export default class Services {
|
|
||||||
try {
|
|
||||||
this.sdkClient.roles_contains_member(JSON.stringify(roles), member);
|
|
||||||
} catch (e) {
|
|
||||||
- console.error(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
203
process.diff
203
process.diff
@ -1,203 +0,0 @@
|
|||||||
diff --git a/src/pages/process/process.ts b/src/pages/process/process.ts
|
|
||||||
index 27c1301..a4f8943 100755
|
|
||||||
--- a/src/pages/process/process.ts
|
|
||||||
+++ b/src/pages/process/process.ts
|
|
||||||
@@ -2,9 +2,15 @@ import { addSubscription } from '../../utils/subscription.utils';
|
|
||||||
import Services from '../../services/service';
|
|
||||||
import { getCorrectDOM } from '~/utils/html.utils';
|
|
||||||
import { Process } from 'pkg/sdk_client';
|
|
||||||
+import chatStyle from '../../../public/style/chat.css?inline';
|
|
||||||
+import { Database } from '../../services/database.service';
|
|
||||||
+
|
|
||||||
+let myProcesses = new Set();
|
|
||||||
+let allProcesses = new Set();
|
|
||||||
|
|
||||||
// Initialize function, create initial tokens with itens that are already selected by the user
|
|
||||||
export async function init() {
|
|
||||||
+
|
|
||||||
const container = getCorrectDOM('process-list-4nk-component') as HTMLElement;
|
|
||||||
const element = container.querySelector('select') as HTMLSelectElement;
|
|
||||||
// Create div that wroaps all the elements inside (select, elements selected, search div) to put select inside
|
|
||||||
@@ -43,6 +49,23 @@ export async function init() {
|
|
||||||
wrapper.appendChild(search_div);
|
|
||||||
|
|
||||||
addPlaceholder(wrapper);
|
|
||||||
+
|
|
||||||
+ await loadAllProcesses();
|
|
||||||
+
|
|
||||||
+ const database = await Database.getInstance();
|
|
||||||
+
|
|
||||||
+ try {
|
|
||||||
+ await database.updateMyProcesses({ myProcessesId: Array.from(myProcesses) });
|
|
||||||
+ const updateProcesses = await database.updateMyProcesses({ myProcessesId: Array.from(myProcesses) });
|
|
||||||
+ console.log("UPDATE PROCESSES d'INIT: ", updateProcesses);
|
|
||||||
+ } catch (error) {
|
|
||||||
+ console.error("Error updating my processes:", error);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
}
|
|
||||||
|
|
||||||
function removePlaceholder(wrapper: HTMLElement) {
|
|
||||||
@@ -155,62 +178,39 @@ function clearAutocompleteList(select: HTMLSelectElement) {
|
|
||||||
if (autocomplete_list) autocomplete_list.innerHTML = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
-// Populate the autocomplete list following a given query from the user
|
|
||||||
-function populateAutocompleteList(select: HTMLSelectElement, query: string, dropdown = false) {
|
|
||||||
+async function populateAutocompleteList(select: HTMLSelectElement, query: string, dropdown = false) {
|
|
||||||
const { autocomplete_options } = getOptions(select);
|
|
||||||
|
|
||||||
- let options_to_show;
|
|
||||||
+ let options_to_show = [];
|
|
||||||
|
|
||||||
- if (dropdown) {
|
|
||||||
- let messagingCounter = 1;
|
|
||||||
- const messagingOptions = select.querySelectorAll('option[value="messaging"]');
|
|
||||||
-
|
|
||||||
- options_to_show = autocomplete_options.map(option => {
|
|
||||||
- if (option === 'messaging') {
|
|
||||||
- // Récupérer l'élément option correspondant au compteur actuel
|
|
||||||
- const currentOption = messagingOptions[messagingCounter - 1];
|
|
||||||
- const processId = currentOption?.getAttribute('data-process-id');
|
|
||||||
- console.log(`Mapping messaging ${messagingCounter} with processId:`, processId);
|
|
||||||
-
|
|
||||||
- const optionText = `messaging ${messagingCounter}`;
|
|
||||||
- messagingCounter++;
|
|
||||||
-
|
|
||||||
- // Stocker le processId dans un attribut data sur le select
|
|
||||||
- select.setAttribute(`data-messaging-id-${messagingCounter - 1}`, processId || '');
|
|
||||||
-
|
|
||||||
- return optionText;
|
|
||||||
- }
|
|
||||||
- return option;
|
|
||||||
- });
|
|
||||||
- } else {
|
|
||||||
- options_to_show = autocomplete(query, autocomplete_options);
|
|
||||||
- }
|
|
||||||
+ console.log(myProcesses);
|
|
||||||
+
|
|
||||||
+ const mineArray = Array.from(myProcesses);
|
|
||||||
+ const allArray = Array.from(allProcesses).filter(id => !myProcesses.has(id));
|
|
||||||
|
|
||||||
const wrapper = select.parentNode;
|
|
||||||
const input_search = wrapper?.querySelector('.search-container');
|
|
||||||
const autocomplete_list = wrapper?.querySelector('.autocomplete-list');
|
|
||||||
if (autocomplete_list) autocomplete_list.innerHTML = '';
|
|
||||||
- const result_size = options_to_show.length;
|
|
||||||
|
|
||||||
- if (result_size == 1) {
|
|
||||||
+ const addProcessToList = (processId:string, isMine: boolean) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
- li.innerText = options_to_show[0];
|
|
||||||
- li.setAttribute('data-value', options_to_show[0]);
|
|
||||||
+ li.innerText = processId;
|
|
||||||
+ li.setAttribute("data-value", processId);
|
|
||||||
+
|
|
||||||
+ if (isMine) {
|
|
||||||
+ li.classList.add("my-process");
|
|
||||||
+ li.style.cssText = `color: var(--accent-color)`;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if (li) addSubscription(li, 'click', selectOption);
|
|
||||||
autocomplete_list?.appendChild(li);
|
|
||||||
- if (query.length == options_to_show[0].length) {
|
|
||||||
- const event = new Event('click');
|
|
||||||
- li.dispatchEvent(event);
|
|
||||||
- }
|
|
||||||
- } else if (result_size > 1) {
|
|
||||||
- for (let i = 0; i < result_size; i++) {
|
|
||||||
- const li = document.createElement('li');
|
|
||||||
- li.innerText = options_to_show[i];
|
|
||||||
- li.setAttribute('data-value', options_to_show[i]);
|
|
||||||
- if (li) addSubscription(li, 'click', selectOption);
|
|
||||||
- autocomplete_list?.appendChild(li);
|
|
||||||
- }
|
|
||||||
- } else {
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ mineArray.forEach(processId => addProcessToList(processId, true));
|
|
||||||
+ allArray.forEach(processId => addProcessToList(processId, false));
|
|
||||||
+
|
|
||||||
+ if (myProcesses.size === 0 && allProcesses.size === 0) {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.classList.add('not-cursor');
|
|
||||||
li.innerText = 'No options found';
|
|
||||||
@@ -392,6 +392,20 @@ addSubscription(document, 'click', () => {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
+async function loadAllProcesses() {
|
|
||||||
+ try {
|
|
||||||
+ const [allProcessesNew, myProcessesNew] = await Promise.all([
|
|
||||||
+ getProcesses(),
|
|
||||||
+ getMyProcesses()
|
|
||||||
+ ]);
|
|
||||||
+
|
|
||||||
+ myProcesses = myProcesses.union(myProcessesNew);
|
|
||||||
+ allProcesses = allProcesses.union(allProcessesNew);
|
|
||||||
+ } catch (error) {
|
|
||||||
+ console.error("Error loading processes:", error);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
async function showSelectedProcess(elem: MouseEvent) {
|
|
||||||
const container = getCorrectDOM('process-list-4nk-component') as HTMLElement;
|
|
||||||
|
|
||||||
@@ -539,43 +553,36 @@ async function getDescription(processId: string, process: Process): Promise<stri
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
-async function getProcesses(): Promise<any[]> {
|
|
||||||
+async function getProcesses(): Promise<Set<string>> {
|
|
||||||
const service = await Services.getInstance();
|
|
||||||
const processes = await service.getProcesses();
|
|
||||||
+ const processIds = new Set<string>(Object.keys(processes));
|
|
||||||
|
|
||||||
- const res = Object.entries(processes).map(([key, value]) => ({
|
|
||||||
- key,
|
|
||||||
- value,
|
|
||||||
- }));
|
|
||||||
-
|
|
||||||
- return res;
|
|
||||||
+ return processIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
-async function getMyProcesses() {
|
|
||||||
+async function getMyProcesses(): Promise<Set<string>> {
|
|
||||||
const service = await Services.getInstance();
|
|
||||||
try {
|
|
||||||
const processes = await service.getProcesses();
|
|
||||||
- const userProcessSet = new Set();
|
|
||||||
-
|
|
||||||
+ const userProcessSet = new Set<string>();
|
|
||||||
+
|
|
||||||
for (const [processId, process] of Object.entries(processes)) {
|
|
||||||
let roles;
|
|
||||||
try {
|
|
||||||
- roles = await this.getRoles(process);
|
|
||||||
+ roles = await service.getRoles(process);
|
|
||||||
if (!roles) {
|
|
||||||
roles = await process.states[0].encrypted_pcd.roles;
|
|
||||||
}
|
|
||||||
+ console.log("ROLES: ", roles);
|
|
||||||
+
|
|
||||||
+ const hasCurrentUser = service.rolesContainsUs(roles);
|
|
||||||
|
|
||||||
- const hasCurrentUser = Object.values(roles).some(role =>
|
|
||||||
- service.rolesContainsUs(role)
|
|
||||||
- );
|
|
||||||
-
|
|
||||||
if (hasCurrentUser) {
|
|
||||||
userProcessSet.add(processId);
|
|
||||||
}
|
|
||||||
-
|
|
||||||
} catch (e) {
|
|
||||||
continue;
|
|
||||||
- console.error(`Error processing process ${processId}:`, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -599,7 +599,7 @@ body {
|
|||||||
margin-top: 9vh;
|
margin-top: 9vh;
|
||||||
margin-left: -1%;
|
margin-left: -1%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 209vh;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Liste des information sur l'account */
|
/* Liste des information sur l'account */
|
||||||
@ -1364,3 +1364,62 @@ body {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------------------Style pour le QR code--------------------- */
|
||||||
|
|
||||||
|
.qr-code {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code:hover {
|
||||||
|
transform: scale(1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-modal-content {
|
||||||
|
background-color: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-qr-modal {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 5px;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-qr-modal:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-large {
|
||||||
|
max-width: 300px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-address {
|
||||||
|
margin-top: 10px;
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
@ -186,6 +186,28 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.group-list .member-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-list .member-container button {
|
||||||
|
margin-left: 40px;
|
||||||
|
padding: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
border: 0px solid var(--primary-color);
|
||||||
|
border-radius: 50px;
|
||||||
|
position: absolute;
|
||||||
|
top: -25px;
|
||||||
|
right: -25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-list .member-container button:hover {
|
||||||
|
background: var(--accent-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Zone de chat */
|
/* Zone de chat */
|
||||||
.chat-area {
|
.chat-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -553,3 +575,23 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--secondary-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--accent-color);
|
||||||
|
}
|
40
src/4nk.css
40
src/4nk.css
@ -316,7 +316,7 @@ h1 {
|
|||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sp-address-btn {
|
.create-btn {
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #d0d0d7;
|
background-color: #d0d0d7;
|
||||||
@ -778,3 +778,41 @@ select[data-multi-select-plugin] {
|
|||||||
.process-card-action {
|
.process-card-action {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************************** Select Member Home Page ******************************************************/
|
||||||
|
.custom-select {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 150px;
|
||||||
|
overflow-y: auto;
|
||||||
|
direction: ltr;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select option {
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select option:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select::-webkit-scrollbar-track {
|
||||||
|
background: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select::-webkit-scrollbar-thumb {
|
||||||
|
background: #888;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
@ -29,7 +29,7 @@
|
|||||||
<a onclick="navigate('chat')">Chat</a>
|
<a onclick="navigate('chat')">Chat</a>
|
||||||
<a onclick="navigate('signature')">Signatures</a>
|
<a onclick="navigate('signature')">Signatures</a>
|
||||||
<a onclick="navigate('process')">Process</a>
|
<a onclick="navigate('process')">Process</a>
|
||||||
<a onclick="navigate('home')">Disconnect</a>
|
<a onclick="disconnect()">Disconnect</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -181,3 +181,40 @@ async function createBackUp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(window as any).createBackUp = createBackUp;
|
(window as any).createBackUp = createBackUp;
|
||||||
|
|
||||||
|
async function disconnect() {
|
||||||
|
console.log('Disconnecting...');
|
||||||
|
try {
|
||||||
|
localStorage.clear();
|
||||||
|
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
const request = indexedDB.deleteDatabase('4nk');
|
||||||
|
request.onsuccess = () => {
|
||||||
|
console.log('IndexedDB deleted successfully');
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
request.onerror = () => reject(request.error);
|
||||||
|
request.onblocked = () => {
|
||||||
|
console.log('Database deletion was blocked');
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||||
|
await Promise.all(registrations.map(registration => registration.unregister()));
|
||||||
|
console.log('Service worker unregistered');
|
||||||
|
|
||||||
|
navigate('home');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = window.location.origin;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error during disconnect:', error);
|
||||||
|
// force reload
|
||||||
|
window.location.href = window.location.origin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(window as any).disconnect = disconnect;
|
14
src/components/modal/creation-modal.html
Normal file
14
src/components/modal/creation-modal.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<div id="creation-modal" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-title">Login</div>
|
||||||
|
<div class="message">
|
||||||
|
Do you want to create a 4NK member?<br />
|
||||||
|
Attempting to create a member with address <br />
|
||||||
|
<strong>{{device1}}</strong> <br />
|
||||||
|
</div>
|
||||||
|
<div class="confirmation-box">
|
||||||
|
<a class="btn confirmation-btn" onclick="confirm()">Confirm</a>
|
||||||
|
<a class="btn refusal-btn" onclick="closeConfirmationModal()">Refuse</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -2,7 +2,7 @@ declare global {
|
|||||||
interface Window {
|
interface Window {
|
||||||
initAccount: () => void;
|
initAccount: () => void;
|
||||||
showContractPopup: (contractId: string) => void;
|
showContractPopup: (contractId: string) => void;
|
||||||
showPairing: () => void;
|
showPairing: () => Promise<void>;
|
||||||
showWallet: () => void;
|
showWallet: () => void;
|
||||||
showData: () => void;
|
showData: () => void;
|
||||||
addWalletRow: () => void;
|
addWalletRow: () => void;
|
||||||
@ -36,6 +36,7 @@ declare global {
|
|||||||
generateRecoveryWords: () => string[];
|
generateRecoveryWords: () => string[];
|
||||||
exportUserData: () => void;
|
exportUserData: () => void;
|
||||||
updateActionButtons: () => void;
|
updateActionButtons: () => void;
|
||||||
|
showQRCodeModal: (address: string) => void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ import { Row, WalletRow, DataRow, Notification, Contract, NotificationMessage }
|
|||||||
import { addressToEmoji } from '../../utils/sp-address.utils';
|
import { addressToEmoji } from '../../utils/sp-address.utils';
|
||||||
import { getCorrectDOM } from '../../utils/document.utils';
|
import { getCorrectDOM } from '../../utils/document.utils';
|
||||||
import accountStyle from '../../../public/style/account.css?inline';
|
import accountStyle from '../../../public/style/account.css?inline';
|
||||||
|
import Services from '../../services/service';
|
||||||
|
|
||||||
let isAddingRow = false;
|
let isAddingRow = false;
|
||||||
let currentRow: HTMLTableRowElement | null = null;
|
let currentRow: HTMLTableRowElement | null = null;
|
||||||
@ -197,6 +199,7 @@ class AccountElement extends HTMLElement {
|
|||||||
window.updateActionButtons = () => this.updateActionButtons();
|
window.updateActionButtons = () => this.updateActionButtons();
|
||||||
window.openAvatarPopup = () => this.openAvatarPopup();
|
window.openAvatarPopup = () => this.openAvatarPopup();
|
||||||
window.closeAvatarPopup = () => this.closeAvatarPopup();
|
window.closeAvatarPopup = () => this.closeAvatarPopup();
|
||||||
|
window.showQRCodeModal = (address: string) => this.showQRCodeModal(address);
|
||||||
|
|
||||||
if (!localStorage.getItem('rows')) {
|
if (!localStorage.getItem('rows')) {
|
||||||
localStorage.setItem('rows', JSON.stringify(defaultRows));
|
localStorage.setItem('rows', JSON.stringify(defaultRows));
|
||||||
@ -552,6 +555,13 @@ private updateTableContent(rows: Row[]): void {
|
|||||||
<td>${row.column1}</td>
|
<td>${row.column1}</td>
|
||||||
<td class="device-name" onclick="window.editDeviceName(this)">${row.column2}</td>
|
<td class="device-name" onclick="window.editDeviceName(this)">${row.column2}</td>
|
||||||
<td>${row.column3}</td>
|
<td>${row.column3}</td>
|
||||||
|
<td>
|
||||||
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=50x50&data=${encodeURIComponent(row.column1)}"
|
||||||
|
alt="QR Code"
|
||||||
|
title="${row.column1}"
|
||||||
|
class="qr-code"
|
||||||
|
onclick="window.showQRCodeModal('${encodeURIComponent(row.column1)}')">
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="delete-button" onclick="window.deleteRow(this)">
|
<button class="delete-button" onclick="window.deleteRow(this)">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16" fill="red">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16" fill="red">
|
||||||
@ -627,29 +637,26 @@ private deleteRow(button: HTMLButtonElement): void {
|
|||||||
const table = row.closest('tbody');
|
const table = row.closest('tbody');
|
||||||
if (!table) return;
|
if (!table) return;
|
||||||
|
|
||||||
// Vérifier le nombre de lignes restantes
|
|
||||||
const remainingRows = table.getElementsByTagName('tr').length;
|
const remainingRows = table.getElementsByTagName('tr').length;
|
||||||
if (remainingRows <= 2) {
|
if (remainingRows <= 2) {
|
||||||
this.showAlert('You must keep at least 2 devices paired');
|
this.showAlert('You must keep at least 2 devices paired');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animation de suppression
|
const index = Array.from(table.children).indexOf(row);
|
||||||
row.style.transition = 'opacity 0.3s';
|
row.style.transition = 'opacity 0.3s, transform 0.3s';
|
||||||
row.style.opacity = '0';
|
row.style.opacity = '0';
|
||||||
|
row.style.transform = 'translateX(-100%)';
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Obtenir l'index avant la suppression
|
|
||||||
const index = Array.from(table.children).indexOf(row);
|
|
||||||
|
|
||||||
// Supprimer la ligne du DOM
|
|
||||||
row.remove();
|
row.remove();
|
||||||
|
|
||||||
// Mettre à jour le localStorage
|
|
||||||
const storageKey = STORAGE_KEYS[currentMode];
|
const storageKey = STORAGE_KEYS[currentMode];
|
||||||
const rows = JSON.parse(localStorage.getItem(storageKey) || '[]');
|
const rows = JSON.parse(localStorage.getItem(storageKey) || '[]');
|
||||||
|
if (index > -1) {
|
||||||
rows.splice(index, 1);
|
rows.splice(index, 1);
|
||||||
localStorage.setItem(storageKey, JSON.stringify(rows));
|
localStorage.setItem(storageKey, JSON.stringify(rows));
|
||||||
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -887,9 +894,6 @@ private showContractPopup(contractId: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ajouter à l'objet window
|
|
||||||
|
|
||||||
|
|
||||||
// Fonction utilitaire pour cacher tous les contenus
|
// Fonction utilitaire pour cacher tous les contenus
|
||||||
private hideAllContent(): void {
|
private hideAllContent(): void {
|
||||||
const contents = ['pairing-content', 'wallet-content', 'process-content', 'data-content'];
|
const contents = ['pairing-content', 'wallet-content', 'process-content', 'data-content'];
|
||||||
@ -902,25 +906,22 @@ private hideAllContent(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fonctions d'affichage des sections
|
// Fonctions d'affichage des sections
|
||||||
private showPairing(): void {
|
private async showPairing(): Promise<void> {
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
const spAddress = await service.getDeviceAddress();
|
||||||
|
|
||||||
isAddingRow = false;
|
isAddingRow = false;
|
||||||
currentRow = null;
|
currentRow = null;
|
||||||
|
|
||||||
currentMode = 'pairing';
|
currentMode = 'pairing';
|
||||||
// Cacher tous les contenus
|
|
||||||
this.hideAllContent();
|
this.hideAllContent();
|
||||||
|
|
||||||
// Mettre à jour le titre
|
|
||||||
const headerElement = this.shadowRoot?.getElementById('parameter-header');
|
const headerElement = this.shadowRoot?.getElementById('parameter-header');
|
||||||
if (headerElement) {
|
if (headerElement) {
|
||||||
headerElement.textContent = 'Pairing';
|
headerElement.textContent = 'Pairing';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Afficher le contenu de pairing
|
|
||||||
const pairingContent = this.shadowRoot?.getElementById('pairing-content');
|
const pairingContent = this.shadowRoot?.getElementById('pairing-content');
|
||||||
|
|
||||||
if (pairingContent) {
|
if (pairingContent) {
|
||||||
pairingContent.style.display = 'block';
|
pairingContent.style.display = 'block';
|
||||||
pairingContent.innerHTML = `
|
pairingContent.innerHTML = `
|
||||||
@ -932,7 +933,8 @@ private showPairing(): void {
|
|||||||
<th>SP Address</th>
|
<th>SP Address</th>
|
||||||
<th>Device Name</th>
|
<th>Device Name</th>
|
||||||
<th>SP Emojis</th>
|
<th>SP Emojis</th>
|
||||||
<th></th>
|
<th>QR Code</th>
|
||||||
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
@ -943,8 +945,46 @@ private showPairing(): void {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Mettre à jour le contenu du tableau
|
let rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.pairing) || '[]');
|
||||||
const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.pairing) || '[]');
|
|
||||||
|
const deviceExists = rows.some((row: Row) => row.column1 === spAddress);
|
||||||
|
|
||||||
|
if (!deviceExists && spAddress) {
|
||||||
|
const emojis = await addressToEmoji(spAddress);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Déboguer le processus de pairing
|
||||||
|
const pairingProcessId = await service.getPairingProcessId();
|
||||||
|
console.log('Pairing Process ID:', pairingProcessId);
|
||||||
|
|
||||||
|
const pairingProcess = await service.getProcess(pairingProcessId);
|
||||||
|
console.log('Pairing Process:', pairingProcess);
|
||||||
|
|
||||||
|
const userName = pairingProcess?.states?.[0]?.metadata?.userName
|
||||||
|
|| pairingProcess?.states?.[0]?.metadata?.name
|
||||||
|
|| localStorage.getItem('userName')
|
||||||
|
|
||||||
|
console.log('Username found:', userName);
|
||||||
|
|
||||||
|
const newRow = {
|
||||||
|
column1: spAddress,
|
||||||
|
column2: userName,
|
||||||
|
column3: emojis
|
||||||
|
};
|
||||||
|
rows = [newRow, ...rows];
|
||||||
|
localStorage.setItem(STORAGE_KEYS.pairing, JSON.stringify(rows));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting pairing process:', error);
|
||||||
|
const newRow = {
|
||||||
|
column1: spAddress,
|
||||||
|
column2: 'This Device',
|
||||||
|
column3: emojis
|
||||||
|
};
|
||||||
|
rows = [newRow, ...rows];
|
||||||
|
localStorage.setItem(STORAGE_KEYS.pairing, JSON.stringify(rows));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.updateTableContent(rows);
|
this.updateTableContent(rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1419,6 +1459,28 @@ private initializeEventListeners() {
|
|||||||
avatarInput.addEventListener('change', this.handleAvatarUpload.bind(this));
|
avatarInput.addEventListener('change', this.handleAvatarUpload.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private showQRCodeModal(address: string): void {
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.className = 'qr-modal';
|
||||||
|
modal.innerHTML = `
|
||||||
|
<div class="qr-modal-content">
|
||||||
|
<span class="close-qr-modal">×</span>
|
||||||
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${address}"
|
||||||
|
alt="QR Code Large"
|
||||||
|
class="qr-code-large">
|
||||||
|
<div class="qr-address">${decodeURIComponent(address)}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.shadowRoot?.appendChild(modal);
|
||||||
|
|
||||||
|
const closeBtn = modal.querySelector('.close-qr-modal');
|
||||||
|
closeBtn?.addEventListener('click', () => modal.remove());
|
||||||
|
modal.addEventListener('click', (e) => {
|
||||||
|
if (e.target === modal) modal.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('account-element', AccountElement);
|
customElements.define('account-element', AccountElement);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,24 +4,23 @@
|
|||||||
|
|
||||||
<div class="tab-container">
|
<div class="tab-container">
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<div class="tab active" data-tab="tab1">Scan QR Code</div>
|
<div class="tab active" data-tab="tab1">Create an account</div>
|
||||||
<div class="tab" data-tab="tab2">Scan other device</div>
|
<div class="tab" data-tab="tab2">Add a device for an existing memeber</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div id="tab1" class="card tab-content active">
|
<div id="tab1" class="card tab-content active">
|
||||||
<div class="card-description">Scan with your other device :</div>
|
<div class="card-description">Create an account :</div>
|
||||||
<div class="pairing-request"></div>
|
<div class="pairing-request"></div>
|
||||||
<div class="card-image qr-code">
|
<!-- <div class="card-image qr-code">
|
||||||
<img src="assets/qr_code.png" alt="QR Code" width="150" height="150" />
|
<img src="assets/qr_code.png" alt="QR Code" width="150" height="150" />
|
||||||
</div>
|
</div> -->
|
||||||
<button id="copyBtn" class="sp-address-btn"></button>
|
<button id="createButton" class="create-btn"></button>
|
||||||
<div class="card-image emoji-display" id="emoji-display"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<div id="tab2" class="card tab-content">
|
<div id="tab2" class="card tab-content">
|
||||||
<div class="card-description">Scan your other device :</div>
|
<div class="card-description">Add a device for an existing member :</div>
|
||||||
<div class="card-image camera-card">
|
<div class="card-image camera-card">
|
||||||
<img id="scanner" src="assets/camera.jpg" alt="QR Code" width="150" height="150" />
|
<img id="scanner" src="assets/camera.jpg" alt="QR Code" width="150" height="150" />
|
||||||
<button id="scan-btn" onclick="scanDevice()">Scan</button>
|
<button id="scan-btn" onclick="scanDevice()">Scan</button>
|
||||||
@ -31,8 +30,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Or</p>
|
<p>Or</p>
|
||||||
<input type="text" id="addressInput" placeholder="Paste address" />
|
<!-- <input type="text" id="addressInput" placeholder="Paste address" />
|
||||||
<div id="emoji-display-2"></div>
|
<div id="emoji-display-2"></div> -->
|
||||||
|
<div class="card-description">Chose a member :</div>
|
||||||
|
<select name="memberSelect" id="memberSelect" size="5" class="custom-select">
|
||||||
|
<!-- Options -->
|
||||||
|
</select>
|
||||||
|
|
||||||
<button id="okButton" style="display: none">OK</button>
|
<button id="okButton" style="display: none">OK</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Routing from '../../services/modal.service';
|
import Routing from '../../services/modal.service';
|
||||||
import Services from '../../services/service';
|
import Services from '../../services/service';
|
||||||
import { addSubscription } from '../../utils/subscription.utils';
|
import { addSubscription } from '../../utils/subscription.utils';
|
||||||
import { displayEmojis, generateQRCode } from '../../utils/sp-address.utils';
|
import { displayEmojis, generateQRCode, generateCreateBtn, addressToEmoji } from '../../utils/sp-address.utils';
|
||||||
import { getCorrectDOM } from '../../utils/html.utils';
|
import { getCorrectDOM } from '../../utils/html.utils';
|
||||||
import QrScannerComponent from '../../components/qrcode-scanner/qrcode-scanner-component';
|
import QrScannerComponent from '../../components/qrcode-scanner/qrcode-scanner-component';
|
||||||
export { QrScannerComponent };
|
export { QrScannerComponent };
|
||||||
@ -20,8 +20,12 @@ 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();
|
||||||
generateQRCode(spAddress);
|
// generateQRCode(spAddress);
|
||||||
|
generateCreateBtn ();
|
||||||
displayEmojis(spAddress);
|
displayEmojis(spAddress);
|
||||||
|
|
||||||
|
// Add this line to populate the select when the page loads
|
||||||
|
await populateMemberSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
//// Modal
|
//// Modal
|
||||||
@ -45,4 +49,46 @@ function scanDevice() {
|
|||||||
if (reader) reader.innerHTML = '<qr-scanner></qr-scanner>';
|
if (reader) reader.innerHTML = '<qr-scanner></qr-scanner>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function populateMemberSelect() {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const memberSelect = container.querySelector('#memberSelect') as HTMLSelectElement;
|
||||||
|
|
||||||
|
if (!memberSelect) {
|
||||||
|
console.error('Could not find memberSelect element');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
const members = service.getAllMembersSorted();
|
||||||
|
|
||||||
|
for (const [processId, member] of Object.entries(members)) {
|
||||||
|
const process = await service.getProcess(processId);
|
||||||
|
let memberPublicName;
|
||||||
|
|
||||||
|
if (process) {
|
||||||
|
const publicMemberData = service.getPublicData(process);
|
||||||
|
if (publicMemberData) {
|
||||||
|
const extractedName = publicMemberData['memberPublicName'];
|
||||||
|
if (extractedName !== undefined && extractedName !== null) {
|
||||||
|
memberPublicName = extractedName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!memberPublicName) {
|
||||||
|
memberPublicName = 'Unnamed Member';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer les emojis pour ce processId
|
||||||
|
const emojis = await addressToEmoji(processId);
|
||||||
|
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = processId;
|
||||||
|
option.textContent = `${memberPublicName} (${emojis})`;
|
||||||
|
memberSelect.appendChild(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(window as any).populateMemberSelect = populateMemberSelect;
|
||||||
|
|
||||||
(window as any).scanDevice = scanDevice;
|
(window as any).scanDevice = scanDevice;
|
||||||
|
@ -5,9 +5,6 @@ import { Process } from 'pkg/sdk_client';
|
|||||||
import chatStyle from '../../../public/style/chat.css?inline';
|
import chatStyle from '../../../public/style/chat.css?inline';
|
||||||
import { Database } from '../../services/database.service';
|
import { Database } from '../../services/database.service';
|
||||||
|
|
||||||
let myProcesses = new Set();
|
|
||||||
let allProcesses = new Set();
|
|
||||||
|
|
||||||
// Initialize function, create initial tokens with itens that are already selected by the user
|
// Initialize function, create initial tokens with itens that are already selected by the user
|
||||||
export async function init() {
|
export async function init() {
|
||||||
|
|
||||||
@ -49,16 +46,6 @@ export async function init() {
|
|||||||
wrapper.appendChild(search_div);
|
wrapper.appendChild(search_div);
|
||||||
|
|
||||||
addPlaceholder(wrapper);
|
addPlaceholder(wrapper);
|
||||||
|
|
||||||
await loadAllProcesses();
|
|
||||||
|
|
||||||
const database = await Database.getInstance();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await database.updateMyProcesses({ myProcessesId: Array.from(myProcesses) });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating my processes:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removePlaceholder(wrapper: HTMLElement) {
|
function removePlaceholder(wrapper: HTMLElement) {
|
||||||
@ -176,10 +163,10 @@ async function populateAutocompleteList(select: HTMLSelectElement, query: string
|
|||||||
|
|
||||||
let options_to_show = [];
|
let options_to_show = [];
|
||||||
|
|
||||||
console.log(myProcesses);
|
const service = await Services.getInstance();
|
||||||
|
const mineArray: string[] = await service.getMyProcesses();
|
||||||
const mineArray = Array.from(myProcesses);
|
const allProcesses = await service.getProcesses();
|
||||||
const allArray = Array.from(allProcesses).filter(id => !myProcesses.has(id));
|
const allArray: string[] = Object.keys(allProcesses).filter(x => !mineArray.includes(x));
|
||||||
|
|
||||||
const wrapper = select.parentNode;
|
const wrapper = select.parentNode;
|
||||||
const input_search = wrapper?.querySelector('.search-container');
|
const input_search = wrapper?.querySelector('.search-container');
|
||||||
@ -203,7 +190,7 @@ async function populateAutocompleteList(select: HTMLSelectElement, query: string
|
|||||||
mineArray.forEach(processId => addProcessToList(processId, true));
|
mineArray.forEach(processId => addProcessToList(processId, true));
|
||||||
allArray.forEach(processId => addProcessToList(processId, false));
|
allArray.forEach(processId => addProcessToList(processId, false));
|
||||||
|
|
||||||
if (myProcesses.size === 0 && allProcesses.size === 0) {
|
if (mineArray.length === 0 && allArray.length === 0) {
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.classList.add('not-cursor');
|
li.classList.add('not-cursor');
|
||||||
li.innerText = 'No options found';
|
li.innerText = 'No options found';
|
||||||
@ -385,20 +372,6 @@ addSubscription(document, 'click', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadAllProcesses() {
|
|
||||||
try {
|
|
||||||
const [allProcessesNew, myProcessesNew] = await Promise.all([
|
|
||||||
getProcesses(),
|
|
||||||
getMyProcesses()
|
|
||||||
]);
|
|
||||||
|
|
||||||
myProcesses = myProcesses.union(myProcessesNew);
|
|
||||||
allProcesses = allProcesses.union(allProcessesNew);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error loading processes:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showSelectedProcess(elem: MouseEvent) {
|
async function showSelectedProcess(elem: MouseEvent) {
|
||||||
const container = getCorrectDOM('process-list-4nk-component') as HTMLElement;
|
const container = getCorrectDOM('process-list-4nk-component') as HTMLElement;
|
||||||
|
|
||||||
@ -506,7 +479,7 @@ async function createMessagingProcess(): Promise<void> {
|
|||||||
await service.handleApiReturn(createProcessReturn);
|
await service.handleApiReturn(createProcessReturn);
|
||||||
const createPrdReturn = await service.createPrdUpdate(processId, stateId);
|
const createPrdReturn = await service.createPrdUpdate(processId, stateId);
|
||||||
await service.handleApiReturn(createPrdReturn);
|
await service.handleApiReturn(createPrdReturn);
|
||||||
const approveChangeReturn = service.approveChange(processId, stateId);
|
const approveChangeReturn = await service.approveChange(processId, stateId);
|
||||||
await service.handleApiReturn(approveChangeReturn);
|
await service.handleApiReturn(approveChangeReturn);
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
@ -545,40 +518,3 @@ async function getDescription(processId: string, process: Process): Promise<stri
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getProcesses(): Promise<Set<string>> {
|
|
||||||
const service = await Services.getInstance();
|
|
||||||
const processes = await service.getProcesses();
|
|
||||||
const processIds = new Set<string>(Object.keys(processes));
|
|
||||||
|
|
||||||
return processIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getMyProcesses(): Promise<Set<string>> {
|
|
||||||
const service = await Services.getInstance();
|
|
||||||
try {
|
|
||||||
const processes = await service.getProcesses();
|
|
||||||
const userProcessSet = new Set<string>();
|
|
||||||
|
|
||||||
for (const [processId, process] of Object.entries(processes)) {
|
|
||||||
let roles;
|
|
||||||
try {
|
|
||||||
roles = await service.getRoles(process);
|
|
||||||
// console.log("ROLES: ", roles);
|
|
||||||
|
|
||||||
const hasCurrentUser = service.rolesContainsUs(roles);
|
|
||||||
|
|
||||||
if (hasCurrentUser) {
|
|
||||||
userProcessSet.add(processId);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return userProcessSet;
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Failed to get processes:", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -148,7 +148,7 @@ export async function init(): Promise<void> {
|
|||||||
await services.restoreSecretsFromDB();
|
await services.restoreSecretsFromDB();
|
||||||
|
|
||||||
if (services.isPaired()) {
|
if (services.isPaired()) {
|
||||||
await navigate('process');
|
await navigate('chat');
|
||||||
} else {
|
} else {
|
||||||
const queryString = window.location.search;
|
const queryString = window.location.search;
|
||||||
const urlParams = new URLSearchParams(queryString);
|
const urlParams = new URLSearchParams(queryString);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
let processesToScan = new Set();
|
const EMPTY32BYTES = String('').padStart(64, '0');
|
||||||
let toDownload = [];
|
|
||||||
|
|
||||||
self.addEventListener('install', (event) => {
|
self.addEventListener('install', (event) => {
|
||||||
event.waitUntil(self.skipWaiting()); // Activate worker immediately
|
event.waitUntil(self.skipWaiting()); // Activate worker immediately
|
||||||
@ -12,71 +11,24 @@ self.addEventListener('activate', (event) => {
|
|||||||
// Event listener for messages from clients
|
// Event listener for messages from clients
|
||||||
self.addEventListener('message', async (event) => {
|
self.addEventListener('message', async (event) => {
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
if (data.type === 'START') {
|
console.log(data);
|
||||||
const fetchNotifications = async () => {
|
|
||||||
const itemsWithFlag = await getAllItemsWithFlag();
|
|
||||||
|
|
||||||
// Process items with the specific flag
|
if (data.type === 'SCAN') {
|
||||||
itemsWithFlag?.forEach((item) => {
|
|
||||||
console.log(item); // Do something with each flagged item
|
|
||||||
});
|
|
||||||
|
|
||||||
event.ports[0].postMessage({
|
|
||||||
type: 'NOTIFICATIONS',
|
|
||||||
data: itemsWithFlag,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const scanMissingData = async () => {
|
|
||||||
console.log('Scanning for missing data...');
|
|
||||||
const myProcesses = getProcesses(processesToScan);
|
|
||||||
|
|
||||||
let toDownload = [];
|
|
||||||
// Iterate on each process
|
|
||||||
for (const process of myProcesses) {
|
|
||||||
console.log(process);
|
|
||||||
// Iterate on states
|
|
||||||
for (const state of process.states) {
|
|
||||||
console.log(state);
|
|
||||||
// iterate on pcd_commitment
|
|
||||||
for (const hash of state.pcd_commitment) {
|
|
||||||
console.log(hash);
|
|
||||||
toDownload.push(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event.ports[0].postMessage({
|
|
||||||
type: 'TO_DOWNLOAD',
|
|
||||||
data: toDownload,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
fetchNotifications();
|
|
||||||
setInterval(fetchNotifications, 2 * 60 * 1000);
|
|
||||||
scanMissingData();
|
|
||||||
setInterval(scanMissingData, 2 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.type === 'UPDATE_PROCESSES') {
|
|
||||||
try {
|
try {
|
||||||
const { myProcessesId } = data.payload;
|
const myProcessesId = data.payload;
|
||||||
if (myProcessesId && myProcessesId.length != 0) {
|
if (myProcessesId && myProcessesId.length != 0) {
|
||||||
for (const processId of myProcessesId) {
|
const toDownload = await scanMissingData(myProcessesId);
|
||||||
if (processesToScan) {
|
if (toDownload.length != 0) {
|
||||||
processesToScan.add(processId);
|
console.log('Sending TO_DOWNLOAD message');
|
||||||
} else {
|
event.source.postMessage({ type: 'TO_DOWNLOAD', data: toDownload});
|
||||||
throw new Error('No processesToScan');
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
console.log(processesToScan);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Empty processIds list');
|
event.source.postMessage({ status: 'error', message: 'Empty lists' });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.ports[0].postMessage({ status: 'error', message: error.message });
|
event.source.postMessage({ status: 'error', message: error.message });
|
||||||
}
|
}
|
||||||
}
|
} else if (data.type === 'ADD_OBJECT') {
|
||||||
|
|
||||||
if (data.type === 'ADD_OBJECT') {
|
|
||||||
try {
|
try {
|
||||||
const { storeName, object, key } = data.payload;
|
const { storeName, object, key } = data.payload;
|
||||||
const db = await openDatabase();
|
const db = await openDatabase();
|
||||||
@ -96,6 +48,44 @@ self.addEventListener('message', async (event) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function scanMissingData(processesToScan) {
|
||||||
|
console.log('Scanning for missing data...');
|
||||||
|
const myProcesses = await getProcesses(processesToScan);
|
||||||
|
|
||||||
|
let toDownload = new Set();
|
||||||
|
// Iterate on each process
|
||||||
|
if (myProcesses && myProcesses.length != 0) {
|
||||||
|
for (const process of myProcesses) {
|
||||||
|
// Iterate on states
|
||||||
|
const firstState = process.states[0];
|
||||||
|
const processId = firstState.commited_in;
|
||||||
|
for (const state of process.states) {
|
||||||
|
if (state.state_id === EMPTY32BYTES) continue;
|
||||||
|
// iterate on pcd_commitment
|
||||||
|
for (const [field, hash] of Object.entries(state.pcd_commitment)) {
|
||||||
|
// Skip public fields
|
||||||
|
if (state.public_data[field] !== undefined || field === 'roles') continue;
|
||||||
|
// Check if we have the data in db
|
||||||
|
const existingData = await getBlob(hash);
|
||||||
|
if (!existingData) {
|
||||||
|
toDownload.add(hash);
|
||||||
|
// We also add an entry in diff, in case it doesn't already exist
|
||||||
|
await addDiff(processId, state.state_id, hash, state.roles, field);
|
||||||
|
} else {
|
||||||
|
// We remove it if we have it in the set
|
||||||
|
if (toDownload.delete(hash)) {
|
||||||
|
console.log(`Removing ${hash} from the set`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(toDownload);
|
||||||
|
return Array.from(toDownload);
|
||||||
|
}
|
||||||
|
|
||||||
async function openDatabase() {
|
async function openDatabase() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const request = indexedDB.open('4nk', 1);
|
const request = indexedDB.open('4nk', 1);
|
||||||
@ -149,7 +139,7 @@ async function getProcesses(processIds) {
|
|||||||
const tx = db.transaction('processes', 'readonly');
|
const tx = db.transaction('processes', 'readonly');
|
||||||
const store = tx.objectStore('processes');
|
const store = tx.objectStore('processes');
|
||||||
|
|
||||||
const requests = processIds.map((processId) => {
|
const requests = Array.from(processIds).map((processId) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const request = store.get(processId);
|
const request = store.get(processId);
|
||||||
request.onsuccess = () => resolve(request.result);
|
request.onsuccess = () => resolve(request.result);
|
||||||
@ -221,3 +211,56 @@ async function getAllDiffsNeedValidation() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getBlob(hash) {
|
||||||
|
const db = await openDatabase();
|
||||||
|
const storeName = 'data';
|
||||||
|
const tx = db.transaction(storeName, 'readonly');
|
||||||
|
const store = tx.objectStore(storeName);
|
||||||
|
const result = await new Promise((resolve, reject) => {
|
||||||
|
const getRequest = store.get(hash);
|
||||||
|
getRequest.onsuccess = () => resolve(getRequest.result);
|
||||||
|
getRequest.onerror = () => reject(getRequest.error);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addDiff(processId, stateId, hash, roles, field) {
|
||||||
|
const db = await openDatabase();
|
||||||
|
const storeName = 'diffs';
|
||||||
|
const tx = db.transaction(storeName, 'readwrite');
|
||||||
|
const store = tx.objectStore(storeName);
|
||||||
|
|
||||||
|
// Check if the diff already exists
|
||||||
|
const existingDiff = await new Promise((resolve, reject) => {
|
||||||
|
const getRequest = store.get(hash);
|
||||||
|
getRequest.onsuccess = () => resolve(getRequest.result);
|
||||||
|
getRequest.onerror = () => reject(getRequest.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingDiff) {
|
||||||
|
const newDiff = {
|
||||||
|
process_id: processId,
|
||||||
|
state_id: stateId,
|
||||||
|
value_commitment: hash,
|
||||||
|
roles: roles,
|
||||||
|
field: field,
|
||||||
|
description: null,
|
||||||
|
previous_value: null,
|
||||||
|
new_value: null,
|
||||||
|
notify_user: false,
|
||||||
|
need_validation: false,
|
||||||
|
validation_status: 'None'
|
||||||
|
};
|
||||||
|
|
||||||
|
const insertResult = await new Promise((resolve, reject) => {
|
||||||
|
const putRequest = store.put(newDiff);
|
||||||
|
putRequest.onsuccess = () => resolve(putRequest.result);
|
||||||
|
putRequest.onerror = () => reject(putRequest.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return insertResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingDiff;
|
||||||
|
}
|
||||||
|
@ -6,6 +6,9 @@ export class Database {
|
|||||||
private dbName: string = '4nk';
|
private dbName: string = '4nk';
|
||||||
private dbVersion: number = 1;
|
private dbVersion: number = 1;
|
||||||
private serviceWorkerRegistration: ServiceWorkerRegistration | null = null;
|
private serviceWorkerRegistration: ServiceWorkerRegistration | null = null;
|
||||||
|
private messageChannel: MessageChannel | null = null;
|
||||||
|
private messageChannelForGet: MessageChannel | null = null;
|
||||||
|
private serviceWorkerCheckIntervalId: number | null = null;
|
||||||
private storeDefinitions = {
|
private storeDefinitions = {
|
||||||
AnkLabels: {
|
AnkLabels: {
|
||||||
name: 'labels',
|
name: 'labels',
|
||||||
@ -41,6 +44,11 @@ export class Database {
|
|||||||
{ name: 'byStatus', keyPath: 'validation_status', options: { unique: false } },
|
{ name: 'byStatus', keyPath: 'validation_status', options: { unique: false } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
AnkData: {
|
||||||
|
name: 'data',
|
||||||
|
options: {},
|
||||||
|
indices: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Private constructor to prevent direct instantiation from outside
|
// Private constructor to prevent direct instantiation from outside
|
||||||
@ -74,12 +82,10 @@ export class Database {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = () => {
|
request.onsuccess = async () => {
|
||||||
setTimeout(() => {
|
|
||||||
this.db = request.result;
|
this.db = request.result;
|
||||||
this.initServiceWorker();
|
await this.initServiceWorker();
|
||||||
resolve();
|
resolve();
|
||||||
}, 500);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onerror = () => {
|
request.onerror = () => {
|
||||||
@ -104,36 +110,67 @@ export class Database {
|
|||||||
return objectList;
|
return objectList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createMessageChannel(responseHandler: (event: MessageEvent) => void): MessageChannel {
|
private async initServiceWorker() {
|
||||||
const messageChannel = new MessageChannel();
|
if (!('serviceWorker' in navigator)) return; // Ensure service workers are supported
|
||||||
messageChannel.port1.onmessage = responseHandler;
|
|
||||||
return messageChannel;
|
try {
|
||||||
|
// Get existing service worker registrations
|
||||||
|
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||||
|
if (registrations.length === 0) {
|
||||||
|
// No existing workers: register a new one.
|
||||||
|
this.serviceWorkerRegistration = await navigator.serviceWorker.register('/src/service-workers/database.worker.js', { type: 'module' });
|
||||||
|
console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope);
|
||||||
|
} else if (registrations.length === 1) {
|
||||||
|
// One existing worker: update it (restart it) without unregistering.
|
||||||
|
this.serviceWorkerRegistration = registrations[0];
|
||||||
|
await this.serviceWorkerRegistration.update();
|
||||||
|
console.log('Service Worker updated');
|
||||||
|
} else {
|
||||||
|
// More than one existing worker: unregister them all and register a new one.
|
||||||
|
console.log('Multiple Service Worker(s) detected. Unregistering all...');
|
||||||
|
await Promise.all(registrations.map(reg => reg.unregister()));
|
||||||
|
console.log('All previous Service Workers unregistered.');
|
||||||
|
this.serviceWorkerRegistration = await navigator.serviceWorker.register('/src/service-workers/database.worker.js', { type: 'module' });
|
||||||
|
console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initServiceWorker() {
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
try {
|
|
||||||
const registration = await navigator.serviceWorker.register('/src/service-workers/database.worker.js', { type: 'module' });
|
|
||||||
console.log('Service Worker registered with scope:', registration.scope);
|
|
||||||
|
|
||||||
this.serviceWorkerRegistration = registration
|
|
||||||
await this.checkForUpdates();
|
await this.checkForUpdates();
|
||||||
|
|
||||||
const messageChannel = new MessageChannel();
|
// Set up a global message listener for responses from the service worker.
|
||||||
|
navigator.serviceWorker.addEventListener('message', async (event) => {
|
||||||
|
console.log('Received message from service worker:', event.data);
|
||||||
|
await this.handleServiceWorkerMessage(event.data);
|
||||||
|
});
|
||||||
|
|
||||||
messageChannel.port1.onmessage = this.handleAddObjectResponse;
|
// Set up a periodic check to ensure the service worker is active and to send a SYNC message.
|
||||||
const readyRegistration = await navigator.serviceWorker.ready;
|
this.serviceWorkerCheckIntervalId = window.setInterval(async () => {
|
||||||
readyRegistration.active?.postMessage(
|
const activeWorker = this.serviceWorkerRegistration.active || (await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration));
|
||||||
{
|
const service = await Services.getInstance();
|
||||||
type: 'START',
|
const payload = await service.getMyProcesses();
|
||||||
},
|
if (payload.length != 0) {
|
||||||
[messageChannel.port2],
|
activeWorker?.postMessage({ type: 'SCAN', payload });
|
||||||
);
|
}
|
||||||
// Optionally, initialize service worker with some data
|
}, 5000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Service Worker registration failed:', error);
|
console.error('Service Worker registration failed:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to wait for service worker activation
|
||||||
|
private async waitForServiceWorkerActivation(registration: ServiceWorkerRegistration): Promise<ServiceWorker | null> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (registration.active) {
|
||||||
|
resolve(registration.active);
|
||||||
|
} else {
|
||||||
|
const listener = () => {
|
||||||
|
if (registration.active) {
|
||||||
|
navigator.serviceWorker.removeEventListener('controllerchange', listener);
|
||||||
|
resolve(registration.active);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
navigator.serviceWorker.addEventListener('controllerchange', listener);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async checkForUpdates() {
|
private async checkForUpdates() {
|
||||||
@ -152,59 +189,109 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handleServiceWorkerMessage(message: any) {
|
||||||
|
switch (message.type) {
|
||||||
|
case 'TO_DOWNLOAD':
|
||||||
|
await this.handleDownloadList(message.data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn('Unknown message type received from service worker:', message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleDownloadList(downloadList: string[]): void {
|
||||||
|
// Download the missing data
|
||||||
|
let requestedStateId = [];
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
for (const hash of downloadList) {
|
||||||
|
const diff = await service.getDiffByValue(hash);
|
||||||
|
if (!diff) {
|
||||||
|
// This should never happen
|
||||||
|
console.warn(`Missing a diff for hash ${hash}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const processId = diff.process_id;
|
||||||
|
const stateId = diff.state_id;
|
||||||
|
const roles = diff.roles;
|
||||||
|
try {
|
||||||
|
const valueBytes = await service.fetchValueFromStorage(hash);
|
||||||
|
if (valueBytes) {
|
||||||
|
// Save data to db
|
||||||
|
const blob = new Blob([valueBytes], {type: "application/octet-stream"});
|
||||||
|
await service.saveBlobToDb(hash, blob);
|
||||||
|
document.dispatchEvent(new CustomEvent('newDataReceived', {
|
||||||
|
detail: {
|
||||||
|
processId,
|
||||||
|
stateId,
|
||||||
|
hash,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// We first request the data from managers
|
||||||
|
console.log('Request data from managers of the process');
|
||||||
|
// get the diff from db
|
||||||
|
if (!requestedStateId.includes(stateId)) {
|
||||||
|
await service.requestDataFromPeers(processId, [stateId], [roles]);
|
||||||
|
requestedStateId.push(stateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private handleAddObjectResponse = async (event: MessageEvent) => {
|
private handleAddObjectResponse = async (event: MessageEvent) => {
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
console.log('Received response from service worker (ADD_OBJECT):', data);
|
console.log('Received response from service worker (ADD_OBJECT):', data);
|
||||||
if (data.type === 'NOTIFICATIONS') {
|
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
|
if (data.type === 'NOTIFICATIONS') {
|
||||||
service.setNotifications(data.data);
|
service.setNotifications(data.data);
|
||||||
}
|
} else if (data.type === 'TO_DOWNLOAD') {
|
||||||
};
|
console.log(`Received missing data ${data}`);
|
||||||
|
// Download the missing data
|
||||||
private handleUpdateProcessesResponse = async (event: MessageEvent) => {
|
let requestedStateId = [];
|
||||||
|
for (const hash of data.data) {
|
||||||
const data = event.data;
|
|
||||||
console.log('Received response from service worker (UPDATE_PROCESSES):', data);
|
|
||||||
for (const process of data.data) {
|
|
||||||
const hash = process.states.pcd_commitment[0];
|
|
||||||
console.log("HASH: ", hash);
|
|
||||||
const diff = await this.requestStoreByIndex('diffs', 'byStateId', hash);
|
|
||||||
console.log("DIFF: ", diff);
|
|
||||||
if (diff && diff.new_value !== null) {
|
|
||||||
try {
|
try {
|
||||||
const newState = JSON.parse(diff.new_value);
|
const valueBytes = await service.fetchValueFromStorage(hash);
|
||||||
} catch (error) {
|
if (valueBytes) {
|
||||||
console.error('Error updating process:', error);
|
// Save data to db
|
||||||
}
|
const blob = new Blob([valueBytes], {type: "application/octet-stream"});
|
||||||
|
await service.saveBlobToDb(hash, blob);
|
||||||
} else {
|
} else {
|
||||||
console.log("No diff found for process: ", process.id);
|
// We first request the data from managers
|
||||||
|
console.log('Request data from managers of the process');
|
||||||
|
// get the diff from db
|
||||||
|
const diff = await service.getDiffByValue(hash);
|
||||||
|
const processId = diff.process_id;
|
||||||
|
const stateId = diff.state_id;
|
||||||
|
const roles = diff.roles;
|
||||||
|
if (!requestedStateId.includes(stateId)) {
|
||||||
|
await service.requestDataFromPeers(processId, [stateId], [roles]);
|
||||||
|
requestedStateId.push(stateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// TODO : get the message from the service worker to the client
|
|
||||||
|
|
||||||
// TODO : get the message from the service worker to the client
|
|
||||||
// we get an object, then we have to loop to look for the diffs
|
|
||||||
// for each hash in INDEXEDB --> request for true or false
|
|
||||||
// if the diffs is found, we have to add the diff to the object
|
|
||||||
// we have to do that for each object
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private handleGetObjectResponse = (event: MessageEvent) => {
|
private handleGetObjectResponse = (event: MessageEvent) => {
|
||||||
console.log('Received response from service worker (GET_OBJECT):', event.data);
|
console.log('Received response from service worker (GET_OBJECT):', event.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public addObject(payload: { storeName: string; object: any; key: any }): Promise<void> {
|
public addObject(payload: { storeName: string; object: any; key: any }): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
// Check if the service worker is active
|
// Check if the service worker is active
|
||||||
if (!this.serviceWorkerRegistration?.active) {
|
if (!this.serviceWorkerRegistration) {
|
||||||
reject(new Error('Service worker is not active'));
|
// console.warn('Service worker registration is not ready. Waiting...');
|
||||||
return;
|
this.serviceWorkerRegistration = await navigator.serviceWorker.ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeWorker = await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration);
|
||||||
|
|
||||||
// Create a message channel for communication
|
// Create a message channel for communication
|
||||||
const messageChannel = new MessageChannel();
|
const messageChannel = new MessageChannel();
|
||||||
|
|
||||||
@ -220,7 +307,7 @@ export class Database {
|
|||||||
|
|
||||||
// Send the add object request to the service worker
|
// Send the add object request to the service worker
|
||||||
try {
|
try {
|
||||||
this.serviceWorkerRegistration.active.postMessage(
|
activeWorker?.postMessage(
|
||||||
{
|
{
|
||||||
type: 'ADD_OBJECT',
|
type: 'ADD_OBJECT',
|
||||||
payload,
|
payload,
|
||||||
@ -233,44 +320,6 @@ export class Database {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateMyProcesses(payload: { myProcessesId: string[] }): Promise<void> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Check if the service worker is active
|
|
||||||
if (!this.serviceWorkerRegistration?.active) {
|
|
||||||
reject(new Error('Service worker is not active'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a message channel for communication
|
|
||||||
const messageChannel = new MessageChannel();
|
|
||||||
|
|
||||||
// Handle the response from the service worker
|
|
||||||
messageChannel.port1.onmessage = (event) => {
|
|
||||||
if (event.data.status === 'success') {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
const error = event.data.message;
|
|
||||||
reject(new Error(error || 'Unknown error occurred while scanning our processes'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('Sending UPDATE_PROCESSES msg with payload', payload);
|
|
||||||
this.serviceWorkerRegistration.active.postMessage(
|
|
||||||
{
|
|
||||||
type: 'UPDATE_PROCESSES',
|
|
||||||
|
|
||||||
payload,
|
|
||||||
},
|
|
||||||
[messageChannel.port2],
|
|
||||||
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
reject(new Error(`Failed to send message to service worker: ${error}`));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getObject(storeName: string, key: string): Promise<any | null> {
|
public async getObject(storeName: string, key: string): Promise<any | null> {
|
||||||
const db = await this.getDb();
|
const db = await this.getDb();
|
||||||
const tx = db.transaction(storeName, 'readonly');
|
const tx = db.transaction(storeName, 'readonly');
|
||||||
|
@ -14,6 +14,7 @@ export default class ModalService {
|
|||||||
private processId: string | null = null;
|
private processId: string | null = null;
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
private paired_addresses: string[] = [];
|
private paired_addresses: string[] = [];
|
||||||
|
private modal: HTMLElement | null = null;
|
||||||
|
|
||||||
// Method to access the singleton instance of Services
|
// Method to access the singleton instance of Services
|
||||||
public static async getInstance(): Promise<ModalService> {
|
public static async getInstance(): Promise<ModalService> {
|
||||||
@ -54,6 +55,21 @@ export default class ModalService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async injectCreationModal(members: any[]) {
|
||||||
|
const container = document.querySelector('#containerId');
|
||||||
|
if (container) {
|
||||||
|
let html = await fetch('/src/components/modal/creation-modal.html').then((res) => res.text());
|
||||||
|
html = html.replace('{{device1}}', await addressToEmoji(members[0]['sp_addresses'][0]));
|
||||||
|
container.innerHTML += html;
|
||||||
|
|
||||||
|
// Dynamically load the header JS
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = '/src/components/modal/confirmation-modal.ts';
|
||||||
|
script.type = 'module';
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Device 1 wait Device 2
|
// Device 1 wait Device 2
|
||||||
async injectWaitingModal() {
|
async injectWaitingModal() {
|
||||||
const container = document.querySelector('#containerId');
|
const container = document.querySelector('#containerId');
|
||||||
@ -106,32 +122,37 @@ export default class ModalService {
|
|||||||
throw new Error('Must have exactly 1 member');
|
throw new Error('Must have exactly 1 member');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("MEMBERS:", members);
|
||||||
// We take all the addresses except our own
|
// We take all the addresses except our own
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
const localAddress = await service.getDeviceAddress();
|
const localAddress = await service.getDeviceAddress();
|
||||||
for (const member of members) {
|
for (const member of members) {
|
||||||
for (const address of member['sp_addresses']) {
|
if (member.sp_addresses) {
|
||||||
|
for (const address of member.sp_addresses) {
|
||||||
if (address !== localAddress) {
|
if (address !== localAddress) {
|
||||||
this.paired_addresses.push(address);
|
this.paired_addresses.push(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.processId = processId;
|
this.processId = processId;
|
||||||
this.stateId = stateId;
|
this.stateId = stateId;
|
||||||
await this.injectModal(members);
|
|
||||||
const modal = document.getElementById('modal');
|
|
||||||
if (modal) modal.style.display = 'flex';
|
|
||||||
// const newScript = document.createElement('script');
|
|
||||||
// newScript.setAttribute('type', 'module');
|
|
||||||
// newScript.textContent = confirmationModalScript;
|
|
||||||
// document.head.appendChild(newScript).parentNode?.removeChild(newScript);
|
|
||||||
|
|
||||||
// Add correct text
|
if (members[0].sp_addresses.length === 1) {
|
||||||
|
await this.injectCreationModal(members);
|
||||||
|
this.modal = document.getElementById('creation-modal');
|
||||||
|
console.log("LENGTH:", members[0].sp_addresses.length);
|
||||||
|
} else {
|
||||||
|
await this.injectModal(members);
|
||||||
|
this.modal = document.getElementById('modal');
|
||||||
|
console.log("LENGTH:", members[0].sp_addresses.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.modal) this.modal.style.display = 'flex';
|
||||||
|
|
||||||
// Close modal when clicking outside of it
|
// Close modal when clicking outside of it
|
||||||
window.onclick = (event) => {
|
window.onclick = (event) => {
|
||||||
const modal = document.getElementById('modal');
|
if (event.target === this.modal) {
|
||||||
if (event.target === modal) {
|
|
||||||
this.closeConfirmationModal();
|
this.closeConfirmationModal();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -140,14 +161,12 @@ export default class ModalService {
|
|||||||
console.log('=============> Confirm Login');
|
console.log('=============> Confirm Login');
|
||||||
}
|
}
|
||||||
async closeLoginModal() {
|
async closeLoginModal() {
|
||||||
const modal = document.getElementById('login-modal');
|
if (this.modal) this.modal.style.display = 'none';
|
||||||
if (modal) modal.style.display = 'none';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async confirmPairing() {
|
async confirmPairing() {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
const modal = document.getElementById('modal');
|
if (this.modal) this.modal.style.display = 'none';
|
||||||
if (modal) modal.style.display = 'none';
|
|
||||||
|
|
||||||
if (service.device1) {
|
if (service.device1) {
|
||||||
console.log("Device 1 detected");
|
console.log("Device 1 detected");
|
||||||
@ -166,7 +185,7 @@ export default class ModalService {
|
|||||||
|
|
||||||
// We send confirmation that we validate the change
|
// We send confirmation that we validate the change
|
||||||
try {
|
try {
|
||||||
const approveChangeReturn = service.approveChange(this.processId!, this.stateId!);
|
const approveChangeReturn = await service.approveChange(this.processId!, this.stateId!);
|
||||||
await service.handleApiReturn(approveChangeReturn);
|
await service.handleApiReturn(approveChangeReturn);
|
||||||
|
|
||||||
await this.injectWaitingModal();
|
await this.injectWaitingModal();
|
||||||
@ -186,7 +205,7 @@ export default class ModalService {
|
|||||||
const newDevice = service.dumpDeviceFromMemory();
|
const newDevice = service.dumpDeviceFromMemory();
|
||||||
console.log(newDevice);
|
console.log(newDevice);
|
||||||
await service.saveDeviceInDatabase(newDevice);
|
await service.saveDeviceInDatabase(newDevice);
|
||||||
navigate('process');
|
navigate('chat');
|
||||||
service.resetState();
|
service.resetState();
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -215,7 +234,7 @@ export default class ModalService {
|
|||||||
|
|
||||||
// We send confirmation that we validate the change
|
// We send confirmation that we validate the change
|
||||||
try {
|
try {
|
||||||
const approveChangeReturn = service.approveChange(this.processId!, this.stateId!);
|
const approveChangeReturn = await service.approveChange(this.processId!, this.stateId!);
|
||||||
await service.handleApiReturn(approveChangeReturn);
|
await service.handleApiReturn(approveChangeReturn);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw e;
|
throw e;
|
||||||
@ -229,14 +248,13 @@ export default class ModalService {
|
|||||||
const newDevice = service.dumpDeviceFromMemory();
|
const newDevice = service.dumpDeviceFromMemory();
|
||||||
console.log(newDevice);
|
console.log(newDevice);
|
||||||
await service.saveDeviceInDatabase(newDevice);
|
await service.saveDeviceInDatabase(newDevice);
|
||||||
navigate('process');
|
navigate('chat');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async closeConfirmationModal() {
|
async closeConfirmationModal() {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
await service.unpairDevice();
|
await service.unpairDevice();
|
||||||
const modal = document.getElementById('modal');
|
if (this.modal) this.modal.style.display = 'none';
|
||||||
if (modal) modal.style.display = 'none';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,19 @@ import { BackUp } from '~/models/backup.model';
|
|||||||
|
|
||||||
export const U32_MAX = 4294967295;
|
export const U32_MAX = 4294967295;
|
||||||
|
|
||||||
const storageUrl = `/storage`;
|
const BASEURL = `https://demo.4nkweb.com`;
|
||||||
const BOOTSTRAPURL = [`https://demo.4nkweb.com/ws/`];
|
const BOOTSTRAPURL = [`${BASEURL}/ws/`];
|
||||||
|
const STORAGEURL = `${BASEURL}/storage`
|
||||||
const DEFAULTAMOUNT = 1000n;
|
const DEFAULTAMOUNT = 1000n;
|
||||||
|
const EMPTY32BYTES = String('').padStart(64, '0');
|
||||||
|
|
||||||
export default class Services {
|
export default class Services {
|
||||||
private static initializing: Promise<Services> | null = null;
|
private static initializing: Promise<Services> | null = null;
|
||||||
private static instance: Services;
|
private static instance: Services;
|
||||||
private currentProcess: string | null = null;
|
private currentProcess: string | null = null;
|
||||||
private pendingUpdates: any | null = null;
|
|
||||||
private currentUpdateMerkleRoot: string | null = null;
|
private currentUpdateMerkleRoot: string | null = null;
|
||||||
private localAddress: string | null = null;
|
|
||||||
private pairedAddresses: string[] = [];
|
|
||||||
private sdkClient: any;
|
private sdkClient: any;
|
||||||
private processes: IProcess[] | null = null;
|
private myProcesses: Set = new Set();
|
||||||
private notifications: any[] | null = null;
|
private notifications: any[] | null = null;
|
||||||
private subscriptions: { element: Element; event: string; eventHandler: string }[] = [];
|
private subscriptions: { element: Element; event: string; eventHandler: string }[] = [];
|
||||||
private database: any;
|
private database: any;
|
||||||
@ -211,7 +210,7 @@ export default class Services {
|
|||||||
|
|
||||||
private async ensureSufficientAmount(): Promise<void> {
|
private async ensureSufficientAmount(): Promise<void> {
|
||||||
const availableAmt = this.getAmount();
|
const availableAmt = this.getAmount();
|
||||||
const target: BigInt = DEFAULTAMOUNT * BigInt(2);
|
const target: BigInt = DEFAULTAMOUNT * BigInt(10);
|
||||||
|
|
||||||
if (availableAmt < target) {
|
if (availableAmt < target) {
|
||||||
const faucetMsg = this.createFaucetMessage();
|
const faucetMsg = this.createFaucetMessage();
|
||||||
@ -239,64 +238,7 @@ export default class Services {
|
|||||||
throw new Error('Amount is still 0 after 3 attempts');
|
throw new Error('Amount is still 0 after 3 attempts');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createMessagingProcess(otherMembers: Member[],relayAddress: string, feeRate: number): Promise<ApiReturn> {
|
public async createPairingProcess(userName: string, pairWith: string[], relayAddress: string, feeRate: number): Promise<ApiReturn> {
|
||||||
if (!this.isPaired()) {
|
|
||||||
throw new Error('Device not paired');
|
|
||||||
}
|
|
||||||
const me = await this.getMemberFromDevice();
|
|
||||||
if (!me) {
|
|
||||||
throw new Error('No paired member in device');
|
|
||||||
}
|
|
||||||
const allMembers: Member[] = otherMembers;
|
|
||||||
const meAndOne = [{ sp_addresses: me }, otherMembers.pop()!];
|
|
||||||
allMembers.push({ sp_addresses: me });
|
|
||||||
const everyOneElse = otherMembers;
|
|
||||||
const messagingTemplate = {
|
|
||||||
description: 'messaging',
|
|
||||||
roles: {
|
|
||||||
public: {
|
|
||||||
members: allMembers,
|
|
||||||
validation_rules: [
|
|
||||||
{
|
|
||||||
quorum: 0.0,
|
|
||||||
fields: ['description', 'roles'],
|
|
||||||
min_sig_member: 0.0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
storages: [storageUrl]
|
|
||||||
},
|
|
||||||
owner: {
|
|
||||||
members: meAndOne,
|
|
||||||
validation_rules: [
|
|
||||||
{
|
|
||||||
quorum: 1.0,
|
|
||||||
fields: ['description', 'roles'],
|
|
||||||
min_sig_member: 1.0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
storages: [storageUrl]
|
|
||||||
},
|
|
||||||
users: {
|
|
||||||
members: everyOneElse,
|
|
||||||
validation_rules: [
|
|
||||||
{
|
|
||||||
quorum: 0.0,
|
|
||||||
fields: ['description', 'roles'],
|
|
||||||
min_sig_member: 0.0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
storages: [storageUrl]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
return this.sdkClient.create_new_process(JSON.stringify(messagingTemplate), null, relayAddress, feeRate);
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(`Creating process failed: ${e}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async createPairingProcess(pairWith: string[], relayAddress: string, feeRate: number): Promise<ApiReturn> {
|
|
||||||
if (this.sdkClient.is_paired()) {
|
if (this.sdkClient.is_paired()) {
|
||||||
throw new Error('Device already paired');
|
throw new Error('Device already paired');
|
||||||
}
|
}
|
||||||
@ -308,22 +250,25 @@ export default class Services {
|
|||||||
validation_rules: [
|
validation_rules: [
|
||||||
{
|
{
|
||||||
quorum: 1.0,
|
quorum: 1.0,
|
||||||
fields: ['description', 'counter'],
|
fields: ['description', 'counter', 'roles', 'memberPublicName'],
|
||||||
min_sig_member: 1.0,
|
min_sig_member: 1.0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
storages: [storageUrl]
|
storages: [STORAGEURL]
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const pairingTemplate = {
|
const pairingTemplate = {
|
||||||
description: 'pairing',
|
description: 'pairing',
|
||||||
counter: 0,
|
counter: 0,
|
||||||
};
|
};
|
||||||
|
const publicData = {
|
||||||
|
memberPublicName: userName
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return this.sdkClient.create_new_process(
|
return this.sdkClient.create_new_process(
|
||||||
JSON.stringify(pairingTemplate),
|
pairingTemplate,
|
||||||
JSON.stringify(roles),
|
roles,
|
||||||
null,
|
publicData,
|
||||||
relayAddress,
|
relayAddress,
|
||||||
feeRate
|
feeRate
|
||||||
);
|
);
|
||||||
@ -353,14 +298,10 @@ export default class Services {
|
|||||||
throw new Error('No paired member found');
|
throw new Error('No paired member found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const dmTemplate = {
|
const roles = {
|
||||||
description: 'dm',
|
demiurge: {
|
||||||
message: '',
|
|
||||||
roles: {
|
|
||||||
dm: {
|
|
||||||
members: [
|
members: [
|
||||||
{ sp_addresses: myAddresses },
|
{ sp_addresses: myAddresses },
|
||||||
{ sp_addresses: otherMember }
|
|
||||||
],
|
],
|
||||||
validation_rules: [
|
validation_rules: [
|
||||||
{
|
{
|
||||||
@ -369,22 +310,41 @@ export default class Services {
|
|||||||
min_sig_member: 0.01,
|
min_sig_member: 0.01,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
storages: [storageUrl]
|
storages: [STORAGEURL]
|
||||||
|
}
|
||||||
|
dm: {
|
||||||
|
members: [
|
||||||
|
{ sp_addresses: myAddresses },
|
||||||
|
{ sp_addresses: otherMember }
|
||||||
|
],
|
||||||
|
validation_rules: [
|
||||||
|
{
|
||||||
|
quorum: 0.01,
|
||||||
|
fields: ['message', 'description'],
|
||||||
|
min_sig_member: 0.01,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
storages: [STORAGEURL]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dmTemplate = {
|
||||||
|
description: 'dm',
|
||||||
|
message: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('📋 Template final:', JSON.stringify(dmTemplate, null, 2));
|
console.log('📋 Template final:', JSON.stringify(dmTemplate, null, 2));
|
||||||
|
|
||||||
const relayAddress = this.getAllRelays()[0]['spAddress'];
|
const relayAddress = this.getAllRelays()[0]['spAddress'];
|
||||||
const feeRate = 1;
|
const feeRate = 1;
|
||||||
const initState = JSON.stringify(dmTemplate);
|
const publicData = {};
|
||||||
|
|
||||||
await this.checkConnections ([{ sp_addresses: otherMember }]);
|
await this.checkConnections ([{ sp_addresses: otherMember }]);
|
||||||
|
|
||||||
const result = this.sdkClient.create_new_process (
|
const result = this.sdkClient.create_new_process (
|
||||||
initState,
|
dmTemplate,
|
||||||
null,
|
roles,
|
||||||
|
publicData,
|
||||||
relayAddress,
|
relayAddress,
|
||||||
feeRate
|
feeRate
|
||||||
);
|
);
|
||||||
@ -397,12 +357,15 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateProcess(processId: string, new_state: any): Promise<ApiReturn> {
|
public async updateProcess(process: Process, new_state: any, roles: Record<string, RoleDefinition> | null): Promise<ApiReturn> {
|
||||||
const roles = new_state.roles;
|
// If roles is null, we just take the last commited state roles
|
||||||
if (!roles) {
|
if (!roles) {
|
||||||
throw new Error('new state doesn\'t contain roles');
|
roles = this.getRoles(process);
|
||||||
|
} else {
|
||||||
|
// We should check that we have the right to change the roles here, or maybe it's better leave it to the wasm
|
||||||
|
console.log('Provided new roles:', JSON.stringify(roles));
|
||||||
}
|
}
|
||||||
|
console.log(roles);
|
||||||
let members = new Set();
|
let members = new Set();
|
||||||
for (const role of Object.values(roles)) {
|
for (const role of Object.values(roles)) {
|
||||||
for (const member of role.members) {
|
for (const member of role.members) {
|
||||||
@ -412,7 +375,8 @@ export default class Services {
|
|||||||
console.log(members);
|
console.log(members);
|
||||||
await this.checkConnections([...members]);
|
await this.checkConnections([...members]);
|
||||||
try {
|
try {
|
||||||
return this.sdkClient.update_process(processId, JSON.stringify(new_state));
|
console.log(process);
|
||||||
|
return this.sdkClient.update_process(process, new_state, roles, {});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Failed to update process: ${e}`);
|
throw new Error(`Failed to update process: ${e}`);
|
||||||
}
|
}
|
||||||
@ -439,21 +403,25 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public approveChange(processId: string, stateId: string): ApiReturn {
|
public async approveChange(processId: string, stateId: string): Promise<ApiReturn> {
|
||||||
|
const process = await this.getProcess(processId);
|
||||||
|
if (!process) {
|
||||||
|
throw new Error('Failed to get process from db');
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return this.sdkClient.validate_state(processId, stateId);
|
return this.sdkClient.validate_state(process, stateId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Failed to create prd response: ${e}`);
|
throw new Error(`Failed to create prd response: ${e}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public rejectChange(): ApiReturn {
|
public async rejectChange(processId: string, stateId: string): Promise<ApiReturn> {
|
||||||
if (!this.currentProcess || !this.currentUpdateMerkleRoot) {
|
const process = await this.getProcess(processId);
|
||||||
throw new Error('No current process and/or current update defined');
|
if (!process) {
|
||||||
|
throw new Error('Failed to get process from db');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return this.sdkClient.refuse_state(this.currentProcess, this.currentUpdateMerkleRoot);
|
return this.sdkClient.refuse_state(process, stateId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Failed to create prd response: ${e}`);
|
throw new Error(`Failed to create prd response: ${e}`);
|
||||||
}
|
}
|
||||||
@ -526,90 +494,6 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getCipherForDiff(diff: UserDiff): Promise<string | null> {
|
|
||||||
// get the process
|
|
||||||
try {
|
|
||||||
const process = await this.getProcess(diff.process_id);
|
|
||||||
const state = process.states.find(state => state.state_id === diff.state_id);
|
|
||||||
if (state) {
|
|
||||||
// Now we return the encrypted value for that field
|
|
||||||
const cipher = state.encrypted_pcd[diff.field];
|
|
||||||
if (cipher) {
|
|
||||||
return cipher;
|
|
||||||
} else {
|
|
||||||
console.error('Failed to get encrypted value');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to get process:', e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async tryFetchDiffValue(diffs: UserDiff[]): Promise<[UserDiff[], Record<string, string>]>{
|
|
||||||
if (diffs.length === 0) {
|
|
||||||
return [[], {}];
|
|
||||||
}
|
|
||||||
|
|
||||||
// We check if we have the value in diffs
|
|
||||||
let retrievedValues: Record<string, string> = {};
|
|
||||||
for (const diff of diffs) {
|
|
||||||
const hash = diff.value_commitment;
|
|
||||||
if (!hash) {
|
|
||||||
console.error('No commitment for diff');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = diff.new_value;
|
|
||||||
// Check if `new_value` is missing
|
|
||||||
if (value === null) {
|
|
||||||
try {
|
|
||||||
const res = await this.fetchValueFromStorage(hash);
|
|
||||||
if (!res) {
|
|
||||||
console.error('Failed to fetch value for hash', hash);
|
|
||||||
} else {
|
|
||||||
diff.new_value = res['value'];
|
|
||||||
retrievedValues[hash] = res['value'];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to fetch new_value for diff: ${JSON.stringify(diff)}`, error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We should have it in db if it came from the wasm, but just in case
|
|
||||||
try {
|
|
||||||
await this.saveDiffsToDb([diff]);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Failed to save diff to db: ${e}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We already have this value, so we check if it's on storage and push it if not
|
|
||||||
const dataIsOnServers: Record<string, boolean | null> = await this.testDataInStorage(hash);
|
|
||||||
if (dataIsOnServers === null) {
|
|
||||||
console.error('Failed to test data presence in storage');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const [server, status] of Object.entries(dataIsOnServers)) {
|
|
||||||
if (status === false) {
|
|
||||||
const cipher = await this.getCipherForDiff(diff);
|
|
||||||
if (cipher) {
|
|
||||||
try {
|
|
||||||
await this.saveDataToStorage(hash, cipher, null);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Failed to save to storage: ${e}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('Failed to get cipher for diff');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [diffs, retrievedValues];
|
|
||||||
}
|
|
||||||
|
|
||||||
public async handleApiReturn(apiReturn: ApiReturn) {
|
public async handleApiReturn(apiReturn: ApiReturn) {
|
||||||
console.log(apiReturn);
|
console.log(apiReturn);
|
||||||
if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.transaction.length != 0) {
|
if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.transaction.length != 0) {
|
||||||
@ -651,12 +535,21 @@ export default class Services {
|
|||||||
|
|
||||||
const processId: string = updatedProcess.process_id;
|
const processId: string = updatedProcess.process_id;
|
||||||
|
|
||||||
// Save process to db
|
if (updatedProcess.encrypted_data && Object.keys(updatedProcess.encrypted_data).length != 0) {
|
||||||
|
for (const [hash, cipher] of Object.entries(updatedProcess.encrypted_data)) {
|
||||||
|
console.log(hash);
|
||||||
|
console.log(cipher);
|
||||||
|
const blob = this.hexToBlob(cipher);
|
||||||
try {
|
try {
|
||||||
await this.saveProcessToDb(processId, updatedProcess.current_process);
|
await this.saveBlobToDb(hash, blob);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw e;
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save process to db
|
||||||
|
await this.saveProcessToDb(processId, updatedProcess.current_process);
|
||||||
|
|
||||||
const isPaired = this.isPaired();
|
const isPaired = this.isPaired();
|
||||||
|
|
||||||
@ -666,12 +559,23 @@ export default class Services {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to save diffs to db:', e);
|
console.error('Failed to save diffs to db:', e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!isPaired) {
|
if (!isPaired) {
|
||||||
console.log(updatedProcess);
|
console.log(updatedProcess);
|
||||||
await this.openPairingConfirmationModal(updatedProcess.current_process.states[0]);
|
await this.openPairingConfirmationModal(updatedProcess.current_process.states[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiReturn.push_to_storage && apiReturn.push_to_storage.length != 0) {
|
||||||
|
for (const hash of apiReturn.push_to_storage) {
|
||||||
|
const blob = await this.getBlobFromDb(hash);
|
||||||
|
if (blob) {
|
||||||
|
await this.saveDataToStorage(hash, blob, null);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to get data from db');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (apiReturn.commit_to_send) {
|
if (apiReturn.commit_to_send) {
|
||||||
const commit = apiReturn.commit_to_send;
|
const commit = apiReturn.commit_to_send;
|
||||||
@ -719,6 +623,15 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dumpNeuteredDevice(): Device | null {
|
||||||
|
try {
|
||||||
|
return this.sdkClient.dump_neutered_device();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Failed to dump device: ${e}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getPairingProcessId(): string {
|
public getPairingProcessId(): string {
|
||||||
try {
|
try {
|
||||||
return this.sdkClient.get_pairing_process_id();
|
return this.sdkClient.get_pairing_process_id();
|
||||||
@ -787,33 +700,27 @@ export default class Services {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
rolesContainsUs(roles: any): boolean {
|
rolesContainsUs(roles: Record<string, RoleDefinition>): boolean {
|
||||||
|
let us;
|
||||||
try {
|
try {
|
||||||
this.sdkClient.roles_contains_us(JSON.stringify(roles));
|
us = this.sdkClient.get_member();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return this.rolesContainsMember(roles, us.sp_addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
rolesContainsMember(roles: any, member: string[]): boolean {
|
rolesContainsMember(roles: Record<string, RoleDefinition>, member: string[]): boolean {
|
||||||
try {
|
let res = false;
|
||||||
this.sdkClient.roles_contains_member(JSON.stringify(roles), member);
|
for (const [roleName, roleDef] of Object.entries(roles)) {
|
||||||
} catch (e) {
|
for (const otherMember of roleDef.members) {
|
||||||
return false;
|
if (res) { return true }
|
||||||
|
res = this.compareMembers(member, otherMember.sp_addresses);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return res;
|
||||||
}
|
|
||||||
|
|
||||||
membersInSameRoleThanUs(roles: any): Member[] | null {
|
|
||||||
try {
|
|
||||||
return this.sdkClient.members_in_same_roles_me(JSON.stringify(roles));
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async dumpWallet() {
|
async dumpWallet() {
|
||||||
@ -871,36 +778,34 @@ export default class Services {
|
|||||||
key: processId,
|
key: processId,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Failed to save process: ${e}`);
|
console.error(`Failed to save process ${processId}: ${e}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveStatesToStorage(process: Process, state_ids: string[]) {
|
public async saveBlobToDb(hash: string, data: Blob) {
|
||||||
// We check how many copies in storage nodes
|
const db = await Database.getInstance();
|
||||||
// We check the storage nodes in the process itself
|
try {
|
||||||
// this.sdkClient.get_storages(commitedIn);
|
await db.addObject({
|
||||||
const storages = [storageUrl];
|
storeName: 'data',
|
||||||
|
object: data,
|
||||||
for (const state of process.states) {
|
key: hash,
|
||||||
if (state.state_id === "") {
|
});
|
||||||
continue;
|
} catch (e) {
|
||||||
}
|
console.error(`Failed to save data to db: ${e}`);
|
||||||
if (!state.encrypted_pcd) {
|
|
||||||
console.warn('Empty encrypted pcd, skipping...');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (state_ids.includes(state.state_id)) {
|
|
||||||
for (const [field, hash] of Object.entries(state.pcd_commitment)) {
|
|
||||||
// get the encrypted value with the field name
|
|
||||||
const value = state.encrypted_pcd[field];
|
|
||||||
await storeData(storages, hash, value, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveDataToStorage(hash: string, data: string, ttl: number | null) {
|
public async getBlobFromDb(hash: string): Promise<Blob | null> {
|
||||||
const storages = [storageUrl];
|
const db = await Database.getInstance();
|
||||||
|
try {
|
||||||
|
return await db.getObject('data', hash);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async saveDataToStorage(hash: string, data: Blob, ttl: number | null) {
|
||||||
|
const storages = [STORAGEURL];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await storeData(storages, hash, data, ttl);
|
await storeData(storages, hash, data, ttl);
|
||||||
@ -910,13 +815,13 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async fetchValueFromStorage(hash: string): Promise<any | null> {
|
public async fetchValueFromStorage(hash: string): Promise<any | null> {
|
||||||
const storages = [storageUrl];
|
const storages = [STORAGEURL];
|
||||||
|
|
||||||
return await retrieveData(storages, hash);
|
return await retrieveData(storages, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async testDataInStorage(hash: string): Promise<Record<string, boolean | null> | null> {
|
public async testDataInStorage(hash: string): Promise<Record<string, boolean | null> | null> {
|
||||||
const storages = [storageUrl];
|
const storages = [STORAGEURL];
|
||||||
|
|
||||||
return await testData(storages, hash);
|
return await testData(storages, hash);
|
||||||
}
|
}
|
||||||
@ -978,18 +883,6 @@ export default class Services {
|
|||||||
await this.restoreProcessesFromDB();
|
await this.restoreProcessesFromDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match what we get from relay against what we already know and fetch missing data
|
|
||||||
public async updateProcessesFromRelay(processes: Record<string, Process>) {
|
|
||||||
const db = await Database.getInstance();
|
|
||||||
for (const [processId, process] of Object.entries(processes)) {
|
|
||||||
try {
|
|
||||||
this.sdkClient.sync_process_from_relay(processId, JSON.stringify(process));
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore process in wasm with persistent storage
|
// Restore process in wasm with persistent storage
|
||||||
public async restoreProcessesFromDB() {
|
public async restoreProcessesFromDB() {
|
||||||
const db = await Database.getInstance();
|
const db = await Database.getInstance();
|
||||||
@ -1054,35 +947,41 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDescription(processId: string, process: Process): Promise<string | null> {
|
async decryptAttribute(processId: string, state: ProcessState, attribute: string): Promise<string | null> {
|
||||||
// Get the `commited_in` value of the last state and remove it from the array
|
let hash = state.pcd_commitment[attribute];
|
||||||
const currentCommitedIn = process.states.at(-1)?.commited_in;
|
let key = state.keys[attribute];
|
||||||
|
|
||||||
if (currentCommitedIn === undefined) {
|
// If hash or key is missing, request an update and then retry
|
||||||
return null; // No states available
|
if (!hash || !key) {
|
||||||
|
await this.requestDataFromPeers(processId, [state.state_id], [state.roles]);
|
||||||
|
|
||||||
|
const maxRetries = 5;
|
||||||
|
const retryDelay = 500; // delay in milliseconds
|
||||||
|
let retries = 0;
|
||||||
|
|
||||||
|
while ((!hash || !key) && retries < maxRetries) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
// Re-read hash and key after waiting
|
||||||
|
hash = state.pcd_commitment[attribute];
|
||||||
|
key = state.keys[attribute];
|
||||||
|
retries++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the last state where `commited_in` is different
|
if (hash && key) {
|
||||||
let lastDifferentState = process.states.findLast(
|
const blob = await this.getBlobFromDb(hash);
|
||||||
state => state.commited_in !== currentCommitedIn
|
if (blob) {
|
||||||
);
|
// Decrypt the data
|
||||||
|
const buf = await blob.arrayBuffer();
|
||||||
|
const cipher = new Uint8Array(buf);
|
||||||
|
const keyBlob = this.hexToBlob(key);
|
||||||
|
const keyBuf = await keyBlob.arrayBuffer();
|
||||||
|
|
||||||
if (!lastDifferentState) {
|
const clear = this.sdkClient.decrypt_data(new Uint8Array(keyBuf), cipher);
|
||||||
// It means that we only have one state that is not commited yet, that can happen with process we just created
|
if (clear) {
|
||||||
// let's assume that the right description is in the last concurrent state and not handle the (arguably rare) case where we have multiple concurrent states on a creation
|
// Parse the stringified JSON
|
||||||
lastDifferentState = process.states.at(-1);
|
return JSON.parse(clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lastDifferentState.pcd_commitment) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take the description out of the state, if any
|
|
||||||
const description = lastDifferentState!.pcd_commitment['description'];
|
|
||||||
if (description) {
|
|
||||||
const userDiff = await this.getDiffByValue(description);
|
|
||||||
if (userDiff) {
|
|
||||||
return userDiff.new_value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1176,18 +1075,15 @@ export default class Services {
|
|||||||
|
|
||||||
// Handle the handshake message
|
// Handle the handshake message
|
||||||
public async handleHandshakeMsg(url: string, parsedMsg: any) {
|
public async handleHandshakeMsg(url: string, parsedMsg: any) {
|
||||||
// Get the current user
|
|
||||||
const us = this.getMemberFromDevice();
|
|
||||||
console.log("Je suis le us de la fonction handleHandshakeMsg:", us);
|
|
||||||
try {
|
try {
|
||||||
const handshakeMsg: HandshakeMessage = JSON.parse(parsedMsg);
|
const handshakeMsg: HandshakeMessage = JSON.parse(parsedMsg);
|
||||||
this.updateRelay(url, handshakeMsg.sp_address);
|
this.updateRelay(url, handshakeMsg.sp_address);
|
||||||
const members = handshakeMsg.peers_list;
|
const members = handshakeMsg.peers_list;
|
||||||
if (this.membersList && Object.keys(this.membersList).length === 0) {
|
if (this.membersList && Object.keys(this.membersList).length === 0) {
|
||||||
|
// We start from an empty list, just copy it over
|
||||||
this.membersList = handshakeMsg.peers_list;
|
this.membersList = handshakeMsg.peers_list;
|
||||||
} else {
|
} else {
|
||||||
// console.log('Received members:');
|
// We are incrementing our list
|
||||||
// console.log(handshakeMsg.peers_list);
|
|
||||||
for (const [processId, member] of Object.entries(handshakeMsg.peers_list)) {
|
for (const [processId, member] of Object.entries(handshakeMsg.peers_list)) {
|
||||||
this.membersList[processId] = member as Member;
|
this.membersList[processId] = member as Member;
|
||||||
}
|
}
|
||||||
@ -1197,67 +1093,87 @@ export default class Services {
|
|||||||
const newProcesses = handshakeMsg.processes_list;
|
const newProcesses = handshakeMsg.processes_list;
|
||||||
if (newProcesses && Object.keys(newProcesses).length !== 0) {
|
if (newProcesses && Object.keys(newProcesses).length !== 0) {
|
||||||
for (const [processId, process] of Object.entries(newProcesses)) {
|
for (const [processId, process] of Object.entries(newProcesses)) {
|
||||||
// We check if we're part of the process
|
const existing = await this.getProcess(processId);
|
||||||
if (process.states.length < 2) continue;
|
if (existing) {
|
||||||
let stateIds = [];
|
// Look for state id we don't know yet
|
||||||
let managers = new Set();
|
let new_states = [];
|
||||||
|
let roles = [];
|
||||||
for (const state of process.states) {
|
for (const state of process.states) {
|
||||||
if (state.encrypted_pcd === null) continue;
|
if (!state.state_id || state.state_id === EMPTY32BYTES) { continue; }
|
||||||
const roles = state.encrypted_pcd['roles'];
|
if (!this.lookForStateId(existing, state.state_id)) {
|
||||||
if (!roles) {
|
new_states.push(state.state_id);
|
||||||
console.error('Can\'t find roles');
|
roles.push(state.roles);
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.rolesContainsUs(roles)) {
|
if (new_states.length != 0) {
|
||||||
// We add this state to the list to request
|
// We request the new states
|
||||||
stateIds.push(state.state_id);
|
await this.requestDataFromPeers(processId, new_states, roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we're probably just in the initial loading at page initialization
|
||||||
|
|
||||||
|
// We may learn an update for this process
|
||||||
|
// TODO maybe actually check if what the relay is sending us contains more information than what we have
|
||||||
|
// relay should always have more info than us, but we never know
|
||||||
|
// For now let's keep it simple and let the worker do the job
|
||||||
} else {
|
} else {
|
||||||
continue;
|
// We add it to db
|
||||||
}
|
console.log(`Saving ${processId} to db`);
|
||||||
|
await this.saveProcessToDb(processId, process as Process);
|
||||||
// For now we just add everyone that is in the same role than us
|
|
||||||
// const sendTo = this.membersInSameRoleThanUs(roles);
|
|
||||||
for (const [_, role] of Object.entries(roles)) {
|
|
||||||
if (!role.members.includes(us)) continue;
|
|
||||||
for (const member of role.members) {
|
|
||||||
if (member !== us) {
|
|
||||||
managers.push(member);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.sdkClient.request_data(processId, stateIds, managers);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await this.updateProcessesFromRelay(newProcesses);
|
|
||||||
}
|
|
||||||
}, 500)
|
}, 500)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to parse init message:', e);
|
console.error('Failed to parse init message:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lookForStateId(process: Process, stateId: string): boolean {
|
||||||
|
for (const state of process.states) {
|
||||||
|
if (state.state_id === stateId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retourne la liste de tous les membres
|
* Retourne la liste de tous les membres ordonnés par leur process id
|
||||||
* @returns Un tableau contenant tous les membres
|
* @returns Un tableau contenant tous les membres
|
||||||
*/
|
*/
|
||||||
public getAllMembers(): Record<string, Member> {
|
public getAllMembersSorted(): Record<string, Member> {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(this.membersList).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
|
Object.entries(this.membersList).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAllMembers(): Record<string, Member> {
|
||||||
|
return this.membersList;
|
||||||
|
}
|
||||||
|
|
||||||
public getAddressesForMemberId(memberId: string): string[] | null {
|
public getAddressesForMemberId(memberId: string): string[] | null {
|
||||||
return this.membersList[memberId];
|
return this.membersList[memberId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public compareMembers(memberA: string[], memberB: string[]): boolean {
|
||||||
|
if (!memberA || !memberB) { return false }
|
||||||
|
if (memberA.length !== memberB.length) { return false }
|
||||||
|
|
||||||
|
const res = memberA.every(item => memberB.includes(item)) && memberB.every(item => memberA.includes(item));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
public async handleCommitError(response: string) {
|
public async handleCommitError(response: string) {
|
||||||
const content = JSON.parse(response);
|
const content = JSON.parse(response);
|
||||||
|
const error = content.error;
|
||||||
|
const errorMsg = error['GenericError'];
|
||||||
|
if (errorMsg === 'State is identical to the previous state') {
|
||||||
|
return;
|
||||||
|
} else if (errorMsg === 'Not enough valid proofs') { return; }
|
||||||
// Wait and retry
|
// Wait and retry
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await this.sendCommitMessage(JSON.stringify(content));
|
await this.sendCommitMessage(JSON.stringify(content));
|
||||||
@ -1265,25 +1181,111 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async getRoles(process: Process): Promise<Record<string, RoleDefinition>> {
|
public getRoles(process: Process): Record<string, RoleDefinition> | null {
|
||||||
const currentCommitedIn = process.states.pop()?.commited_in;
|
const lastCommitedState = this.getLastCommitedState(process);
|
||||||
|
if (lastCommitedState && lastCommitedState.roles && Object.keys(lastCommitedState.roles).length != 0) {
|
||||||
if (currentCommitedIn === undefined) {
|
return lastCommitedState!.roles;
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastDifferentState = process.states.findLast(
|
|
||||||
state => state.commited_in !== currentCommitedIn
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!lastDifferentState) {
|
|
||||||
lastDifferentState = process.states.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastDifferentState && lastDifferentState.roles) {
|
|
||||||
return lastDifferentState!.roles;
|
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPublicData(process: Process): Record<string, any> | null {
|
||||||
|
const lastCommitedState = this.getLastCommitedState(process);
|
||||||
|
if (lastCommitedState && lastCommitedState.public_data) {
|
||||||
|
return lastCommitedState.public_data;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getProcessName(process: Process): string | null {
|
||||||
|
const lastCommitedState = this.getLastCommitedState(process);
|
||||||
|
if (lastCommitedState && lastCommitedState.public_data) {
|
||||||
|
const processName = lastCommitedState!.public_data['processName'];
|
||||||
|
if (processName) { return processName }
|
||||||
|
else { return null }
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getMyProcesses(): Promise<string[]> {
|
||||||
|
try {
|
||||||
|
const processes = await this.getProcesses();
|
||||||
|
|
||||||
|
for (const [processId, process] of Object.entries(processes)) {
|
||||||
|
// We use myProcesses attribute to not reevaluate all processes everytime
|
||||||
|
if (this.myProcesses && this.myProcesses.has(processId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const roles = this.getRoles(process);
|
||||||
|
|
||||||
|
if (roles && this.rolesContainsUs(roles)) {
|
||||||
|
this.myProcesses.add(processId);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.from(this.myProcesses);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to get processes:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async requestDataFromPeers(processId: string, stateIds: string[], roles: Record<string, RoleDefinition>[]) {
|
||||||
|
console.log('Requesting data from peers');
|
||||||
|
console.log(roles);
|
||||||
|
try {
|
||||||
|
const res = this.sdkClient.request_data(processId, stateIds, roles);
|
||||||
|
await this.handleApiReturn(res);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public hexToBlob(hexString: string): Blob {
|
||||||
|
if (hexString.length % 2 !== 0) {
|
||||||
|
throw new Error("Invalid hex string: length must be even");
|
||||||
|
}
|
||||||
|
const uint8Array = new Uint8Array(hexString.length / 2);
|
||||||
|
for (let i = 0; i < hexString.length; i += 2) {
|
||||||
|
uint8Array[i / 2] = parseInt(hexString.substr(i, 2), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Blob([uint8Array], { type: "application/octet-stream" });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async blobToHex(blob: Blob): Promise<string> {
|
||||||
|
const buffer = await blob.arrayBuffer();
|
||||||
|
const bytes = new Uint8Array(buffer);
|
||||||
|
return Array.from(bytes)
|
||||||
|
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastCommitedState(process: Process): ProcessState {
|
||||||
|
if (process.states.length === 0) return null;
|
||||||
|
const processTip = process.states[process.states.length - 1].commited_in;
|
||||||
|
const lastCommitedState = process.states.findLast(state => state.commited_in !== processTip);
|
||||||
|
if (lastCommitedState) {
|
||||||
|
return lastCommitedState;
|
||||||
|
} else {
|
||||||
|
console.error('Can\'t find last commited state');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public isPairingProcess(roles: Record<string, RoleDefinition>): boolean {
|
||||||
|
if (Object.keys(roles).length != 1) { return false }
|
||||||
|
const pairingRole = roles['pairing'];
|
||||||
|
if (pairingRole) {
|
||||||
|
// For now that's enough, we should probably test more things
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,21 @@
|
|||||||
import axios, { AxiosResponse } from 'axios';
|
import axios, { AxiosResponse } from 'axios';
|
||||||
|
|
||||||
export async function storeData(servers: string[], key: string, value: any, ttl: number | null): Promise<AxiosResponse | null> {
|
export async function storeData(servers: string[], key: string, value: Blob, ttl: number | null): Promise<AxiosResponse | null> {
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`${server}/store`, { key, value, ttl });
|
// Append key and ttl as query parameters
|
||||||
|
const url = new URL(`${server}/store`);
|
||||||
|
url.searchParams.append('key', key);
|
||||||
|
if (ttl !== null) {
|
||||||
|
url.searchParams.append('ttl', ttl.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the encrypted ArrayBuffer as the raw request body.
|
||||||
|
const response = await axios.post(url.toString(), value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/octet-stream'
|
||||||
|
},
|
||||||
|
});
|
||||||
console.log('Data stored successfully:', key);
|
console.log('Data stored successfully:', key);
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.error('Received response status', response.status);
|
console.error('Received response status', response.status);
|
||||||
@ -20,15 +32,18 @@ export async function storeData(servers: string[], key: string, value: any, ttl:
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function retrieveData(servers: string[], key: string): Promise<any | null> {
|
export async function retrieveData(servers: string[], key: string): Promise<ArrayBuffer | null> {
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${server}/retrieve/${key}`);
|
// When fetching the data from the server:
|
||||||
|
const response = await axios.get(`${server}/retrieve/${key}`, {
|
||||||
|
responseType: 'arraybuffer'
|
||||||
|
});
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.error('Received response status', response.status);
|
console.error('Received response status', response.status);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
console.log('Retrieved data:', response.data);
|
// console.log('Retrieved data:', response.data);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error retrieving data:', error);
|
console.error('Error retrieving data:', error);
|
||||||
|
@ -104,6 +104,7 @@ export function initAddressInput() {
|
|||||||
const addressInput = container.querySelector('#addressInput') as HTMLInputElement;
|
const addressInput = container.querySelector('#addressInput') as HTMLInputElement;
|
||||||
const emojiDisplay = container.querySelector('#emoji-display-2');
|
const emojiDisplay = container.querySelector('#emoji-display-2');
|
||||||
const okButton = container.querySelector('#okButton') as HTMLButtonElement;
|
const okButton = container.querySelector('#okButton') as HTMLButtonElement;
|
||||||
|
const createButton = container.querySelector('#createButton') as HTMLButtonElement;
|
||||||
addSubscription(addressInput, 'input', async () => {
|
addSubscription(addressInput, 'input', async () => {
|
||||||
let address = addressInput.value;
|
let address = addressInput.value;
|
||||||
|
|
||||||
@ -145,6 +146,12 @@ export function initAddressInput() {
|
|||||||
onOkButtonClick();
|
onOkButtonClick();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (createButton) {
|
||||||
|
addSubscription(createButton, 'click', () => {
|
||||||
|
onCreateButtonClick();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onOkButtonClick() {
|
async function onOkButtonClick() {
|
||||||
@ -158,21 +165,46 @@ async function onOkButtonClick() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function prepareAndSendPairingTx(secondDeviceAddress: string) {
|
async function onCreateButtonClick() {
|
||||||
|
try {
|
||||||
|
await prepareAndSendPairingTx();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`onCreateButtonClick error: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prepareAndSendPairingTx(promptName: boolean = false) {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
// Device 1 wait Device 2
|
// Device 1 wait Device 2
|
||||||
service.device1 = true;
|
// service.device1 = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await service.checkConnections([{ sp_addresses: [secondDeviceAddress] }]);
|
await service.checkConnections([]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
// Create the process
|
|
||||||
|
// Prompt the user for a username.
|
||||||
|
let userName;
|
||||||
|
if (promptName) {
|
||||||
|
userName = prompt("Please enter your user name:");
|
||||||
|
} else {
|
||||||
|
userName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the process after a delay.
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
const relayAddress = service.getAllRelays();
|
const relayAddress = service.getAllRelays();
|
||||||
const createPairingProcessReturn = await service.createPairingProcess([secondDeviceAddress], relayAddress[0].spAddress, 1);
|
|
||||||
|
// Pass the userName as an additional parameter.
|
||||||
|
const createPairingProcessReturn = await service.createPairingProcess(
|
||||||
|
userName,
|
||||||
|
[],
|
||||||
|
relayAddress[0].spAddress,
|
||||||
|
1,
|
||||||
|
userName
|
||||||
|
);
|
||||||
|
|
||||||
if (!createPairingProcessReturn.updated_process) {
|
if (!createPairingProcessReturn.updated_process) {
|
||||||
throw new Error('createPairingProcess returned an empty new process'); // This should never happen
|
throw new Error('createPairingProcess returned an empty new process'); // This should never happen
|
||||||
@ -189,17 +221,21 @@ export async function generateQRCode(spAddress: string) {
|
|||||||
const url = await QRCode.toDataURL(currentUrl + '?sp_address=' + spAddress);
|
const url = await QRCode.toDataURL(currentUrl + '?sp_address=' + spAddress);
|
||||||
const qrCode = container?.querySelector('.qr-code img');
|
const qrCode = container?.querySelector('.qr-code img');
|
||||||
qrCode?.setAttribute('src', url);
|
qrCode?.setAttribute('src', url);
|
||||||
|
|
||||||
//Generate Address CopyBtn
|
|
||||||
const address = container?.querySelector('.sp-address-btn');
|
|
||||||
if (address) {
|
|
||||||
address.textContent = 'Copy address';
|
|
||||||
}
|
|
||||||
const copyBtn = container.querySelector('#copyBtn');
|
|
||||||
if (copyBtn) {
|
|
||||||
addSubscription(copyBtn, 'click', () => copyToClipboard(currentUrl + '?sp_address=' + spAddress));
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function generateCreateBtn() {
|
||||||
|
try{
|
||||||
|
//Generate CreateBtn
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement
|
||||||
|
const createBtn = container?.querySelector('.create-btn');
|
||||||
|
if (createBtn) {
|
||||||
|
createBtn.textContent = 'CREATE';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,12 +5,15 @@ export function cleanSubscriptions(): void {
|
|||||||
for (const sub of subscriptions) {
|
for (const sub of subscriptions) {
|
||||||
const el = sub.element;
|
const el = sub.element;
|
||||||
const eventHandler = sub.eventHandler;
|
const eventHandler = sub.eventHandler;
|
||||||
|
if (el) {
|
||||||
el.removeEventListener(sub.event, eventHandler);
|
el.removeEventListener(sub.event, eventHandler);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
subscriptions = [];
|
subscriptions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addSubscription(element: Element | Document, event: any, eventHandler: EventListenerOrEventListenerObject): void {
|
export function addSubscription(element: Element | Document, event: any, eventHandler: EventListenerOrEventListenerObject): void {
|
||||||
|
if (!element) return;
|
||||||
subscriptions.push({ element, event, eventHandler });
|
subscriptions.push({ element, event, eventHandler });
|
||||||
element.addEventListener(event, eventHandler);
|
element.addEventListener(event, eventHandler);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user