ci: docker_tag=ext fix(ihm_client): build passes locally; adjust pkg imports, null-guards, types

This commit is contained in:
4NK CI Bot 2025-09-22 12:48:30 +00:00
parent 3f3fdc6b55
commit 2d3ac5a884
10 changed files with 106 additions and 98 deletions

View File

@ -1,4 +1,4 @@
import { Device, Process, SecretsStore } from ".././pkg/sdk_client.js";
import { Device, Process, SecretsStore } from "../../pkg/sdk_client.js";
export interface BackUp {
device: Device,

View File

@ -188,16 +188,16 @@ class AccountElement extends HTMLElement {
<div class="content-container">
<div id="pairing-content"></div>
<!-- <div id="wallet-content"></div> -->
<div id="process-content"></div>
<div id="process-creation-content"></div>
<div id="document-validation-content"></div>
<div id="process-content"></div>
<div id="process-creation-content"></div>
<div id="document-validation-content"></div>
<!-- <div id="data-content"></div> -->
</div>
</div>
</div>
`;
window.showPairing = () => this.showPairing();
window.showWallet = () => this.showWallet();
window.showProcess = () => this.showProcess();
@ -238,22 +238,22 @@ class AccountElement extends HTMLElement {
this.initializeEventListeners();
this.loadSavedBanner();
this.loadUserInfo();
const savedAvatar = localStorage.getItem('userAvatar');
const savedBanner = localStorage.getItem('userBanner');
const savedName = localStorage.getItem('userName');
const savedLastName = localStorage.getItem('userLastName');
if (savedAvatar) {
const navAvatar = this.shadowRoot?.querySelector('.avatar') as HTMLImageElement;
if (navAvatar) navAvatar.src = savedAvatar;
}
if (savedBanner) {
const navBanner = this.shadowRoot?.querySelector('.banner-image') as HTMLImageElement;
if (navBanner) navBanner.src = savedBanner;
}
if (savedName) {
this.updateNavbarName(savedName);
}
@ -270,17 +270,17 @@ class AccountElement extends HTMLElement {
alertPopup.className = 'alert-popup';
this.shadowRoot?.appendChild(alertPopup);
}
// Définir le message et afficher la popup
alertPopup.textContent = message;
(alertPopup as HTMLElement).style.display = 'block';
// Cacher la popup après 3 secondes
setTimeout(() => {
(alertPopup as HTMLElement).style.display = 'none';
}, 3000);
}
// Fonctions de gestion des comptes et de l'interface utilisateur
private confirmDeleteAccount(): void {
@ -321,13 +321,13 @@ private updateNavbarBanner(imageUrl: string): void {
if (!navbarSection) return;
let bannerImg = navbarSection.querySelector<HTMLImageElement>('.banner-image');
if (!bannerImg) {
bannerImg = document.createElement('img');
bannerImg.className = 'banner-image';
navbarSection.insertBefore(bannerImg, navbarSection.firstChild);
}
bannerImg.src = imageUrl;
}
@ -368,7 +368,7 @@ private markAsRead(processName: string, messageId: number, element: HTMLElement)
element.classList.remove('unread');
element.classList.add('read');
const statusIcon = element.querySelector('.notification-status');
if (statusIcon) {
statusIcon.innerHTML = `
@ -381,7 +381,7 @@ private markAsRead(processName: string, messageId: number, element: HTMLElement)
const countElement = this.shadowRoot?.querySelector(`.notification-count[data-process="${processName}"]`);
if (countElement) {
countElement.textContent = `${notifCount.unread}/${notifCount.total}`;
const bellContainer = countElement.closest('.notification-container');
const bell = bellContainer?.querySelector('svg'); // Changé de .fa-bell à svg
if (bell && bellContainer && notifCount.unread === 0) {
@ -418,7 +418,7 @@ private exportRecovery(): void {
if (result.isConfirmed) {
const recoveryWords = this.generateRecoveryWords();
localStorage.setItem('recoveryWords', JSON.stringify(recoveryWords));
Swal.fire({
title: 'Your Recovery Words',
html: `
@ -447,7 +447,7 @@ private exportRecovery(): void {
if (result.isConfirmed) {
// Stocker l'état du bouton dans le localStorage
localStorage.setItem('recoveryExported', 'true');
const exportRecoveryBtn = this.shadowRoot?.querySelector('.recovery-btn') as HTMLButtonElement;
if (exportRecoveryBtn) {
exportRecoveryBtn.disabled = true;
@ -627,8 +627,8 @@ private updateTableContent(rows: Row[]): void {
<td class="device-name" onclick="window.editDeviceName(this)">${row.column2}</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"
<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)}')">
@ -677,18 +677,18 @@ private confirmRowPairing(): void {
isAddingRow = false;
currentRow = null;
this.resetButtonContainer();
this.updateTableContent(rows);
}
private cancelRowPairing(): void {
if (!currentRow) return;
currentRow.remove();
isAddingRow = false;
currentRow = null;
this.resetButtonContainer();
}
@ -739,7 +739,7 @@ private deleteRowPairing(button: HTMLButtonElement): void {
const index = Array.from(table.children).indexOf(row);
const storageKey = STORAGE_KEYS[currentMode];
const rows = JSON.parse(localStorage.getItem(storageKey) || '[]');
// Supprimer du localStorage
if (index > -1) {
rows.splice(index, 1);
@ -771,7 +771,7 @@ private editDeviceName(cell: HTMLTableCellElement): void {
input.type = 'text';
input.value = currentValue;
input.className = 'edit-input';
input.addEventListener('blur', () => this.finishEditing(cell, input));
input.addEventListener('keypress', (e: KeyboardEvent) => {
if (e.key === 'Enter') {
@ -797,7 +797,7 @@ private async finishEditing(cell: HTMLTableCellElement, input: HTMLInputElement)
const service = await Services.getInstance();
const pairingProcessId = service.getPairingProcessId();
const process = await service.getProcess(pairingProcessId);
if (!process) throw new Error('Pairing process not found');
// Mettre à jour le nom via le service
await service.updateMemberPublicName(process, newValue);
@ -816,17 +816,17 @@ private async finishEditing(cell: HTMLTableCellElement, input: HTMLInputElement)
private handleAvatarUpload(event: Event): void {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (e: ProgressEvent<FileReader>) => {
const result = e.target?.result as string;
const popupAvatar = this.shadowRoot?.getElementById('popup-avatar-img') as HTMLImageElement;
const navAvatar = this.shadowRoot?.querySelector('.nav-wrapper .avatar') as HTMLImageElement;
if (popupAvatar) popupAvatar.src = result;
if (navAvatar) navAvatar.src = result;
localStorage.setItem('userAvatar', result);
};
reader.readAsDataURL(file);
@ -856,8 +856,9 @@ private async showProcess(): Promise<void> {
const service = await Services.getInstance();
const myProcesses = await service.getMyProcesses();
if (myProcesses && myProcesses.length != 0) {
const myProcessesDataUnfiltered: { name: string, publicData: Record<string, any> }[] = await Promise.all(myProcesses.map(async processId => {
const myProcessesDataUnfiltered = await Promise.all(myProcesses.map(async processId => {
const process = await service.getProcess(processId);
if (!process) return undefined;
const lastState = service.getLastCommitedState(process);
if (!lastState) {
return {
@ -879,9 +880,9 @@ private async showProcess(): Promise<void> {
publicData: publicData
};
}));
const myProcessesData = myProcessesDataUnfiltered.filter(
(p) => p.name !== '' && Object.keys(p.publicData).length != 0
);
const myProcessesData = (myProcessesDataUnfiltered.filter(
(p) => p && p.name !== '' && Object.keys(p.publicData).length != 0
)) as { name: string, publicData: Record<string, any> }[];
createProcessTab(container, myProcessesData);
} else {
@ -896,15 +897,15 @@ private showProcessNotifications(processName: string): void {
const modal = document.createElement('div');
modal.className = 'notifications-modal';
let notificationsList = process.notification.messages.map(msg => `
<div class="notification-item ${msg.read ? 'read' : 'unread'}"
<div class="notification-item ${msg.read ? 'read' : 'unread'}"
onclick="window.markAsRead('${processName}', ${msg.id}, this)">
<div class="notification-status">
${msg.read ?
${msg.read ?
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16" fill="green">
<path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/>
</svg>` :
</svg>` :
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16" fill="black">
<path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"/>
</svg>`
@ -959,7 +960,7 @@ private showContractPopup(contractId: string, event?: Event) {
if (event) {
event.preventDefault();
}
// Check if the contract exists in mockContracts
const contract = mockContracts[contractId as keyof typeof mockContracts];
if (!contract) {
@ -989,7 +990,7 @@ private showContractPopup(contractId: string, event?: Event) {
const closeBtn = popup.querySelector('.close-contract-popup');
const closePopup = () => popup.remove();
closeBtn?.addEventListener('click', closePopup);
popup.addEventListener('click', (e) => {
if (e.target === popup) closePopup();
@ -1011,13 +1012,13 @@ private hideAllContent(): void {
private async showPairing(): Promise<void> {
const service = await Services.getInstance();
const spAddress = await service.getDeviceAddress();
isAddingRow = false;
currentRow = null;
currentMode = 'pairing';
this.hideAllContent();
const headerElement = this.shadowRoot?.getElementById('parameter-header');
if (headerElement) {
headerElement.textContent = 'Pairing';
@ -1047,23 +1048,23 @@ private async showPairing(): Promise<void> {
`;
let 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]?.public_data?.memberPublicName
const userName = pairingProcess?.states?.[0]?.public_data?.memberPublicName
|| localStorage.getItem('userName')
console.log('Username found:', userName);
const newRow = {
@ -1084,7 +1085,7 @@ private async showPairing(): Promise<void> {
localStorage.setItem(STORAGE_KEYS.pairing, JSON.stringify(rows));
}
}
this.updateTableContent(rows);
}
}
@ -1095,11 +1096,11 @@ private showWallet(): void {
currentMode = 'wallet';
this.hideAllContent();
// Mettre à jour le titre
const headerTitle = this.shadowRoot?.getElementById('header-title');
if (headerTitle) headerTitle.textContent = 'Wallet';
const walletContent = this.shadowRoot?.getElementById('wallet-content');
if (!walletContent) return;
walletContent.style.display = 'block';
@ -1121,7 +1122,7 @@ private showWallet(): void {
</div>
</div>
`;
const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || '[]');
this.updateWalletTableContent(rows);
}
@ -1144,10 +1145,10 @@ private showData(): void {
//console.log("showData called");
currentMode = 'data';
this.hideAllContent();
const headerTitle = this.shadowRoot?.getElementById('header-title');
if (headerTitle) headerTitle.textContent = 'Data';
const dataContent = this.shadowRoot?.getElementById('data-content');
if (dataContent) {
dataContent.style.display = 'block';
@ -1169,7 +1170,7 @@ private showData(): void {
</table>
</div>
`;
const rows = mockDataRows || JSON.parse(localStorage.getItem(STORAGE_KEYS.data) || '[]');
this.updateDataTableContent(rows);
}
@ -1198,7 +1199,7 @@ private addWalletRow(): void {
// Remplacer le bouton "Add a line" par les boutons de confirmation/annulation
const buttonContainer = this.shadowRoot?.querySelector('#wallet-content .button-container');
if (!buttonContainer) return;
buttonContainer.innerHTML = `
<div class="action-buttons-wrapper">
<button onclick="confirmWalletRow()" class="action-button confirm-button"></button>
@ -1236,20 +1237,20 @@ private confirmWalletRow(): void {
private cancelWalletRow(): void {
if (!currentRow) return;
currentRow.remove();
isAddingRow = false;
currentRow = null;
// Réinitialiser le conteneur de boutons avec le bouton "Add a line"
const buttonContainer = this.shadowRoot?.querySelector('#wallet-content .button-container');
if (!buttonContainer) return;
buttonContainer.innerHTML = `
<button class="add-row-button button-style" onclick="window.addWalletRow()">Add a line</button>
`;
}
private updateDataTableContent(rows: DataRow[]): void {
@ -1318,7 +1319,7 @@ private openAvatarPopup(): void {
<span>${savedAddress}</span>
</div>
</div>
<div class="popup-button-container">
<div class="action-buttons-row">
<button class="export-btn" onclick="window.exportUserData()">Export User Data</button>
@ -1336,7 +1337,7 @@ private openAvatarPopup(): void {
// Ajouter le gestionnaire d'événements pour la bannière
const bannerImg = popup.querySelector('#popup-banner-img');
const bannerInput = popup.querySelector('#banner-upload') as HTMLInputElement;
if (bannerImg && bannerInput) {
bannerImg.addEventListener('click', () => {
bannerInput.click();
@ -1375,10 +1376,10 @@ private setupEventListeners(popup: HTMLElement): void {
// Mise à jour de l'avatar dans la preview et le popup
const popupAvatar = this.shadowRoot?.getElementById('popup-avatar-img') as HTMLImageElement;
const previewAvatar = this.shadowRoot?.querySelector('.preview-avatar') as HTMLImageElement;
if (popupAvatar) popupAvatar.src = result;
if (previewAvatar) previewAvatar.src = result;
localStorage.setItem('userAvatar', result);
};
reader.readAsDataURL(file);
@ -1398,10 +1399,10 @@ private setupEventListeners(popup: HTMLElement): void {
// Mise à jour de la bannière dans la preview et le popup
const popupBanner = this.shadowRoot?.getElementById('popup-banner-img') as HTMLImageElement;
const previewBanner = this.shadowRoot?.querySelector('.preview-banner-img') as HTMLImageElement;
if (popupBanner) popupBanner.src = result;
if (previewBanner) previewBanner.src = result;
localStorage.setItem('userBanner', result);
};
reader.readAsDataURL(file);
@ -1454,7 +1455,7 @@ private loadUserInfo(): void {
const savedLastName = localStorage.getItem('userLastName');
const savedAvatar = localStorage.getItem('userAvatar');
const savedBanner = localStorage.getItem('userBanner');
// Mise à jour du nom dans la preview
if (savedName) {
const previewName = this.shadowRoot?.querySelector('.preview-name');
@ -1470,7 +1471,7 @@ private loadUserInfo(): void {
previewLastName.textContent = savedLastName;
}
}
// Mise à jour de l'avatar dans la preview
if (savedAvatar) {
const previewAvatar = this.shadowRoot?.querySelector('.preview-avatar') as HTMLImageElement;
@ -1478,7 +1479,7 @@ private loadUserInfo(): void {
previewAvatar.src = savedAvatar;
}
}
// Mise à jour de la bannière dans la preview
if (savedBanner) {
const previewBanner = this.shadowRoot?.querySelector('.preview-banner-img') as HTMLImageElement;
@ -1502,11 +1503,11 @@ private updateNavbarLastName(lastName: string): void {
}
}
private updateProfilePreview(data: {
avatar?: string,
banner?: string,
name?: string,
lastName?: string
private updateProfilePreview(data: {
avatar?: string,
banner?: string,
name?: string,
lastName?: string
}): void {
if (data.avatar) {
const previewAvatar = this.shadowRoot?.querySelector('.preview-avatar') as HTMLImageElement;
@ -1544,7 +1545,7 @@ private initializeEventListeners() {
input.type = 'text';
input.value = currentValue;
input.className = 'edit-input';
field.textContent = '';
field.appendChild(input);
field.classList.add('editing');
@ -1566,8 +1567,8 @@ private showQRCodeModal(pairingId: string): void {
modal.innerHTML = `
<div class="qr-modal-content">
<span class="close-qr-modal">&times;</span>
<img src="https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${pairingId}"
alt="QR Code Large"
<img src="https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${pairingId}"
alt="QR Code Large"
class="qr-code-large">
<div class="qr-address">${decodeURIComponent(pairingId)}</div>
</div>

View File

@ -1,4 +1,4 @@
import { ProcessState } from '.././pkg/sdk_client.js';
import { ProcessState } from '../../../pkg/sdk_client.js';
import Services from '../../services/service';
interface State {
@ -171,8 +171,8 @@ export function getDocumentValidation(container: HTMLElement) {
) {
state.certificate = json as ProcessState;
state.commitmentHashes = Object.values(json.pcd_commitment).map((h: string) =>
h.toLowerCase()
state.commitmentHashes = Object.values(json.pcd_commitment).map((value: unknown, _idx: number, _arr: unknown[]) =>
String(value).toLowerCase()
);
updateVisuals(file);
@ -206,7 +206,7 @@ export function getDocumentValidation(container: HTMLElement) {
const commitedIn = state.certificate.commited_in;
if (!commitedIn) return;
const [prevTxid, prevTxVout] = commitedIn.split(':');
const processId = state.certificate.process_id;
const processId = (state.certificate as any).process_id as string;
const stateId = state.certificate.state_id;
const process = await service.getProcess(processId);
if (!process) return;

View File

@ -1,4 +1,4 @@
import { ValidationRule, RoleDefinition } from '.././pkg/sdk_client.js';
import { ValidationRule, RoleDefinition } from '../../../pkg/sdk_client.js';
import { showValidationRuleModal } from '../../components/validation-rule-modal/validation-rule-modal';
export function createKeyValueSection(title: string, id: string, isRoleSection = false) {

View File

@ -1,7 +1,7 @@
import { createKeyValueSection } from './key-value-section';
import { loadValidationRuleModal } from '../../components/validation-rule-modal/validation-rule-modal';
import Services from '../../services/service';
import { RoleDefinition } from '.././pkg/sdk_client.js';
import { RoleDefinition } from '../../../pkg/sdk_client.js';
export async function getProcessCreation(container: HTMLElement) {
await loadValidationRuleModal();
@ -55,11 +55,13 @@ export async function getProcessCreation(container: HTMLElement) {
await service.handleApiReturn(approveChangeResult);
if (approveChangeResult) {
const process = await service.getProcess(processId);
let newState = service.getStateFromId(process, stateId);
if (!process) return;
const newState = service.getStateFromId(process, stateId);
if (!newState) return;
for (const label of Object.keys(newState.keys)) {
const hash = newState.pcd_commitment[label];
const encryptedData = await service.getBlobFromDb(hash);
if (!encryptedData) continue;
const filename = `${label}-${hash.slice(0,8)}.bin`;
const blob = new Blob([encryptedData], { type: "application/octet-stream" });
@ -71,11 +73,13 @@ export async function getProcessCreation(container: HTMLElement) {
setTimeout(() => URL.revokeObjectURL(link.href), 1000);
}
await service.generateProcessPdf(processId, newState);
if (typeof (service as any).generateProcessPdf === 'function') {
await (service as any).generateProcessPdf(processId, newState);
}
// Add processId to the state we export
newState['process_id'] = processId;
const blob = new Blob([JSON.stringify(newState, null, 2)], { type: 'application/json' });
(newState as any)['process_id'] = processId;
const blob = new Blob([JSON.stringify(newState as unknown as object, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');

View File

@ -5,7 +5,7 @@
}
import { membersMock } from '../../mocks/mock-signature/membersMocks';
import { ApiReturn, Device, Member, Process, RoleDefinition } from '.././pkg/sdk_client.js';
import { ApiReturn, Device, Member, Process, RoleDefinition } from '../pkg/sdk_client.js';
import { getCorrectDOM } from '../../utils/document.utils';
import chatStyle from '../../../public/style/chat.css?inline';
import { addressToEmoji } from '../../utils/sp-address.utils';

View File

@ -1,6 +1,6 @@
import { interpolate } from '../../utils/html.utils';
import Services from '../../services/service';
import { Process } from '.././pkg/sdk_client.js';
import { Process } from '../../../pkg/sdk_client.js';
import { getCorrectDOM } from '~/utils/document.utils';
let currentPageStyle: HTMLStyleElement | null = null;

View File

@ -10,7 +10,7 @@ import { prepareAndSendPairingTx } from './utils/sp-address.utils';
import ModalService from './services/modal.service';
import { MessageType } from './models/process.model';
import { splitPrivateData, isValid32ByteHex } from './utils/service.utils';
import { MerkleProofResult } from '.././pkg/sdk_client.js';
import { MerkleProofResult } from '../pkg/sdk_client.js';
const routes: { [key: string]: string } = {
home: '/src/pages/home/home.html',

View File

@ -4,7 +4,7 @@ import validationModalStyle from '../components/validation-modal/validation-moda
import Services from './service';
import { init, navigate } from '../router';
import { addressToEmoji } from '../utils/sp-address.utils';
import { RoleDefinition } from '.././pkg/sdk_client.js';
import { RoleDefinition } from '../../pkg/sdk_client.js';
import { initValidationModal } from '~/components/validation-modal/validation-modal';
import { interpolate } from '~/utils/html.utils';

View File

@ -438,7 +438,7 @@ export default class Services {
}
} catch (error) {
// Vérifier si l'erreur est liée à des fonds insuffisants
const errorMessage = error.toString().toLowerCase();
const errorMessage = String(error).toLowerCase();
if (errorMessage.includes('insufficient funds') || errorMessage.includes('missing') && errorMessage.includes('sats')) {
console.log('🔍 Fonds insuffisants détectés, tentative de transfert automatique...');
@ -466,11 +466,11 @@ export default class Services {
}
} catch (transferError) {
console.error('❌ Échec du transfert automatique de fonds:', transferError);
throw new Error(`Failed to create process due to insufficient funds and automatic transfer failed: ${transferError}`);
throw new Error(`Failed to create process due to insufficient funds and automatic transfer failed: ${String(transferError)}`);
}
} else {
// Re-lancer l'erreur originale si ce n'est pas un problème de fonds
throw error;
throw (error instanceof Error ? error : new Error(String(error)));
}
}
}
@ -515,7 +515,8 @@ export default class Services {
// une API ou un service pour déclencher le transfert
throw new Error('Fallback not implemented in browser environment');
} catch (fallbackError) {
throw new Error(`Automatic funds transfer failed: ${error.message}`);
const errMsg = (error instanceof Error) ? error.message : String(error);
throw new Error(`Automatic funds transfer failed: ${errMsg}`);
}
}
}
@ -1747,8 +1748,10 @@ export default class Services {
public hexToBlob(hexString: string): Blob {
const uint8Array = this.hexToUInt8Array(hexString);
return new Blob([uint8Array], { type: "application/octet-stream" });
// Ensure BlobPart compatibility by passing ArrayBuffer
// Use a copy to ensure a regular ArrayBuffer, avoiding SharedArrayBuffer issues
const copy = new Uint8Array(uint8Array);
return new Blob([copy.buffer.slice(0)], { type: "application/octet-stream" });
}
public hexToUInt8Array(hexString: string): Uint8Array {