feat: banner setting

This commit is contained in:
DIYgod 2022-11-10 00:59:55 +00:00
parent accd9ff183
commit 7d8b85bb0e
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
5 changed files with 144 additions and 10 deletions

View File

@ -76,9 +76,10 @@ export const SiteHeader: React.FC<{
<div className="xlog-banner absolute top-0 bottom-0 left-0 right-0 -z-10 overflow-hidden">
{site?.banners?.[0]?.mime_type.split("/")[0] === "image" && (
<Image
className="max-w-screen-md mx-auto object-cover h-full w-full"
className="max-w-screen-md mx-auto object-cover"
src={site?.banners?.[0]?.address}
alt="banner"
fill
/>
)}
{site?.banners?.[0]?.mime_type.split("/")[0] === "video" && (

View File

@ -63,7 +63,7 @@ export const Avatar: React.FC<
}}
>
<Image
className="overflow-hidden"
className="overflow-hidden object-cover"
src={image}
width={size}
height={size}

View File

@ -3,6 +3,8 @@ import React, { ChangeEvent, forwardRef, useEffect } from "react"
import { useState } from "react"
import { useUploadFile } from "~/hooks/useUploadFile"
import { toGateway } from "~/lib/ipfs-parser"
import { Image } from "~/components/ui/Image"
import { XMarkIcon } from "@heroicons/react/20/solid"
const getBase64 = (img: File, callback: (url: string) => void) => {
const reader = new FileReader()
@ -16,12 +18,23 @@ export const ImageUploader = forwardRef(function ImageUploader(
image,
uploadStart,
uploadEnd,
withMimeType,
hasClose,
...inputProps
}: {
className?: string
image?: string
uploadStart?: () => void
uploadEnd?: (key: string) => void
uploadEnd?: (
key:
| string
| {
address?: string
mime_type?: string
},
) => void
withMimeType?: boolean
hasClose?: boolean
} & React.ComponentPropsWithRef<"input">,
ref: React.ForwardedRef<HTMLInputElement>,
) {
@ -38,32 +51,94 @@ export const ImageUploader = forwardRef(function ImageUploader(
setLoading(true)
uploadStart?.()
const key = (await uploadFile(event.target.files[0])).key
uploadEnd?.(key)
uploadEnd?.(
withMimeType
? {
address: key,
mime_type: event.target.files[0].type,
}
: key,
)
setLoading(false)
} else if (event.target.value) {
setImageUrl(event.target.value)
}
}
const clear = () => {
setImageUrl(undefined)
uploadEnd?.(withMimeType ? {} : "")
}
useEffect(() => {
if (!imageUrl) {
setImageUrl((inputProps.value as string) || "")
setImageUrl(
((withMimeType
? (inputProps.value as any)?.address
: inputProps.value) as string) || "",
)
}
}, [inputProps.value])
return (
<div
className={clsx(
"relative flex-col overflow-hidden bg-gray-400",
"relative flex-col overflow-hidden border border-gray-100",
className,
)}
>
<input {...inputProps} onChange={handleChange} className="hidden" />
{imageUrl && <img src={toGateway(imageUrl)} className="w-full h-full" />}
<input
{...(withMimeType
? {
value: (inputProps.value as any)?.address,
...inputProps,
}
: inputProps)}
onChange={handleChange}
className="hidden"
/>
{imageUrl && !withMimeType && (
<Image
className="mx-auto object-cover"
src={toGateway(imageUrl)}
alt="banner"
fill
/>
)}
{imageUrl &&
withMimeType &&
(inputProps.value as any)?.mime_type.split("/")[0] === "image" && (
<Image
className="mx-auto object-cover"
src={toGateway(imageUrl)}
alt="banner"
fill
/>
)}
{imageUrl &&
withMimeType &&
(inputProps.value as any)?.mime_type.split("/")[0] === "video" && (
<video
className="max-w-screen-md mx-auto object-cover h-full w-full"
src={toGateway(imageUrl)}
autoPlay
muted
playsInline
/>
)}
<input
onChange={handleChange}
type="file"
className="absolute top-0 bottom-0 left-0 right-0 opacity-0"
/>
{hasClose && (
<div
onClick={clear}
className="w-8 h-8 absolute top-4 right-4 shadow-lg bg-white rounded-full cursor-pointer"
>
<XMarkIcon />
</div>
)}
{loading && (
<div className="absolute top-0 bottom-0 left-0 right-0 flex justify-center items-center bg-gray-500 opacity-50">
<div className="loading flex justify-center items-center relative text-white"></div>

View File

@ -238,6 +238,10 @@ export async function updateSite(
css?: string
ga?: string
custom_domain?: string
banner?: {
address: string
mime_type: string
}
},
customUnidata?: Unidata,
) {
@ -252,6 +256,7 @@ export async function updateSite(
...(payload.name && { name: payload.name }),
...(payload.description && { bio: payload.description }),
...(payload.icon && { avatars: [payload.icon] }),
...(payload.banner && { banners: [payload.banner] }),
...(payload.subdomain && { username: payload.subdomain }),
...((payload.navigation !== undefined ||
payload.css !== undefined ||

View File

@ -22,15 +22,26 @@ export default function SiteSettingsGeneralPage() {
const form = useForm({
defaultValues: {
icon: "",
banner: undefined,
name: "",
description: "",
ga: "",
} as {
icon: string
banner?: {
address: string
mime_type: string
}
name: string
description: string
ga: string
},
})
const handleSubmit = form.handleSubmit((values) => {
updateSite.mutate({
icon: values.icon,
banner: values.banner,
site: subdomain,
name: values.name,
description: values.description,
@ -54,6 +65,16 @@ export default function SiteSettingsGeneralPage() {
if (site.data) {
!form.getValues("icon") &&
form.setValue("icon", toIPFS(site.data?.avatars?.[0] || ""))
!form.getValues("banner") &&
form.setValue(
"banner",
site.data?.banners?.[0]
? {
address: toIPFS(site.data?.banners?.[0].address || ""),
mime_type: site.data?.banners?.[0].mime_type,
}
: undefined,
)
!form.getValues("name") && form.setValue("name", site.data.name || "")
!form.getValues("description") &&
form.setValue("description", site.data.bio || "")
@ -62,6 +83,7 @@ export default function SiteSettingsGeneralPage() {
}, [site.data, form])
const [iconUploading, setIconUploading] = useState(false)
const [bannerUploading, setBannerUploading] = useState(false)
return (
<DashboardLayout title="Site Settings">
@ -82,7 +104,7 @@ export default function SiteSettingsGeneralPage() {
setIconUploading(true)
}}
uploadEnd={(key) => {
form.setValue("icon", key)
form.setValue("icon", key as string)
setIconUploading(false)
}}
{...field}
@ -90,6 +112,37 @@ export default function SiteSettingsGeneralPage() {
)}
/>
</div>
<div className="mt-5">
<label htmlFor="icon" className="form-label">
Banner
</label>
<Controller
name="banner"
control={form.control}
render={({ field }) => (
<ImageUploader
id="banner"
className="max-w-screen-md h-[220px]"
uploadStart={() => {
setBannerUploading(true)
}}
uploadEnd={(key) => {
form.setValue(
"banner",
key as { address: string; mime_type: string },
)
setBannerUploading(false)
}}
withMimeType={true}
hasClose={true}
{...(field as any)}
/>
)}
/>
<div className="text-xs text-gray-400 mt-1">
Click to upload. Supports both pictures and videos.
</div>
</div>
<div className="mt-5">
<Input required label="Name" id="name" {...form.register("name")} />
</div>
@ -131,7 +184,7 @@ export default function SiteSettingsGeneralPage() {
<Button
type="submit"
isLoading={updateSite.isLoading}
isDisabled={iconUploading}
isDisabled={iconUploading || bannerUploading}
>
Save
</Button>