feat: support media files upload via paste and drop
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import Note from '@/components/Note'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { useToast } from '@/hooks/use-toast'
|
||||
import { createCommentDraftEvent, createShortTextNoteDraftEvent } from '@/lib/draft-event'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
@@ -9,7 +10,7 @@ import { ChevronDown, ImageUp, LoaderCircle } from 'lucide-react'
|
||||
import { Event, kinds } from 'nostr-tools'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import TextareaWithMentions from '../TextareaWithMentions'
|
||||
import PostTextarea from '../PostTextarea'
|
||||
import Mentions from './Mentions'
|
||||
import PostOptions from './PostOptions'
|
||||
import Preview from './Preview'
|
||||
@@ -123,14 +124,29 @@ export default function NormalPostContent({
|
||||
</div>
|
||||
</ScrollArea>
|
||||
)}
|
||||
<TextareaWithMentions
|
||||
className="h-32"
|
||||
<Tabs defaultValue="edit" className="space-y-4">
|
||||
<TabsList>
|
||||
<TabsTrigger value="edit">{t('Edit')}</TabsTrigger>
|
||||
<TabsTrigger value="preview">{t('Preview')}</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="edit">
|
||||
<PostTextarea
|
||||
className="h-52"
|
||||
setTextValue={setContent}
|
||||
textValue={content}
|
||||
placeholder={t('Write something...')}
|
||||
placeholder={
|
||||
t('Write something...') + ' (' + t('Paste or drop media files to upload') + ')'
|
||||
}
|
||||
cursorOffset={cursorOffset}
|
||||
onUploadImage={({ url, tags }) => {
|
||||
setPictureInfos((prev) => [...prev, { url, tags }])
|
||||
}}
|
||||
/>
|
||||
{processedContent && <Preview content={processedContent} />}
|
||||
</TabsContent>
|
||||
<TabsContent value="preview">
|
||||
<Preview content={processedContent} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
<SendOnlyToSwitch
|
||||
parentEvent={parentEvent}
|
||||
specifiedRelayUrls={specifiedRelayUrls}
|
||||
@@ -141,7 +157,7 @@ export default function NormalPostContent({
|
||||
<Uploader
|
||||
onUploadSuccess={({ url, tags }) => {
|
||||
setPictureInfos((prev) => [...prev, { url, tags }])
|
||||
setContent((prev) => `${prev}\n${url}`)
|
||||
setContent((prev) => (prev === '' ? url : `${prev}\n${url}`))
|
||||
}}
|
||||
onUploadingChange={setUploadingPicture}
|
||||
accept="image/*,video/*,audio/*"
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ChevronDown, Loader, LoaderCircle, Plus, X } from 'lucide-react'
|
||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Image from '../Image'
|
||||
import TextareaWithMentions from '../TextareaWithMentions'
|
||||
import PostTextarea from '../PostTextarea'
|
||||
import Mentions from './Mentions'
|
||||
import PostOptions from './PostOptions'
|
||||
import SendOnlyToSwitch from './SendOnlyToSwitch'
|
||||
@@ -105,7 +105,7 @@ export default function PicturePostContent({ close }: { close: () => void }) {
|
||||
{t('A special note for picture-first clients like Olas')}
|
||||
</div>
|
||||
<PictureUploader pictureInfos={pictureInfos} setPictureInfos={setPictureInfos} />
|
||||
<TextareaWithMentions
|
||||
<PostTextarea
|
||||
className="h-32"
|
||||
setTextValue={setContent}
|
||||
textValue={content}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Content from '../Content'
|
||||
|
||||
export default function Preview({ content }: { content: string }) {
|
||||
return (
|
||||
<Card className="p-3">
|
||||
<Card className="p-3 min-h-52">
|
||||
<Content
|
||||
event={{
|
||||
content,
|
||||
@@ -15,7 +15,7 @@ export default function Preview({ content }: { content: string }) {
|
||||
pubkey: '',
|
||||
sig: ''
|
||||
}}
|
||||
className="pointer-events-none"
|
||||
className="pointer-events-none h-full"
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -20,14 +20,15 @@ export default function Uploader({
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0]
|
||||
if (!file) return
|
||||
if (!event.target.files) return
|
||||
|
||||
try {
|
||||
onUploadingChange?.(true)
|
||||
try {
|
||||
for (const file of event.target.files) {
|
||||
const result = await upload(file)
|
||||
console.log('File uploaded successfully', result)
|
||||
onUploadSuccess(result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error uploading file', error)
|
||||
toast({
|
||||
@@ -59,6 +60,7 @@ export default function Uploader({
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleFileChange}
|
||||
accept={accept}
|
||||
multiple
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -6,14 +6,17 @@ import {
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle
|
||||
} from '@/components/ui/sheet'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { Dispatch, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../ui/sheet'
|
||||
import NormalPostContent from './NormalPostContent'
|
||||
import PicturePostContent from './PicturePostContent'
|
||||
import Title from './Title'
|
||||
|
||||
export default function PostEditor({
|
||||
@@ -27,35 +30,17 @@ export default function PostEditor({
|
||||
open: boolean
|
||||
setOpen: Dispatch<boolean>
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
|
||||
const content = useMemo(() => {
|
||||
return parentEvent || defaultContent ? (
|
||||
return (
|
||||
<NormalPostContent
|
||||
defaultContent={defaultContent}
|
||||
parentEvent={parentEvent}
|
||||
close={() => setOpen(false)}
|
||||
/>
|
||||
) : (
|
||||
<Tabs defaultValue="normal" className="space-y-4">
|
||||
<TabsList>
|
||||
<TabsTrigger value="normal">{t('Normal Note')}</TabsTrigger>
|
||||
<TabsTrigger value="picture">{t('Picture Note')}</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="normal">
|
||||
<NormalPostContent
|
||||
defaultContent={defaultContent}
|
||||
parentEvent={parentEvent}
|
||||
close={() => setOpen(false)}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="picture">
|
||||
<PicturePostContent close={() => setOpen(false)} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
)
|
||||
}, [parentEvent, defaultContent])
|
||||
}, [])
|
||||
|
||||
if (isSmallScreen) {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Command, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { useToast } from '@/hooks'
|
||||
import { pubkeyToNpub } from '@/lib/pubkey'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useMediaUploadService } from '@/providers/MediaUploadServiceProvider'
|
||||
import client from '@/services/client.service'
|
||||
import { TProfile } from '@/types'
|
||||
import React, {
|
||||
@@ -18,22 +20,27 @@ import { SimpleUserAvatar } from '../UserAvatar'
|
||||
import { SimpleUsername } from '../Username'
|
||||
import { getCurrentWord, replaceWord } from './utils'
|
||||
|
||||
export default function TextareaWithMentions({
|
||||
export default function PostTextarea({
|
||||
textValue,
|
||||
setTextValue,
|
||||
cursorOffset = 0,
|
||||
onUploadImage,
|
||||
...props
|
||||
}: ComponentProps<'textarea'> & {
|
||||
textValue: string
|
||||
setTextValue: Dispatch<SetStateAction<string>>
|
||||
cursorOffset?: number
|
||||
onUploadImage?: ({ url, tags }: { url: string; tags: string[][] }) => void
|
||||
}) {
|
||||
const { toast } = useToast()
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const { upload } = useMediaUploadService()
|
||||
const [commandValue, setCommandValue] = useState('')
|
||||
const [debouncedCommandValue, setDebouncedCommandValue] = useState(commandValue)
|
||||
const [profiles, setProfiles] = useState<TProfile[]>([])
|
||||
const [dragover, setDragover] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (textareaRef.current && cursorOffset !== 0) {
|
||||
@@ -132,7 +139,7 @@ export default function TextareaWithMentions({
|
||||
const textarea = textareaRef.current
|
||||
const dropdown = dropdownRef.current
|
||||
if (textarea && dropdown) {
|
||||
replaceWord(textarea, `${value}`)
|
||||
replaceWord(textarea, `${value} `)
|
||||
setCommandValue('')
|
||||
dropdown.classList.add('hidden')
|
||||
}
|
||||
@@ -170,9 +177,73 @@ export default function TextareaWithMentions({
|
||||
}
|
||||
}, [handleBlur, handleKeyDown, handleMouseDown, handleSectionChange])
|
||||
|
||||
const uploadImages = async (files: File[]) => {
|
||||
for (const file of files) {
|
||||
if (file.type.startsWith('image/') || file.type.startsWith('video/')) {
|
||||
const placeholder = `[Uploading "${file.name}"...]`
|
||||
if (textValue.includes(placeholder)) {
|
||||
continue
|
||||
}
|
||||
setTextValue((prev) => (prev === '' ? placeholder : `${prev}\n${placeholder}`))
|
||||
try {
|
||||
const result = await upload(file)
|
||||
setTextValue((prev) => {
|
||||
if (prev.includes(placeholder)) {
|
||||
return prev.replace(placeholder, result.url)
|
||||
} else {
|
||||
return prev + `\n${result.url}`
|
||||
}
|
||||
})
|
||||
onUploadImage?.(result)
|
||||
} catch (error) {
|
||||
console.error('Error uploading file', error)
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: 'Failed to upload file',
|
||||
description: (error as Error).message
|
||||
})
|
||||
setTextValue((prev) => prev.replace(placeholder, ''))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handlePast = async (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
||||
await uploadImages(
|
||||
Array.from(e.clipboardData.items)
|
||||
.map((item) => item.getAsFile())
|
||||
.filter(Boolean) as File[]
|
||||
)
|
||||
}
|
||||
|
||||
const handleDrop = async (e: React.DragEvent<HTMLTextAreaElement>) => {
|
||||
e.preventDefault()
|
||||
setDragover(false)
|
||||
await uploadImages(Array.from(e.dataTransfer.files))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<Textarea {...props} ref={textareaRef} value={textValue} onChange={onTextValueChange} />
|
||||
<div
|
||||
className={cn(
|
||||
'relative w-full',
|
||||
dragover && 'outline-2 outline-offset-4 outline-dashed outline-border rounded-md'
|
||||
)}
|
||||
>
|
||||
<Textarea
|
||||
{...props}
|
||||
ref={textareaRef}
|
||||
value={textValue}
|
||||
onChange={onTextValueChange}
|
||||
onPaste={handlePast}
|
||||
onDrop={handleDrop}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault()
|
||||
setDragover(true)
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
setDragover(false)
|
||||
}}
|
||||
/>
|
||||
<Command
|
||||
ref={dropdownRef}
|
||||
className={cn(
|
||||
@@ -110,8 +110,6 @@ export default {
|
||||
'Picture note requires images': 'ملاحظة الصورة تتطلب صور',
|
||||
Relays: 'الريلايات',
|
||||
image: 'صورة',
|
||||
'Normal Note': 'ملاحظة عادية',
|
||||
'Picture Note': 'ملاحظة الصورة',
|
||||
'R & W': 'قراءة وكتابة',
|
||||
Read: 'قراءة',
|
||||
Write: 'كتابة',
|
||||
@@ -227,6 +225,8 @@ export default {
|
||||
'Show more': 'عرض المزيد',
|
||||
General: 'عام',
|
||||
Autoplay: 'التشغيل التلقائي',
|
||||
'Enable video autoplay on this device': 'تمكين التشغيل التلقائي للفيديو على هذا الجهاز'
|
||||
'Enable video autoplay on this device': 'تمكين التشغيل التلقائي للفيديو على هذا الجهاز',
|
||||
'Paste or drop media files to upload': 'الصق أو اسحب ملفات الوسائط لتحميلها',
|
||||
Preview: 'معاينة'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,6 @@ export default {
|
||||
'Picture note requires images': 'Bildnotiz erfordert Bilder',
|
||||
Relays: 'Relays',
|
||||
image: 'Bild',
|
||||
'Normal Note': 'Normale Notiz',
|
||||
'Picture Note': 'Bildnotiz',
|
||||
'R & W': 'R & W',
|
||||
Read: 'Lesen',
|
||||
Write: 'Schreiben',
|
||||
@@ -232,6 +230,9 @@ export default {
|
||||
General: 'Allgemein',
|
||||
Autoplay: 'Automatische Wiedergabe',
|
||||
'Enable video autoplay on this device':
|
||||
'Aktiviere die automatische Video-Wiedergabe auf diesem Gerät'
|
||||
'Aktiviere die automatische Video-Wiedergabe auf diesem Gerät',
|
||||
'Paste or drop media files to upload':
|
||||
'Füge Medien-Dateien ein oder ziehe sie hierher, um sie hochzuladen',
|
||||
Preview: 'Vorschau'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +110,6 @@ export default {
|
||||
'Picture note requires images': 'Picture note requires images',
|
||||
Relays: 'Relays',
|
||||
image: 'image',
|
||||
'Normal Note': 'Normal Note',
|
||||
'Picture Note': 'Picture Note',
|
||||
'R & W': 'R & W',
|
||||
Read: 'Read',
|
||||
Write: 'Write',
|
||||
@@ -227,6 +225,8 @@ export default {
|
||||
'Show more': 'Show more',
|
||||
General: 'General',
|
||||
Autoplay: 'Autoplay',
|
||||
'Enable video autoplay on this device': 'Enable video autoplay on this device'
|
||||
'Enable video autoplay on this device': 'Enable video autoplay on this device',
|
||||
'Paste or drop media files to upload': 'Paste or drop media files to upload',
|
||||
Preview: 'Preview'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,6 @@ export default {
|
||||
'Picture note requires images': 'La nota con imagen requiere imágenes',
|
||||
Relays: 'Relés',
|
||||
image: 'imagen',
|
||||
'Normal Note': 'Nota normal',
|
||||
'Picture Note': 'Nota con imagen',
|
||||
'R & W': 'L y E',
|
||||
Read: 'Leer',
|
||||
Write: 'Escribir',
|
||||
@@ -232,6 +230,8 @@ export default {
|
||||
General: 'General',
|
||||
Autoplay: 'Reproducción automática',
|
||||
'Enable video autoplay on this device':
|
||||
'Habilitar reproducción automática de video en este dispositivo'
|
||||
'Habilitar reproducción automática de video en este dispositivo',
|
||||
'Paste or drop media files to upload': 'Pegar o soltar archivos multimedia para cargar',
|
||||
Preview: 'Vista previa'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,6 @@ export default {
|
||||
'Picture note requires images': 'La note image nécessite des images',
|
||||
Relays: 'Relais',
|
||||
image: 'image',
|
||||
'Normal Note': 'Note normale',
|
||||
'Picture Note': 'Note image',
|
||||
'R & W': 'R & W',
|
||||
Read: 'Lire',
|
||||
Write: 'Écrire',
|
||||
@@ -231,6 +229,9 @@ export default {
|
||||
General: 'Général',
|
||||
Autoplay: 'Lecture automatique',
|
||||
'Enable video autoplay on this device':
|
||||
'Activer la lecture automatique des vidéos sur cet appareil'
|
||||
'Activer la lecture automatique des vidéos sur cet appareil',
|
||||
'Paste or drop media files to upload':
|
||||
'Coller ou déposer des fichiers multimédias à télécharger',
|
||||
Preview: 'Aperçu'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,6 @@ export default {
|
||||
'Picture note requires images': 'La nota illustrativa richiede immagini',
|
||||
Relays: 'Relays',
|
||||
image: 'immagine',
|
||||
'Normal Note': 'Nota Normale',
|
||||
'Picture Note': 'Nota Immagine',
|
||||
'R & W': 'L & S',
|
||||
Read: 'Leggi',
|
||||
Write: 'Scrivi',
|
||||
@@ -231,6 +229,8 @@ export default {
|
||||
General: 'Generale',
|
||||
Autoplay: 'Riproduzione automatica',
|
||||
'Enable video autoplay on this device':
|
||||
'Abilita riproduzione automatica video su questo dispositivo'
|
||||
'Abilita riproduzione automatica video su questo dispositivo',
|
||||
'Paste or drop media files to upload': 'Incolla o trascina i file multimediali per caricarli',
|
||||
Preview: 'Anteprima'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,6 @@ export default {
|
||||
'Picture note requires images': '画像ノートには画像が必要です',
|
||||
Relays: 'リレイ',
|
||||
image: '画像',
|
||||
'Normal Note': '通常ノート',
|
||||
'Picture Note': '画像ノート',
|
||||
'R & W': '読&書',
|
||||
Read: '読む',
|
||||
Write: '書く',
|
||||
@@ -228,6 +226,8 @@ export default {
|
||||
'Show more': 'もっと見る',
|
||||
General: '一般',
|
||||
Autoplay: '自動再生',
|
||||
'Enable video autoplay on this device': 'このデバイスでのビデオ自動再生を有効にする'
|
||||
'Enable video autoplay on this device': 'このデバイスでのビデオ自動再生を有効にする',
|
||||
'Paste or drop media files to upload': 'メディアファイルを貼り付けるかドロップしてアップロード',
|
||||
Preview: 'プレビュー'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +110,6 @@ export default {
|
||||
'Picture note requires images': 'Wpis graficzny wymaga obrazów',
|
||||
Relays: 'Transmitery',
|
||||
image: 'grafika',
|
||||
'Normal Note': 'Zwykły wpis',
|
||||
'Picture Note': 'Wpis graficzny',
|
||||
'R & W': 'O & Z',
|
||||
Read: 'Odczyt',
|
||||
Write: 'Zapis',
|
||||
@@ -229,6 +227,9 @@ export default {
|
||||
'Show more': 'Pokaż więcej',
|
||||
General: 'Ogólne',
|
||||
Autoplay: 'Autoodtwarzanie',
|
||||
'Enable video autoplay on this device': 'Włącz automatyczne odtwarzanie wideo na tym urządzeniu'
|
||||
'Enable video autoplay on this device':
|
||||
'Włącz automatyczne odtwarzanie wideo na tym urządzeniu',
|
||||
'Paste or drop media files to upload': 'Wklej lub upuść pliki multimedialne, aby przesłać',
|
||||
Preview: 'Podgląd'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +110,6 @@ export default {
|
||||
'Picture note requires images': 'Nota de imagem requer imagens',
|
||||
Relays: 'Relés',
|
||||
image: 'imagem',
|
||||
'Normal Note': 'Nota de texto',
|
||||
'Picture Note': 'Nota de imagem',
|
||||
'R & W': 'Leitura & Escrita',
|
||||
Read: 'Ler',
|
||||
Write: 'Escrever',
|
||||
@@ -230,6 +228,8 @@ export default {
|
||||
General: 'Geral',
|
||||
Autoplay: 'Reprodução automática',
|
||||
'Enable video autoplay on this device':
|
||||
'Habilitar reprodução automática de vídeo neste dispositivo'
|
||||
'Habilitar reprodução automática de vídeo neste dispositivo',
|
||||
'Paste or drop media files to upload': 'Cole ou solte arquivos de mídia para fazer upload',
|
||||
Preview: 'Pré-visualização'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,6 @@ export default {
|
||||
'Picture note requires images': 'Nota de imagem requer imagens',
|
||||
Relays: 'Relés',
|
||||
image: 'imagem',
|
||||
'Normal Note': 'Nota Normal',
|
||||
'Picture Note': 'Nota de Imagem',
|
||||
'R & W': 'Leitura & Escrita',
|
||||
Read: 'Ler',
|
||||
Write: 'Escrever',
|
||||
@@ -231,6 +229,8 @@ export default {
|
||||
General: 'Geral',
|
||||
Autoplay: 'Reprodução Automática',
|
||||
'Enable video autoplay on this device':
|
||||
'Habilitar reprodução automática de vídeo neste dispositivo'
|
||||
'Habilitar reprodução automática de vídeo neste dispositivo',
|
||||
'Paste or drop media files to upload': 'Cole ou solte arquivos de mídia para fazer upload',
|
||||
Preview: 'Pré-visualização'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,8 +112,6 @@ export default {
|
||||
'Picture note requires images': 'Заметка с изображением требует наличия изображений',
|
||||
Relays: 'Ретрансляторы',
|
||||
image: 'изображение',
|
||||
'Normal Note': 'Обычная заметка',
|
||||
'Picture Note': 'Заметка с изображением',
|
||||
'R & W': 'Чтение & Запись',
|
||||
Read: 'Читать',
|
||||
Write: 'Писать',
|
||||
@@ -231,6 +229,8 @@ export default {
|
||||
'Show more': 'Показать больше',
|
||||
General: 'Общие',
|
||||
Autoplay: 'Автовоспроизведение',
|
||||
'Enable video autoplay on this device': 'Включить автовоспроизведение видео на этом устройстве'
|
||||
'Enable video autoplay on this device': 'Включить автовоспроизведение видео на этом устройстве',
|
||||
'Paste or drop media files to upload': 'Вставьте или перетащите медиафайлы для загрузки',
|
||||
Preview: 'Предварительный просмотр'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +110,6 @@ export default {
|
||||
Relays: '服务器',
|
||||
image: '图片',
|
||||
Normal: '普通',
|
||||
'Normal Note': '普通笔记',
|
||||
'Picture Note': '图片笔记',
|
||||
'R & W': '读写',
|
||||
Read: '只读',
|
||||
Write: '只写',
|
||||
@@ -228,6 +226,8 @@ export default {
|
||||
'Show more': '显示更多',
|
||||
General: '常规',
|
||||
Autoplay: '自动播放',
|
||||
'Enable video autoplay on this device': '在此设备上启用视频自动播放'
|
||||
'Enable video autoplay on this device': '在此设备上启用视频自动播放',
|
||||
'Paste or drop media files to upload': '支持粘贴或拖放媒体文件进行上传',
|
||||
Preview: '预览'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export function MediaUploadServiceProvider({ children }: { children: React.React
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(response.status.toString())
|
||||
throw new Error(response.status.toString() + ' ' + response.statusText)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
Reference in New Issue
Block a user