-
-
-
+
+
+
{(editorType === 'markdown' || editorType === 'html') && (
-
+ <>
+
+
+ >
)}
-
+
{
- if (editor?.isFocused) {
+ if (editorRef?.current?.editor?.isFocused) {
return;
}
- editor?.commands?.focus('end');
+ editorRef?.current?.editor?.commands?.focus('end');
}}
>
{
- if (editor?.isFocused) {
+ if (editorRef?.current?.editor?.isFocused) {
return;
}
- editor?.commands?.focus('end');
+ editorRef?.current?.editor?.commands?.focus('end');
}}
>
{setImages && (
@@ -732,3 +714,68 @@ export const Editor: FC<{
);
};
+
+export const OnlyEditor = forwardRef<
+ any,
+ {
+ editorType: 'normal' | 'markdown' | 'html';
+ value: string;
+ onChange: (value: string) => void;
+ paste?: (event: ClipboardEvent | File[]) => void;
+ }
+>(({ editorType, value, onChange, paste }, ref) => {
+ const editorOptions = useMemo(() => {
+ if (editorType === 'normal') {
+ return [];
+ }
+
+ const list = [];
+
+ if (
+ editorType === ('markdown' as const) ||
+ editorType === ('html' as const)
+ ) {
+ list.push(
+ BulletList,
+ ListItem,
+ Heading.configure({
+ levels: [1, 2, 3],
+ })
+ );
+ }
+
+ return list;
+ }, [editorType]);
+
+ const editor = useEditor({
+ extensions: [
+ Document,
+ Paragraph,
+ Text,
+ Underline,
+ Bold,
+ InterceptBoldShortcut,
+ InterceptUnderlineShortcut,
+ Span,
+ History.configure({
+ depth: 100, // default is 100
+ newGroupDelay: 100, // default is 500ms
+ }),
+ ...editorOptions,
+ ],
+ content: value || '',
+ shouldRerenderOnTransaction: true,
+ immediatelyRender: false,
+ // @ts-ignore
+ onPaste: paste,
+ onUpdate: (innerProps) => {
+ onChange?.(innerProps.editor.getHTML());
+ },
+ });
+
+ useImperativeHandle(ref, () => ({
+ editor,
+ }));
+
+ return
;
+});
diff --git a/apps/frontend/src/components/new-launch/heading.component.tsx b/apps/frontend/src/components/new-launch/heading.component.tsx
new file mode 100644
index 00000000..d4204839
--- /dev/null
+++ b/apps/frontend/src/components/new-launch/heading.component.tsx
@@ -0,0 +1,73 @@
+'use client';
+
+import { FC, useCallback } from 'react';
+
+export const HeadingComponent: FC<{
+ editor: any;
+ currentValue: string;
+}> = ({ editor }) => {
+ const setHeading = useCallback((level: number) => () => {
+ editor.commands.toggleHeading({ level })
+ }, []);
+
+ return (
+
+ );
+};
diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx
index 6102d4ad..87d07a5a 100644
--- a/apps/frontend/src/components/new-launch/manage.modal.tsx
+++ b/apps/frontend/src/components/new-launch/manage.modal.tsx
@@ -166,7 +166,6 @@ export const ManageModal: FC
= (props) => {
return;
}
- console.log(checkAllValid);
for (const item of checkAllValid) {
if (item.valid === false) {
toaster.show('Some fields are not valid', 'warning');
diff --git a/apps/frontend/src/components/new-launch/providers/devto/devto.provider.tsx b/apps/frontend/src/components/new-launch/providers/devto/devto.provider.tsx
index 0898a7b3..66c7e5e7 100644
--- a/apps/frontend/src/components/new-launch/providers/devto/devto.provider.tsx
+++ b/apps/frontend/src/components/new-launch/providers/devto/devto.provider.tsx
@@ -12,55 +12,10 @@ import { SelectOrganization } from '@gitroom/frontend/components/new-launch/prov
import { DevtoTags } from '@gitroom/frontend/components/new-launch/providers/devto/devto.tags';
import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory';
import clsx from 'clsx';
-import MDEditor from '@uiw/react-md-editor';
import { Canonical } from '@gitroom/react/form/canonical';
import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration';
import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values';
-const DevtoPreview: FC = () => {
- const { value } = useIntegration();
- const settings = useSettings();
- const image = useMediaDirectory();
- const [coverPicture, title, tags] = settings.watch([
- 'main_image',
- 'title',
- 'tags',
- ]);
- return (
-
- {!!coverPicture?.path && (
-
-
})
-
- )}
-
-
{title}
-
- {tags?.map((p: any) => (
-
#{p.label}
- ))}
-
-
-
- p.content).join('\n')}
- />
-
-
- );
-};
const DevtoSettings: FC = () => {
const form = useSettings();
const { date } = useIntegration();
diff --git a/apps/frontend/src/components/new-launch/providers/hashnode/hashnode.provider.tsx b/apps/frontend/src/components/new-launch/providers/hashnode/hashnode.provider.tsx
index 432b3e4e..b4793ff2 100644
--- a/apps/frontend/src/components/new-launch/providers/hashnode/hashnode.provider.tsx
+++ b/apps/frontend/src/components/new-launch/providers/hashnode/hashnode.provider.tsx
@@ -13,55 +13,9 @@ import { HashnodeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/provid
import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration';
import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory';
import clsx from 'clsx';
-import MDEditor from '@uiw/react-md-editor';
import { MediaComponent } from '@gitroom/frontend/components/media/media.component';
import { Canonical } from '@gitroom/react/form/canonical';
-const HashnodePreview: FC = () => {
- const { value } = useIntegration();
- const settings = useSettings();
- const image = useMediaDirectory();
- const [coverPicture, title, subtitle] = settings.watch([
- 'main_image',
- 'title',
- 'subtitle',
- ]);
- return (
-
- {!!coverPicture?.path && (
-
-
})
-
- )}
-
-
- {title}
-
-
- {subtitle}
-
-
-
- p.content).join('\n')}
- />
-
-
- );
-};
const HashnodeSettings: FC = () => {
const form = useSettings();
const { date } = useIntegration();
diff --git a/apps/frontend/src/components/new-launch/providers/medium/medium.provider.tsx b/apps/frontend/src/components/new-launch/providers/medium/medium.provider.tsx
index dbc32bf9..ee29ac51 100644
--- a/apps/frontend/src/components/new-launch/providers/medium/medium.provider.tsx
+++ b/apps/frontend/src/components/new-launch/providers/medium/medium.provider.tsx
@@ -11,40 +11,8 @@ import { MediumPublications } from '@gitroom/frontend/components/new-launch/prov
import { MediumTags } from '@gitroom/frontend/components/new-launch/providers/medium/medium.tags';
import { MediumSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/medium.settings.dto';
import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration';
-import clsx from 'clsx';
-import MDEditor from '@uiw/react-md-editor';
import { Canonical } from '@gitroom/react/form/canonical';
-import interClass from '@gitroom/react/helpers/inter.font';
-const MediumPreview: FC = () => {
- const { value } = useIntegration();
- const settings = useSettings();
- const [title, subtitle] = settings.watch(['title', 'subtitle']);
- return (
-
-
-
{title}
-
- {subtitle}
-
-
-
- p.content).join('\n')}
- />
-
-
- );
-};
const MediumSettings: FC = () => {
const form = useSettings();
const { date } = useIntegration();
diff --git a/apps/frontend/src/components/new-launch/providers/reddit/reddit.provider.tsx b/apps/frontend/src/components/new-launch/providers/reddit/reddit.provider.tsx
index 98a66d20..e27754b8 100644
--- a/apps/frontend/src/components/new-launch/providers/reddit/reddit.provider.tsx
+++ b/apps/frontend/src/components/new-launch/providers/reddit/reddit.provider.tsx
@@ -17,7 +17,6 @@ import {
import clsx from 'clsx';
import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
-import MDEditor from '@uiw/react-md-editor';
import interClass from '@gitroom/react/helpers/inter.font';
import Image from 'next/image';
import { useT } from '@gitroom/react/translation/get.transation.service.client';
@@ -38,14 +37,12 @@ const RenderRedditComponent: FC<{
switch (type) {
case 'self':
return (
-
);
case 'link':
@@ -143,13 +140,11 @@ const RedditPreview: FC = (props) => {
{integration?.name}
-
diff --git a/apps/frontend/src/components/post-url-selector/post.url.selector.tsx b/apps/frontend/src/components/post-url-selector/post.url.selector.tsx
index 899a7cd4..88cfefbb 100644
--- a/apps/frontend/src/components/post-url-selector/post.url.selector.tsx
+++ b/apps/frontend/src/components/post-url-selector/post.url.selector.tsx
@@ -3,13 +3,6 @@
import { EventEmitter } from 'events';
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
-import {
- executeCommand,
- ExecuteState,
- ICommand,
- selectWord,
- TextAreaTextApi,
-} from '@uiw/react-md-editor';
import dayjs from 'dayjs';
import useSWR from 'swr';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
@@ -211,45 +204,3 @@ export const PostSelector: FC<{
>
);
};
-export const postSelector = (date: dayjs.Dayjs): ICommand => ({
- name: 'postselector',
- keyCommand: 'postselector',
- shortcuts: 'ctrlcmd+p',
- prefix: '(post:',
- suffix: ')',
- buttonProps: {
- 'aria-label': 'Add Post Url',
- title: 'Add Post Url',
- },
- icon: (
-
- ),
- execute: async (state: ExecuteState, api: TextAreaTextApi) => {
- const newSelectionRange = selectWord({
- text: state.text,
- selection: state.selection,
- prefix: state.command.prefix!,
- suffix: state.command.suffix,
- });
- const state1 = api.setSelectionRange(newSelectionRange);
- const media = await showPostSelector(date);
- executeCommand({
- api,
- selectedText: state1.selectedText,
- selection: state.selection,
- prefix: media,
- suffix: '',
- });
- },
-});
diff --git a/libraries/helpers/src/utils/strip.html.validation.ts b/libraries/helpers/src/utils/strip.html.validation.ts
index dd8c71b3..5e4d8e7e 100644
--- a/libraries/helpers/src/utils/strip.html.validation.ts
+++ b/libraries/helpers/src/utils/strip.html.validation.ts
@@ -132,15 +132,20 @@ const underlineMap = {
};
export const stripHtmlValidation = (
- type: 'normal' | 'markdown' | 'html',
+ type: 'plain' | 'none' | 'normal' | 'markdown' | 'html',
value: string,
- replaceBold = false
+ replaceBold = false,
+ none = false
): string => {
+ if (type === 'plain') {
+ return value;
+ }
+
if (type === 'markdown') {
return NodeHtmlMarkdown.translate(value);
}
- if (value.indexOf('
') === -1) {
+ if (value.indexOf('
') === -1 && !none) {
return value;
}
@@ -150,15 +155,22 @@ export const stripHtmlValidation = (
.replace(/
]*>/gi, '\n')
.replace(/<\/p>/gi, '');
+ if (none) {
+ return striptags(html);
+ }
+
if (replaceBold) {
return striptags(convertLinkedinMention(convertToAscii(html)), [
'ul',
'li',
+ 'h1',
+ 'h2',
+ 'h3',
]);
}
// Strip all other tags
- return striptags(html, ['ul', 'li']);
+ return striptags(html, ['ul', 'li', 'h1', 'h2', 'h3']);
};
export const convertLinkedinMention = (value: string) => {
diff --git a/package.json b/package.json
index 6b988745..ff995a76 100644
--- a/package.json
+++ b/package.json
@@ -81,6 +81,7 @@
"@tailwindcss/postcss": "^4.1.7",
"@tiptap/extension-bold": "^3.0.6",
"@tiptap/extension-document": "^3.0.6",
+ "@tiptap/extension-heading": "^3.0.7",
"@tiptap/extension-history": "^3.0.7",
"@tiptap/extension-list": "^3.0.7",
"@tiptap/extension-paragraph": "^3.0.6",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b4f9dc81..44c8c053 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -120,6 +120,9 @@ importers:
'@tiptap/extension-document':
specifier: ^3.0.6
version: 3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))
+ '@tiptap/extension-heading':
+ specifier: ^3.0.7
+ version: 3.0.7(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))
'@tiptap/extension-history':
specifier: ^3.0.7
version: 3.0.7(@tiptap/extensions@3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))(@tiptap/pm@3.0.6))
@@ -5493,10 +5496,10 @@ packages:
peerDependencies:
'@tiptap/core': ^3.0.6
- '@tiptap/extension-heading@3.0.6':
- resolution: {integrity: sha512-umcsnc4IEacQjlnl7d/IaiFsVA5xG993VvnUG5fK08TOuy0yrliOqXsFGp4czGnRLBEh2zfXMMK+vnm2fuk5zw==}
+ '@tiptap/extension-heading@3.0.7':
+ resolution: {integrity: sha512-uS7fFcilFuzKEvhUgndELqlGweD+nZeLOb6oqUE5hM49vECjM7qVjVQnlhV+MH2W1w8eD08cn1lu6lDxaMOe5w==}
peerDependencies:
- '@tiptap/core': ^3.0.6
+ '@tiptap/core': ^3.0.7
'@tiptap/extension-history@3.0.7':
resolution: {integrity: sha512-F+zjS7Wz53sNCWh3KqSAug4/COgxs060tR9up0OXjw7iB3gJz6JMNpGaWmYF5WdOsLxph7FGRMb/Mr5keCqVDA==}
@@ -21208,7 +21211,7 @@ snapshots:
dependencies:
'@tiptap/core': 3.0.6(@tiptap/pm@3.0.6)
- '@tiptap/extension-heading@3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))':
+ '@tiptap/extension-heading@3.0.7(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))':
dependencies:
'@tiptap/core': 3.0.6(@tiptap/pm@3.0.6)
@@ -21317,7 +21320,7 @@ snapshots:
'@tiptap/extension-dropcursor': 3.0.6(@tiptap/extensions@3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))(@tiptap/pm@3.0.6))
'@tiptap/extension-gapcursor': 3.0.6(@tiptap/extensions@3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))(@tiptap/pm@3.0.6))
'@tiptap/extension-hard-break': 3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))
- '@tiptap/extension-heading': 3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))
+ '@tiptap/extension-heading': 3.0.7(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))
'@tiptap/extension-horizontal-rule': 3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))(@tiptap/pm@3.0.6)
'@tiptap/extension-italic': 3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))
'@tiptap/extension-link': 3.0.6(@tiptap/core@3.0.6(@tiptap/pm@3.0.6))(@tiptap/pm@3.0.6)