mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Chore/support pasting images in feedback widget (#28475)
* Support pasting images in feedback widget * Remove unnecessary usage of local storage * Revert changes to local storeage
This commit is contained in:
1 parent
f4fa35d88f
commit
cf53e46805
2 files changed
+42
-39
No files matched your search
+2
-12
@@ -7,16 +7,6 @@ const FeedbackDropdown = () => {
|
||||
const [feedback, setFeedback] = useState('')
|
||||
const [screenshot, setScreenshot] = useState<string>()
|
||||
|
||||
function onOpen() {
|
||||
setIsOpen((isOpen) => !isOpen)
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
setFeedback('')
|
||||
setScreenshot(undefined)
|
||||
setIsOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover_Shadcn_
|
||||
modal={false}
|
||||
@@ -27,13 +17,13 @@ const FeedbackDropdown = () => {
|
||||
}}
|
||||
>
|
||||
<PopoverTrigger_Shadcn_ asChild>
|
||||
<Button asChild onClick={onOpen} type="outline">
|
||||
<Button asChild onClick={() => setIsOpen((isOpen) => !isOpen)} type="outline">
|
||||
<span className="hidden md:flex">Feedback</span>
|
||||
</Button>
|
||||
</PopoverTrigger_Shadcn_>
|
||||
<PopoverContent_Shadcn_ side="bottom" align="end" className="w-full p-0">
|
||||
<FeedbackWidget
|
||||
onClose={onClose}
|
||||
onClose={() => setIsOpen(false)}
|
||||
feedback={feedback}
|
||||
setFeedback={setFeedback}
|
||||
screenshot={screenshot}
|
||||
|
||||
+40
-27
@@ -1,5 +1,5 @@
|
||||
import { useParams } from 'common'
|
||||
import { toPng } from 'html-to-image'
|
||||
import { Camera, Image as ImageIcon, Upload, X } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { ChangeEvent, useEffect, useRef, useState } from 'react'
|
||||
@@ -10,30 +10,27 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
IconCamera,
|
||||
IconImage,
|
||||
IconUpload,
|
||||
IconX,
|
||||
Input,
|
||||
} from 'ui'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useSendFeedbackMutation } from 'data/feedback/feedback-send'
|
||||
import { timeout } from 'lib/helpers'
|
||||
import { convertB64toBlob, uploadAttachment } from './FeedbackDropdown.utils'
|
||||
|
||||
interface FeedbackWidgetProps {
|
||||
onClose: () => void
|
||||
feedback: string
|
||||
setFeedback: (value: string) => void
|
||||
screenshot: string | undefined
|
||||
onClose: () => void
|
||||
setFeedback: (value: string) => void
|
||||
setScreenshot: (value: string | undefined) => void
|
||||
}
|
||||
|
||||
const FeedbackWidget = ({
|
||||
onClose,
|
||||
feedback,
|
||||
setFeedback,
|
||||
screenshot,
|
||||
onClose,
|
||||
setFeedback,
|
||||
setScreenshot,
|
||||
}: FeedbackWidgetProps) => {
|
||||
const FEEDBACK_STORAGE_KEY = 'feedback_content'
|
||||
@@ -41,11 +38,11 @@ const FeedbackWidget = ({
|
||||
|
||||
const router = useRouter()
|
||||
const { ref, slug } = useParams()
|
||||
const inputRef = useRef<any>(null)
|
||||
const uploadButtonRef = useRef()
|
||||
const uploadButtonRef = useRef(null)
|
||||
|
||||
const [isSending, setSending] = useState(false)
|
||||
const [isSavingScreenshot, setIsSavingScreenshot] = useState(false)
|
||||
|
||||
const { mutate: submitFeedback } = useSendFeedbackMutation({
|
||||
onSuccess: () => {
|
||||
setFeedback('')
|
||||
@@ -86,14 +83,6 @@ const FeedbackWidget = ({
|
||||
}
|
||||
}, [screenshot])
|
||||
|
||||
useEffect(() => {
|
||||
inputRef?.current?.focus()
|
||||
}, [inputRef])
|
||||
|
||||
function onFeedbackChange(e: any) {
|
||||
setFeedback(e.target.value)
|
||||
}
|
||||
|
||||
const clearFeedback = () => {
|
||||
setFeedback('')
|
||||
setScreenshot(undefined)
|
||||
@@ -118,7 +107,7 @@ const FeedbackWidget = ({
|
||||
localStorage.setItem(SCREENSHOT_STORAGE_KEY, dataUrl)
|
||||
setScreenshot(dataUrl)
|
||||
})
|
||||
.catch((error: any) => toast.error('Failed to capture screenshot'))
|
||||
.catch(() => toast.error('Failed to capture screenshot'))
|
||||
.finally(() => {
|
||||
setIsSavingScreenshot(false)
|
||||
})
|
||||
@@ -140,6 +129,24 @@ const FeedbackWidget = ({
|
||||
event.target.value = ''
|
||||
}
|
||||
|
||||
const handlePasteEvent = async () => {
|
||||
// [Joshen] Support pasting images via Cmd / Ctrl + V
|
||||
const [data] = await navigator.clipboard.read()
|
||||
|
||||
if (screenshot === undefined && data.types[0] === 'image/png') {
|
||||
const blob = await data.getType('image/png')
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (event) {
|
||||
const dataUrl = event.target?.result
|
||||
if (typeof dataUrl === 'string') {
|
||||
setScreenshot(dataUrl)
|
||||
localStorage.setItem(SCREENSHOT_STORAGE_KEY, dataUrl)
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
}
|
||||
}
|
||||
|
||||
const sendFeedback = async () => {
|
||||
if (feedback.length === 0 && screenshot !== undefined) {
|
||||
return toast.error('Please include a message in your feedback.')
|
||||
@@ -171,12 +178,19 @@ const FeedbackWidget = ({
|
||||
placeholder="Ideas on how to improve this page. Use the Support Form for technical issues."
|
||||
rows={5}
|
||||
value={feedback}
|
||||
onChange={onFeedbackChange}
|
||||
onChange={(e) => setFeedback(e.target.value)}
|
||||
onPaste={handlePasteEvent}
|
||||
/>
|
||||
<div className="w-full h-px bg-border" />
|
||||
<div className="w-80 space-y-3 px-3 py-2 pb-4">
|
||||
<div className="flex justify-between space-x-2">
|
||||
<Button type="default" onClick={onClose}>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => {
|
||||
clearFeedback()
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<div className="flex items-center space-x-2">
|
||||
@@ -203,7 +217,7 @@ const FeedbackWidget = ({
|
||||
setScreenshot(undefined)
|
||||
}}
|
||||
>
|
||||
<IconX size={8} strokeWidth={3} />
|
||||
<X size={8} strokeWidth={3} />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
@@ -215,7 +229,7 @@ const FeedbackWidget = ({
|
||||
loading={isSavingScreenshot}
|
||||
className="px-2 py-1.5"
|
||||
>
|
||||
<IconImage size={14} />
|
||||
<ImageIcon size={14} />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="bottom" align="end">
|
||||
@@ -226,7 +240,7 @@ const FeedbackWidget = ({
|
||||
if (uploadButtonRef.current) (uploadButtonRef.current as any).click()
|
||||
}}
|
||||
>
|
||||
<IconUpload size={14} />
|
||||
<Upload size={14} />
|
||||
Upload screenshot
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
@@ -234,7 +248,7 @@ const FeedbackWidget = ({
|
||||
key="capture-screenshot"
|
||||
onSelect={() => captureScreenshot()}
|
||||
>
|
||||
<IconCamera size={14} />
|
||||
<Camera size={14} />
|
||||
Capture screenshot
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
@@ -242,7 +256,6 @@ const FeedbackWidget = ({
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
// @ts-ignore
|
||||
ref={uploadButtonRef}
|
||||
className="hidden"
|
||||
accept="image/png"
|
||||
|
||||
Reference in new issue
Block a user