feat: remove the 10,000-character article body limit and make the New article body optional
This commit is contained in:
1 parent
5e05b606a7
commit
10ec74c8cf
28 files changed
+860
-397
No files matched your search
@@ -2,7 +2,6 @@
|
||||
import { t, translate } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import CharacterCounter from '@/components/CharacterCounter.vue'
|
||||
import { ApiError, useApi } from '@/composables/useApi'
|
||||
import type { Article } from '@/composables/useArticles'
|
||||
|
||||
@@ -17,9 +16,7 @@ const emit = defineEmits<{ created: [article: Article] }>()
|
||||
const api = useApi()
|
||||
|
||||
const title = ref('')
|
||||
const body = ref('')
|
||||
const titleTouched = ref(false)
|
||||
const bodyTouched = ref(false)
|
||||
const titleFocused = ref(false)
|
||||
const submitting = ref(false)
|
||||
const titleServerError = ref('')
|
||||
@@ -29,7 +26,6 @@ const titleLength = computed(() => [...title.value].length)
|
||||
const titleValid = computed(
|
||||
() => titleLength.value >= TITLE_MIN_LENGTH && titleLength.value <= TITLE_MAX_LENGTH,
|
||||
)
|
||||
const bodyValid = computed(() => body.value.length > 0)
|
||||
|
||||
const titleLengthError = computed(() => {
|
||||
if (!titleTouched.value) {
|
||||
@@ -46,14 +42,12 @@ const titleLengthError = computed(() => {
|
||||
|
||||
const titleError = computed(() => titleServerError.value || titleLengthError.value)
|
||||
|
||||
const bodyError = computed(() =>
|
||||
bodyTouched.value && !bodyValid.value
|
||||
? t('Add a first paragraph. It can be a single sentence.')
|
||||
: '',
|
||||
const titleCounterInvalid = computed(
|
||||
() =>
|
||||
titleLength.value > TITLE_MAX_LENGTH ||
|
||||
(titleTouched.value && titleLength.value < TITLE_MIN_LENGTH),
|
||||
)
|
||||
|
||||
const titleCounterMin = computed(() => (titleTouched.value ? TITLE_MIN_LENGTH : 0))
|
||||
|
||||
const showHelp = computed(() => !submitting.value && formAlert.value === '')
|
||||
|
||||
const alertMessage = computed(() => {
|
||||
@@ -68,9 +62,7 @@ const alertMessage = computed(() => {
|
||||
|
||||
function resetForm() {
|
||||
title.value = ''
|
||||
body.value = ''
|
||||
titleTouched.value = false
|
||||
bodyTouched.value = false
|
||||
titleFocused.value = false
|
||||
submitting.value = false
|
||||
titleServerError.value = ''
|
||||
@@ -103,16 +95,11 @@ function onTitleBlur() {
|
||||
titleFocused.value = false
|
||||
}
|
||||
|
||||
function onBodyBlur() {
|
||||
bodyTouched.value = true
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
titleTouched.value = true
|
||||
bodyTouched.value = true
|
||||
titleFocused.value = false
|
||||
|
||||
if (!titleValid.value || !bodyValid.value) {
|
||||
if (!titleValid.value) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -123,7 +110,7 @@ async function onSubmit() {
|
||||
const article = await api.request<Article>('/v1/article/?format=html', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title: title.value, body: body.value }),
|
||||
body: JSON.stringify({ title: title.value }),
|
||||
})
|
||||
open.value = false
|
||||
emit('created', article)
|
||||
@@ -156,9 +143,10 @@ function onCancel() {
|
||||
:dismissible="!submitting"
|
||||
:ui="{
|
||||
content:
|
||||
'divide-y-0 shadow-(--shadow-modal) ring-0 sm:top-35 sm:max-w-140 sm:translate-y-0 max-sm:!inset-x-4 max-sm:!top-auto max-sm:!bottom-4 max-sm:!left-4 max-sm:!w-auto max-sm:!max-w-none max-sm:!translate-x-0 max-sm:!translate-y-0',
|
||||
header: 'items-start gap-3 px-6 pt-5 pb-0 sm:px-6',
|
||||
wrapper: 'pe-10',
|
||||
'divide-y-0 shadow-(--shadow-modal) ring-0 sm:top-35 sm:max-w-140 sm:translate-y-0 max-sm:!inset-x-0 max-sm:!top-auto max-sm:!bottom-0 max-sm:!left-0 max-sm:!w-auto max-sm:!max-w-none max-sm:before:absolute max-sm:before:top-2 max-sm:before:left-1/2 max-sm:before:h-1 max-sm:before:w-9 max-sm:before:-translate-x-1/2 max-sm:before:bg-accented max-sm:!translate-x-0 max-sm:!translate-y-0',
|
||||
header: 'items-start gap-3 px-6 pt-7.5 pb-0 sm:px-6 sm:pt-5',
|
||||
wrapper: 'pe-10 max-sm:pe-18',
|
||||
close: 'max-sm:end-8 max-sm:top-9',
|
||||
title: 'text-lg/6 font-semibold',
|
||||
body: 'px-6 pt-5 pb-0 sm:px-6 sm:pt-5 sm:pb-0',
|
||||
footer: 'px-6 py-5 sm:px-6',
|
||||
@@ -176,19 +164,18 @@ function onCancel() {
|
||||
:ui="{ root: 'gap-2 px-3 py-2.5' }"
|
||||
:description="alertMessage"
|
||||
/>
|
||||
<UForm :state="{ title, body }" class="flex flex-col gap-4">
|
||||
<UForm :state="{ title }" class="flex flex-col gap-4">
|
||||
<UFormField
|
||||
:label="t('Title')"
|
||||
name="title"
|
||||
:error="translate(titleError) || undefined"
|
||||
>
|
||||
<template #hint>
|
||||
<CharacterCounter
|
||||
:value="titleLength"
|
||||
:max="TITLE_MAX_LENGTH"
|
||||
:min="titleCounterMin"
|
||||
plain-text
|
||||
/>
|
||||
<span
|
||||
class="font-mono text-xs leading-tight"
|
||||
:class="titleCounterInvalid ? 'text-error' : 'text-muted'"
|
||||
>{{ titleLength }} / {{ TITLE_MAX_LENGTH }}</span
|
||||
>
|
||||
</template>
|
||||
<template v-if="titleError" #error="{ error }">
|
||||
<span class="flex items-center gap-1.5">
|
||||
@@ -214,36 +201,6 @@ function onCancel() {
|
||||
@blur="onTitleBlur"
|
||||
/>
|
||||
</UFormField>
|
||||
<UFormField
|
||||
:label="t('First paragraph')"
|
||||
name="body"
|
||||
:error="bodyError || undefined"
|
||||
>
|
||||
<template v-if="bodyError" #error="{ error }">
|
||||
<span class="flex items-center gap-1.5">
|
||||
<UIcon
|
||||
name="i-lucide-circle-alert"
|
||||
class="size-3.5 shrink-0 text-error"
|
||||
/>
|
||||
{{ t(error) }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="showHelp" #help>
|
||||
{{ t('You can format it and add images in the editor.') }}
|
||||
</template>
|
||||
<UTextarea
|
||||
v-model="body"
|
||||
:placeholder="t('Start writing. You\'ll continue in the editor.')"
|
||||
:disabled="submitting"
|
||||
:highlight="!!bodyError"
|
||||
:rows="3"
|
||||
:ui="{
|
||||
base: 'h-30 text-lg disabled:bg-elevated disabled:text-dimmed disabled:opacity-100 md:h-24 md:text-base',
|
||||
}"
|
||||
class="w-full"
|
||||
@blur="onBodyBlur"
|
||||
/>
|
||||
</UFormField>
|
||||
</UForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in new issue
Block a user