1
Fork 0
mirror of https://github.com/Steffo99/festa.git synced 2024-12-22 22:54:22 +00:00

Rework PageEvent to not use editables

This commit is contained in:
Steffo 2022-07-19 20:25:11 +02:00
parent b94b28853e
commit 729f136eb7
Signed by: steffo
GPG key ID: 6965406171929D01
4 changed files with 108 additions and 89 deletions

View file

@ -0,0 +1,42 @@
import { Event } from "@prisma/client"
import { useMemo } from "react"
import { KeyedMutator } from "swr"
import { ViewContent } from "../../generic/views/content"
export type EventsActionViewProps = {
data: Event,
mutate: KeyedMutator<Event>,
}
export const EventsActionEdit = ({ data, mutate }: EventsActionViewProps) => {
return (
<ViewContent
title={
useMemo(
() => (
<input
type="text"
value={data.name}
onChange={e => mutate({ ...data, name: e.target.value }, { revalidate: false })}
/>
),
[data.name]
)
}
content={<>
{useMemo(
() => (
<textarea
rows={12}
value={data.description}
onChange={e => mutate({ ...data, description: e.target.value }, { revalidate: false })}
/>
),
[data.description]
)}
</>}
/>
)
}

View file

@ -0,0 +1,24 @@
import { Event } from "@prisma/client"
import { memo } from "react"
import { FestaMarkdownRenderer } from "../../generic/renderers/markdown"
import { ViewContent } from "../../generic/views/content"
export type EventsActionViewProps = {
data: Event,
}
export const EventsActionView = memo(({ data }: EventsActionViewProps) => {
return (
<ViewContent
title={<div style={{ padding: "10px" }}>
{data.name}
</div>}
content={<div>
<FestaMarkdownRenderer code={data.description} />
</div>}
/>
)
})
EventsActionView.displayName = "EventsActionView"

View file

@ -1,29 +1,27 @@
import { faAsterisk, faPencil, faSave } from "@fortawesome/free-solid-svg-icons" import { faAsterisk, faPencil, faSave } from "@fortawesome/free-solid-svg-icons"
import { useTranslation } from "next-i18next" import { useTranslation } from "next-i18next"
import { useDefinedContext } from "../../../utils/definedContext"
import { EditingContext, EditingMode } from "../../generic/editable/base"
import { usePromise, UsePromiseStatus } from "../../generic/loading/promise" import { usePromise, UsePromiseStatus } from "../../generic/loading/promise"
import { FestaIcon } from "../../generic/renderers/fontawesome" import { FestaIcon } from "../../generic/renderers/fontawesome"
import { Tool } from "../../generic/toolbar/tool" import { Tool } from "../../generic/toolbar/tool"
import cursor from "../../../styles/cursor.module.css" import cursor from "../../../styles/cursor.module.css"
import mood from "../../../styles/mood.module.css" import mood from "../../../styles/mood.module.css"
import { Dispatch } from "react"
export type ToolToggleEditingProps = { export type ToolToggleEditingProps = {
editing: boolean,
setEditing: Dispatch<boolean>,
save: () => Promise<void>, save: () => Promise<void>,
} }
/** /**
* ToolBar {@link Tool} which switches between {@link EditingMode}s of the surrounding context. * ToolBar {@link Tool} which allows the user to start editing an event and then save their changes.
*
* It calls an async function to save data when switching from edit mode to view mode, preventing the user from switching back again until the data is saved, but allowing them to view the updated resource.
*/ */
export function ToolToggleEditing(props: ToolToggleEditingProps) { export function ToolToggleEditing({ editing, setEditing, save: upperSave }: ToolToggleEditingProps) {
const { t } = useTranslation() const { t } = useTranslation()
const [editing, setEditing] = useDefinedContext(EditingContext)
const { run: save, status: saveStatus } = usePromise<void, void>(props.save) const { run: save, status: saveStatus } = usePromise<void, void>(upperSave)
if (saveStatus === UsePromiseStatus.PENDING) { if (saveStatus === UsePromiseStatus.PENDING) {
return ( return (
@ -36,13 +34,13 @@ export function ToolToggleEditing(props: ToolToggleEditingProps) {
</Tool> </Tool>
) )
} }
else if (editing === EditingMode.EDIT) { else if (editing) {
return ( return (
<Tool <Tool
aria-label={t("toolToggleEditingSave")} aria-label={t("toolToggleEditingSave")}
onClick={() => { onClick={() => {
save() save()
setEditing(EditingMode.VIEW) setEditing(false)
}} }}
className={mood.positive} className={mood.positive}
> >
@ -55,7 +53,7 @@ export function ToolToggleEditing(props: ToolToggleEditingProps) {
<Tool <Tool
aria-label={t("toolToggleEditingEdit")} aria-label={t("toolToggleEditingEdit")}
onClick={() => { onClick={() => {
setEditing(EditingMode.EDIT) setEditing(true)
}} }}
> >
<FestaIcon icon={faPencil} /> <FestaIcon icon={faPencil} />

View file

@ -6,8 +6,6 @@ import defaultPostcard from "../../public/postcards/adi-goldstein-Hli3R6LKibo-un
import { Postcard } from '../../components/postcard/changer' import { Postcard } from '../../components/postcard/changer'
import useSWR from 'swr' import useSWR from 'swr'
import { Event } from '@prisma/client' import { Event } from '@prisma/client'
import { EditableMarkdown, EditableText } from '../../components/generic/editable/inputs'
import { EditingContext, EditingMode } from '../../components/generic/editable/base'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { ToolBar } from '../../components/generic/toolbar/bar' import { ToolBar } from '../../components/generic/toolbar/bar'
import { ToolToggleEditing } from '../../components/events/toolbar/toolToggleEditing' import { ToolToggleEditing } from '../../components/events/toolbar/toolToggleEditing'
@ -15,17 +13,10 @@ import { ToolToggleVisibility } from '../../components/postcard/toolbar/toolTogg
import { WIPBanner } from '../../components/generic/wip/banner' import { WIPBanner } from '../../components/generic/wip/banner'
import { AuthContext } from '../../components/auth/base' import { AuthContext } from '../../components/auth/base'
import { useDefinedContext } from '../../utils/definedContext' import { useDefinedContext } from '../../utils/definedContext'
import { ViewContent } from '../../components/generic/views/content'
import { useAxios } from '../../components/auth/requests' import { useAxios } from '../../components/auth/requests'
import { faAsterisk } from '@fortawesome/free-solid-svg-icons'
import { FestaIcon } from '../../components/generic/renderers/fontawesome'
import { promiseMultiplexer, usePromise, UsePromiseStatus } from '../../components/generic/loading/promise'
import { EditingContextProvider } from '../../components/generic/editable/provider'
import { swrMultiplexer } from '../../components/generic/loading/swr'
import { LoadingMain, LoadingInline } from '../../components/generic/loading/renderers'
import { ViewNotice } from '../../components/generic/views/notice'
import { ErrorBlock } from '../../components/generic/errors/renderers'
import { database } from '../../utils/prismaClient' import { database } from '../../utils/prismaClient'
import { EventsActionEdit } from '../../components/events/actions/edit'
import { EventsActionView } from '../../components/events/actions/view'
export async function getServerSideProps(context: NextPageContext) { export async function getServerSideProps(context: NextPageContext) {
@ -34,7 +25,7 @@ export async function getServerSideProps(context: NextPageContext) {
return { return {
props: { props: {
slug, slug,
event: await database.event.findUnique({ where: { slug } }), fallbackData: await database.event.findUnique({ where: { slug } }),
...(await serverSideTranslations(context.locale ?? "it-IT", ["common"])) ...(await serverSideTranslations(context.locale ?? "it-IT", ["common"]))
} }
} }
@ -43,87 +34,51 @@ export async function getServerSideProps(context: NextPageContext) {
type PageEventProps = { type PageEventProps = {
slug: string, slug: string,
event: Event, fallbackData: Event,
} }
const PageEvent: NextPage<PageEventProps> = ({ slug, event }) => { const PageEvent: NextPage<PageEventProps> = ({ slug, fallbackData }) => {
const { t } = useTranslation() const { t } = useTranslation()
const swrHook = useSWR<Event>(`/api/events/${slug}`, { fallback: event })
const [auth,] = useDefinedContext(AuthContext)
const axios = useAxios() const axios = useAxios()
const { data, mutate } = useSWR<Event>(`/api/events/${slug}`, { fallbackData })
const [auth,] = useDefinedContext(AuthContext)
const [eventEditing, eventSetEditing] = useState<boolean>(false)
const save = useCallback( const save = useCallback(
async () => { async () => {
if (swrHook.data === undefined) { const response = await axios.patch<Event>(`/api/events/${slug}`, data!)
console.warn("[PageEvent] Tried to save while no data was available.") mutate(response.data, { revalidate: false })
return
}
await axios.patch(`/api/events/${slug}`, swrHook.data)
swrHook.mutate(swrHook.data)
console.debug("[PageEvent] Saved updated data successfully!")
}, },
[axios, swrHook] [axios, data]
) )
const eventName = data?.name ?? slug
const eventPostcard = data?.postcard || defaultPostcard
const eventCanEdit = auth && data && auth.userId === data.creatorId
return <> return <>
<Head> <Head>
<title key="title">{swrHook.data?.name ?? slug} - {t("siteTitle")}</title> <title key="title">{eventName} - {t("siteTitle")}</title>
</Head> </Head>
<Postcard <Postcard src={eventPostcard} />
src={swrHook.data?.postcard || defaultPostcard}
/>
<WIPBanner /> <WIPBanner />
<EditingContextProvider>
{swrMultiplexer({
hook: swrHook,
loading: () => (
<ViewNotice
notice={
<LoadingMain text={t("eventLoading")} />
}
/>
),
ready: (data) => (
<ViewContent
title={
<EditableText
value={data?.name ?? slug}
onChange={e => swrHook.mutate(async state => state ? { ...state, name: e.target.value } : undefined, { revalidate: false })}
placeholder={t("eventTitlePlaceholder")}
/>
}
content={
<EditableMarkdown
value={data?.description ?? ""}
onChange={e => swrHook.mutate(async state => state ? { ...state, description: e.target.value } : undefined, { revalidate: false })}
placeholder={t("eventDescriptionPlaceholder")}
/>
}
/>
),
error: (error) => (
<ViewNotice
notice={
<ErrorBlock
text={t("eventError")}
error={error}
/>
}
/>
)
})}
<ToolBar vertical="vadapt" horizontal="right"> <ToolBar vertical="vadapt" horizontal="right">
<ToolToggleVisibility /> <ToolToggleVisibility />
{swrHook.data && auth?.userId === swrHook.data?.creatorId && {eventCanEdit &&
<ToolToggleEditing <ToolToggleEditing
editing={eventEditing}
setEditing={eventSetEditing}
save={save} save={save}
/> />
} }
</ToolBar> </ToolBar>
</EditingContextProvider> {eventEditing ?
<EventsActionEdit data={data!} mutate={mutate} />
:
<EventsActionView data={data!} />
}
</> </>
} }