mirror of https://github.com/kern/filepizza
Add retrieve code mode support. (e.g. Like send anywhere)
If enable retrieve mode (NEXT_PUBLIC_RETRIEVE_CODE_MODE=true), it allows user to get retrieve code to be used to download the file directly from home page. BTW, hide long url & short url download links for clean layout.pull/189/head
parent
b79363b750
commit
adabe57cd8
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,19 @@
|
||||
import React, { JSX } from 'react'
|
||||
import { ClipboardIcon } from '@heroicons/react/24/outline'
|
||||
import useClipboard from '../hooks/useClipboard'
|
||||
|
||||
export function CopyableIcon({ value }: { value: string }): JSX.Element {
|
||||
const { hasCopied, onCopy } = useClipboard(value)
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center">
|
||||
<ClipboardIcon
|
||||
onClick={onCopy}
|
||||
className="w-6 h-6 text-gray-500 cursor-pointer hover:text-gray-600"
|
||||
/>
|
||||
{hasCopied && (
|
||||
<span className="absolute ml-8 text-sm text-green-500">Copied!</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
import React, { JSX, useRef, useState } from 'react'
|
||||
import config from '../config'
|
||||
|
||||
type DownloadZoneProps = {
|
||||
onComplete: (code: string) => void
|
||||
}
|
||||
|
||||
export default function DownloadZone({
|
||||
onComplete,
|
||||
}: DownloadZoneProps): JSX.Element {
|
||||
const inputsRef = useRef<(HTMLInputElement | null)[]>([])
|
||||
const [code, setCode] = useState<string[]>(
|
||||
Array(config.retrieveCodeSlug.numChars).fill(''),
|
||||
)
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
index: number,
|
||||
) => {
|
||||
const value = e.target.value
|
||||
// Only allow chars defined in config, in fact, it only has at most 1 char
|
||||
if (
|
||||
!value
|
||||
.split('')
|
||||
.every((char) => config.retrieveCodeSlug.chars.includes(char))
|
||||
) {
|
||||
e.target.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
const newCode = [...code]
|
||||
newCode[index] = value
|
||||
setCode(newCode)
|
||||
|
||||
if (value && index < inputsRef.current.length - 1) {
|
||||
const nextInput = inputsRef.current[index + 1]
|
||||
nextInput?.focus()
|
||||
nextInput?.select()
|
||||
}
|
||||
|
||||
if (index === inputsRef.current.length - 1 && value) {
|
||||
onComplete(newCode.join(''))
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = (
|
||||
e: React.KeyboardEvent<HTMLInputElement>,
|
||||
index: number,
|
||||
) => {
|
||||
if (e.key === 'Backspace' && !e.currentTarget.value && index > 0) {
|
||||
const prevInput = inputsRef.current[index - 1]
|
||||
prevInput?.focus()
|
||||
prevInput?.select()
|
||||
} else if (e.key === 'ArrowLeft' && index > 0) {
|
||||
const prevInput = inputsRef.current[index - 1]
|
||||
prevInput?.focus()
|
||||
prevInput?.select()
|
||||
} else if (e.key === 'ArrowRight' && index < inputsRef.current.length - 1) {
|
||||
const nextInput = inputsRef.current[index + 1]
|
||||
nextInput?.focus()
|
||||
nextInput?.select()
|
||||
}
|
||||
}
|
||||
|
||||
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
|
||||
const pasteData = e.clipboardData.getData('Text')
|
||||
const validChars = pasteData
|
||||
.split('')
|
||||
.filter((char) => config.retrieveCodeSlug.chars.includes(char))
|
||||
const newCode = validChars.slice(0, config.retrieveCodeSlug.numChars)
|
||||
|
||||
newCode.forEach((char, index) => {
|
||||
if (inputsRef.current[index]) {
|
||||
inputsRef.current[index]!.value = char
|
||||
}
|
||||
})
|
||||
|
||||
setCode(newCode)
|
||||
|
||||
if (newCode.length === config.retrieveCodeSlug.numChars) {
|
||||
onComplete(newCode.join(''))
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-4 text-center">
|
||||
<h2 className="text-lg font-semibold">Retrieve your file</h2>
|
||||
<div className="flex justify-center space-x-2 mt-2">
|
||||
{Array(config.retrieveCodeSlug.numChars)
|
||||
.fill('')
|
||||
.map((_, index) => (
|
||||
<input
|
||||
key={index}
|
||||
type="text"
|
||||
maxLength={1}
|
||||
className="w-10 h-10 border border-gray-500 rounded text-center"
|
||||
onChange={(e) => handleInputChange(e, index)}
|
||||
onKeyDown={(e) => handleKeyDown(e, index)}
|
||||
onPaste={handlePaste}
|
||||
ref={(el) => {
|
||||
if (el) {
|
||||
inputsRef.current[index] = el
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-2">
|
||||
Enter the code to download file.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
import React, { JSX } from 'react'
|
||||
import { CopyableIcon } from './CopyableIcon'
|
||||
import config from '../config'
|
||||
|
||||
export function RetrieveCodeBox({
|
||||
retrieveCode,
|
||||
}: {
|
||||
retrieveCode: string
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className="flex-auto text-center pr-12">
|
||||
<div className="flex items-center justify-center space-x-2">
|
||||
<h2 className="text-lg font-semibold">File Retrieve Code</h2>
|
||||
<CopyableIcon value={retrieveCode} />
|
||||
</div>
|
||||
<div className="flex justify-center space-x-2 mt-2">
|
||||
{Array(config.retrieveCodeSlug.numChars)
|
||||
.fill('')
|
||||
.map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="w-10 h-10 border border-gray-500 rounded text-center flex items-center justify-center"
|
||||
>
|
||||
{retrieveCode[index]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-2">
|
||||
Use the above code to retrieve the file.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue