From 74274e511fa063a6b2285511a6b5c52d7c56744d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Pedrini?= Date: Fri, 30 May 2025 13:42:57 +0200 Subject: [PATCH 01/84] fix: Update Italian translation (#1163) --- client/src/locales/it-IT/core.js | 19 ++++++++++++++++++- server/config/i18n.js | 2 +- server/config/locales/it-IT.json | 13 +++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 server/config/locales/it-IT.json diff --git a/client/src/locales/it-IT/core.js b/client/src/locales/it-IT/core.js index 032cf709..8bb6a26e 100644 --- a/client/src/locales/it-IT/core.js +++ b/client/src/locales/it-IT/core.js @@ -284,13 +284,29 @@ export default { unsavedChanges: 'Modifiche non salvate', uploadedImages: 'Immagini caricate', userActions_title: 'Azioni utente', - userAddedThisCardToList: '<0>{{user}} ha aggiunto questa scheda a {{list}}', + userAddedCardToList: '<0>{{user}} ha aggiunto <2>{{card}} a {{list}}', + userAddedThisCardToList: '<0>{{user}} ha aggiunto questa task a {{list}}', + userAddedUserToCard: '<0>{{actorUser}} ha aggiunto {{addedUser}} a <4>{{card}}', + userAddedUserToThisCard: '<0>{{actorUser}} ha aggiunto {{addedUser}} a questa task', + userAddedYouToCard: '<0>{{user}} ti ha aggiunto a <2>{{card}}', + userCompletedTaskOnCard: '<0>{{user}} ha completato {{task}} in <4>{{card}}', + userCompletedTaskOnThisCard: '<0>{{user}} ha completato {{task}} in questa task', + userJoinedCard: `<0>{{user}} è entrato in <2>{{card}}`, + userJoinedThisCard: `<0>{{user}} è entrato in questa task`, userLeftNewCommentToCard: '<0>{{user}} ha lasciato un commento «{{comment}}» a <2>{{card}}', + userLeftCard: '<0>{{user}} ha lasciato <2>{{card}}', + userLeftThisCard: '<0>{{user}} ha lasciato questa task', + userMarkedTaskIncompleteOnCard: + '<0>{{user}} ha contrassegnato {{task}} come incompleta in <4>{{card}}', + userMarkedTaskIncompleteOnThisCard: + '<0>{{user}} ha contrassegnato {{task}} come incompleta in questa task', userMovedCardFromListToList: '<0>{{user}} ha spostato <2>{{card}} da {{fromList}} a {{toList}}', userMovedThisCardFromListToList: '<0>{{user}} ha spostato questa scheda da {{fromList}} a {{toList}}', + userRemovedUserFromCard: '<0>{{actorUser}} ha rimosso {{removedUser}} da <4>{{card}}', + userRemovedUserFromThisCard: '<0>{{actorUser}} ha rimosso {{removedUser}} da questa task', username: 'Username', users: 'Utenti', viewer: 'Visualizzatore', @@ -354,6 +370,7 @@ export default { deleteProject_title: 'Elimina progetto', deleteTask: 'Elimina task', deleteTask_title: 'Elimina task', + deleteTaskList: 'Elimina lista di list', deleteUser: 'Elimina utente', deleteUser_title: 'Elimina utente', dismissAll: 'Ignora tutto', diff --git a/server/config/i18n.js b/server/config/i18n.js index 40d448f1..65ac402d 100644 --- a/server/config/i18n.js +++ b/server/config/i18n.js @@ -19,7 +19,7 @@ module.exports.i18n = { * */ - locales: ['en-GB', 'en-US', 'ru-RU'], + locales: ['en-GB', 'en-US', 'ru-RU', 'it-IT'], /** * diff --git a/server/config/locales/it-IT.json b/server/config/locales/it-IT.json new file mode 100644 index 00000000..13167262 --- /dev/null +++ b/server/config/locales/it-IT.json @@ -0,0 +1,13 @@ +{ + "Card Created": "Nuova task creata", + "Card Moved": "Task spostata", + "New Comment": "Nuovo commento", + "Test Title": "Titolo di test", + "This is a test text message!": "Questo è un messaggio di testo di test!", + "This is a *test* **markdown** `message`!": "Questo è un *test* **markdown** `messaggio`!", + "This is a test html message": "Questo è un test html messaggio", + "You Were Added to Card": "Sei stato aggiunto alla task", + "%s created %s in %s on %s": "%s ha creato %s in %s in %s", + "%s left a new comment to %s on %s": "%s ha commentato %s in %s", + "%s moved %s from %s to %s on %s": "%s ha spostato %s da %s a %s in %s" +} From eb2a3a2875c4bbc7fe93ff145f562e7c7f2c21e8 Mon Sep 17 00:00:00 2001 From: Maksim Eltyshev Date: Fri, 30 May 2025 13:44:58 +0200 Subject: [PATCH 02/84] ref: Fix locale position --- server/config/i18n.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/config/i18n.js b/server/config/i18n.js index 65ac402d..d149879a 100644 --- a/server/config/i18n.js +++ b/server/config/i18n.js @@ -19,7 +19,7 @@ module.exports.i18n = { * */ - locales: ['en-GB', 'en-US', 'ru-RU', 'it-IT'], + locales: ['en-GB', 'en-US', 'it-IT', 'ru-RU'], /** * From c0b04368512928e8696482e0d89def486aedd154 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Fri, 30 May 2025 22:01:29 +0200 Subject: [PATCH 03/84] feat: Add ability to mention users in comments (#1162) --- client/package-lock.json | 45 ++++++++++ client/package.json | 1 + .../src/components/comments/Comments/Add.jsx | 89 ++++++++++++++----- .../comments/Comments/Add.module.scss | 40 ++++++--- .../comments/Comments/Edit.module.scss | 1 - .../notifications/NotificationsStep/Item.jsx | 25 +++++- client/src/configs/markdown-plugins/index.js | 2 + .../src/configs/markdown-plugins/mention.js | 61 +++++++++++++ client/src/constants/Enums.js | 1 + client/src/locales/en-GB/core.js | 2 + client/src/locales/en-US/core.js | 2 + client/src/styles.module.scss | 32 +++++++ client/src/utils/formatters.js | 4 + server/api/helpers/comments/create-one.js | 33 ++++++- .../api/helpers/notifications/create-one.js | 47 +++++++++- server/api/models/Notification.js | 1 + server/config/locales/en-GB.json | 2 + server/config/locales/en-US.json | 2 + server/config/locales/ru-RU.json | 2 + server/utils/formatters.js | 7 ++ 20 files changed, 357 insertions(+), 42 deletions(-) create mode 100644 client/src/configs/markdown-plugins/mention.js create mode 100644 client/src/utils/formatters.js create mode 100644 server/utils/formatters.js diff --git a/client/package-lock.json b/client/package-lock.json index 6f912566..b9891269 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -69,6 +69,7 @@ "react-i18next": "^15.5.1", "react-input-mask": "^2.0.4", "react-intersection-observer": "^9.16.0", + "react-mentions": "^4.4.10", "react-photoswipe-gallery": "^2.2.7", "react-redux": "^8.1.3", "react-router-dom": "^6.30.0", @@ -12296,6 +12297,31 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/react-mentions": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/react-mentions/-/react-mentions-4.4.10.tgz", + "integrity": "sha512-JHiQlgF1oSZR7VYPjq32wy97z1w1oE4x10EuhKjPr4WUKhVzG1uFQhQjKqjQkbVqJrmahf+ldgBTv36NrkpKpA==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "7.4.5", + "invariant": "^2.2.4", + "prop-types": "^15.5.8", + "substyle": "^9.1.0" + }, + "peerDependencies": { + "react": ">=16.8.3", + "react-dom": ">=16.8.3" + } + }, + "node_modules/react-mentions/node_modules/@babel/runtime": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.13.2" + } + }, "node_modules/react-onclickoutside": { "version": "6.13.2", "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.2.tgz", @@ -12677,6 +12703,12 @@ "node": ">=4" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, "node_modules/regexp-match-indices": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", @@ -14183,6 +14215,19 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==" }, + "node_modules/substyle": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/substyle/-/substyle-9.4.1.tgz", + "integrity": "sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.4", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": ">=16.8.3" + } + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", diff --git a/client/package.json b/client/package.json index cf8921fd..c429160a 100755 --- a/client/package.json +++ b/client/package.json @@ -140,6 +140,7 @@ "react-i18next": "^15.5.1", "react-input-mask": "^2.0.4", "react-intersection-observer": "^9.16.0", + "react-mentions": "^4.4.10", "react-photoswipe-gallery": "^2.2.7", "react-redux": "^8.1.3", "react-router-dom": "^6.30.0", diff --git a/client/src/components/comments/Comments/Add.jsx b/client/src/components/comments/Comments/Add.jsx index 56e8b139..24cdf11d 100755 --- a/client/src/components/comments/Comments/Add.jsx +++ b/client/src/components/comments/Comments/Add.jsx @@ -3,16 +3,18 @@ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md */ -import React, { useCallback, useState } from 'react'; -import { useDispatch } from 'react-redux'; +import React, { useCallback, useState, useRef } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; -import TextareaAutosize from 'react-textarea-autosize'; -import { Button, Form, TextArea } from 'semantic-ui-react'; +import { Mention, MentionsInput } from 'react-mentions'; +import { Button, Form } from 'semantic-ui-react'; import { useClickAwayListener, useDidUpdate, useToggle } from '../../../lib/hooks'; +import selectors from '../../../selectors'; import entryActions from '../../../entry-actions'; import { useEscapeInterceptor, useForm, useNestedRef } from '../../../hooks'; import { isModifierKeyPressed } from '../../../utils/event-helpers'; +import UserAvatar from '../../users/UserAvatar'; import styles from './Add.module.scss'; @@ -21,13 +23,16 @@ const DEFAULT_DATA = { }; const Add = React.memo(() => { + const boardMemberships = useSelector(selectors.selectMembershipsForCurrentBoard); + const dispatch = useDispatch(); const [t] = useTranslation(); + const [data, , setData] = useForm(DEFAULT_DATA); const [isOpened, setIsOpened] = useState(false); - const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA); const [selectTextFieldState, selectTextField] = useToggle(); - const [textFieldRef, handleTextFieldRef] = useNestedRef(); + const mentionsInputRef = useRef(null); + const textFieldRef = useRef(null); const [buttonRef, handleButtonRef] = useNestedRef(); const submit = useCallback(() => { @@ -47,6 +52,11 @@ const Add = React.memo(() => { }, [dispatch, data, setData, selectTextField, textFieldRef]); const handleEscape = useCallback(() => { + if (mentionsInputRef.current.isOpened()) { + mentionsInputRef.current.clearSuggestions(); + return; + } + setIsOpened(false); textFieldRef.current.blur(); }, [textFieldRef]); @@ -62,6 +72,15 @@ const Add = React.memo(() => { setIsOpened(true); }, []); + const handleFieldChange = useCallback( + (_, text) => { + setData({ + text, + }); + }, + [setData], + ); + const handleFieldKeyDown = useCallback( (event) => { if (isModifierKeyPressed(event) && event.key === 'Enter') { @@ -85,6 +104,16 @@ const Add = React.memo(() => { handleClickAwayCancel, ); + const suggestionRenderer = useCallback( + (entry, _, highlightedDisplay) => ( +
+ + {highlightedDisplay} +
+ ), + [], + ); + useDidUpdate(() => { if (isOpened) { activateEscapeInterceptor(); @@ -99,21 +128,39 @@ const Add = React.memo(() => { return (
-