From 18805efc0314a014fdb57a594a19e4cc90a68b53 Mon Sep 17 00:00:00 2001 From: pottycat Date: Thu, 7 May 2026 09:44:13 +0000 Subject: [PATCH 1/7] feat: add more text formats to telegram editor --- .../components/new-launch/blockquote.text.tsx | 41 +++ .../src/components/new-launch/bold.text.tsx | 1 - .../src/components/new-launch/code.text.tsx | 35 +++ .../src/components/new-launch/editor.tsx | 27 ++ .../src/components/new-launch/italic.text.tsx | 34 +++ .../src/components/new-launch/link.text.tsx | 171 +++++++++++ .../src/components/new-launch/strike.text.tsx | 34 +++ .../src/components/new-launch/u.text.tsx | 5 +- .../src/utils/strip.html.validation.ts | 288 ++++++++++++++++++ .../database/prisma/posts/posts.repository.ts | 9 +- .../integrations/social/telegram.provider.ts | 2 +- package.json | 4 + pnpm-lock.yaml | 53 ++-- 13 files changed, 677 insertions(+), 27 deletions(-) create mode 100644 apps/frontend/src/components/new-launch/blockquote.text.tsx create mode 100644 apps/frontend/src/components/new-launch/code.text.tsx create mode 100644 apps/frontend/src/components/new-launch/italic.text.tsx create mode 100644 apps/frontend/src/components/new-launch/link.text.tsx create mode 100644 apps/frontend/src/components/new-launch/strike.text.tsx diff --git a/apps/frontend/src/components/new-launch/blockquote.text.tsx b/apps/frontend/src/components/new-launch/blockquote.text.tsx new file mode 100644 index 0000000000..fd1be5bac2 --- /dev/null +++ b/apps/frontend/src/components/new-launch/blockquote.text.tsx @@ -0,0 +1,41 @@ +import { FC } from 'react'; + +export const BlockquoteText: FC<{ + editor: any; +}> = ({ editor }) => { + const mark = () => { + editor?.commands?.toggleBlockquote(); + editor?.commands?.focus(); + }; + return ( +
+ + + + +
+ ); +}; diff --git a/apps/frontend/src/components/new-launch/bold.text.tsx b/apps/frontend/src/components/new-launch/bold.text.tsx index 76ef1d80f0..5d3fabb0bf 100644 --- a/apps/frontend/src/components/new-launch/bold.text.tsx +++ b/apps/frontend/src/components/new-launch/bold.text.tsx @@ -75,7 +75,6 @@ export const BoldText: FC<{ currentValue: string; }> = ({ editor }) => { const mark = () => { - editor?.commands?.unsetUnderline(); editor?.commands?.toggleBold(); editor?.commands?.focus(); }; diff --git a/apps/frontend/src/components/new-launch/code.text.tsx b/apps/frontend/src/components/new-launch/code.text.tsx new file mode 100644 index 0000000000..3563e74862 --- /dev/null +++ b/apps/frontend/src/components/new-launch/code.text.tsx @@ -0,0 +1,35 @@ +import { FC } from 'react'; + +export const CodeText: FC<{ + editor: any; +}> = ({ editor }) => { + const mark = () => { + editor?.commands?.unsetUnderline(); + editor?.commands?.toggleCode(); + editor?.commands?.focus(); + }; + return ( +
+ + + +
+ ); +}; diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 5c357b7c53..3fe4cddd6b 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -17,6 +17,11 @@ import EmojiPicker from 'emoji-picker-react'; import { Theme } from 'emoji-picker-react'; import { BoldText } from '@gitroom/frontend/components/new-launch/bold.text'; import { UText } from '@gitroom/frontend/components/new-launch/u.text'; +import { ItalicText } from '@gitroom/frontend/components/new-launch/italic.text'; +import { CodeText } from '@gitroom/frontend/components/new-launch/code.text'; +import { StrikeText } from '@gitroom/frontend/components/new-launch/strike.text'; +import { BlockquoteText } from '@gitroom/frontend/components/new-launch/blockquote.text'; +import { LinkText } from '@gitroom/frontend/components/new-launch/link.text'; import { SignatureBox } from '@gitroom/frontend/components/signature'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { @@ -45,6 +50,10 @@ import Bold from '@tiptap/extension-bold'; import Text from '@tiptap/extension-text'; import Paragraph from '@tiptap/extension-paragraph'; import Underline from '@tiptap/extension-underline'; +import Italic from '@tiptap/extension-italic'; +import Code from '@tiptap/extension-code'; +import Strike from '@tiptap/extension-strike'; +import Blockquote from '@tiptap/extension-blockquote'; import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; import { History } from '@tiptap/extension-history'; import { BulletList, ListItem } from '@tiptap/extension-list'; @@ -790,6 +799,15 @@ export const Editor: FC<{ /> )} + {editorType === 'html' && identifier === 'telegram' && ( + <> + + + + + + + )} {(editorType === 'markdown' || editorType === 'html') && identifier !== 'telegram' && ( <> @@ -911,10 +929,19 @@ export const OnlyEditor = forwardRef< Text, Underline, Bold, + Italic, + Link, + Code, + Strike, + Blockquote, InterceptBoldShortcut, InterceptUnderlineShortcut, BulletList, ListItem, + // stops link propagation when more texts are typed after link insertion + Link.extend({ + inclusive: false, + }), Placeholder.configure({ placeholder: t('write_something', 'Write something …'), emptyEditorClass: 'is-editor-empty', diff --git a/apps/frontend/src/components/new-launch/italic.text.tsx b/apps/frontend/src/components/new-launch/italic.text.tsx new file mode 100644 index 0000000000..e05fdec00a --- /dev/null +++ b/apps/frontend/src/components/new-launch/italic.text.tsx @@ -0,0 +1,34 @@ +import { FC } from 'react'; + +export const ItalicText: FC<{ + editor: any; +}> = ({ editor }) => { + const mark = () => { + editor?.commands?.toggleItalic(); + editor?.commands?.focus(); + }; + return ( +
+ + + +
+ ); +}; diff --git a/apps/frontend/src/components/new-launch/link.text.tsx b/apps/frontend/src/components/new-launch/link.text.tsx new file mode 100644 index 0000000000..ab6cf60b86 --- /dev/null +++ b/apps/frontend/src/components/new-launch/link.text.tsx @@ -0,0 +1,171 @@ +import { FC, useState, useEffect } from 'react'; +import { useForm, FormProvider } from 'react-hook-form'; +import { Button } from '@gitroom/react/form/button'; +import { Input } from '@gitroom/react/form/input'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +interface LinkModalProps { + isOpen: boolean; + defaultText: string; + onClose: () => void; + onApply: (text: string, url: string) => void; +} + +const LinkModal: FC = ({ + isOpen, + defaultText, + onClose, + onApply, +}) => { + const t = useT(); + const form = useForm(); + const [text, setText] = useState(defaultText); + const [url, setUrl] = useState(''); + + useEffect(() => { + setText(defaultText); + }, [defaultText]); + + if (!isOpen) return null; + + const handleApply = () => { + if (!url) return; + onApply(text, url); + setText(''); + setUrl(''); + }; + + const handleClose = () => { + setText(''); + setUrl(''); + onClose(); + }; + + return ( + +
+
+

{t('insert_link', 'Insert Link')}

+ +
+ setText(e.target.value)} + placeholder={t('display_text', 'Display Text')} + className="border border-gray-200 rounded-[6px] px-3 py-2 text-sm outline-none focus:border-gray-400" + /> +
+ +
+ setUrl(e.target.value)} + placeholder={t('www.example.com')} + className="border border-gray-200 rounded-[6px] px-3 py-2 text-sm outline-none focus:border-gray-400" + /> +
+ +
+ + +
+
+
+
+ ); +}; + +export const LinkText: FC<{ + editor: any; +}> = ({ editor }) => { + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedText, setSelectedText] = useState(''); + // Get the currently selected text from the editor (if any) + const { from, to } = editor?.state?.selection ?? {}; + + const mark = () => { + const highlighted = editor?.state?.doc?.textBetween(from, to, ' ') ?? ''; + setSelectedText(highlighted); + setIsModalOpen(true); + }; + + const handleApply = (text: string, url: string) => { + if (from !== to) { + // Replace selected text with linked version + editor?.commands?.setLink({ href: url }); + } else { + // No selection — insert new linked text at cursor + editor + ?.chain() + ?.focus() + ?.insertContent(`${text || url}`) + ?.run(); + } + + setIsModalOpen(false); + setSelectedText(''); + }; + + return ( + <> +
+ + + + +
+ + { + setIsModalOpen(false); + setSelectedText(''); + }} + onApply={handleApply} + /> + + ); +}; diff --git a/apps/frontend/src/components/new-launch/strike.text.tsx b/apps/frontend/src/components/new-launch/strike.text.tsx new file mode 100644 index 0000000000..d5b4b347d2 --- /dev/null +++ b/apps/frontend/src/components/new-launch/strike.text.tsx @@ -0,0 +1,34 @@ +import { FC } from 'react'; + +export const StrikeText: FC<{ + editor: any; +}> = ({ editor }) => { + const mark = () => { + editor?.commands?.toggleStrike(); + editor?.commands?.focus(); + }; + return ( +
+ + + +
+ ); +}; diff --git a/apps/frontend/src/components/new-launch/u.text.tsx b/apps/frontend/src/components/new-launch/u.text.tsx index a03d392b97..65c7bd1861 100644 --- a/apps/frontend/src/components/new-launch/u.text.tsx +++ b/apps/frontend/src/components/new-launch/u.text.tsx @@ -75,9 +75,8 @@ export const UText: FC<{ currentValue: string; }> = ({ editor }) => { const mark = () => { - editor?.commands?.unsetBold(); - editor?.commands?.toggleUnderline(); - editor?.commands?.focus(); + editor?.commands?.toggleUnderline(); + editor?.commands?.focus(); }; return (
') .replace(/</gi, '<') @@ -300,6 +564,30 @@ export const convertToAscii = (value: string): string => { return underlineMap?.[char] || char; }); + return match.replace(p1, replacer.join('')); + }) + .replace(/(.+?)<\/em>/gi, (match, p1) => { + const replacer = p1.split('').map((char: string) => { + return italic?.[char] || char; + }); + return match.replace(p1, replacer.join('')); + }) + .replace(/(.+?)<\/s>/gi, (match, p1) => { + const replacer = p1.split('').map((char: string) => { + return strikethrough?.[char] || char; + }); + return match.replace(p1, replacer.join('')); + }) + .replace(/
(.+?)<\/blockquote>/gi, (match, p1) => { + const replacer = p1.split('').map((char: string) => { + return blockquote?.[char] || char; + }); + return match.replace(p1, replacer.join('')); + }) + .replace(/(.+?)<\/code>/gi, (match, p1) => { + const replacer = p1.split('').map((char: string) => { + return code?.[char] || char; + }); return match.replace(p1, replacer.join('')); }); }; diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 847679378a..edeafdcbd7 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -496,7 +496,12 @@ export class PostsRepository { ) { const posts: Post[] = []; const uuid = uuidv4(); - + console.log('state', state); + console.log('orgId', orgId); + console.log('date', date); + console.log('body', body); + console.log('tags', tags); + console.log('inter', inter); for (const value of body.value) { const updateData = (type: 'create' | 'update') => ({ publishDate: dayjs(date).toDate(), @@ -541,7 +546,7 @@ export class PostsRepository { }, }, }); - + console.log('updated data', updateData('create')); posts.push( await this._post.model.post.upsert({ where: { diff --git a/libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts index bcdc7a096c..2870073c5d 100644 --- a/libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts @@ -175,7 +175,7 @@ export class TelegramProvider extends SocialAbstract implements SocialProvider { ): Promise { let messageId: number | null = null; const mediaFiles = message.media || []; - const text = striptags(message.message || '', ['u', 'strong', 'p']) + const text = striptags(message.message || '', ['u', 'strong', 'p', 'em', 's', 'blockquote', 'code', 'a']) .replace(//g, '') .replace(/<\/strong>/g, '') .replace(/

(.*?)<\/p>/g, '$1\n'); diff --git a/package.json b/package.json index 26c12635a7..ecd5535f7d 100644 --- a/package.json +++ b/package.json @@ -97,14 +97,18 @@ "@temporalio/common": "^1.14.0", "@temporalio/worker": "^1.14.0", "@temporalio/workflow": "^1.14.0", + "@tiptap/extension-blockquote": "^3.22.5", "@tiptap/extension-bold": "^3.0.6", + "@tiptap/extension-code": "^3.22.4", "@tiptap/extension-document": "^3.0.6", "@tiptap/extension-heading": "^3.0.7", "@tiptap/extension-history": "^3.0.7", + "@tiptap/extension-italic": "^3.22.4", "@tiptap/extension-link": "^3.0.9", "@tiptap/extension-list": "^3.0.7", "@tiptap/extension-mention": "^3.0.7", "@tiptap/extension-paragraph": "^3.0.6", + "@tiptap/extension-strike": "^3.22.4", "@tiptap/extension-text": "^3.0.6", "@tiptap/extension-underline": "^3.0.6", "@tiptap/pm": "^3.0.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 14dbf04304..e32adcebb8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -187,9 +187,15 @@ importers: '@temporalio/workflow': specifier: ^1.14.0 version: 1.15.0 + '@tiptap/extension-blockquote': + specifier: ^3.22.5 + version: 3.22.5(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-bold': specifier: ^3.0.6 version: 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) + '@tiptap/extension-code': + specifier: ^3.22.4 + version: 3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-document': specifier: ^3.0.6 version: 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) @@ -199,6 +205,9 @@ importers: '@tiptap/extension-history': specifier: ^3.0.7 version: 3.20.1(@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)) + '@tiptap/extension-italic': + specifier: ^3.22.4 + version: 3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-link': specifier: ^3.0.9 version: 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1) @@ -211,6 +220,9 @@ importers: '@tiptap/extension-paragraph': specifier: ^3.0.6 version: 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) + '@tiptap/extension-strike': + specifier: ^3.22.4 + version: 3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-text': specifier: ^3.0.6 version: 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) @@ -8079,10 +8091,10 @@ packages: peerDependencies: '@tiptap/pm': ^3.20.1 - '@tiptap/extension-blockquote@3.20.1': - resolution: {integrity: sha512-WzNXk/63PQI2fav4Ta6P0GmYRyu8Gap1pV3VUqaVK829iJ6Zt1T21xayATHEHWMK27VT1GLPJkx9Ycr2jfDyQw==} + '@tiptap/extension-blockquote@3.22.5': + resolution: {integrity: sha512-ajyP5W8fG5Hrru47T/eF3xMKOpNvWofgNJqBTeNuGl02sYxsy9a4EunyFxudsaZP9WW3VOD4SaIWr5+MqpbnOQ==} peerDependencies: - '@tiptap/core': ^3.20.1 + '@tiptap/core': 3.22.5 '@tiptap/extension-bold@3.20.1': resolution: {integrity: sha512-fz++Qv6Rk/Hov0IYG/r7TJ1Y4zWkuGONe0UN5g0KY32NIMg3HeOHicbi4xsNWTm9uAOl3eawWDkezEMrleObMw==} @@ -8106,10 +8118,10 @@ packages: '@tiptap/core': ^3.20.1 '@tiptap/pm': ^3.20.1 - '@tiptap/extension-code@3.20.1': - resolution: {integrity: sha512-509DHINIA/Gg+eTG7TEkfsS8RUiPLH5xZNyLRT0A1oaoaJmECKfrV6aAm05IdfTyqDqz6LW5pbnX6DdUC4keug==} + '@tiptap/extension-code@3.22.4': + resolution: {integrity: sha512-cnbxmVhAcc7X3G81QUYEmKP0ve2hRmvAiFXBuuv9RUtQlBiRnzmhHoJOMgkX0CsMR7+8kMRpTfeDUYq2xp5s5w==} peerDependencies: - '@tiptap/core': ^3.20.1 + '@tiptap/core': 3.22.4 '@tiptap/extension-document@3.20.1': resolution: {integrity: sha512-9vrqdGmRV7bQCSY3NLgu7UhIwgOCDp4sKqMNsoNRX0aZ021QQMTvBQDPkiRkCf7MNsnWrNNnr52PVnULEn3vFQ==} @@ -8154,10 +8166,10 @@ packages: '@tiptap/core': ^3.20.1 '@tiptap/pm': ^3.20.1 - '@tiptap/extension-italic@3.20.1': - resolution: {integrity: sha512-ZYRX13Kt8tR8JOzSXirH3pRpi8x30o7LHxZY58uXBdUvr3tFzOkh03qbN523+diidSVeHP/aMd/+IrplHRkQug==} + '@tiptap/extension-italic@3.22.4': + resolution: {integrity: sha512-fVSDx5AYXgDI3v2zZIqb7V8EewthwM2NJ/ZCX+XaxRsqNEpnjVhgHs7UlvDqK1wj2OJ6zmUNjPtVlAFRxwT+HQ==} peerDependencies: - '@tiptap/core': ^3.20.1 + '@tiptap/core': 3.22.4 '@tiptap/extension-link@3.20.1': resolution: {integrity: sha512-oYTTIgsQMqpkSnJAuAc+UtIKMuI4lv9e1y4LfI1iYm6NkEUHhONppU59smhxHLzb3Ww7YpDffbp5IgDTAiJztA==} @@ -8198,10 +8210,10 @@ packages: peerDependencies: '@tiptap/core': ^3.20.1 - '@tiptap/extension-strike@3.20.1': - resolution: {integrity: sha512-EYgyma10lpsY+rwbVQL9u+gA7hBlKLSMFH7Zgd37FSxukOjr+HE8iKPQQ+SwbGejyDsPlLT8Z5Jnuxo5Ng90Pg==} + '@tiptap/extension-strike@3.22.4': + resolution: {integrity: sha512-aRHWQj42HiailXSC9LkKYM3jWMcSeGwOjbqM4PiuxQZmHVDRFmeHkfJItOdn2cSHaO0vuEVK+TvrWUWsBFi3pg==} peerDependencies: - '@tiptap/core': ^3.20.1 + '@tiptap/core': 3.22.4 '@tiptap/extension-text@3.20.1': resolution: {integrity: sha512-7PlIbYW8UenV6NPOXHmv8IcmPGlGx6HFq66RmkJAOJRPXPkTLAiX0N8rQtzUJ6jDEHqoJpaHFEHJw0xzW1yF+A==} @@ -9883,6 +9895,7 @@ packages: basic-ftp@5.2.0: resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} engines: {node: '>=10.0.0'} + deprecated: Security vulnerability fixed in 5.2.1, please upgrade bcp-47-match@2.0.3: resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} @@ -26929,7 +26942,7 @@ snapshots: dependencies: '@tiptap/pm': 3.20.1 - '@tiptap/extension-blockquote@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))': + '@tiptap/extension-blockquote@3.22.5(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))': dependencies: '@tiptap/core': 3.20.1(@tiptap/pm@3.20.1) @@ -26953,7 +26966,7 @@ snapshots: '@tiptap/core': 3.20.1(@tiptap/pm@3.20.1) '@tiptap/pm': 3.20.1 - '@tiptap/extension-code@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))': + '@tiptap/extension-code@3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))': dependencies: '@tiptap/core': 3.20.1(@tiptap/pm@3.20.1) @@ -26993,7 +27006,7 @@ snapshots: '@tiptap/core': 3.20.1(@tiptap/pm@3.20.1) '@tiptap/pm': 3.20.1 - '@tiptap/extension-italic@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))': + '@tiptap/extension-italic@3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))': dependencies: '@tiptap/core': 3.20.1(@tiptap/pm@3.20.1) @@ -27030,7 +27043,7 @@ snapshots: dependencies: '@tiptap/core': 3.20.1(@tiptap/pm@3.20.1) - '@tiptap/extension-strike@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))': + '@tiptap/extension-strike@3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))': dependencies: '@tiptap/core': 3.20.1(@tiptap/pm@3.20.1) @@ -27088,10 +27101,10 @@ snapshots: '@tiptap/starter-kit@3.20.1': dependencies: '@tiptap/core': 3.20.1(@tiptap/pm@3.20.1) - '@tiptap/extension-blockquote': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) + '@tiptap/extension-blockquote': 3.22.5(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-bold': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-bullet-list': 3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)) - '@tiptap/extension-code': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) + '@tiptap/extension-code': 3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-code-block': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1) '@tiptap/extension-document': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-dropcursor': 3.20.1(@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)) @@ -27099,14 +27112,14 @@ snapshots: '@tiptap/extension-hard-break': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-heading': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-horizontal-rule': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1) - '@tiptap/extension-italic': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) + '@tiptap/extension-italic': 3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-link': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1) '@tiptap/extension-list': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1) '@tiptap/extension-list-item': 3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)) '@tiptap/extension-list-keymap': 3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)) '@tiptap/extension-ordered-list': 3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)) '@tiptap/extension-paragraph': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) - '@tiptap/extension-strike': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) + '@tiptap/extension-strike': 3.22.4(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-text': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extension-underline': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1)) '@tiptap/extensions': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1) From 3b4f2566dffe3306f32aef04458d4a855fd88ff3 Mon Sep 17 00:00:00 2001 From: pottycat Date: Thu, 7 May 2026 09:57:42 +0000 Subject: [PATCH 2/7] chore: remove unnecessary console.log & format code --- .../src/database/prisma/posts/posts.repository.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index edeafdcbd7..b7d6a89021 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -138,7 +138,7 @@ export class PostsRepository { OR: [ { organizationId: orgId, - } + }, ], }, { @@ -496,12 +496,7 @@ export class PostsRepository { ) { const posts: Post[] = []; const uuid = uuidv4(); - console.log('state', state); - console.log('orgId', orgId); - console.log('date', date); - console.log('body', body); - console.log('tags', tags); - console.log('inter', inter); + for (const value of body.value) { const updateData = (type: 'create' | 'update') => ({ publishDate: dayjs(date).toDate(), @@ -546,7 +541,7 @@ export class PostsRepository { }, }, }); - console.log('updated data', updateData('create')); + posts.push( await this._post.model.post.upsert({ where: { From 6ccd9d1c9171a558ed7025d50f695081796ecce8 Mon Sep 17 00:00:00 2001 From: pottycat Date: Thu, 7 May 2026 11:02:15 +0000 Subject: [PATCH 3/7] chore: fix blockquote & Link bug --- apps/frontend/src/components/new-launch/editor.tsx | 7 +------ libraries/helpers/src/utils/strip.html.validation.ts | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 3fe4cddd6b..2d07acc144 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -930,7 +930,6 @@ export const OnlyEditor = forwardRef< Underline, Bold, Italic, - Link, Code, Strike, Blockquote, @@ -938,17 +937,13 @@ export const OnlyEditor = forwardRef< InterceptUnderlineShortcut, BulletList, ListItem, - // stops link propagation when more texts are typed after link insertion - Link.extend({ - inclusive: false, - }), Placeholder.configure({ placeholder: t('write_something', 'Write something …'), emptyEditorClass: 'is-editor-empty', }), ...(editorType === 'html' || editorType === 'markdown' ? [ - Link.configure({ + Link.extend({ inclusive: false }).configure({ openOnClick: false, autolink: true, defaultProtocol: 'https', diff --git a/libraries/helpers/src/utils/strip.html.validation.ts b/libraries/helpers/src/utils/strip.html.validation.ts index 07ce780385..f93c1afe81 100644 --- a/libraries/helpers/src/utils/strip.html.validation.ts +++ b/libraries/helpers/src/utils/strip.html.validation.ts @@ -578,7 +578,7 @@ export const convertToAscii = (value: string): string => { }); return match.replace(p1, replacer.join('')); }) - .replace(/

(.+?)<\/blockquote>/gi, (match, p1) => { + .replace(/
([\s\S]+?)<\/blockquote>/gi, (match, p1) => { const replacer = p1.split('').map((char: string) => { return blockquote?.[char] || char; }); From 0795521504fbcc3d3c8f1c1767174de61420e085 Mon Sep 17 00:00:00 2001 From: pottycat Date: Thu, 7 May 2026 11:25:00 +0000 Subject: [PATCH 4/7] chore: utilise existing AComponent for link insertion --- .../src/components/new-launch/code.text.tsx | 1 - .../src/components/new-launch/editor.tsx | 6 +- .../src/components/new-launch/link.text.tsx | 171 ------------------ 3 files changed, 4 insertions(+), 174 deletions(-) delete mode 100644 apps/frontend/src/components/new-launch/link.text.tsx diff --git a/apps/frontend/src/components/new-launch/code.text.tsx b/apps/frontend/src/components/new-launch/code.text.tsx index 3563e74862..95274e27d0 100644 --- a/apps/frontend/src/components/new-launch/code.text.tsx +++ b/apps/frontend/src/components/new-launch/code.text.tsx @@ -4,7 +4,6 @@ export const CodeText: FC<{ editor: any; }> = ({ editor }) => { const mark = () => { - editor?.commands?.unsetUnderline(); editor?.commands?.toggleCode(); editor?.commands?.focus(); }; diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 2d07acc144..0beed5a381 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -21,7 +21,6 @@ import { ItalicText } from '@gitroom/frontend/components/new-launch/italic.text' import { CodeText } from '@gitroom/frontend/components/new-launch/code.text'; import { StrikeText } from '@gitroom/frontend/components/new-launch/strike.text'; import { BlockquoteText } from '@gitroom/frontend/components/new-launch/blockquote.text'; -import { LinkText } from '@gitroom/frontend/components/new-launch/link.text'; import { SignatureBox } from '@gitroom/frontend/components/signature'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { @@ -802,10 +801,13 @@ export const Editor: FC<{ {editorType === 'html' && identifier === 'telegram' && ( <> - + )} {(editorType === 'markdown' || editorType === 'html') && diff --git a/apps/frontend/src/components/new-launch/link.text.tsx b/apps/frontend/src/components/new-launch/link.text.tsx deleted file mode 100644 index ab6cf60b86..0000000000 --- a/apps/frontend/src/components/new-launch/link.text.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { FC, useState, useEffect } from 'react'; -import { useForm, FormProvider } from 'react-hook-form'; -import { Button } from '@gitroom/react/form/button'; -import { Input } from '@gitroom/react/form/input'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; - -interface LinkModalProps { - isOpen: boolean; - defaultText: string; - onClose: () => void; - onApply: (text: string, url: string) => void; -} - -const LinkModal: FC = ({ - isOpen, - defaultText, - onClose, - onApply, -}) => { - const t = useT(); - const form = useForm(); - const [text, setText] = useState(defaultText); - const [url, setUrl] = useState(''); - - useEffect(() => { - setText(defaultText); - }, [defaultText]); - - if (!isOpen) return null; - - const handleApply = () => { - if (!url) return; - onApply(text, url); - setText(''); - setUrl(''); - }; - - const handleClose = () => { - setText(''); - setUrl(''); - onClose(); - }; - - return ( - -
-
-

{t('insert_link', 'Insert Link')}

- -
- setText(e.target.value)} - placeholder={t('display_text', 'Display Text')} - className="border border-gray-200 rounded-[6px] px-3 py-2 text-sm outline-none focus:border-gray-400" - /> -
- -
- setUrl(e.target.value)} - placeholder={t('www.example.com')} - className="border border-gray-200 rounded-[6px] px-3 py-2 text-sm outline-none focus:border-gray-400" - /> -
- -
- - -
-
-
-
- ); -}; - -export const LinkText: FC<{ - editor: any; -}> = ({ editor }) => { - const [isModalOpen, setIsModalOpen] = useState(false); - const [selectedText, setSelectedText] = useState(''); - // Get the currently selected text from the editor (if any) - const { from, to } = editor?.state?.selection ?? {}; - - const mark = () => { - const highlighted = editor?.state?.doc?.textBetween(from, to, ' ') ?? ''; - setSelectedText(highlighted); - setIsModalOpen(true); - }; - - const handleApply = (text: string, url: string) => { - if (from !== to) { - // Replace selected text with linked version - editor?.commands?.setLink({ href: url }); - } else { - // No selection — insert new linked text at cursor - editor - ?.chain() - ?.focus() - ?.insertContent(`${text || url}`) - ?.run(); - } - - setIsModalOpen(false); - setSelectedText(''); - }; - - return ( - <> -
- - - - -
- - { - setIsModalOpen(false); - setSelectedText(''); - }} - onApply={handleApply} - /> - - ); -}; From 9e6bfa671e922ff5288f2ece898b91c210b7a62e Mon Sep 17 00:00:00 2001 From: pottycat Date: Thu, 14 May 2026 05:58:07 +0000 Subject: [PATCH 5/7] text formats available to all providers --- apps/frontend/src/components/new-launch/editor.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 0beed5a381..de01dbdba0 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -796,18 +796,11 @@ export const Editor: FC<{ editor={editorRef?.current?.editor} currentValue={props.value!} /> - - )} - {editorType === 'html' && identifier === 'telegram' && ( - <> - + )} {(editorType === 'markdown' || editorType === 'html') && From f9a0021b54bd75b2a1aa8e57fe95ce3ca3b3549e Mon Sep 17 00:00:00 2001 From: pottycat Date: Thu, 14 May 2026 06:36:50 +0000 Subject: [PATCH 6/7] align text formats --- apps/frontend/src/components/new-launch/editor.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index de01dbdba0..045e315d6a 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -800,9 +800,18 @@ export const Editor: FC<{ - )} + {/* link insertion may not work on all providers e.g insta, x, facebook */} + {(editorType === 'html' || editorType === 'markdown') && + identifier === 'telegram' && ( + <> + + + )} {(editorType === 'markdown' || editorType === 'html') && identifier !== 'telegram' && ( <> From 00a29cff92f3ecc3b9410ebe266480c01328c75e Mon Sep 17 00:00:00 2001 From: pottycat Date: Mon, 18 May 2026 05:18:41 +0000 Subject: [PATCH 7/7] fix: handle multiline content in convertToAscii --- libraries/helpers/src/utils/strip.html.validation.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/helpers/src/utils/strip.html.validation.ts b/libraries/helpers/src/utils/strip.html.validation.ts index f93c1afe81..4484e45e7a 100644 --- a/libraries/helpers/src/utils/strip.html.validation.ts +++ b/libraries/helpers/src/utils/strip.html.validation.ts @@ -550,7 +550,7 @@ export const convertMention = ( export const convertToAscii = (value: string): string => { return value - .replace(/(.+?)<\/strong>/gi, (match, p1) => { + .replace(/([\s\S]+?)<\/strong>/gi, (match, p1) => { const replacer = p1.split('').map((char: string) => { // @ts-ignore return bold?.[char] || char; @@ -558,7 +558,7 @@ export const convertToAscii = (value: string): string => { return match.replace(p1, replacer.join('')); }) - .replace(/(.+?)<\/u>/gi, (match, p1) => { + .replace(/([\s\S]+?)<\/u>/gi, (match, p1) => { const replacer = p1.split('').map((char: string) => { // @ts-ignore return underlineMap?.[char] || char; @@ -566,13 +566,13 @@ export const convertToAscii = (value: string): string => { return match.replace(p1, replacer.join('')); }) - .replace(/(.+?)<\/em>/gi, (match, p1) => { + .replace(/([\s\S]+?)<\/em>/gi, (match, p1) => { const replacer = p1.split('').map((char: string) => { return italic?.[char] || char; }); return match.replace(p1, replacer.join('')); }) - .replace(/(.+?)<\/s>/gi, (match, p1) => { + .replace(/([\s\S]+?)<\/s>/gi, (match, p1) => { const replacer = p1.split('').map((char: string) => { return strikethrough?.[char] || char; }); @@ -584,7 +584,7 @@ export const convertToAscii = (value: string): string => { }); return match.replace(p1, replacer.join('')); }) - .replace(/(.+?)<\/code>/gi, (match, p1) => { + .replace(/([\s\S]+?)<\/code>/gi, (match, p1) => { const replacer = p1.split('').map((char: string) => { return code?.[char] || char; });