Compare commits

...

2 commits

Author SHA1 Message Date
Anthony Fu
98bc861fab chore: clean up 2023-03-15 13:55:04 +01:00
Anthony Fu
6d0cbd2914 feat: markdown editor, close #1845 2023-03-09 12:06:31 +01:00
3 changed files with 88 additions and 5 deletions

View file

@ -29,6 +29,9 @@ const { t } = useI18n()
const draftState = useDraft(draftKey, initial) const draftState = useDraft(draftKey, initial)
const { draft } = $(draftState) const { draft } = $(draftState)
const settings = useUserSettings()
const textareaEl = ref<HTMLTextAreaElement>()
const { const {
isExceedingAttachmentLimit, isUploading, failedAttachments, isOverDropZone, isExceedingAttachmentLimit, isUploading, failedAttachments, isOverDropZone,
uploadAttachments, pickAttachments, setDescription, removeAttachment, uploadAttachments, pickAttachments, setDescription, removeAttachment,
@ -48,11 +51,17 @@ const { editor } = useTiptap({
set: (newVal) => { set: (newVal) => {
draft.params.status = newVal draft.params.status = newVal
draft.lastUpdated = Date.now() draft.lastUpdated = Date.now()
if (settings.value.editorMode === 'markdown')
onTipTapChanged()
}, },
}), }),
placeholder: computed(() => placeholder ?? draft.params.inReplyToId ? t('placeholder.replying') : t('placeholder.default_1')), placeholder: computed(() => placeholder ?? draft.params.inReplyToId ? t('placeholder.replying') : t('placeholder.default_1')),
autofocus: shouldExpanded, autofocus: shouldExpanded,
onSubmit: publish, onSubmit: () => {
if (settings.value.editorMode === 'markdown')
onMarkdownChanged()
publish()
},
onFocus() { onFocus() {
if (!isExpanded && draft.initialText) { if (!isExpanded && draft.initialText) {
editor.value?.chain().insertContent(`${draft.initialText} `).focus('end').run() editor.value?.chain().insertContent(`${draft.initialText} `).focus('end').run()
@ -113,9 +122,15 @@ async function handlePaste(evt: ClipboardEvent) {
} }
function insertEmoji(name: string) { function insertEmoji(name: string) {
if (settings.value.editorMode === 'markdown')
insertMarkdownAtCursor(name)
else
editor.value?.chain().focus().insertEmoji(name).run() editor.value?.chain().focus().insertEmoji(name).run()
} }
function insertCustomEmoji(image: any) { function insertCustomEmoji(image: any) {
if (settings.value.editorMode === 'markdown')
insertMarkdownAtCursor(`:${image['data-emoji-id']}:`)
else
editor.value?.chain().focus().insertCustomEmoji(image).run() editor.value?.chain().focus().insertCustomEmoji(image).run()
} }
@ -129,6 +144,55 @@ async function publish() {
emit('published', status) emit('published', status)
} }
let markdown = $ref(htmlToText(draft.params.status || ''))
function insertMarkdownAtCursor(text: string) {
if (!textareaEl.value)
return
textareaEl.value.focus()
const start = textareaEl.value.selectionStart || 0
const end = textareaEl.value.selectionEnd || 0
const before = markdown.substring(0, start)
const after = markdown.substring(end)
markdown = before + text + after
textareaEl.value.setSelectionRange(end + text.length, end + text.length)
}
async function onMarkdownChanged() {
draft.params.status = await convertMastodonHTML(markdown)
}
function onTipTapChanged() {
markdown = htmlToText(draft.params.status || '')
}
function toggleEditor() {
if (settings.value.editorMode === 'markdown') {
onMarkdownChanged()
settings.value.editorMode = 'tiptap'
}
else {
onTipTapChanged()
settings.value.editorMode = 'markdown'
}
}
watch(markdown, () => {
if (settings.value.editorMode === 'markdown')
onMarkdownChanged()
})
useTextareaAutosize({
input: markdown,
element: textareaEl,
})
const editorClass = $computed(() =>
shouldExpanded
? 'min-h-30 md:max-h-[calc(100vh-200px)] sm:max-h-[calc(100vh-400px)] max-h-35 of-y-auto overscroll-contain'
: '',
)
useWebShareTarget(async ({ data: { data, action } }: any) => { useWebShareTarget(async ({ data: { data, action } }: any) => {
if (action !== 'compose-with-shared-data') if (action !== 'compose-with-shared-data')
return return
@ -218,10 +282,19 @@ defineExpose({
</CommonErrorMessage> </CommonErrorMessage>
<div relative flex-1 flex flex-col> <div relative flex-1 flex flex-col>
<template v-if="settings.editorMode === 'markdown'">
<textarea
ref="textareaEl"
v-model="markdown"
bg-base font-mono outline-none border-none resize-none
:class="editorClass"
/>
</template>
<EditorContent <EditorContent
v-else
:editor="editor" :editor="editor"
flex max-w-full flex max-w-full
:class="shouldExpanded ? 'min-h-30 md:max-h-[calc(100vh-200px)] sm:max-h-[calc(100vh-400px)] max-h-35 of-y-auto overscroll-contain' : ''" :class="editorClass"
/> />
</div> </div>
@ -292,7 +365,14 @@ defineExpose({
</button> </button>
</CommonTooltip> </CommonTooltip>
<PublishEditorTools v-if="editor" :editor="editor" /> <CommonTooltip placement="top" :content="$t('tooltip.toggle_editor')">
<button btn-action-icon :aria-label="$t('tooltip.toggle_editor')" @click="toggleEditor()">
<div v-if="settings.editorMode === 'markdown'" i-ri:markdown-line />
<div v-else i-ri:file-text-line />
</button>
</CommonTooltip>
<PublishEditorTools v-if="editor && settings.editorMode !== 'markdown'" :editor="editor" />
<div flex-auto /> <div flex-auto />

View file

@ -33,6 +33,7 @@ export interface UserSettings {
disabledTranslationLanguages: string[] disabledTranslationLanguages: string[]
zenMode: boolean zenMode: boolean
themeColors?: ThemeColors themeColors?: ThemeColors
editorMode?: 'tiptap' | 'markdown'
} }
export interface ThemeColors { export interface ThemeColors {

View file

@ -488,6 +488,7 @@
"edited": "(Edited)", "edited": "(Edited)",
"editing": "Editing", "editing": "Editing",
"loading": "Loading...", "loading": "Loading...",
"markdown_editor": "Markdown Editor",
"publish_failed": "Publish failed", "publish_failed": "Publish failed",
"publishing": "Publishing", "publishing": "Publishing",
"upload_failed": "Upload failed", "upload_failed": "Upload failed",
@ -597,6 +598,7 @@
"publish_failed": "Close failed messages at the top of editor to republish posts", "publish_failed": "Close failed messages at the top of editor to republish posts",
"toggle_bold": "Toggle bold", "toggle_bold": "Toggle bold",
"toggle_code_block": "Toggle code block", "toggle_code_block": "Toggle code block",
"toggle_editor": "Toggle between WYSIWYG editor and markdown editor",
"toggle_italic": "Toggle italic" "toggle_italic": "Toggle italic"
}, },
"user": { "user": {