mirror of
https://github.com/plankanban/planka.git
synced 2025-12-25 01:11:49 +03:00
feat: Track storage usage
This commit is contained in:
@@ -80,7 +80,7 @@ const Item = React.memo(({ id, isVisible }) => {
|
||||
break;
|
||||
default:
|
||||
if (attachment.data.encoding === Encodings.UTF8) {
|
||||
if (attachment.data.sizeInBytes <= Config.MAX_SIZE_IN_BYTES_TO_DISPLAY_CONTENT) {
|
||||
if (attachment.data.size <= Config.MAX_SIZE_TO_DISPLAY_CONTENT) {
|
||||
content = (
|
||||
<ContentViewer
|
||||
src={attachment.data.url}
|
||||
|
||||
21
client/src/components/common/Toaster/FileIsTooBig.jsx
Normal file
21
client/src/components/common/Toaster/FileIsTooBig.jsx
Normal file
@@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, Message } from 'semantic-ui-react';
|
||||
|
||||
const FileIsTooBig = React.memo(() => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
return (
|
||||
<Message visible negative size="tiny">
|
||||
<Icon name="file" />
|
||||
{t('common.uploadFailedFileIsTooBig')}
|
||||
</Message>
|
||||
);
|
||||
});
|
||||
|
||||
export default FileIsTooBig;
|
||||
21
client/src/components/common/Toaster/NotEnoughStorage.jsx
Normal file
21
client/src/components/common/Toaster/NotEnoughStorage.jsx
Normal file
@@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, Message } from 'semantic-ui-react';
|
||||
|
||||
const NotEnoughStorage = React.memo(() => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
return (
|
||||
<Message visible negative size="tiny">
|
||||
<Icon name="hdd" />
|
||||
{t('common.uploadFailedNotEnoughStorageSpace')}
|
||||
</Message>
|
||||
);
|
||||
});
|
||||
|
||||
export default NotEnoughStorage;
|
||||
@@ -7,9 +7,13 @@ import React from 'react';
|
||||
import { Toaster as HotToaster, ToastBar as HotToastBar } from 'react-hot-toast';
|
||||
|
||||
import ToastTypes from '../../../constants/ToastTypes';
|
||||
import FileIsTooBig from './FileIsTooBig';
|
||||
import NotEnoughStorage from './NotEnoughStorage';
|
||||
import EmptyTrashToast from './EmptyTrashToast';
|
||||
|
||||
const TOAST_BY_TYPE = {
|
||||
[ToastTypes.FILE_IS_TOO_BIG]: FileIsTooBig,
|
||||
[ToastTypes.NOT_ENOUGH_STORAGE]: NotEnoughStorage,
|
||||
[ToastTypes.EMPTY_TRASH]: EmptyTrashToast,
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const CARDS_LIMIT = 50;
|
||||
const COMMENTS_LIMIT = 50;
|
||||
const ACTIVITIES_LIMIT = 50;
|
||||
|
||||
const MAX_SIZE_IN_BYTES_TO_DISPLAY_CONTENT = 256 * 1024;
|
||||
const MAX_SIZE_TO_DISPLAY_CONTENT = 256 * 1024;
|
||||
|
||||
const IS_MAC = navigator.platform.startsWith('Mac');
|
||||
|
||||
@@ -28,6 +28,6 @@ export default {
|
||||
CARDS_LIMIT,
|
||||
COMMENTS_LIMIT,
|
||||
ACTIVITIES_LIMIT,
|
||||
MAX_SIZE_IN_BYTES_TO_DISPLAY_CONTENT,
|
||||
MAX_SIZE_TO_DISPLAY_CONTENT,
|
||||
IS_MAC,
|
||||
};
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const FILE_IS_TOO_BIG = 'FILE_IS_TOO_BIG';
|
||||
const NOT_ENOUGH_STORAGE = 'NOT_ENOUGH_STORAGE';
|
||||
const EMPTY_TRASH = 'EMPTY_TRASH';
|
||||
|
||||
export default {
|
||||
FILE_IS_TOO_BIG,
|
||||
NOT_ENOUGH_STORAGE,
|
||||
EMPTY_TRASH,
|
||||
};
|
||||
|
||||
@@ -280,6 +280,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'إجراءات المستخدم',
|
||||
|
||||
@@ -284,6 +284,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Потребителски действия',
|
||||
|
||||
@@ -294,6 +294,8 @@ export default {
|
||||
typeNameToConfirm: 'Zadejte název pro potvrzení.',
|
||||
typeTitleToConfirm: 'Zadejte titulek pro potvrzení.',
|
||||
unsavedChanges: 'Neuložené změny',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Nahrané obrázky',
|
||||
url: null,
|
||||
userActions_title: 'Akce uživatele',
|
||||
|
||||
@@ -301,6 +301,8 @@ export default {
|
||||
typeNameToConfirm: 'Skriv navnet for at bekræfte.',
|
||||
typeTitleToConfirm: 'Skriv overskriften for at bekræfte.',
|
||||
unsavedChanges: 'Ikke-gemte ændringer',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Uploadede billeder',
|
||||
url: null,
|
||||
userActions_title: 'Brugerhandlinger',
|
||||
|
||||
@@ -309,6 +309,8 @@ export default {
|
||||
typeNameToConfirm: 'Namen zur Bestätigung eingeben.',
|
||||
typeTitleToConfirm: 'Titel zur Bestätigung eingeben.',
|
||||
unsavedChanges: 'Ungespeicherte Änderungen',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Hochgeladene Bilder',
|
||||
url: null,
|
||||
userActions_title: 'Benutzeraktionen',
|
||||
|
||||
@@ -311,6 +311,8 @@ export default {
|
||||
typeNameToConfirm: 'Πληκτρολογήστε το όνομα για επιβεβαίωση.',
|
||||
typeTitleToConfirm: 'Πληκτρολογήστε τον τίτλο για επιβεβαίωση.',
|
||||
unsavedChanges: 'Μη αποθηκευμένες αλλαγές',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Μεταφορτωμένες εικόνες',
|
||||
url: null,
|
||||
userActions_title: 'Ενέργειες χρήστη',
|
||||
|
||||
@@ -301,6 +301,8 @@ export default {
|
||||
typeNameToConfirm: 'Type the name to confirm.',
|
||||
typeTitleToConfirm: 'Type the title to confirm.',
|
||||
unsavedChanges: 'Unsaved changes',
|
||||
uploadFailedFileIsTooBig: 'Upload failed: File is too big.',
|
||||
uploadFailedNotEnoughStorageSpace: 'Upload failed: Not enough storage space.',
|
||||
uploadedImages: 'Uploaded images',
|
||||
url: 'URL',
|
||||
userActions_title: 'User Actions',
|
||||
|
||||
@@ -296,6 +296,8 @@ export default {
|
||||
typeNameToConfirm: 'Type the name to confirm.',
|
||||
typeTitleToConfirm: 'Type the title to confirm.',
|
||||
unsavedChanges: 'Unsaved changes',
|
||||
uploadFailedFileIsTooBig: 'Upload failed: File is too big.',
|
||||
uploadFailedNotEnoughStorageSpace: 'Upload failed: Not enough storage space.',
|
||||
uploadedImages: 'Uploaded images',
|
||||
url: 'URL',
|
||||
userActions_title: 'User Actions',
|
||||
|
||||
@@ -301,6 +301,8 @@ export default {
|
||||
typeNameToConfirm: 'Escribe el nombre para confirmar',
|
||||
typeTitleToConfirm: 'Escribe el título para confirmar',
|
||||
unsavedChanges: 'Cambios no guardados',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Imágenes subidas',
|
||||
url: null,
|
||||
userActions_title: 'Acciones de Usuario',
|
||||
|
||||
@@ -299,6 +299,8 @@ export default {
|
||||
typeNameToConfirm: 'Sisestage nimi, et kinnitada.',
|
||||
typeTitleToConfirm: 'Sisestage pealkiri, et kinnitada.',
|
||||
unsavedChanges: 'Muudetud andmed',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Laaditud pildid',
|
||||
url: null,
|
||||
userActions_title: 'Kasutaja tegevused',
|
||||
|
||||
@@ -281,6 +281,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'اقدامات کاربر',
|
||||
|
||||
@@ -295,6 +295,8 @@ export default {
|
||||
typeNameToConfirm: 'Kirjoita nimi vahvistaaksesi.',
|
||||
typeTitleToConfirm: 'Kirjoita otsikko vahvistaaksesi.',
|
||||
unsavedChanges: 'Tallentamattomat muutokset',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Ladatut kuvat',
|
||||
url: null,
|
||||
userActions_title: 'Käyttäjän toiminnot',
|
||||
|
||||
@@ -304,6 +304,8 @@ export default {
|
||||
typeNameToConfirm: 'Saissir le nom pour confirmer.',
|
||||
typeTitleToConfirm: 'Saisir le titre pour confirmer.',
|
||||
unsavedChanges: 'Modifications non enregistrées',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Images téléchargées',
|
||||
url: 'URL',
|
||||
userActions_title: "Actions de l'utilisateur",
|
||||
|
||||
@@ -293,6 +293,8 @@ export default {
|
||||
typeNameToConfirm: 'Írja be a nevet a megerősítéshez.',
|
||||
typeTitleToConfirm: 'Írja be a címet a megerősítéshez.',
|
||||
unsavedChanges: 'Mentetlen változtatások',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Feltöltött képek',
|
||||
url: 'URL',
|
||||
userActions_title: 'Felhasználói műveletek',
|
||||
|
||||
@@ -284,6 +284,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Aksi Pengguna',
|
||||
|
||||
@@ -302,6 +302,8 @@ export default {
|
||||
typeNameToConfirm: 'Digita il nome per confermare',
|
||||
typeTitleToConfirm: 'Digita il titolo per confermare',
|
||||
unsavedChanges: 'Modifiche non salvate',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Immagini caricate',
|
||||
url: 'URL',
|
||||
userActions_title: 'Azioni utente',
|
||||
|
||||
@@ -283,6 +283,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'ユーザーのアクション',
|
||||
|
||||
@@ -282,6 +282,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: '사용자 작업',
|
||||
|
||||
@@ -284,6 +284,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Gebruikersacties',
|
||||
|
||||
@@ -291,6 +291,8 @@ export default {
|
||||
typeNameToConfirm: 'Wpisz nazwę aby potwierdzić.',
|
||||
typeTitleToConfirm: 'Wpisz tytuł aby potwierdzić.',
|
||||
unsavedChanges: 'Niezapisane zmiany',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Wgrane obrazy',
|
||||
url: null,
|
||||
userActions_title: 'Akcje Użytkownika',
|
||||
|
||||
@@ -303,6 +303,8 @@ export default {
|
||||
typeNameToConfirm: 'Digite o nome para confirmar.',
|
||||
typeTitleToConfirm: 'Digite o título para confirmar.',
|
||||
unsavedChanges: 'Alterações não salvas',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Imagens enviadas',
|
||||
url: 'URL',
|
||||
userActions_title: 'Ações do Usuário',
|
||||
|
||||
@@ -285,6 +285,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Ações do Utilizador',
|
||||
|
||||
@@ -284,6 +284,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Acțiunile utilizatorului',
|
||||
|
||||
@@ -298,6 +298,8 @@ export default {
|
||||
typeNameToConfirm: 'Введите имя для подтверждения',
|
||||
typeTitleToConfirm: 'Введите название для подтверждения',
|
||||
unsavedChanges: 'Несохранённые изменения',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Загруженные изображения',
|
||||
url: null,
|
||||
userActions_title: 'Действия с пользователем',
|
||||
|
||||
@@ -283,6 +283,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Akcie na používateľovi',
|
||||
|
||||
@@ -283,6 +283,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Корисничке радње',
|
||||
|
||||
@@ -280,6 +280,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Korisničke radnje',
|
||||
|
||||
@@ -282,6 +282,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Användaråtgärder',
|
||||
|
||||
@@ -280,6 +280,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Kullanıcı İşlemleri',
|
||||
|
||||
@@ -297,6 +297,8 @@ export default {
|
||||
typeNameToConfirm: "Введіть ім'я для підтвердження.",
|
||||
typeTitleToConfirm: 'Введіть назву, щоб підтвердити.',
|
||||
unsavedChanges: 'Незбережені зміни',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Завантажені зображення',
|
||||
url: 'Посилання',
|
||||
userActions_title: 'Дії користувача',
|
||||
|
||||
@@ -279,6 +279,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: 'Foydalanuvchi Amallari',
|
||||
|
||||
@@ -281,6 +281,8 @@ export default {
|
||||
typeNameToConfirm: '输入名称以确认',
|
||||
typeTitleToConfirm: '输入标题以确认',
|
||||
unsavedChanges: '未保存的更改',
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: '已上传图片',
|
||||
url: '网址',
|
||||
userActions_title: '用户操作',
|
||||
|
||||
@@ -277,6 +277,8 @@ export default {
|
||||
typeNameToConfirm: null,
|
||||
typeTitleToConfirm: null,
|
||||
unsavedChanges: null,
|
||||
uploadFailedFileIsTooBig: null,
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
userActions_title: '使用者操作',
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import omit from 'lodash/omit';
|
||||
import truncate from 'lodash/truncate';
|
||||
import { call, put, select } from 'redux-saga/effects';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
import request from '../request';
|
||||
import selectors from '../../../selectors';
|
||||
@@ -13,6 +14,7 @@ import actions from '../../../actions';
|
||||
import api from '../../../api';
|
||||
import { createLocalId } from '../../../utils/local-id';
|
||||
import { AttachmentTypes } from '../../../constants/Enums';
|
||||
import ToastTypes from '../../../constants/ToastTypes';
|
||||
|
||||
export function* createAttachment(cardId, data) {
|
||||
const localId = yield call(createLocalId);
|
||||
@@ -41,6 +43,22 @@ export function* createAttachment(cardId, data) {
|
||||
: call(request, api.createAttachment, cardId, nextData));
|
||||
} catch (error) {
|
||||
yield put(actions.createAttachment.failure(localId, error));
|
||||
|
||||
if (error.code === 'E_UNPROCESSABLE_ENTITY') {
|
||||
let toastType;
|
||||
if (error.message.startsWith('Upload limit')) {
|
||||
toastType = ToastTypes.FILE_IS_TOO_BIG;
|
||||
} else if (error.message === 'Storage limit reached') {
|
||||
toastType = ToastTypes.NOT_ENOUGH_STORAGE;
|
||||
}
|
||||
|
||||
if (toastType) {
|
||||
yield call(toast, {
|
||||
type: toastType,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user