From 1543002030d45c96e724284aefc715b6642d68e2 Mon Sep 17 00:00:00 2001 From: Ramon Candel Segura Date: Thu, 26 Mar 2026 10:09:27 +0100 Subject: [PATCH 1/4] Added typescript check, precommit ts check, ci typescript check, and fixed typescript errors --- .github/workflows/ci.yml | 20 +++++++++ .husky/pre-commit | 2 +- package.json | 3 +- src/app/analytics/impact.service.test.ts | 19 ++++----- src/app/crypto/services/pgp.service.ts | 6 ++- .../FileImageViewer/FileImageViewer.tsx | 16 ++++++-- .../viewers/FilePdfViewer/FilePdfViewer.tsx | 17 +++++--- .../services/download.service/downloadFile.ts | 8 ++-- .../worker.service/downloadWorkerHandler.ts | 2 +- src/app/drive/types/index.ts | 2 +- src/app/network/NetworkFacade.ts | 8 ++-- src/app/network/download.ts | 41 +++---------------- src/app/store/slices/ui/index.ts | 2 +- src/app/workers/downloadWorker.ts | 2 +- src/components/BreadcrumbsBackupsView.tsx | 2 +- src/libs/streamSaver/index.ts | 5 ++- src/libs/streamSaver/types.ts | 2 +- src/react-app-env.d.ts | 7 ++++ src/services/auth.service.test.ts | 8 ++-- src/services/auth.service.ts | 19 ++++----- .../sockets/event-handler.service.test.ts | 8 +++- src/services/workspace.service.test.ts | 2 +- src/utils/isFileEmpty.test.ts | 6 +++ src/utils/isFileEmpty.ts | 5 ++- src/views/Backups/BackupsView.tsx | 2 +- .../Backups/components/DeleteBackupDialog.tsx | 2 +- src/views/Backups/components/DeviceList.tsx | 2 +- .../Backups/components/DeviceListItem.tsx | 2 +- .../hooks/useBackupDeviceActions.test.ts | 2 +- .../Backups/hooks/useBackupDeviceActions.ts | 5 +-- .../hooks/useBackupListActions.test.ts | 2 +- .../Backups/hooks/useBackupListActions.ts | 2 +- src/views/Backups/store/backupsSlice.test.ts | 2 +- src/views/Backups/store/backupsSlice.ts | 2 +- .../Checkout/hooks/useAuthCheckout.test.ts | 8 ++-- src/views/Checkout/hooks/useProducts.test.ts | 4 +- src/views/Drive/DriveView.tsx | 24 +++++------ .../Drive/components/CreateFolderDialog.tsx | 20 ++++----- .../components/VersionHistory/Sidebar.tsx | 38 ++++++++--------- tsconfig.json | 2 +- yarn.lock | 8 ++-- 41 files changed, 183 insertions(+), 156 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09cdf97ead..e2aab09af5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,26 @@ jobs: run: yarn playwright install - name: Unit test run run: yarn test + typecheck: + runs-on: ubuntu-22.04 + strategy: + matrix: + node-version: [20.x] + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + registry-url: 'https://npm.pkg.github.com' + - run: echo "registry=https://registry.yarnpkg.com/" > .npmrc + - run: echo "@internxt:registry=https://npm.pkg.github.com" >> .npmrc + - run: echo //npm.pkg.github.com/:_authToken=${{ secrets.PERSONAL_ACCESS_TOKEN }} >> .npmrc + - run: echo "always-auth=true" >> .npmrc + - name: Install + run: yarn + - name: Typecheck + run: yarn typecheck build: runs-on: ubuntu-latest strategy: diff --git a/.husky/pre-commit b/.husky/pre-commit index 34ba8d7cee..e2343c5f4d 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,5 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -yarn run lint:fix; npx lint-staged; yarn run test +yarn run lint:fix; npx lint-staged; yarn run typecheck; yarn run test diff --git a/package.json b/package.json index 5737adfc7c..8e4563d4e1 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "lint:scss:fix": "yarn run lint:scss --fix", "lint": "yarn run lint:ts && yarn run lint:scss", "lint:fix": "yarn run lint:ts:fix && yarn run lint:scss:fix", + "typecheck": "tsc --noEmit", "analyze": "cross-env ANALYZE=true yarn build", "add:npmrc": "echo \"registry=https://registry.yarnpkg.com/\" >> .npmrc && echo \"@internxt:registry=https://npm.pkg.github.com\" >> .npmrc && echo \"//npm.pkg.github.com/:_authToken=$NPM_TOKEN\" >> .npmrc && echo \"always-auth=true\" >> .npmrc" }, @@ -137,7 +138,7 @@ "@types/react-redux": "^7.1.17", "@types/react-router-dom": "^5.3.3", "@types/react-window": "^1.8.8", - "@types/wicg-file-system-access": "^2020.9.4", + "@types/wicg-file-system-access": "^2023.10.7", "@vitejs/plugin-react": "=4.5.1", "@vitest/coverage-istanbul": "2.1.9", "autoprefixer": "^10.4.16", diff --git a/src/app/analytics/impact.service.test.ts b/src/app/analytics/impact.service.test.ts index cf9e32253a..8c991a47e2 100644 --- a/src/app/analytics/impact.service.test.ts +++ b/src/app/analytics/impact.service.test.ts @@ -1,14 +1,13 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { savePaymentDataInLocalStorage, trackPaymentConversion, trackSignUp } from './impact.service'; import { PriceWithTax } from '@internxt/sdk/dist/payments/types'; -import { getProductAmount } from 'views/Checkout/utils'; -import axios from 'axios'; -import localStorageService from 'services/local-storage.service'; import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; import { bytesToString } from 'app/drive/services/size.service'; -import errorService from 'services/error.service'; -import dayjs from 'dayjs'; +import axios from 'axios'; import envService from 'services/env.service'; +import errorService from 'services/error.service'; +import localStorageService from 'services/local-storage.service'; +import { getProductAmount } from 'views/Checkout/utils'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { savePaymentDataInLocalStorage, trackPaymentConversion, trackSignUp } from './impact.service'; vi.mock('services/local-storage.service', () => ({ default: { @@ -201,7 +200,7 @@ describe('Testing Impact Service', () => { await trackSignUp(mockedUserUuid); - const callArgs = axiosSpy.mock.calls[0][1]; + const callArgs = axiosSpy.mock.calls[0][1] as { messageId: string }; expect(callArgs).toHaveProperty('messageId'); expect(callArgs.messageId).toBe(mockedUserUuid); }); @@ -259,7 +258,7 @@ describe('Testing Impact Service', () => { await trackPaymentConversion(); - const callArgs = axiosSpy.mock.calls[0][1]; + const callArgs = axiosSpy.mock.calls[0][1] as { messageId: string; properties: Record }; expect(callArgs.properties.impact_value).toBe(0.01); }); @@ -268,7 +267,7 @@ describe('Testing Impact Service', () => { await trackPaymentConversion(); - const callArgs = axiosSpy.mock.calls[0][1]; + const callArgs = axiosSpy.mock.calls[0][1] as { messageId: string; properties: Record }; expect(callArgs.properties).toHaveProperty('order_promo_code', promoCode.codeName); }); diff --git a/src/app/crypto/services/pgp.service.ts b/src/app/crypto/services/pgp.service.ts index 4035db72eb..83c955e2e4 100644 --- a/src/app/crypto/services/pgp.service.ts +++ b/src/app/crypto/services/pgp.service.ts @@ -1,9 +1,11 @@ import { Buffer } from 'buffer'; -import { Data, MaybeStream, WebStream, PrivateKey, PublicKey, Message } from 'openpgp'; +import { MaybeStream, Message, PrivateKey, PublicKey, WebStream } from 'openpgp'; + import kemBuilder from '@dashlane/pqc-kem-kyber512-browser'; import { extendSecret } from './utils'; const WORDS_HYBRID_MODE_IN_BASE64 = 'SHlicmlkTW9kZQ=='; // 'HybridMode' in BASE64 format +type Data = Uint8Array | string; export async function getOpenpgp(): Promise { return import('openpgp'); @@ -32,7 +34,7 @@ export async function generateNewKeys(): Promise<{ const { privateKey, publicKey, revocationCertificate } = await openpgp.generateKey({ userIDs: [{ email: 'inxt@inxt.com' }], - curve: 'ed25519', + curve: 'ed25519Legacy', }); const kem = await kemBuilder(); diff --git a/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx b/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx index f5a75b4ece..e0b5ba29be 100644 --- a/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx +++ b/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from 'react'; import heic2any from 'heic2any'; +import { useEffect, useState } from 'react'; import { FormatFileViewerProps } from '../../FileViewer'; @@ -25,10 +25,14 @@ const FileImageViewer = ({ const updatedFile = { ...file }; handlersForSpecialItems?.handleUpdateProgress(PROGRESS_BAR_STATUS.PENDING); + if (!blob) { + return; + } const convertedBlob = await heic2any({ blob: blob }); updatedFile.type = 'png'; setImageBlob(convertedBlob as Blob); + handlersForSpecialItems?.handleUpdateProgress(PROGRESS_BAR_STATUS.COMPLETED); await handlersForSpecialItems?.handleUpdateThumbnail(updatedFile, convertedBlob as Blob); } else { @@ -37,7 +41,6 @@ const FileImageViewer = ({ } catch (error) { console.error('Error converting HEIC to another format:', error); setIsPreviewAvailable(false); - } finally { handlersForSpecialItems?.handleUpdateProgress(PROGRESS_BAR_STATUS.COMPLETED); } }; @@ -59,7 +62,14 @@ const FileImageViewer = ({ return (
- + {fileUrl && ( + setIsPreviewAvailable(false)} + /> + )}
); diff --git a/src/app/drive/components/FileViewer/viewers/FilePdfViewer/FilePdfViewer.tsx b/src/app/drive/components/FileViewer/viewers/FilePdfViewer/FilePdfViewer.tsx index c52584b3f6..d9813fdce1 100644 --- a/src/app/drive/components/FileViewer/viewers/FilePdfViewer/FilePdfViewer.tsx +++ b/src/app/drive/components/FileViewer/viewers/FilePdfViewer/FilePdfViewer.tsx @@ -1,8 +1,8 @@ -import { Document, Page, pdfjs } from 'react-pdf'; -import { useState, useEffect } from 'react'; -import { FormatFileViewerProps } from '../../FileViewer'; import { MagnifyingGlassMinus, MagnifyingGlassPlus } from '@phosphor-icons/react'; import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; +import { useEffect, useState } from 'react'; +import { Document, Page, pdfjs } from 'react-pdf'; +import { FormatFileViewerProps } from '../../FileViewer'; import workerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?raw'; const blob = new Blob([workerUrl], { type: 'application/javascript' }); @@ -59,9 +59,9 @@ const PageWithObserver: React.FC = ({ pageNumber, zoom, o }; const DEFAULT_ZOOM = 1; -const FilePdfViewer = (props: FormatFileViewerProps): JSX.Element => { +const FilePdfViewer = ({ blob, setIsPreviewAvailable }: FormatFileViewerProps): JSX.Element => { const { translate } = useTranslationContext(); - const [fileUrl] = useState(URL.createObjectURL(new Blob([props.blob], { type: 'application/pdf' }))); + const [fileUrl] = useState(blob ? URL.createObjectURL(new Blob([blob], { type: 'application/pdf' })) : null); const [numPages, setNumPages] = useState(0); const [currentPage, setCurrentPage] = useState(1); const [zoom, setZoom] = useState(DEFAULT_ZOOM); @@ -99,7 +99,12 @@ const FilePdfViewer = (props: FormatFileViewerProps): JSX.Element => {
- + setIsPreviewAvailable(false)} + >
{Array.from(new Array(renderPages), (el, index) => ( { const reader = readable.getReader(); diff --git a/src/app/drive/services/worker.service/downloadWorkerHandler.ts b/src/app/drive/services/worker.service/downloadWorkerHandler.ts index bbdf5f8b34..b3c77cc10e 100644 --- a/src/app/drive/services/worker.service/downloadWorkerHandler.ts +++ b/src/app/drive/services/worker.service/downloadWorkerHandler.ts @@ -42,7 +42,7 @@ export class DownloadWorkerHandler { }: HandleWorkerMessagesPayload) { const fileName = itemData.plainName ?? itemData.name; const completeFilename = downloadName || (itemData.type ? `${fileName}.${itemData.type}` : fileName); - const downloadId = itemData.fileId; + const downloadId = itemData.fileId as string; const fileSize = itemData.size; return new Promise((resolve, reject) => { diff --git a/src/app/drive/types/index.ts b/src/app/drive/types/index.ts index 96ace2f643..1beb2c2518 100644 --- a/src/app/drive/types/index.ts +++ b/src/app/drive/types/index.ts @@ -46,7 +46,7 @@ export interface DriveFileData { deleted: boolean; deletedAt: null; encrypt_version: string; - fileId: string; + fileId: string | null; folderId: number; folder_id: number; folderUuid: string; diff --git a/src/app/network/NetworkFacade.ts b/src/app/network/NetworkFacade.ts index 4a9e7c7b18..bce020546c 100644 --- a/src/app/network/NetworkFacade.ts +++ b/src/app/network/NetworkFacade.ts @@ -6,23 +6,23 @@ import { validateMnemonic } from 'bip39'; import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; import { EncryptFileFunction, UploadFileMultipartFunction } from '@internxt/sdk/dist/network'; +import { queue, QueueObject } from 'async'; import envService from 'services/env.service'; import { buildProgressStream, decryptStream } from 'services/stream.service'; -import { queue, QueueObject } from 'async'; +import { WORKER_MESSAGE_STATES } from 'app/drive/services/worker.service/types/upload'; import { waitForContinueUploadSignal } from 'app/drive/services/worker.service/uploadWorkerUtils'; import { TaskStatus } from '../tasks/types'; import { encryptStreamInParts, generateFileKey, getEncryptedFile, processEveryFileBlobReturnHash } from './crypto'; import { DownloadProgressCallback, getDecryptedStream } from './download'; -import { uploadFileUint8Array, UploadProgressCallback } from './upload-utils'; -import { UPLOAD_CHUNK_SIZE, ALLOWED_CHUNK_OVERHEAD } from './networkConstants'; -import { WORKER_MESSAGE_STATES } from 'app/drive/services/worker.service/types/upload'; import { DownloadAbortedByUserError, DownloadFailedWithUnknownError, NoContentReceivedError, } from './errors/download.errors'; +import { ALLOWED_CHUNK_OVERHEAD, UPLOAD_CHUNK_SIZE } from './networkConstants'; import { DownloadChunkPayload } from './types/index'; +import { uploadFileUint8Array, UploadProgressCallback } from './upload-utils'; interface UploadOptions { uploadingCallback: UploadProgressCallback; diff --git a/src/app/network/download.ts b/src/app/network/download.ts index ec427972cf..c581b12aa3 100644 --- a/src/app/network/download.ts +++ b/src/app/network/download.ts @@ -1,7 +1,6 @@ import { createDecipheriv, Decipher } from 'crypto'; import { buildProgressStream, joinReadableBinaryStreams } from 'services/stream.service'; -import { Abortable } from './Abortable'; import { getFileInfoWithAuth, getFileInfoWithToken, getMirrors, Mirror } from './requests'; import { FileVersionOneError } from '@internxt/sdk/dist/network/download'; @@ -11,28 +10,7 @@ import { generateFileKey } from './crypto'; import downloadFileV2, { multipartDownload } from './download/v2'; export type DownloadProgressCallback = (totalBytes: number, downloadedBytes: number) => void; -export type Downloadable = { fileId: string; bucketId: string }; - -type BinaryStream = ReadableStream; - -export async function binaryStreamToBlob(stream: BinaryStream): Promise { - const reader = stream.getReader(); - const slices: Uint8Array[] = []; - - let finish = false; - - while (!finish) { - const { done, value } = await reader.read(); - - if (!done) { - slices.push(value as Uint8Array); - } - - finish = done; - } - - return new Blob(slices); -} +export type Downloadable = { fileId: string | null; bucketId: string }; interface FileInfo { bucket: string; @@ -115,7 +93,7 @@ export interface NetworkCredentials { export interface IDownloadParams { bucketId: string; - fileId: string; + fileId: string | null; creds?: NetworkCredentials; mnemonic?: string; encryptionKey?: Buffer; @@ -169,7 +147,8 @@ export function downloadFile(params: IDownloadParams): Promise> { - const downloadMultipartPromise = multipartDownload(params); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const downloadMultipartPromise = multipartDownload(params as any); return downloadMultipartPromise.catch((err) => { if (err instanceof FileVersionOneError) { @@ -186,9 +165,9 @@ export async function _downloadFile(params: IDownloadParams): Promise, Abortable] { - const downloadStreamPromise = downloadFile(params); - - return [downloadStreamPromise.then((readable) => readable.pipeTo(params.destination)), { abort: () => null }]; -} diff --git a/src/app/store/slices/ui/index.ts b/src/app/store/slices/ui/index.ts index bb8a0ad3a8..e637d49659 100644 --- a/src/app/store/slices/ui/index.ts +++ b/src/app/store/slices/ui/index.ts @@ -1,7 +1,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { DriveItemData, DriveItemDetails, FileInfoMenuItem, UpgradePlanDialogInfo } from 'app/drive/types'; import { PreviewFileItem } from '../../../share/types'; -import { FileVersion } from 'views/Drive/components/VersionHistory/types'; +import { FileVersion } from '@internxt/sdk/dist/drive/storage/types'; interface UISliceState { isSidenavCollapsed: boolean; diff --git a/src/app/workers/downloadWorker.ts b/src/app/workers/downloadWorker.ts index 81582df086..1c10b782d6 100644 --- a/src/app/workers/downloadWorker.ts +++ b/src/app/workers/downloadWorker.ts @@ -49,7 +49,7 @@ export class DownloadWorker { await this.downloadUsingChunks(streamReader, callbacks.onChunk); } - callbacks.onSuccess(file.fileId); + callbacks.onSuccess(file.fileId as string); } catch (err) { console.log('[DOWNLOAD-WORKER] ERROR -->', err); callbacks.onError(err); diff --git a/src/components/BreadcrumbsBackupsView.tsx b/src/components/BreadcrumbsBackupsView.tsx index af846e2892..e16a546cc2 100644 --- a/src/components/BreadcrumbsBackupsView.tsx +++ b/src/components/BreadcrumbsBackupsView.tsx @@ -1,4 +1,4 @@ -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; import { useAppDispatch, useAppSelector } from 'app/store/hooks'; import { backupsActions } from 'views/Backups/store/backupsSlice'; import { t } from 'i18next'; diff --git a/src/libs/streamSaver/index.ts b/src/libs/streamSaver/index.ts index dcafc5d90f..4fc7f305ed 100644 --- a/src/libs/streamSaver/index.ts +++ b/src/libs/streamSaver/index.ts @@ -1,5 +1,5 @@ /* eslint-disable quotes */ -import { StreamSaverOptions, MitmTransporter, ServiceWorkerMessage, ServiceWorkerResponse } from './types'; +import { MitmTransporter, ServiceWorkerMessage, ServiceWorkerResponse, StreamSaverOptions } from './types'; export const STREAM_SAVER_MITM = '/streamsaver/mitm.html?version=2.0.0'; @@ -40,7 +40,8 @@ export class StreamSaver { const transport: MitmTransporter = { frame: iframe, loaded: false, - postMessage: (...args: any[]) => iframe.contentWindow?.postMessage(...args), + postMessage: (message: ServiceWorkerMessage, targetOrigin: string, transfer?: Transferable[]) => + iframe.contentWindow?.postMessage(message, targetOrigin, transfer), remove: () => iframe.remove(), addEventListener: (type: string, listener: EventListener, options?: AddEventListenerOptions) => iframe.addEventListener(type, listener, options), diff --git a/src/libs/streamSaver/types.ts b/src/libs/streamSaver/types.ts index e2a1056af0..ca1ca74cab 100644 --- a/src/libs/streamSaver/types.ts +++ b/src/libs/streamSaver/types.ts @@ -7,7 +7,7 @@ export interface StreamSaverOptions { export interface MitmTransporter { frame?: Window | HTMLIFrameElement | null; loaded: boolean; - postMessage: (...args: any[]) => void; + postMessage: (message: ServiceWorkerMessage, targetOrigin: string, transfer: Transferable[]) => void; remove: () => void; addEventListener: (type: string, listener: EventListener, options?: AddEventListenerOptions) => void; removeEventListener: (type: string, listener: EventListener) => void; diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index b6a47d96f2..1055debee1 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -77,6 +77,13 @@ interface ReferralBootOptions { themeMode?: 'light' | 'dark'; } +interface Array { + toSorted(compareFn?: (a: T, b: T) => number): T[]; + toReversed(): T[]; + toSpliced(start: number, deleteCount?: number, ...items: T[]): T[]; + with(index: number, value: T): T[]; +} + interface Navigator { brave?: { isBrave: () => Promise }; diff --git a/src/services/auth.service.test.ts b/src/services/auth.service.test.ts index 6f7898fea7..4e5ca45945 100644 --- a/src/services/auth.service.test.ts +++ b/src/services/auth.service.test.ts @@ -726,8 +726,8 @@ describe('updateCredentialsWithToken', () => { expect(encryptedMnemonic).toBeDefined(); expect(keys).toBeDefined(); - expect(keys.ecc).toBe('mock-encrypted-data'); - expect(keys.kyber).toBeUndefined(); + expect(keys.private.ecc).toBe('mock-encrypted-data'); + expect(keys.private.kyber).toBeUndefined(); }); it('should successfully update credentials with token and with backup data (ECC and Kyber)', async () => { @@ -766,8 +766,8 @@ describe('updateCredentialsWithToken', () => { expect(encryptedMnemonic).toBeDefined(); expect(keys).toBeDefined(); - expect(keys.ecc).toBe('mock-encrypted-data'); - expect(keys.kyber).toBe('mock-encrypted-data'); + expect(keys.private.ecc).toBe('mock-encrypted-data'); + expect(keys.private.kyber).toBe('mock-encrypted-data'); }); it('should throw an error when mnemonic is invalid', async () => { diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 84cd99063d..ef5b993bd0 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,4 +1,5 @@ import { aes } from '@internxt/lib'; +import { AppError } from '@internxt/sdk'; import { CryptoProvider, Keys, @@ -13,10 +14,7 @@ import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; import { trackSignUp } from 'app/analytics/impact.service'; import { trackLead } from 'app/analytics/meta.service'; import { getCookie, setCookie } from 'app/analytics/utils'; -import localStorageService from 'services/local-storage.service'; -import navigationService from 'services/navigation.service'; -import RealtimeService from 'services/sockets/socket.service'; -import { AppError } from '@internxt/sdk'; +import { SdkFactory } from 'app/core/factory/sdk'; import { AppView } from 'app/core/types'; import { assertPrivateKeyIsValid, @@ -32,18 +30,20 @@ import { passToHash, } from 'app/crypto/services/utils'; import databaseService from 'app/database/services/database.service'; -import { AuthMethodTypes } from 'views/Checkout/types'; import { AppDispatch } from 'app/store'; import { planThunks } from 'app/store/slices/plan'; import { productsThunks } from 'app/store/slices/products'; import { initializeUserThunk, userActions, userThunks } from 'app/store/slices/user'; import { workspaceThunks } from 'app/store/slices/workspaces/workspacesStore'; -import { BackupData, detectBackupKeyFormat, prepareOldBackupRecoverPayloadForBackend } from 'utils/backupKeyUtils'; import { generateMnemonic, validateMnemonic } from 'bip39'; -import { SdkFactory } from 'app/core/factory/sdk'; import errorService from 'services/error.service'; -import vpnAuthService from './vpnAuth.service'; +import localStorageService from 'services/local-storage.service'; +import navigationService from 'services/navigation.service'; +import RealtimeService from 'services/sockets/socket.service'; import { generateCaptchaToken } from 'utils'; +import { BackupData, detectBackupKeyFormat, prepareOldBackupRecoverPayloadForBackend } from 'utils/backupKeyUtils'; +import { AuthMethodTypes } from 'views/Checkout/types'; +import vpnAuthService from './vpnAuth.service'; type ProfileInfo = { user: UserSettings; @@ -343,8 +343,7 @@ export const updateCredentialsWithToken = async ( const keys = encryptedEccPrivateKey || encryptedKyberPrivateKey ? { - ecc: encryptedEccPrivateKey, - kyber: encryptedKyberPrivateKey, + private: { ecc: encryptedEccPrivateKey, kyber: encryptedKyberPrivateKey }, } : undefined; diff --git a/src/services/sockets/event-handler.service.test.ts b/src/services/sockets/event-handler.service.test.ts index 49bd21d7eb..e2692bf2da 100644 --- a/src/services/sockets/event-handler.service.test.ts +++ b/src/services/sockets/event-handler.service.test.ts @@ -1,6 +1,7 @@ import { beforeEach, describe, expect, vi, test } from 'vitest'; import { EventHandler } from './event-handler.service'; import { SOCKET_EVENTS, EventData } from './types/socket.types'; +import { DriveItemData } from 'app/drive/types'; import { store } from 'app/store'; import { planActions, planThunks } from 'app/store/slices/plan'; import { storageActions } from 'app/store/slices/storage'; @@ -103,6 +104,9 @@ describe('Event Handler', () => { removed: false, removed_at: null, status: 'EXISTS' as const, + plain_name: 'test.txt', + thumbnails: [], + currentThumbnail: null, }; test('When a file is created, then it should push item to storage', () => { @@ -111,7 +115,7 @@ describe('Event Handler', () => { email: 'test@example.com', clientId: 'client-123', userId: 'user-123', - payload: mockFileItem, + payload: mockFileItem as unknown as DriveItemData, }; eventHandler.onFileCreated(eventData, 'folder-123'); @@ -137,7 +141,7 @@ describe('Event Handler', () => { email: 'test@example.com', clientId: 'client-123', userId: 'user-123', - payload: mockFileItem, + payload: mockFileItem as unknown as DriveItemData, }; eventHandler.onFileCreated(eventData, 'different-folder-123'); diff --git a/src/services/workspace.service.test.ts b/src/services/workspace.service.test.ts index 232972fe4b..de58ce4ca3 100644 --- a/src/services/workspace.service.test.ts +++ b/src/services/workspace.service.test.ts @@ -509,7 +509,7 @@ describe('workspace service', () => { activity: [WorkspaceLogType.ShareFile, WorkspaceLogType.ShareFolder], lastDays: 7, summary: true, - orderBy: 'createdAt:ASC', + orderBy: 'createdAt:ASC' as const, }, expectedCall: [ mockIds.workspaceId, diff --git a/src/utils/isFileEmpty.test.ts b/src/utils/isFileEmpty.test.ts index 08858f1592..de3fbfdc02 100644 --- a/src/utils/isFileEmpty.test.ts +++ b/src/utils/isFileEmpty.test.ts @@ -9,6 +9,12 @@ describe('Is file empty check', () => { expect(isFileEmpty(emptyFile)).toBe(true); }); + test('When the file has no fileId, then it should indicate it is empty', () => { + const emptyFile = { size: 0, fileId: null } as DriveFileData; + + expect(isFileEmpty(emptyFile)).toBe(true); + }); + test('When the file size is not 0, then it should indicate so', () => { const nonEmptyFile = new File(['content'], 'file.txt'); diff --git a/src/utils/isFileEmpty.ts b/src/utils/isFileEmpty.ts index a85de49a58..aebd6c793d 100644 --- a/src/utils/isFileEmpty.ts +++ b/src/utils/isFileEmpty.ts @@ -1,3 +1,6 @@ import { DriveFileData } from 'app/drive/types'; -export const isFileEmpty = (file: DriveFileData | File) => file.size === 0; +export const isFileEmpty = (file: DriveFileData | File) => { + if (file instanceof File) return file.size === 0; + return file.size === 0 || file.fileId === null; +}; diff --git a/src/views/Backups/BackupsView.tsx b/src/views/Backups/BackupsView.tsx index 28b11fedeb..37dc4aeaeb 100644 --- a/src/views/Backups/BackupsView.tsx +++ b/src/views/Backups/BackupsView.tsx @@ -12,7 +12,7 @@ import newStorageService from 'app/drive/services/new-storage.service'; import { deleteFile } from 'app/drive/services/file.service'; import { deleteItemsThunk } from 'app/store/slices/storage/storage.thunks/deleteItemsThunk'; import { DriveItemData } from 'app/drive/types'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; import { contextMenuSelectedBackupItems } from 'views/Drive/components/DriveExplorer/components'; import { useBackupListActions } from './hooks/useBackupListActions'; import { useBackupDeviceActions } from './hooks/useBackupDeviceActions'; diff --git a/src/views/Backups/components/DeleteBackupDialog.tsx b/src/views/Backups/components/DeleteBackupDialog.tsx index 80043ac9f9..99f3222f27 100644 --- a/src/views/Backups/components/DeleteBackupDialog.tsx +++ b/src/views/Backups/components/DeleteBackupDialog.tsx @@ -5,7 +5,7 @@ import { RootState } from 'app/store'; import { DriveFolderData as DriveWebFolderData, DriveItemData } from 'app/drive/types'; import { deleteItemsThunk } from 'app/store/slices/storage/storage.thunks/deleteItemsThunk'; import { backupsThunks } from '../store/backupsSlice'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; import { useState } from 'react'; import { Dialog } from '@internxt/ui'; diff --git a/src/views/Backups/components/DeviceList.tsx b/src/views/Backups/components/DeviceList.tsx index 7de72eb6a3..3402bc759a 100644 --- a/src/views/Backups/components/DeviceList.tsx +++ b/src/views/Backups/components/DeviceList.tsx @@ -5,7 +5,7 @@ import { DownloadSimple } from '@phosphor-icons/react'; import Empty from 'components/Empty'; import { useTranslationContext } from '../../../app/i18n/provider/TranslationProvider'; import { contextMenuBackupItems } from 'views/Drive/components/DriveExplorer/components'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; import { skinSkeleton } from 'components/Skeleton'; import { List } from '@internxt/ui'; import { Device } from '@internxt/sdk/dist/drive/backups/types'; diff --git a/src/views/Backups/components/DeviceListItem.tsx b/src/views/Backups/components/DeviceListItem.tsx index d1faf9c2e8..27f4e41d81 100644 --- a/src/views/Backups/components/DeviceListItem.tsx +++ b/src/views/Backups/components/DeviceListItem.tsx @@ -5,7 +5,7 @@ import UilDesktop from '@iconscout/react-unicons/icons/uil-desktop'; import dateService from 'services/date.service'; import sizeService from '../../../app/drive/services/size.service'; import { Device } from '@internxt/sdk/dist/drive/backups/types'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; type Item = (Device & { name: string; size: number }) | (DriveFolderData & { size: number }); diff --git a/src/views/Backups/hooks/useBackupDeviceActions.test.ts b/src/views/Backups/hooks/useBackupDeviceActions.test.ts index 896e79e4f9..1a8058f37e 100644 --- a/src/views/Backups/hooks/useBackupDeviceActions.test.ts +++ b/src/views/Backups/hooks/useBackupDeviceActions.test.ts @@ -5,7 +5,7 @@ import { backupsActions, backupsThunks } from '../store/backupsSlice'; import { deleteItemsThunk } from 'app/store/slices/storage/storage.thunks/deleteItemsThunk'; import backupsService from '../services/backups.service'; import { Device } from '@internxt/sdk/dist/drive/backups/types'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; import { useAppSelector } from 'app/store/hooks'; vi.mock('app/store/hooks', () => ({ diff --git a/src/views/Backups/hooks/useBackupDeviceActions.ts b/src/views/Backups/hooks/useBackupDeviceActions.ts index 19f82ab0d4..8f0a8e3655 100644 --- a/src/views/Backups/hooks/useBackupDeviceActions.ts +++ b/src/views/Backups/hooks/useBackupDeviceActions.ts @@ -1,6 +1,5 @@ -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; import backupsService from '../services/backups.service'; -import { DriveItemData, DriveFolderData as DriveWebFolderData } from 'app/drive/types'; +import { DriveItemData, DriveFolderData } from 'app/drive/types'; import { AppDispatch } from 'app/store'; import { useAppSelector } from 'app/store/hooks'; import { backupsActions, backupsThunks } from '../store/backupsSlice'; @@ -87,7 +86,7 @@ export const useBackupDeviceActions = ( await dispatch(backupsThunks.deleteDeviceThunk(selectedDevice)).unwrap(); } else { await dispatch(deleteItemsThunk([selectedDevice as DriveItemData])).unwrap(); - await backupsService.deleteBackupDeviceAsFolder((selectedDevice as DriveWebFolderData).uuid); + await backupsService.deleteBackupDeviceAsFolder((selectedDevice as DriveFolderData).uuid); await dispatch(backupsThunks.fetchDevicesThunk()); } } diff --git a/src/views/Backups/hooks/useBackupListActions.test.ts b/src/views/Backups/hooks/useBackupListActions.test.ts index 9eb3db26c6..f9c787ef1a 100644 --- a/src/views/Backups/hooks/useBackupListActions.test.ts +++ b/src/views/Backups/hooks/useBackupListActions.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach, Mock } from 'vitest'; import { useBackupListActions } from './useBackupListActions'; import { backupsActions } from '../store/backupsSlice'; import { DriveItemData } from 'app/drive/types'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; vi.mock('../store/backupsSlice', () => ({ backupsActions: { diff --git a/src/views/Backups/hooks/useBackupListActions.ts b/src/views/Backups/hooks/useBackupListActions.ts index 9fcda03675..14d6dd89ee 100644 --- a/src/views/Backups/hooks/useBackupListActions.ts +++ b/src/views/Backups/hooks/useBackupListActions.ts @@ -3,7 +3,7 @@ import { DriveItemData } from 'app/drive/types'; import { AppDispatch } from 'app/store'; import { backupsActions } from '../store/backupsSlice'; import { PreviewFileItem } from 'app/share/types'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; export const useBackupListActions = ( onBreadcrumbFolderChanges: Dispatch>, diff --git a/src/views/Backups/store/backupsSlice.test.ts b/src/views/Backups/store/backupsSlice.test.ts index a689868330..3caafee353 100644 --- a/src/views/Backups/store/backupsSlice.test.ts +++ b/src/views/Backups/store/backupsSlice.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import backupsReducer, { backupsActions, backupsThunks } from './backupsSlice'; import backupsService from '../services/backups.service'; import { Device } from '@internxt/sdk/dist/drive/backups/types'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; +import { DriveFolderData } from 'app/drive/types'; vi.mock('../services/backups.service', () => ({ default: { diff --git a/src/views/Backups/store/backupsSlice.ts b/src/views/Backups/store/backupsSlice.ts index 44b9534ef2..5ba8af38ed 100644 --- a/src/views/Backups/store/backupsSlice.ts +++ b/src/views/Backups/store/backupsSlice.ts @@ -2,8 +2,8 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import { RootState } from 'app/store'; import { Device, DeviceBackup } from '@internxt/sdk/dist/drive/backups/types'; +import { DriveFolderData } from 'app/drive/types'; import backupsService from '../services/backups.service'; -import { DriveFolderData } from '@internxt/sdk/dist/drive/storage/types'; type BackupDevice = Device | DriveFolderData; diff --git a/src/views/Checkout/hooks/useAuthCheckout.test.ts b/src/views/Checkout/hooks/useAuthCheckout.test.ts index 2cf9c15422..4eaee80f20 100644 --- a/src/views/Checkout/hooks/useAuthCheckout.test.ts +++ b/src/views/Checkout/hooks/useAuthCheckout.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, test, vi, beforeEach, Mock } from 'vitest'; -import { AuthCheckoutProps, useAuthCheckout } from './useAuthCheckout'; import { act, renderHook } from '@testing-library/react'; -import { authenticateUser } from 'services/auth.service'; import databaseService from 'app/database/services/database.service'; import { localStorageService, RealtimeService } from 'services'; +import { authenticateUser } from 'services/auth.service'; +import { beforeEach, describe, expect, Mock, test, vi } from 'vitest'; +import { AuthCheckoutProps, useAuthCheckout } from './useAuthCheckout'; vi.mock('services/auth.service', () => ({ authenticateUser: vi.fn(), @@ -115,7 +115,7 @@ describe('Authentication Checkout Custom hook', () => { expect(mockedAuthenticateUserProps.onAuthenticationFail).toHaveBeenCalled(); }); - test("When the user wants to log out, then all services are cleared and the auth method is set to 'sign up'", async () => { + test('When the user wants to log out, then all services are cleared and the auth method is set to \'sign up\'', async () => { const changeAuthMethod = vi.fn(); const stopRealTimeServiceSpy = vi.spyOn(RealtimeService.prototype, 'stop').mockResolvedValue(); diff --git a/src/views/Checkout/hooks/useProducts.test.ts b/src/views/Checkout/hooks/useProducts.test.ts index 6054e946d5..8a6f25d671 100644 --- a/src/views/Checkout/hooks/useProducts.test.ts +++ b/src/views/Checkout/hooks/useProducts.test.ts @@ -124,8 +124,8 @@ describe('Products custom hook', () => { await result.current.fetchSelectedPlan({ priceId: 'price_123', currency: 'eur', mobileToken: 'mobile_token' }); }); - expect(result.current.selectedPlan?.amount).toBe(0); - expect(result.current.selectedPlan?.decimalAmount).toBe(0); + expect((result.current.selectedPlan as any)?.amount).toBe(0); + expect((result.current.selectedPlan as any)?.decimalAmount).toBe(0); }); test('When fetching a plan returns a plan with seats, then the they are updated', async () => { diff --git a/src/views/Drive/DriveView.tsx b/src/views/Drive/DriveView.tsx index 78dbff8852..b33d780b4d 100644 --- a/src/views/Drive/DriveView.tsx +++ b/src/views/Drive/DriveView.tsx @@ -1,29 +1,29 @@ import { useEffect, useState } from 'react'; import { connect, useSelector } from 'react-redux'; -import errorService from 'services/error.service'; -import navigationService from 'services/navigation.service'; import { AppView } from 'app/core/types'; import fileService from 'app/drive/services/file.service'; import newStorageService from 'app/drive/services/new-storage.service'; -import BreadcrumbsDriveView from 'components/BreadcrumbsDriveView'; +import { DriveItemData, FolderPath } from 'app/drive/types'; +import useDriveNavigation from 'app/routes/hooks/Drive/useDrive'; import { AppDispatch, RootState } from 'app/store'; +import { useAppSelector } from 'app/store/hooks'; +import { fetchVersionLimitsThunk } from 'app/store/slices/fileVersions'; import { storageActions, storageSelectors } from 'app/store/slices/storage'; import storageThunks from 'app/store/slices/storage/storage.thunks'; import { uiActions } from 'app/store/slices/ui'; -import { Helmet } from 'react-helmet-async'; -import useDriveNavigation from 'app/routes/hooks/Drive/useDrive'; -import { useAppSelector } from 'app/store/hooks'; import workspacesSelectors from 'app/store/slices/workspaces/workspaces.selectors'; -import DriveExplorer from 'views/Drive/components/DriveExplorer/DriveExplorer'; -import { DriveItemData, FolderPath } from 'app/drive/types'; import { workspacesActions, workspaceThunks } from 'app/store/slices/workspaces/workspacesStore'; +import BreadcrumbsDriveView from 'components/BreadcrumbsDriveView'; +import { Helmet } from 'react-helmet-async'; +import { useHistory } from 'react-router-dom'; +import envService from 'services/env.service'; +import errorService from 'services/error.service'; import localStorageService from 'services/local-storage.service'; +import navigationService from 'services/navigation.service'; import { STORAGE_KEYS } from 'services/storage-keys'; import workspacesService from 'services/workspace.service'; -import { useHistory } from 'react-router-dom'; -import envService from 'services/env.service'; -import { fetchVersionLimitsThunk } from 'app/store/slices/fileVersions'; +import DriveExplorer from 'views/Drive/components/DriveExplorer/DriveExplorer'; export interface DriveViewProps { namePath: FolderPath[]; @@ -48,7 +48,7 @@ const DriveView = (props: DriveViewProps) => { dispatch(uiActions.setIsGlobalSearch(false)); dispatch(storageThunks.resetNamePathThunk()); dispatch(storageActions.clearSelectedItems()); - dispatch(fetchVersionLimitsThunk()); + dispatch(fetchVersionLimitsThunk({})); }, []); useEffect(() => { diff --git a/src/views/Drive/components/CreateFolderDialog.tsx b/src/views/Drive/components/CreateFolderDialog.tsx index cd2a5332b8..88267f2324 100644 --- a/src/views/Drive/components/CreateFolderDialog.tsx +++ b/src/views/Drive/components/CreateFolderDialog.tsx @@ -1,15 +1,15 @@ -import { useEffect, useState } from 'react'; -import { connect } from 'react-redux'; -import { useAppDispatch, useAppSelector } from 'app/store/hooks'; -import { RootState } from 'app/store'; -import { uiActions } from 'app/store/slices/ui'; -import storageThunks from 'app/store/slices/storage/storage.thunks'; -import storageSelectors from 'app/store/slices/storage/storage.selectors'; -import { Button, Modal, Input } from '@internxt/ui'; +import { Button, Input, Modal } from '@internxt/ui'; import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; -import errorService from 'services/error.service'; +import { RootState } from 'app/store'; +import { useAppDispatch, useAppSelector } from 'app/store/hooks'; import { storageActions } from 'app/store/slices/storage'; +import storageSelectors from 'app/store/slices/storage/storage.selectors'; +import storageThunks from 'app/store/slices/storage/storage.thunks'; import { fetchSortedFolderContentThunk } from 'app/store/slices/storage/storage.thunks/fetchSortedFolderContentThunk'; +import { uiActions } from 'app/store/slices/ui'; +import { useEffect, useState } from 'react'; +import { connect } from 'react-redux'; +import errorService from 'services/error.service'; interface CreateFolderDialogProps { onFolderCreated?: () => void; @@ -62,7 +62,7 @@ const CreateFolderDialog = ({ onFolderCreated, currentFolderId, neededFolderId } }, 500); }) .catch((e) => { - errorService.reportError(e, { extra: { folderName, parentFolderId: currentFolderId } }); + errorService.reportError(e); setHasError(true); setIsLoading(false); return e; diff --git a/src/views/Drive/components/VersionHistory/Sidebar.tsx b/src/views/Drive/components/VersionHistory/Sidebar.tsx index 2ab6a87561..c9ed73023d 100644 --- a/src/views/Drive/components/VersionHistory/Sidebar.tsx +++ b/src/views/Drive/components/VersionHistory/Sidebar.tsx @@ -1,32 +1,32 @@ -import { useState, useEffect, useCallback, useMemo } from 'react'; +import { FileVersion } from '@internxt/sdk/dist/drive/storage/types'; +import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; +import notificationsService, { ToastType } from 'app/notifications/services/notifications.service'; import { RootState } from 'app/store'; import { useAppDispatch, useAppSelector } from 'app/store/hooks'; -import { uiActions } from 'app/store/slices/ui'; -import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; +import { + fetchFileVersionsThunk, + fetchVersionLimitsThunk, + fileVersionsActions, + fileVersionsSelectors, +} from 'app/store/slices/fileVersions'; import storageSelectors from 'app/store/slices/storage/storage.selectors'; import { fetchSortedFolderContentThunk } from 'app/store/slices/storage/storage.thunks/fetchSortedFolderContentThunk'; +import { uiActions } from 'app/store/slices/ui'; import workspacesSelectors from 'app/store/slices/workspaces/workspaces.selectors'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { getDaysUntilExpiration } from 'services/date.service'; +import errorService from 'services/error.service'; import navigationService from 'services/navigation.service'; +import fileVersionService from 'views/Drive/services/fileVersion.service'; import { - Header, - CurrentVersionItem, - VersionItem, AutosaveSection, + CurrentVersionItem, + Header, + LockedFeatureModal, VersionActionDialog, VersionHistorySkeleton, - LockedFeatureModal, + VersionItem, } from './components'; -import fileVersionService from 'views/Drive/services/fileVersion.service'; -import errorService from 'services/error.service'; -import notificationsService, { ToastType } from 'app/notifications/services/notifications.service'; -import { - fetchFileVersionsThunk, - fetchVersionLimitsThunk, - fileVersionsActions, - fileVersionsSelectors, -} from 'app/store/slices/fileVersions'; -import { getDaysUntilExpiration } from 'services/date.service'; -import { FileVersion } from '@internxt/sdk/dist/drive/storage/types'; type VersionInfo = { id: string; updatedAt: string }; @@ -88,7 +88,7 @@ const Sidebar = () => { useEffect(() => { if (item) { setCurrentVersion({ - id: item.fileId, + id: item.uuid, updatedAt: item.updatedAt, }); } diff --git a/tsconfig.json b/tsconfig.json index 9ffaf5260e..9c1ec718e9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,7 @@ "noImplicitAny": false, "downlevelIteration": true, "incremental": true, - "types": ["vite/client", "vite-plugin-svgr/client"] + "types": ["vite/client", "vite-plugin-svgr/client", "wicg-file-system-access"] }, "types": ["filesystem", "@types/react"], "include": ["src", "vite-env.d.ts"] diff --git a/yarn.lock b/yarn.lock index 0dfa6a746d..c4c245bb4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3529,10 +3529,10 @@ resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.1.tgz#a62d1d2b7f34376da84ee0afe0145152e62b9699" integrity sha512-ywJmriP+nvjBKNBEMaNZgj2irZHoxcKeYcyMLbqhYKbDVn8yCIULy2Ol/tvIb37O3IBeZj3RU4tXqQTtGwoAMg== -"@types/wicg-file-system-access@^2020.9.4": - version "2020.9.8" - resolved "https://registry.yarnpkg.com/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.8.tgz#a8b739854ccb74b8048ef607d3701e9d506830e7" - integrity sha512-ggMz8nOygG7d/stpH40WVaNvBwuyYLnrg5Mbyf6bmsj/8+gb6Ei4ZZ9/4PNpcPNTT8th9Q8sM8wYmWGjMWLX/A== +"@types/wicg-file-system-access@^2023.10.7": + version "2023.10.7" + resolved "https://registry.yarnpkg.com/@types/wicg-file-system-access/-/wicg-file-system-access-2023.10.7.tgz#fab9868dfe0743003be6cb8c37ae480c9ce8537b" + integrity sha512-g49ijasEJvCd7ifmAY2D0wdEtt1xRjBbA33PJTiv8mKBr7DoMsPeISoJ8oQOTopSRi+FBWPpPW5ouDj2QPKtGA== "@types/yargs-parser@*": version "21.0.1" From 962d6e955d0941aff6537c7168b58251f8281dca Mon Sep 17 00:00:00 2001 From: Ramon Candel Segura Date: Thu, 26 Mar 2026 12:37:39 +0100 Subject: [PATCH 2/4] simplify VersionInfo type and clean up Sidebar component, removed unnecessary token from typecheck action --- .github/workflows/ci.yml | 9 ++------- .../Drive/components/VersionHistory/Sidebar.tsx | 14 +++----------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2aab09af5..664f2d3e7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,16 +59,11 @@ jobs: matrix: node-version: [20.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} - registry-url: 'https://npm.pkg.github.com' - - run: echo "registry=https://registry.yarnpkg.com/" > .npmrc - - run: echo "@internxt:registry=https://npm.pkg.github.com" >> .npmrc - - run: echo //npm.pkg.github.com/:_authToken=${{ secrets.PERSONAL_ACCESS_TOKEN }} >> .npmrc - - run: echo "always-auth=true" >> .npmrc - name: Install run: yarn - name: Typecheck diff --git a/src/views/Drive/components/VersionHistory/Sidebar.tsx b/src/views/Drive/components/VersionHistory/Sidebar.tsx index c9ed73023d..1fe39d48ea 100644 --- a/src/views/Drive/components/VersionHistory/Sidebar.tsx +++ b/src/views/Drive/components/VersionHistory/Sidebar.tsx @@ -28,7 +28,7 @@ import { VersionItem, } from './components'; -type VersionInfo = { id: string; updatedAt: string }; +type VersionInfo = { updatedAt: string }; const EMPTY_ARRAY: FileVersion[] = []; @@ -56,7 +56,6 @@ const Sidebar = () => { const [selectedAutosaveVersions, setSelectedAutosaveVersions] = useState>(new Set()); const [isBatchDeleteMode, setIsBatchDeleteMode] = useState(false); const [currentVersion, setCurrentVersion] = useState({ - id: '', updatedAt: '', }); @@ -88,7 +87,6 @@ const Sidebar = () => { useEffect(() => { if (item) { setCurrentVersion({ - id: item.uuid, updatedAt: item.updatedAt, }); } @@ -191,10 +189,9 @@ const Sidebar = () => { if (!versionToRestore || !item) return; try { - const restoredVersion = await fileVersionService.restoreVersion(item.uuid, versionToRestore.id); + await fileVersionService.restoreVersion(item.uuid, versionToRestore.id); setCurrentVersion({ - id: restoredVersion.fileId as string, updatedAt: new Date().toISOString(), }); @@ -263,12 +260,7 @@ const Sidebar = () => { ) : ( <> - + Date: Thu, 26 Mar 2026 12:48:44 +0100 Subject: [PATCH 3/4] handle blob check before conversion in FileImageViewer --- .../viewers/FileImageViewer/FileImageViewer.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx b/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx index e0b5ba29be..f20ad31f60 100644 --- a/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx +++ b/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx @@ -25,10 +25,7 @@ const FileImageViewer = ({ const updatedFile = { ...file }; handlersForSpecialItems?.handleUpdateProgress(PROGRESS_BAR_STATUS.PENDING); - if (!blob) { - return; - } - const convertedBlob = await heic2any({ blob: blob }); + const convertedBlob = await heic2any({ blob: blob as Blob }); updatedFile.type = 'png'; setImageBlob(convertedBlob as Blob); @@ -36,6 +33,10 @@ const FileImageViewer = ({ await handlersForSpecialItems?.handleUpdateThumbnail(updatedFile, convertedBlob as Blob); } else { + if (!blob) { + setIsPreviewAvailable(false); + return; + } setImageBlob(blob); } } catch (error) { From 36ed1814d6220ee7bbaefd01105deee0ad8eed7d Mon Sep 17 00:00:00 2001 From: Ramon Candel Segura Date: Thu, 26 Mar 2026 12:55:34 +0100 Subject: [PATCH 4/4] Address sonar cloud issues: add alt attribute to image in FileImageViewer and refactor imports in Backups components --- .../FileImageViewer/FileImageViewer.tsx | 1 + src/views/Backups/BackupsView.tsx | 31 +++++++++---------- .../Backups/components/DeleteBackupDialog.tsx | 17 +++++----- .../Backups/hooks/useBackupDeviceActions.ts | 10 +++--- .../Backups/hooks/useBackupListActions.ts | 7 ++--- 5 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx b/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx index f20ad31f60..cca6cdd02f 100644 --- a/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx +++ b/src/app/drive/components/FileViewer/viewers/FileImageViewer/FileImageViewer.tsx @@ -66,6 +66,7 @@ const FileImageViewer = ({ {fileUrl && ( {file.name} setIsPreviewAvailable(false)} diff --git a/src/views/Backups/BackupsView.tsx b/src/views/Backups/BackupsView.tsx index 37dc4aeaeb..e81b0d360e 100644 --- a/src/views/Backups/BackupsView.tsx +++ b/src/views/Backups/BackupsView.tsx @@ -1,27 +1,26 @@ -import { useState } from 'react'; +import { Dialog, MenuItemType } from '@internxt/ui'; +import FileViewerWrapper from 'app/drive/components/FileViewer/FileViewerWrapper'; +import { deleteFile } from 'app/drive/services/file.service'; +import newStorageService from 'app/drive/services/new-storage.service'; +import { DriveFolderData, DriveItemData } from 'app/drive/types'; import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; -import BreadcrumbsBackupsView from 'components/BreadcrumbsBackupsView'; +import { DownloadManager } from 'app/network/DownloadManager'; +import notificationsService, { ToastType } from 'app/notifications/services/notifications.service'; import { useAppDispatch, useAppSelector } from 'app/store/hooks'; +import { deleteItemsThunk } from 'app/store/slices/storage/storage.thunks/deleteItemsThunk'; +import workspacesSelectors from 'app/store/slices/workspaces/workspaces.selectors'; +import BreadcrumbsBackupsView from 'components/BreadcrumbsBackupsView'; +import { useState } from 'react'; import { Helmet } from 'react-helmet-async'; -import { DeleteBackupDialog } from './components'; +import errorService from 'services/error.service'; +import { contextMenuSelectedBackupItems } from 'views/Drive/components/DriveExplorer/components'; import WarningMessageWrapper from 'views/Home/components/WarningMessageWrapper'; +import { DeleteBackupDialog } from './components'; import BackupsAsFoldersList from './components/BackupsAsFoldersList'; import DeviceList from './components/DeviceList'; -import FileViewerWrapper from 'app/drive/components/FileViewer/FileViewerWrapper'; -import newStorageService from 'app/drive/services/new-storage.service'; -import { deleteFile } from 'app/drive/services/file.service'; -import { deleteItemsThunk } from 'app/store/slices/storage/storage.thunks/deleteItemsThunk'; -import { DriveItemData } from 'app/drive/types'; -import { DriveFolderData } from 'app/drive/types'; -import { contextMenuSelectedBackupItems } from 'views/Drive/components/DriveExplorer/components'; -import { useBackupListActions } from './hooks/useBackupListActions'; import { useBackupDeviceActions } from './hooks/useBackupDeviceActions'; +import { useBackupListActions } from './hooks/useBackupListActions'; import { useBackupsPagination } from './hooks/useBackupsPagination'; -import errorService from 'services/error.service'; -import notificationsService, { ToastType } from 'app/notifications/services/notifications.service'; -import { Dialog, MenuItemType } from '@internxt/ui'; -import { DownloadManager } from 'app/network/DownloadManager'; -import workspacesSelectors from 'app/store/slices/workspaces/workspaces.selectors'; export default function BackupsView(): JSX.Element { const { translate } = useTranslationContext(); diff --git a/src/views/Backups/components/DeleteBackupDialog.tsx b/src/views/Backups/components/DeleteBackupDialog.tsx index 99f3222f27..586cdba5c6 100644 --- a/src/views/Backups/components/DeleteBackupDialog.tsx +++ b/src/views/Backups/components/DeleteBackupDialog.tsx @@ -1,15 +1,14 @@ -import errorService from 'services/error.service'; -import { uiActions } from 'app/store/slices/ui'; -import { useAppDispatch, useAppSelector } from 'app/store/hooks'; +import { Dialog } from '@internxt/ui'; +import { DriveFolderData, DriveItemData } from 'app/drive/types'; +import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; import { RootState } from 'app/store'; -import { DriveFolderData as DriveWebFolderData, DriveItemData } from 'app/drive/types'; +import { useAppDispatch, useAppSelector } from 'app/store/hooks'; import { deleteItemsThunk } from 'app/store/slices/storage/storage.thunks/deleteItemsThunk'; -import { backupsThunks } from '../store/backupsSlice'; -import { DriveFolderData } from 'app/drive/types'; -import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; +import { uiActions } from 'app/store/slices/ui'; import { useState } from 'react'; -import { Dialog } from '@internxt/ui'; +import errorService from 'services/error.service'; import backupsService from '../services/backups.service'; +import { backupsThunks } from '../store/backupsSlice'; interface DeleteBackupDialogProps { backupsAsFoldersPath: DriveFolderData[]; @@ -38,7 +37,7 @@ const DeleteBackupDialog = (props: DeleteBackupDialogProps): JSX.Element => { dispatch(backupsThunks.deleteDeviceThunk(currentDevice)); } else { await dispatch(deleteItemsThunk([currentDevice as DriveItemData])).unwrap(); - await backupsService.deleteBackupDeviceAsFolder((currentDevice as DriveWebFolderData).uuid); + await backupsService.deleteBackupDeviceAsFolder((currentDevice as DriveFolderData).uuid); await dispatch(backupsThunks.fetchDevicesThunk()); } }; diff --git a/src/views/Backups/hooks/useBackupDeviceActions.ts b/src/views/Backups/hooks/useBackupDeviceActions.ts index 8f0a8e3655..71e08614bf 100644 --- a/src/views/Backups/hooks/useBackupDeviceActions.ts +++ b/src/views/Backups/hooks/useBackupDeviceActions.ts @@ -1,12 +1,12 @@ -import backupsService from '../services/backups.service'; -import { DriveItemData, DriveFolderData } from 'app/drive/types'; +import { Device } from '@internxt/sdk/dist/drive/backups/types'; +import { DriveFolderData, DriveItemData } from 'app/drive/types'; import { AppDispatch } from 'app/store'; import { useAppSelector } from 'app/store/hooks'; -import { backupsActions, backupsThunks } from '../store/backupsSlice'; import { deleteItemsThunk } from 'app/store/slices/storage/storage.thunks/deleteItemsThunk'; import { Dispatch, SetStateAction, useEffect, useState } from 'react'; -import { Device } from '@internxt/sdk/dist/drive/backups/types'; import errorService from 'services/error.service'; +import backupsService from '../services/backups.service'; +import { backupsActions, backupsThunks } from '../store/backupsSlice'; export const useBackupDeviceActions = ( onFolderUuidChanges: (folderUuid?: string) => void, @@ -86,7 +86,7 @@ export const useBackupDeviceActions = ( await dispatch(backupsThunks.deleteDeviceThunk(selectedDevice)).unwrap(); } else { await dispatch(deleteItemsThunk([selectedDevice as DriveItemData])).unwrap(); - await backupsService.deleteBackupDeviceAsFolder((selectedDevice as DriveFolderData).uuid); + await backupsService.deleteBackupDeviceAsFolder(selectedDevice.uuid); await dispatch(backupsThunks.fetchDevicesThunk()); } } diff --git a/src/views/Backups/hooks/useBackupListActions.ts b/src/views/Backups/hooks/useBackupListActions.ts index 14d6dd89ee..b1a4539079 100644 --- a/src/views/Backups/hooks/useBackupListActions.ts +++ b/src/views/Backups/hooks/useBackupListActions.ts @@ -1,9 +1,8 @@ -import { Dispatch, SetStateAction, useState } from 'react'; -import { DriveItemData } from 'app/drive/types'; +import { DriveFolderData, DriveItemData } from 'app/drive/types'; +import { PreviewFileItem } from 'app/share/types'; import { AppDispatch } from 'app/store'; +import { Dispatch, SetStateAction, useState } from 'react'; import { backupsActions } from '../store/backupsSlice'; -import { PreviewFileItem } from 'app/share/types'; -import { DriveFolderData } from 'app/drive/types'; export const useBackupListActions = ( onBreadcrumbFolderChanges: Dispatch>,