1
Fork 0
mirror of https://github.com/Steffo99/festa.git synced 2024-10-16 06:57:26 +00:00

WIP: file state things

This commit is contained in:
Steffo 2022-06-08 19:14:00 +02:00
parent 63efa5b896
commit dafd64b4c9
Signed by: steffo
GPG key ID: 6965406171929D01
11 changed files with 164 additions and 164 deletions

View file

@ -11,5 +11,6 @@
"node_modules": true,
".next": true,
"yarn.lock": true,
}
},
"editor.formatOnSave": true
}

View file

@ -0,0 +1,15 @@
import { ReactNode } from "react";
import { useDefinedContext } from "../../utils/definedContext";
import { EditingContext } from "./EditingContext";
type EditableProps = {
editing: JSX.Element,
preview: JSX.Element,
}
export function Editable({editing, preview}: EditableProps) {
const [isEditing,] = useDefinedContext(EditingContext)
return isEditing ? editing : preview
}

View file

@ -1,72 +0,0 @@
import { faChevronRight, faClock } from "@fortawesome/free-solid-svg-icons"
import { HTMLProps } from "react"
import { EditingContext } from "./EditingContext"
import { useDefinedContext } from "../../utils/definedContext"
import { FestaIcon } from "../extensions/FestaIcon"
import { FormDateRange } from "../form/FormDateRange"
import { HumanDate } from "../HumanDate"
type EditableDateRangeProps = {
startProps: HTMLProps<HTMLInputElement> & {value?: string},
endProps: HTMLProps<HTMLInputElement> & {value?: string},
}
export function EditableDateRange(props: EditableDateRangeProps) {
const [editing,] = useDefinedContext(EditingContext)
if(editing) {
return (
<FormDateRange
preview={false}
icon={
<FestaIcon icon={faClock}/>
}
start={
<input
type="datetime-local"
{...props.startProps}
/>
}
connector={
<FestaIcon icon={faChevronRight}/>
}
end={
<input
type="datetime-local"
{...props.endProps}
/>
}
/>
)
}
const startTime = Date.parse(props.startProps.value!)
const endTime = Date.parse(props.endProps.value!)
if(Number.isNaN(startTime) && Number.isNaN(endTime)) {
return null
}
const startDate = new Date(startTime)
const endDate = new Date(endTime)
return (
<FormDateRange
preview={true}
icon={
<FestaIcon icon={faClock}/>
}
start={
<HumanDate date={startDate}/>
}
connector={
<FestaIcon icon={faChevronRight}/>
}
end={
<HumanDate date={endDate}/>
}
/>
)
}

View file

@ -0,0 +1,16 @@
import { HTMLProps } from "react";
import { HumanDate } from "../HumanDate";
import { Editable } from "./Editable";
export function EditableDateTimeLocal(props: HTMLProps<HTMLInputElement> & { value: Date }) {
return (
<Editable
editing={
<input type="datetime-local" {...props} value={props.value.toISOString()} />
}
preview={
<HumanDate date={props.value} />
}
/>
)
}

View file

@ -0,0 +1,19 @@
import { HTMLProps } from "react";
import { Editable } from "./Editable";
/**
* Controlled input component which displays an `input[type="file"]` in editing mode, and is invisible in preview mode.
*
* Value is the file's [fakepath](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#value), as a string.
*/
export function EditableFile(props: HTMLProps<HTMLInputElement> & { value: string }) {
return (
<Editable
editing={
<input type="file" {...props} />
}
preview={<></>}
/>
)
}

View file

@ -1,17 +1,19 @@
import { HTMLProps } from "react";
import { EditingContext } from "./EditingContext";
import { useDefinedContext } from "../../utils/definedContext";
import { FestaMarkdown } from "../extensions/FestaMarkdown";
import { Editable } from "./Editable";
/**
* Controlled input component which displays a `textarea` in editing mode, and renders the input in Markdown using {@link FestaMarkdown} in preview mode.
*/
export function EditableMarkdown({value, ...props}: HTMLProps<HTMLTextAreaElement>) {
const [editing,] = useDefinedContext(EditingContext)
return editing ? (
<textarea value={value} {...props}/>
) : (
<FestaMarkdown markdown={value as string}/>
export function EditableMarkdown(props: HTMLProps<HTMLTextAreaElement> & { value: string }) {
return (
<Editable
editing={
<textarea {...props} />
}
preview={
<FestaMarkdown markdown={props.value} />
}
/>
)
}

View file

@ -1,21 +0,0 @@
import { HTMLProps } from "react";
import { EditingContext } from "./EditingContext";
import { useDefinedContext } from "../../utils/definedContext";
import { Postcard } from "../postcard/Postcard";
/**
* Controlled input component which displays an `input[type="file"]` in editing mode, and is invisible in preview mode.
*
* As a side effect, it changes the {@link Postcard} to the input file when it's rendered.
*/
export function EditablePostcard({value, ...props}: HTMLProps<HTMLInputElement> & {value: string | undefined}) {
const [editing,] = useDefinedContext(EditingContext)
return <>
{editing ?
<input type="file" value={undefined} {...props}/>
: null}
<Postcard src={value}/>
</>
}

View file

@ -1,17 +1,19 @@
import { HTMLProps } from "react";
import { EditingContext } from "./EditingContext";
import { useDefinedContext } from "../../utils/definedContext";
import { Editable } from "./Editable";
/**
* Controlled input component which displays an `input[type="text"]` in editing mode, and a `span` displaying the input in preview mode.
*/
export function EditableText(props: HTMLProps<HTMLInputElement> & {value: string}) {
const [editing,] = useDefinedContext(EditingContext)
return editing ? (
<input type="text" {...props}/>
) : (
<span>{props.value}</span>
export function EditableText(props: HTMLProps<HTMLInputElement> & { value: string }) {
return (
<Editable
editing={
<input type="text" {...props} />
}
preview={
<span>{props.value}</span>
}
/>
)
}

View file

@ -7,13 +7,16 @@ type PostcardProps = {
src?: string | StaticImageData
}
export function Postcard({src}: PostcardProps) {
const {setPostcard} = useDefinedContext(PostcardContext)
export function Postcard({ src }: PostcardProps) {
const { setPostcard } = useDefinedContext(PostcardContext)
useEffect(
() => {
if(src) {
if(typeof src === "object") {
if (src) {
if (src === undefined) {
return
}
if (typeof src === "object") {
setPostcard(src.src)
}
else {

58
hooks/useFileState.ts Normal file
View file

@ -0,0 +1,58 @@
import { useReducer } from "react";
type FileState = {
value: string,
file: File,
url: URL,
}
type FileActionClear = { type: "clear" }
type FileActionSet = { type: "set", value: string, file: File }
type FileAction = FileActionClear | FileActionSet
function fileReducer(state, action) {
switch (action.type) {
case "clear":
case "set":
}
}
export function useFileState() {
const [value, dispatch] = useReducer(
)
/*
const [value, setValue] = useState<string>("")
const [file, setFile] = useState<File | null>(null)
const onChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value)
const file = e.target.files![0]
setFile(file ?? null)
},
[]
)
const doClear = useCallback(
() => {
setValue("")
setFile(null)
},
[]
)
return {
value,
file,
onChange,
doClear,
}
*/
}

View file

@ -10,25 +10,28 @@ import { EditableText } from "../../components/editable/EditableText";
import { ToolToggleEditing } from "../../components/tools/ToolToggleEditing";
import { EditingContext } from "../../components/editable/EditingContext";
import { database } from "../../utils/prismaClient";
import { EditablePostcard } from "../../components/editable/EditablePostcard";
import { EditableFile } from "../../components/editable/EditableFile";
import { ViewEvent } from "../../components/view/ViewEvent";
import { ToolToggleVisible } from "../../components/tools/ToolToggleVisible";
import { EditableDateRange } from "../../components/editable/EditableDateRange";
import { WorkInProgress } from "../../components/WorkInProgress";
import { FormDateRange } from "../../components/form/FormDateRange";
import { Postcard } from "../../components/postcard/Postcard";
import { useFileState } from "../../hooks/useFileState";
export async function getServerSideProps(context: NextPageContext) {
const slug = context.query.slug as string
if(typeof slug === "object") {
return {notFound: true}
if (typeof slug === "object") {
return { notFound: true }
}
const event = await database.event.findUnique({
where: {slug},
include: {creator: true}
where: { slug },
include: { creator: true }
})
if(!event) {
return {notFound: true}
if (!event) {
return { notFound: true }
}
return {
@ -41,79 +44,53 @@ export async function getServerSideProps(context: NextPageContext) {
type PageEventDetailProps = {
event: Event & {creator: User}
event: Event & { creator: User }
}
export default function PageEventDetail({event}: PageEventDetailProps) {
const {t} = useTranslation()
export default function PageEventDetail({ event }: PageEventDetailProps) {
const { t } = useTranslation()
const editState = useState<boolean>(false)
const [title, setTitle] = useState<string>(event.name)
const [description, setDescription] = useState<string>(event.description)
const [postcard, setPostcard] = useState<string | null>(event.postcard)
const postcard = useFileState()
const [startingAt, setStartingAt] = useState<string>(event.startingAt?.toISOString() ?? "")
const [endingAt, setEndingAt] = useState<string>(event.endingAt?.toISOString() ?? "")
const setPostcardBlob = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files![0]
if(!file) {
setPostcard(null)
return
}
const blobUrl = URL.createObjectURL(file)
setPostcard(blobUrl)
},
[]
)
return <>
<Head>
<title key="title">{event.name} - {t("siteTitle")}</title>
</Head>
<WorkInProgress/>
<WorkInProgress />
<Postcard src={postcard.file ? URL.createObjectURL(postcard.file) : event.postcard ?? undefined} />
<EditingContext.Provider value={editState}>
<ToolBar vertical="vadapt" horizontal="right">
<ToolToggleEditing/>
<ToolToggleVisible/>
<ToolToggleEditing />
<ToolToggleVisible />
</ToolBar>
<ViewEvent
title={
<EditableText
<EditableText
value={title}
onChange={(e: ChangeEvent<HTMLInputElement>) => setTitle(e.target.value)}
placeholder={t("eventDetailsTitlePlaceholder")}
/>
}
postcard={
<EditablePostcard
value={postcard ?? undefined}
onChange={setPostcardBlob}
<EditableFile
value={postcard.value}
onChange={postcard.onChange}
placeholder={t("eventDetailsPostcardPlaceholder")}
/>
}
description={<>
<EditableMarkdown
value={description}
<EditableMarkdown
value={description}
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => setDescription(e.target.value)}
rows={3}
placeholder={t("eventDetailsDescriptionPlaceholder")}
/>
</>}
daterange={
<EditableDateRange
startProps={{
value: startingAt ?? "",
onChange: (e: ChangeEvent<HTMLInputElement>) => setStartingAt(e.target.value)
}}
endProps={{
value: endingAt ?? "",
onChange: (e: ChangeEvent<HTMLInputElement>) => setEndingAt(e.target.value)
}}
/>
}
daterange={<></>}
/>
</EditingContext.Provider>
</>