Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@iconscout/react-unicons": "^1.1.6",
"@internxt/css-config": "1.1.0",
"@internxt/lib": "1.4.1",
"@internxt/sdk": "=1.12.6",
"@internxt/sdk": "=1.13.2",
"@internxt/ui": "=0.1.4",
"@phosphor-icons/react": "^2.1.7",
"@popperjs/core": "^2.11.6",
Expand Down
10 changes: 0 additions & 10 deletions src/app/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,6 @@ export interface AppViewConfig {
hideSearch?: boolean;
}

export default class AppError extends Error {
readonly status?: number;

constructor(message: string, status?: number) {
super(message);

this.status = status;
}
}

export enum Workspace {
Individuals = 'personal',
Business = 'business',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ export const useShareItemUserRoles = ({ isRestrictedSharingAvailable, itemToShar
actionDispatch(setAccessMode(mode));
} catch (error) {
errorService.reportError(error);
const castedError = errorService.castError(error);
notificationsService.show({
text: translate('modals.shareModal.errors.update-sharing-access'),
type: ToastType.Error,
requestId: castedError.requestId,
});
}
actionDispatch(setIsLoading(false));
Expand Down Expand Up @@ -83,7 +85,12 @@ export const useShareItemUserRoles = ({ isRestrictedSharingAvailable, itemToShar
}
} catch (error) {
errorService.reportError(error);
notificationsService.show({ text: translate('modals.shareModal.errors.updatingRole'), type: ToastType.Error });
const castedError = errorService.castError(error);
notificationsService.show({
text: translate('modals.shareModal.errors.updatingRole'),
type: ToastType.Error,
requestId: castedError.requestId,
});
}
};

Expand Down
6 changes: 5 additions & 1 deletion src/app/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,8 @@
"maxSizeUploadLimitError": "Datei zu groß (größer als 40 GB)",
"braveNotSupportMultiplePhotosDowload": "Brave unterstützt nicht das Herunterladen mehrerer Fotos.",
"updateAvatarError": "Fehler beim Aktualisieren des Avatars",
"featuresUnavailable": "Einige Funktionen sind möglicherweise nicht verfügbar"
"featuresUnavailable": "Einige Funktionen sind möglicherweise nicht verfügbar",
"errorSettingWorkspace": "Fehler beim Konfigurieren des Arbeitsbereichs"
},
"backups": {
"backups-from": "Backups von {{deviceName}}",
Expand Down Expand Up @@ -1425,6 +1426,9 @@
"logOut": "Abmelden",
"locked": "Gesperrt"
},
"toastNotification": {
"textCopied": "Text in die Zwischenablage kopiert"
},
"drive": {
"usage": "Verwendung",
"currentPlan": "Derzeitiger Plan",
Expand Down
6 changes: 5 additions & 1 deletion src/app/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1161,7 +1161,8 @@
"connectionLostError": "Internet connection lost",
"errorLoadingTrashItems": "Error loading trash items",
"updateAvatarError": "Error updating avatar",
"featuresUnavailable": "Some features may be unavailable"
"featuresUnavailable": "Some features may be unavailable",
"errorSettingWorkspace": "Error setting up workspace"
},
"backups": {
"backups-from": "Backups from {{deviceName}}",
Expand Down Expand Up @@ -1507,6 +1508,9 @@
"logOut": "Log out",
"locked": "Locked"
},
"toastNotification": {
"textCopied": "Text copied to clipboard"
},
"drive": {
"usage": "Usage",
"currentPlan": "Current plan",
Expand Down
6 changes: 5 additions & 1 deletion src/app/i18n/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,8 @@
"connectionLostError": "Conexión a internet perdida",
"errorLoadingTrashItems": "Error al cargar items de la papelera",
"updateAvatarError": "Error al actualizar el avatar",
"featuresUnavailable": "Algunas funciones pueden no estar disponibles"
"featuresUnavailable": "Algunas funciones pueden no estar disponibles",
"errorSettingWorkspace": "Error al configurar el espacio de trabajo"
},
"backups": {
"backups-from": "Copias de seguridad de {{deviceName}}",
Expand Down Expand Up @@ -1485,6 +1486,9 @@
"logOut": "Cerrar sesión",
"locked": "Bloqueado"
},
"toastNotification": {
"textCopied": "Texto copiado al portapapeles"
},
"drive": {
"usage": "Uso",
"currentPlan": "Actual plan",
Expand Down
6 changes: 5 additions & 1 deletion src/app/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,8 @@
"connectionLostError": "Lost Internet connection",
"errorLoadingTrashItems": "Erreur de chargement des éléments de la corbeille",
"updateAvatarError": "Erreur lors de la mise à jour de l'avatar",
"featuresUnavailable": "Certaines fonctionnalités peuvent être indisponibles"
"featuresUnavailable": "Certaines fonctionnalités peuvent être indisponibles",
"errorSettingWorkspace": "Erreur lors de la configuration de l'espace de travail"
},
"backups": {
"backups-from": "Sauvagardes de {{deviceName}}",
Expand Down Expand Up @@ -1431,6 +1432,9 @@
"logOut": "Se déconnecter",
"locked": "Verrouillé"
},
"toastNotification": {
"textCopied": "Texte copié dans le presse-papiers"
},
"drive": {
"usage": "Usage",
"currentPlan": "Plan actuel",
Expand Down
6 changes: 5 additions & 1 deletion src/app/i18n/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -1196,7 +1196,8 @@
"connectionLostError": "Connessione Internet persa",
"errorLoadingTrashItems": "Errore nel caricamento degli elementi del cestino",
"updateAvatarError": "Errore nell'aggiornamento dell'avatar",
"featuresUnavailable": "Alcune funzionalità potrebbero non essere disponibili"
"featuresUnavailable": "Alcune funzionalità potrebbero non essere disponibili",
"errorSettingWorkspace": "Errore nella configurazione dello spazio di lavoro"
},
"backups": {
"backups-from": "Backup da {{deviceName}}",
Expand Down Expand Up @@ -1538,6 +1539,9 @@
"logOut": "Disconnettersi",
"locked": "Bloccato"
},
"toastNotification": {
"textCopied": "Testo copiato negli appunti"
},
"drive": {
"usage": "Utilizzo",
"currentPlan": "Piano attuale",
Expand Down
6 changes: 5 additions & 1 deletion src/app/i18n/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,8 @@
"connectionLostError": "Потеряно подключение к Интернету",
"errorLoadingTrashItems": "Ошибка при загрузке элементов корзины",
"updateAvatarError": "Ошибка при обновлении аватара",
"featuresUnavailable": "Некоторые функции могут быть недоступны"
"featuresUnavailable": "Некоторые функции могут быть недоступны",
"errorSettingWorkspace": "Ошибка при настройке рабочего стола"
},
"backups": {
"backups-from": "Резервные копии с {{deviceName}}",
Expand Down Expand Up @@ -1444,6 +1445,9 @@
"logOut": "Выйти",
"locked": "Заблокировано"
},
"toastNotification": {
"textCopied": "Текст скопирован в буфер обмена"
},
"drive": {
"usage": "Использование",
"currentPlan": "Текущий план",
Expand Down
6 changes: 5 additions & 1 deletion src/app/i18n/locales/tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,8 @@
"connectionLostError": "網絡連接已丟失",
"errorLoadingTrashItems": "加載垃圾桶項目時出錯",
"updateAvatarError": "更新頭像時發生錯誤",
"featuresUnavailable": "某些功能可能無法使用"
"featuresUnavailable": "某些功能可能無法使用",
"errorSettingWorkspace": "設定工作區時出錯"
},
"backups": {
"backups-from": "來自{{deviceName}}的備份",
Expand Down Expand Up @@ -1437,6 +1438,9 @@
"logOut": "登出",
"locked": "已鎖定"
},
"toastNotification": {
"textCopied": "文字已複製到剪貼簿"
},
"drive": {
"usage": "使用量",
"currentPlan": "當前計劃",
Expand Down
6 changes: 5 additions & 1 deletion src/app/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -1126,7 +1126,8 @@
"connectionLostError": "互联网连接丢失",
"errorLoadingTrashItems": "加载垃圾箱内的项目时出错",
"updateAvatarError": "更新头像时出错",
"featuresUnavailable": "某些功能可能无法使用"
"featuresUnavailable": "某些功能可能无法使用",
"errorSettingWorkspace": "设置工作区时出错"
},
"backups": {
"backups-from": "来自 {{deviceName}} 的备份",
Expand Down Expand Up @@ -1472,6 +1473,9 @@
"logOut": "登出",
"locked": "已锁定"
},
"toastNotification": {
"textCopied": "文本已复制到剪贴板"
},
"drive": {
"usage": "使用情况",
"currentPlan": "目前的计划",
Expand Down
1 change: 1 addition & 0 deletions src/app/network/DownloadManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ export class DownloadManager {
notificationsService.show({
text: t(errorText, { message: castedError.message || '' }),
type: ToastType.Error,
requestId: castedError.requestId,
});
};

Expand Down
2 changes: 1 addition & 1 deletion src/app/network/UploadFolderManager.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import errorService from 'services/error.service';
import AppError from 'app/core/types';
import { AppError } from '@internxt/sdk';
import { DriveFolderData } from 'app/drive/types';
import { createFolder } from 'app/store/slices/storage/folderUtils/createFolder';
import { checkFolderDuplicated } from 'app/store/slices/storage/folderUtils/checkFolderDuplicated';
Expand Down
2 changes: 1 addition & 1 deletion src/app/network/UploadManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { uploadFileWithManager } from './UploadManager';
import tasksService from 'app/tasks/services/tasks.service';
import errorService from 'services/error.service';
import AppError from 'app/core/types';
import { AppError } from '@internxt/sdk';
import uploadFile from 'app/drive/services/file.service/uploadFile';
import DatabaseUploadRepository from 'app/repositories/DatabaseUploadRepository';
import { DriveFileData } from 'app/drive/types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,30 @@ import { Transition } from '@headlessui/react';
import { Loader } from '@internxt/ui';
import { CheckCircle, Info, Warning, WarningOctagon, X } from '@phosphor-icons/react';
import { NavLink } from 'react-router-dom';
import { ToastShowProps, ToastType } from '../../services/notifications.service';
import notificationsService, { ToastShowProps, ToastType } from '../../services/notifications.service';
import { copyTextToClipboard } from 'utils/copyToClipboard.utils';
import { useTranslationContext } from 'app/i18n/provider/TranslationProvider';

const LongNotificationToast = ({
text,
type,
action,
visible,
closable,
requestId,
onClose,
}: Omit<ToastShowProps, 'duration'> & { visible: boolean; onClose: () => void }): JSX.Element => {
let Icon: typeof CheckCircle | undefined;
let IconColor: string | undefined;
const { translate } = useTranslationContext();

const handleCopyRequestId = () => {
if (requestId) {
copyTextToClipboard(requestId);
notificationsService.show({ text: translate('toastNotification.textCopied'), type: ToastType.Success });
}
};
Comment on lines 22 to 27
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WWhen the user presses copy, some visual feedback would be great, otherwise they won't know if it has been copied to the clipboard or not

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed


switch (type) {
case ToastType.Success:
Icon = CheckCircle;
Expand Down Expand Up @@ -55,7 +67,18 @@ const LongNotificationToast = ({
{Icon && <Icon weight="fill" className={`${IconColor} mr-1.5`} size={24} />}
</div>
<div className="ml-1.5 flex-1">
<p className="whitespace-normal break-words text-gray-80">{text}</p>
<div className="flex-1">
<p className="line-clamp-2 whitespace-pre break-words text-gray-80">{text}</p>
{requestId && type === ToastType.Error && (
<button
onClick={handleCopyRequestId}
className="mt-1 text-xs text-gray-50 hover:text-gray-60"
title="Click to copy"
>
ID: {requestId}
</button>
)}
</div>
{action &&
(action.to ? (
<NavLink
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,25 @@ import { Transition } from '@headlessui/react';
import { Loader } from '@internxt/ui';
import { CheckCircle, Info, Warning, WarningOctagon, X } from '@phosphor-icons/react';
import { NavLink } from 'react-router-dom';
import { ToastShowProps, ToastType } from '../../services/notifications.service';
import notificationsService, { ToastShowProps, ToastType } from '../../services/notifications.service';
import { useTranslationContext } from 'app/i18n/provider/TranslationProvider';

const NotificationToast = ({
text,
type,
action,
visible,
closable,
requestId,
onClose,
}: Omit<ToastShowProps, 'duration'> & { visible: boolean; onClose: () => void }): JSX.Element => {
const { translate } = useTranslationContext();
const handleCopyRequestId = () => {
if (requestId) {
navigator.clipboard.writeText(requestId);
notificationsService.show({ text: translate('toastNotification.textCopied'), type: ToastType.Success });
}
};
let Icon: typeof CheckCircle | undefined;
let IconColor: string | undefined;

Expand Down Expand Up @@ -55,7 +64,18 @@ const NotificationToast = ({
{type === ToastType.Loading && <Loader classNameLoader="mr-1.5 h-6 w-6" />}
{Icon && <Icon weight="fill" className={`${IconColor} mr-1.5`} size={24} />}

<p className="line-clamp-2 flex-1 whitespace-pre break-words text-gray-80">{text}</p>
<div className="flex-1">
<p className="line-clamp-2 whitespace-pre break-words text-gray-80">{text}</p>
{requestId && type === ToastType.Error && (
<button
onClick={handleCopyRequestId}
className="mt-1 text-xs text-gray-50 hover:text-gray-60"
title="Click to copy"
>
ID: {requestId}
</button>
)}
</div>
{action &&
(action.to ? (
<NavLink
Expand Down
27 changes: 27 additions & 0 deletions src/app/notifications/services/longNotification.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import toast from 'react-hot-toast';
import { describe, expect, test, vi } from 'vitest';
import longNotificationsService, { ToastType } from './longNotification.service';

describe('Long notification service', () => {
describe('Long notification is created', () => {
test('When the request id is present, then it should be created with it', async () => {
vi.spyOn(toast, 'custom').mockReturnValue('toast-id-123');

longNotificationsService.show({
text: 'test',
type: ToastType.Error,
duration: 5000,
closable: true,
requestId: 'test-request-id',
});

expect(toast.custom).toHaveBeenCalledWith(expect.any(Function), { duration: 5000 });

// Invoke the render function and check the resulting React element has requestId in props
const renderFn = (toast.custom as ReturnType<typeof vi.fn>).mock.calls[0][0];
const element = renderFn({ visible: true });

expect(element.props).toMatchObject({ requestId: 'test-request-id' });
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ export type ToastShowProps = {
action?: { text: string; to?: string; onClick: () => void };
duration?: number;
closable?: boolean;
requestId?: string;
};

const longNotificationsService = {
show: ({ text, type, action, duration = 5000, closable = true }: ToastShowProps): string => {
show: ({ text, type, action, duration = 5000, closable = true, requestId }: ToastShowProps): string => {
const id = toast.custom(
(t) =>
createElement(LongNotificationToast, {
Expand All @@ -28,6 +29,7 @@ const longNotificationsService = {
visible: t.visible,
action,
closable,
requestId,
onClose() {
toast.dismiss(id);
},
Expand Down
Loading
Loading