mirror of
https://github.com/linsa-io/linsa.git
synced 2026-03-27 19:41:20 +01:00
* wip * wip page * chore: style * wip pages * wip pages * chore: toggle * chore: link * feat: topic search * chore: page section * refactor: apply tailwind class ordering * fix: handle loggedIn user for guest route * feat: folder & image schema * chore: move utils to shared * refactor: tailwind class ordering * feat: img ext for editor * refactor: remove qa * fix: tanstack start * fix: wrong import * chore: use toast * chore: schema
126 lines
3.3 KiB
TypeScript
126 lines
3.3 KiB
TypeScript
import * as React from "react"
|
|
import type { Editor } from "@tiptap/react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Label } from "@/components/ui/label"
|
|
import { Input } from "@/components/ui/input"
|
|
import { cn } from "@/lib/utils"
|
|
import { storeImageFn } from "@shared/actions"
|
|
import { ZodError } from "zod"
|
|
|
|
interface ImageEditBlockProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
editor: Editor
|
|
close: () => void
|
|
}
|
|
|
|
const ImageEditBlock = ({
|
|
editor,
|
|
className,
|
|
close,
|
|
...props
|
|
}: ImageEditBlockProps) => {
|
|
const fileInputRef = React.useRef<HTMLInputElement>(null)
|
|
const [link, setLink] = React.useState<string>("")
|
|
const [isUploading, setIsUploading] = React.useState<boolean>(false)
|
|
const [error, setError] = React.useState<string | null>(null)
|
|
|
|
const handleClick = (e: React.MouseEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
fileInputRef.current?.click()
|
|
}
|
|
|
|
const handleLink = () => {
|
|
editor.chain().focus().setImage({ src: link }).run()
|
|
close()
|
|
}
|
|
|
|
const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const files = e.target.files
|
|
if (!files || files.length === 0) return
|
|
|
|
setIsUploading(true)
|
|
setError(null)
|
|
|
|
const formData = new FormData()
|
|
formData.append("file", files[0])
|
|
|
|
try {
|
|
const response = await storeImageFn(formData)
|
|
|
|
editor
|
|
.chain()
|
|
.setImage({ src: response.fileModel.content.src })
|
|
.focus()
|
|
.run()
|
|
close()
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
try {
|
|
const errors = JSON.parse(error.message)
|
|
if (errors.body.name === "ZodError") {
|
|
setError(
|
|
(errors.body as ZodError).issues
|
|
.map((issue) => issue.message)
|
|
.join(", "),
|
|
)
|
|
} else {
|
|
setError(error.message)
|
|
}
|
|
} catch (parseError) {
|
|
setError(error.message)
|
|
}
|
|
} else {
|
|
setError("An unknown error occurred")
|
|
}
|
|
} finally {
|
|
setIsUploading(false)
|
|
}
|
|
}
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
handleLink()
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit}>
|
|
<div className={cn("space-y-6", className)} {...props}>
|
|
<div className="space-y-1">
|
|
<Label>Attach an image link</Label>
|
|
<div className="flex">
|
|
<Input
|
|
type="url"
|
|
required
|
|
placeholder="https://example.com"
|
|
value={link}
|
|
className="grow"
|
|
onChange={(e) => setLink(e.target.value)}
|
|
/>
|
|
<Button type="submit" className="ml-2 inline-block">
|
|
Submit
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<Button className="w-full" onClick={handleClick} disabled={isUploading}>
|
|
{isUploading ? "Uploading..." : "Upload from your computer"}
|
|
</Button>
|
|
<input
|
|
type="file"
|
|
accept="image/jpeg,image/png,image/gif,image/webp"
|
|
ref={fileInputRef}
|
|
className="hidden"
|
|
onChange={handleFile}
|
|
/>
|
|
{error && (
|
|
<div className="rounded bg-destructive/10 p-2 text-sm text-destructive">
|
|
{error}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</form>
|
|
)
|
|
}
|
|
|
|
export { ImageEditBlock }
|