200 lines
8.2 KiB
TypeScript
200 lines
8.2 KiB
TypeScript
|
|
import { type MouseEventHandler, forwardRef, useEffect, useMemo, useState } from 'react'
|
||
|
|
import type { BackendArt } from '../../functions/BackendTypes'
|
||
|
|
import { WEB_BASE } from '../../functions/Backend'
|
||
|
|
import { wtEvent } from '../../functions/Watchtower'
|
||
|
|
import { toast } from '../../functions/Context'
|
||
|
|
import './styles/ModalEmbed.css'
|
||
|
|
|
||
|
|
import VectorBackgroundEmbed from '../../vectors/background-embed.svg'
|
||
|
|
import HeaderMessage from './HeaderMessage'
|
||
|
|
import InputButtonRow from '../inputs/ButtonRow'
|
||
|
|
import InputButton from '../inputs/Button'
|
||
|
|
import InputDescription from '../inputs/Description'
|
||
|
|
import InputLabel from '../inputs/Label'
|
||
|
|
|
||
|
|
interface PropsForModalEmbed {
|
||
|
|
item: BackendArt
|
||
|
|
onClose: MouseEventHandler<HTMLButtonElement>
|
||
|
|
}
|
||
|
|
|
||
|
|
export default forwardRef<HTMLDialogElement, PropsForModalEmbed>(function ModalEmbed(
|
||
|
|
{ item, onClose }: PropsForModalEmbed,
|
||
|
|
ref,
|
||
|
|
) {
|
||
|
|
// Keep User Preferences
|
||
|
|
const KEY_QUALITY = 'preference_embed_quality'
|
||
|
|
const KEY_SCALE = 'preference_embed_scale'
|
||
|
|
|
||
|
|
const [preferQuality, setQuality] = useState<'standard' | 'transparent'>(
|
||
|
|
(() => {
|
||
|
|
let raw = localStorage.getItem(KEY_QUALITY) ?? 'standard'
|
||
|
|
if (raw !== 'standard' && raw !== 'transparent') {
|
||
|
|
return 'standard'
|
||
|
|
} else {
|
||
|
|
return raw
|
||
|
|
}
|
||
|
|
})(),
|
||
|
|
)
|
||
|
|
|
||
|
|
const [preferScale, setScale] = useState<number>(
|
||
|
|
(() => {
|
||
|
|
let raw = localStorage.getItem(KEY_SCALE) ?? String('1')
|
||
|
|
let val = parseFloat(raw)
|
||
|
|
if (isNaN(val) || val < 0 || val > 1) return 1
|
||
|
|
return val
|
||
|
|
})(),
|
||
|
|
)
|
||
|
|
|
||
|
|
useEffect(() => localStorage.setItem(KEY_QUALITY, String(preferQuality)), [preferQuality])
|
||
|
|
useEffect(() => localStorage.setItem(KEY_SCALE, String(preferScale)), [preferScale])
|
||
|
|
|
||
|
|
// Calculate Embed Values
|
||
|
|
const embedScale = useMemo(() => {
|
||
|
|
const maxDim = Math.max(item.width, item.height)
|
||
|
|
const baseScale = maxDim > 640 ? 640 / maxDim : 1
|
||
|
|
return baseScale * preferScale
|
||
|
|
}, [item.width, item.height, preferScale])
|
||
|
|
|
||
|
|
const embedHeight = useMemo(() => (item.height * embedScale) | 0, [embedScale])
|
||
|
|
const embedWidth = useMemo(() => (item.width * embedScale) | 0, [embedScale])
|
||
|
|
|
||
|
|
// const embedQuality = useMemo(() => {
|
||
|
|
// if (preferQuality === 'standard.avif') return 'standard'
|
||
|
|
// return 'transparent'
|
||
|
|
// }, [preferQuality])
|
||
|
|
|
||
|
|
const embedHTML = useMemo(
|
||
|
|
() =>
|
||
|
|
`<iframe src="${WEB_BASE}/embed.html?id=${item.id}&quality=${preferQuality}" width="${embedWidth}" height="${embedHeight}" style="border:none; background: transparent" allowtransparency="true"></iframe>`,
|
||
|
|
[preferQuality, embedScale],
|
||
|
|
)
|
||
|
|
|
||
|
|
function onCopy() {
|
||
|
|
navigator.clipboard.writeText(embedHTML)
|
||
|
|
toast('action-copy', 'Copied Code to Clipboard!')
|
||
|
|
wtEvent('action_animation_embed_copy', {
|
||
|
|
id: item.id,
|
||
|
|
height: embedHeight,
|
||
|
|
width: embedWidth,
|
||
|
|
scale: (embedScale * 100) | 0,
|
||
|
|
quality: preferQuality,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<dialog ref={ref} className="modal-embed animation-fall-in animation-caution">
|
||
|
|
<HeaderMessage label="MENU: Embed Generator" />
|
||
|
|
<div className="wrapper">
|
||
|
|
{/* Left-Pane */}
|
||
|
|
<div className="preview">
|
||
|
|
<img className="background animation-fade-in" src={VectorBackgroundEmbed} />
|
||
|
|
<iframe
|
||
|
|
className="animation-fall-in"
|
||
|
|
style={{ border: 'none', background: 'transparent' }}
|
||
|
|
src={`${WEB_BASE}/embed.html?id=${item.id}&quality=${preferQuality}`}
|
||
|
|
width={embedWidth}
|
||
|
|
height={embedHeight}
|
||
|
|
allowTransparency
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Right-Pane */}
|
||
|
|
<div className="toggles">
|
||
|
|
<InputLabel for="" label="Quality" />
|
||
|
|
<InputDescription>
|
||
|
|
We recommend using Standard quality, if you require transparency use Alpha quality.
|
||
|
|
</InputDescription>
|
||
|
|
<InputDescription>
|
||
|
|
Using more than three Alpha embeds may slow down your site, and up to twelve can be displayed at
|
||
|
|
any given time.
|
||
|
|
</InputDescription>
|
||
|
|
<InputButtonRow split={false}>
|
||
|
|
<InputButton
|
||
|
|
id="quality-alpha"
|
||
|
|
label="Alpha"
|
||
|
|
onClick={() => setQuality('transparent')}
|
||
|
|
selected={preferQuality === 'transparent'}
|
||
|
|
disabled={false}
|
||
|
|
rainbow={false}
|
||
|
|
/>
|
||
|
|
<InputButton
|
||
|
|
id="quality-standard"
|
||
|
|
label="Standard"
|
||
|
|
onClick={() => setQuality('standard')}
|
||
|
|
selected={preferQuality === 'standard'}
|
||
|
|
disabled={false}
|
||
|
|
rainbow={false}
|
||
|
|
/>
|
||
|
|
</InputButtonRow>
|
||
|
|
|
||
|
|
<InputLabel for="" label="Scale" />
|
||
|
|
<InputDescription>
|
||
|
|
Sizing is as follows: Small @ 320px; Medium @ 480px; Large @ 640px.
|
||
|
|
</InputDescription>
|
||
|
|
<InputDescription>If an image is too small, it wont get any larger.</InputDescription>
|
||
|
|
<InputButtonRow split={false}>
|
||
|
|
<InputButton
|
||
|
|
id="size-small"
|
||
|
|
label="Small"
|
||
|
|
selected={preferScale < 0.6}
|
||
|
|
onClick={() => setScale(0.5)}
|
||
|
|
disabled={false}
|
||
|
|
rainbow={false}
|
||
|
|
/>
|
||
|
|
<InputButton
|
||
|
|
id="size-medium"
|
||
|
|
label="Medium"
|
||
|
|
selected={preferScale > 0.6 && preferScale < 0.9}
|
||
|
|
onClick={() => setScale(0.75)}
|
||
|
|
disabled={false}
|
||
|
|
rainbow={false}
|
||
|
|
/>
|
||
|
|
<InputButton
|
||
|
|
id="size-large"
|
||
|
|
label="Large"
|
||
|
|
selected={preferScale > 0.9}
|
||
|
|
onClick={() => setScale(1)}
|
||
|
|
disabled={false}
|
||
|
|
rainbow={false}
|
||
|
|
/>
|
||
|
|
</InputButtonRow>
|
||
|
|
|
||
|
|
<InputLabel for="" label="Code" />
|
||
|
|
<InputDescription>
|
||
|
|
Use this code snippet to display this {item.sticker ? 'sticker' : 'animation'} on your website.
|
||
|
|
</InputDescription>
|
||
|
|
<InputDescription>Clicking on the embed will direct users to gifuu in a new tab.</InputDescription>
|
||
|
|
|
||
|
|
<textarea
|
||
|
|
id="input-url"
|
||
|
|
className="input-url"
|
||
|
|
value={embedHTML}
|
||
|
|
onKeyDown={(e) => e.preventDefault()}
|
||
|
|
/>
|
||
|
|
|
||
|
|
<InputLabel for="" label="" /* lazy divider */ />
|
||
|
|
|
||
|
|
<InputButtonRow split={true}>
|
||
|
|
<InputButton
|
||
|
|
id="action-copy"
|
||
|
|
label="Copy HTML"
|
||
|
|
onClick={onCopy}
|
||
|
|
selected={false}
|
||
|
|
disabled={false}
|
||
|
|
rainbow={false}
|
||
|
|
/>
|
||
|
|
<InputButton
|
||
|
|
id="action-exit"
|
||
|
|
label="Exit"
|
||
|
|
onClick={onClose}
|
||
|
|
selected={false}
|
||
|
|
disabled={false}
|
||
|
|
rainbow={false}
|
||
|
|
/>
|
||
|
|
</InputButtonRow>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</dialog>
|
||
|
|
)
|
||
|
|
})
|