feat: add more text formats to telegram editor - #1499
Conversation
| Link.extend({ | ||
| inclusive: false, | ||
| }), |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| .replace(/<blockquote>(.+?)<\/blockquote>/gi, (match, p1) => { | ||
| const replacer = p1.split('').map((char: string) => { | ||
| return blockquote?.[char] || char; | ||
| }); | ||
| return match.replace(p1, replacer.join('')); | ||
| }) |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| editor?.commands?.unsetUnderline(); | ||
| editor?.commands?.toggleCode(); | ||
| editor?.commands?.focus(); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| 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 ?? {}; |
There was a problem hiding this comment.
Bug: The LinkText component fails to apply links because it captures the text selection at render time, and the selection is lost when the link modal opens.
Severity: HIGH
Suggested Fix
Before calling editor?.commands?.setLink(), the editor's focus and selection range should be restored. This can be achieved by calling editor?.chain()?.focus() and potentially extendMarkRange('link') to re-select the intended text, similar to the implementation in a.component.tsx.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: apps/frontend/src/components/new-launch/link.text.tsx#L103
Potential issue: The `LinkText` component captures the editor's selection state (`from`,
`to`) at the time the component renders, not when the link button is clicked. When the
button is clicked, a modal opens, which causes the editor to lose focus and its text
selection to be cleared. When the user confirms the link in the modal, the `handleApply`
function calls `editor?.commands?.setLink()`. This command operates on the editor's
current selection, which is now empty. As a result, the link is not applied to the
originally selected text.
|
@pottycat since you added also an ascii version of it, I think we can enable it on all the providers no? |
This comment has been minimized.
This comment has been minimized.
|
@pottycat's application for Postiz was approved — reopening this PR. |
I tested it. So, yes we can enable it on all platforms. I will make changes to enable it in all providers not just in telegram |
5af8474 to
143dd06
Compare
| editor={editorRef?.current?.editor} | ||
| currentValue={props.value!} | ||
| /> | ||
| <ItalicText editor={editorRef?.current?.editor} /> | ||
| <StrikeText editor={editorRef?.current?.editor} /> | ||
| <BlockquoteText editor={editorRef?.current?.editor} /> | ||
| <CodeText editor={editorRef?.current?.editor} /> | ||
| <AComponent editor={editorRef?.current?.editor} /> | ||
| </> | ||
| )} | ||
| {(editorType === 'markdown' || editorType === 'html') && |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| <BlockquoteText editor={editorRef?.current?.editor} /> | ||
| <CodeText editor={editorRef?.current?.editor} /> | ||
| </> | ||
| )} | ||
| {/* link insertion may not work on all providers e.g insta, x, facebook */} | ||
| {(editorType === 'html' || editorType === 'markdown') && | ||
| identifier === 'telegram' && ( |
There was a problem hiding this comment.
Bug: The blockquote button is incorrectly enabled for non-Telegram providers, causing text to be converted to full-width Unicode characters when posted.
Severity: HIGH
Suggested Fix
Conditionally render the blockquote button to only appear for editors where it is semantically supported, such as Telegram. This can be achieved by moving the button inside the identifier === 'telegram' check, similar to how the link insertion button is handled.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: apps/frontend/src/components/new-launch/editor.tsx#L800-L806
Potential issue: The blockquote formatting button has been made available for all editor
types, including 'normal' editors used for platforms like X, Instagram, and LinkedIn.
However, the underlying implementation for these platforms was not updated to support
blockquotes. When a user applies blockquote formatting, the content is processed by
`convertToAscii`, which converts the text within `<blockquote>` tags into full-width
Unicode characters (e.g., 'a' becomes 'a'). This results in posts with garbled,
unintended text on platforms that do not have native blockquote support, as the feature
was only intended to work for Telegram.
Also affects:
libraries/helpers/src/utils/strip.html.validation.ts:581~586
There was a problem hiding this comment.
@nevo-david I am facing confusion about placement of text format buttons. I am thinking about placing Bold, Italic and Underline in global mode. Strikethrough, Blockquote, and Code in editorType html or mardown. The link insertion button on Telegram only.
There was a problem hiding this comment.
@nevo-david I am facing confusion about placement of text format buttons. I am thinking about placing Bold, Italic and Underline in global mode. Strikethrough, Blockquote, and Code in editorType html or mardown. The link insertion button on Telegram only.
I have placed all Bold, Italic, Underline, Strikethough, Blockquote and Code in global mode because it is converted into ASCII which is rendered well in all the platform. The link insertion is only available to Telegram for the moment. I am thinking about raising a separate PR for including link insertion options to all the platform that supports it in message.
| .replace(/<em>(.+?)<\/em>/gi, (match, p1) => { | ||
| const replacer = p1.split('').map((char: string) => { | ||
| return italic?.[char] || char; | ||
| }); | ||
| return match.replace(p1, replacer.join('')); | ||
| }) | ||
| .replace(/<s>(.+?)<\/s>/gi, (match, p1) => { | ||
| const replacer = p1.split('').map((char: string) => { | ||
| return strikethrough?.[char] || char; | ||
| }); | ||
| return match.replace(p1, replacer.join('')); |
There was a problem hiding this comment.
Bug: The regex for <em>, <s>, and <code> tags in convertToAscii does not handle multiline content, unlike the regex for <blockquote>.
Severity: LOW
Suggested Fix
Update the regular expressions for <em>, <s>, and <code> tags to use [\s\S]+? instead of .+?. This will ensure consistency with the <blockquote> handling and correctly process multiline content within these tags.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: libraries/helpers/src/utils/strip.html.validation.ts#L569-L579
Potential issue: In the `convertToAscii` function, the regular expressions used to
capture content within `<em>`, `<s>`, and `<code>` tags use the pattern `.+?`. This
pattern does not match newline characters. This is inconsistent with the regex for
`<blockquote>`, which correctly uses `[\s\S]+?` to handle multiline content. While the
current editor configuration makes it unlikely for these inline tags to contain
newlines, this is a latent bug. If content with newlines is pasted into the editor or if
the editor's behavior changes in the future (e.g., to support soft breaks), formatting
will be silently lost for any multiline formatted text.
b4bac3d to
00a29cf
Compare
|
Hi @pottycat! Before we can accept contributions to Postiz you need to sign the Contributor License Agreement. Sign here: https://contribute.postiz.com/p/postiz/cla. Your PR stays open and we'll re-check automatically once signed. |
|
Hey @SuperDrak 👋 Would love to see this land — Telegram inline links are a great addition. Quick heads-up on the red CI: the production
One line each — annotate the maps as -const bold = {
+const bold: Record<string, string> = {…same for Verified by building the Docker image off this branch + the patch — |
I am happy to receive a PR into my branch. please go ahead! |
|
Found one more missing piece for full formatting support: Adding the missing tags fixes it end-to-end: const ALLOWED_TAGS = [
'p', 'br', 'strong', 'u', 'a', 'ul', 'li', 'h1', 'h2', 'h3', 'span',
+ 'em', 'i', 's', 'code', 'pre', 'blockquote',
];Verified live on a build off this branch (+ the earlier |
i was totally unaware of this one 😅 |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
| const text = striptags(message.message || '', ['u', 'strong', 'p', 'em', 's', 'blockquote', 'code', 'a']) | ||
| .replace(/<strong>/g, '<b>') | ||
| .replace(/<\/strong>/g, '</b>') | ||
| .replace(/<p>(.*?)<\/p>/g, '$1\n'); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
|
|
||
| return match.replace(p1, replacer.join('')); | ||
| }) | ||
| .replace(/<em>([\s\S]+?)<\/em>/gi, (match, p1) => { |
There was a problem hiding this comment.
Bug: In convertToAscii, the string replacement logic can corrupt HTML tags if the inner text matches part of the tag name, such as with <em>em</em>.
Severity: MEDIUM
Suggested Fix
Update the replacement logic to only target the content within the tags, not the tags themselves. Instead of replacing within the match string, construct a new string by concatenating the opening tag, the new Unicode content, and the closing tag.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: libraries/helpers/src/utils/strip.html.validation.ts#L569
Potential issue: The `convertToAscii` function replaces HTML tags with Unicode character
equivalents for platforms that do not support HTML. The replacement logic uses
`match.replace(p1, replacer)`, where `match` is the full matched string (e.g.,
`<em>em</em>`) and `p1` is the captured inner text (e.g., `em`).
`String.prototype.replace()` only replaces the first occurrence. If the inner text is
also present in the tag name, the tag itself is corrupted. For example, `<em>em</em>`
incorrectly becomes `<𝘦𝘮>em</em>`. This affects posts to platforms like Bluesky,
Threads, and X.
Also affects:
libraries/helpers/src/utils/strip.html.validation.ts:574~574libraries/helpers/src/utils/strip.html.validation.ts:579~579libraries/helpers/src/utils/strip.html.validation.ts:584~584libraries/helpers/src/utils/strip.html.validation.ts:589~592
Did we get this right? 👍 / 👎 to inform future reviews.
| .replace(/<p>(.*?)<\/p>/g, '$1\n'); | ||
| .replace(/<p>([\s\S]*?)<\/p>/g, '$1\n'); | ||
|
|
||
| console.log(text); |
There was a problem hiding this comment.
Bug: A console.log(text) statement in the sendMessage method of the Telegram provider leaks the full content of every post and comment to the server logs.
Severity: HIGH
Suggested Fix
Remove the console.log(text) statement from line 183 in libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts to prevent user content from being logged in production environments.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts#L183
Potential issue: The `sendMessage` method in `telegram.provider.ts` contains a
`console.log(text)` statement that logs the full content of every user post and comment
sent via Telegram. This method is called by the public `post()` and `comment()`
functions, which are part of the main production code path. Since there are no
conditional checks for the environment (e.g., `NODE_ENV`), this logging will occur in
production, exposing all user-generated content, which may be sensitive, to the server
logs. This appears to be a leftover debugging statement.
| editor={editorRef?.current?.editor} | ||
| currentValue={props.value!} | ||
| /> | ||
| <BlockquoteText | ||
| editor={editorRef?.current?.editor} | ||
| currentValue={props.value!} | ||
| /> | ||
| <CodeText | ||
| editor={editorRef?.current?.editor} | ||
| currentValue={props.value!} | ||
| /> |
There was a problem hiding this comment.
Bug: New text formatting buttons are incorrectly displayed for all providers, not just Telegram, causing unintended Unicode character transformations on platforms like Twitter/X.
Severity: MEDIUM
Suggested Fix
Update the visibility condition for the new formatting buttons in editor.tsx. Restrict their display to only the Telegram provider by changing the condition from {editorType !== 'none'} to a check like {identifier === 'telegram'}.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: apps/frontend/src/components/new-launch/editor.tsx#L796-L814
Potential issue: New text formatting buttons (Italic, Strike, Blockquote, Code) are
displayed for all social media providers, not just Telegram. The visibility is
controlled by `editorType !== 'none'`, making them appear universally. For providers
with `editor = 'normal'` (like Twitter/X), this results in the backend `convertToAscii`
function transforming the formatted text into Unicode characters, which may not be the
intended visual output on those platforms. This is inconsistent with the link insertion
button, which is correctly restricted to Telegram only.
Also affects:
libraries/helpers/src/utils/strip.html.validation.ts:550~593libraries/helpers/src/utils/sanitize.post.content.ts:12~23
|
@nevo-david requesting review! |
| .replace(/<blockquote>([\s\S]+?)<\/blockquote>/gi, (match, p1) => { | ||
| const replacer = p1.split('').map((char: string) => { | ||
| return blockquote?.[char] || char; | ||
| }); | ||
| return match.replace(p1, replacer.join('')); | ||
| }) |
There was a problem hiding this comment.
Bug: The convertToAscii function incorrectly replaces the 'p' in <p> tags inside blockquotes with a full-width character, corrupting the HTML for posts on platforms like X and LinkedIn.
Severity: HIGH
Suggested Fix
Modify the convertToAscii function to avoid replacing characters that are part of HTML tags. The logic should process only the text nodes within the <blockquote> element while preserving the integrity of child HTML tags like <p>.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: libraries/helpers/src/utils/strip.html.validation.ts#L581-L586
Potential issue: The `convertToAscii` function, which is called when
`stripHtmlValidation` is used with `replaceBold: true` (e.g., for X and LinkedIn),
incorrectly processes HTML content within `<blockquote>` tags. The function performs a
character-by-character replacement on the entire content inside the blockquote,
including HTML tags. Because the character map includes a replacement for 'p', the `<p>`
and `</p>` tags generated by the Tiptap editor inside a blockquote are corrupted into
`<p>` and `</p>`. This malformed HTML can lead to incorrect rendering or visible tag
remnants in posts on affected platforms.
| .replace(/<s>([\s\S]+?)<\/s>/gi, (match, p1) => { | ||
| const replacer = p1.split('').map((char: string) => { | ||
| return strikethrough?.[char] || char; | ||
| }); | ||
| return match.replace(p1, replacer.join('')); |
There was a problem hiding this comment.
Bug: The use of match.replace(p1, ...) can incorrectly modify an opening HTML tag if the tag's content also appears within the tag name, like in <s>s</s>.
Severity: MEDIUM
Suggested Fix
Instead of using match.replace(p1, ...), which can be ambiguous, construct the new string explicitly. A safer approach would be to return a new string literal like "<${tag}>${replacer.join('')}</${tag}>", where tag is the HTML tag name (e.g., 's', 'em'). This ensures only the content is replaced and the tags remain intact.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: libraries/helpers/src/utils/strip.html.validation.ts#L575-L579
Potential issue: In `convertToAscii`, HTML tags are processed using a regular expression
and `String.prototype.replace`. The inner replacement logic, `match.replace(p1,
replacer.join(''))`, is flawed because it replaces the first occurrence of the captured
content (`p1`) within the entire matched string (`match`). If the content also appears
in the opening tag (e.g., `<s>s</s>` or ``code``), the tag itself will be corrupted
(e.g., to `<s̶>s</s>` or `<𝚌𝚘𝚍𝚎>code</code>`). This produces malformed HTML, which can
lead to incorrect rendering or parsing downstream.
Also affects:
libraries/helpers/src/utils/strip.html.validation.ts:569~574libraries/helpers/src/utils/strip.html.validation.ts:581~586libraries/helpers/src/utils/strip.html.validation.ts:587~592
Closes #1352
What kind of change does this PR introduce?
It adds following text formats to telegram editor
Why was this change needed?
Currently, Postiz editor only provides underline and bold text format options. Telegram also supports italic, strikethough, blockquote, code and link insertion in text messages. The change will help user utilize all the formats supported by telegram in postiz editor.
Other information:
a working demo
Postiz.Calendar.-.7.May.2026.mp4
telegram result

Checklist:
Put a "X" in the boxes below to indicate you have followed the checklist;