1
Fork 0
mirror of https://github.com/Steffo99/festa.git synced 2024-10-16 06:57:26 +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 { useTranslation } from "next-i18next"
import { useDefinedContext } from "../../../utils/definedContext"
import { EditingContext, EditingMode } from "../../generic/editable/base"
import { usePromise, UsePromiseStatus } from "../../generic/loading/promise"
import { FestaIcon } from "../../generic/renderers/fontawesome"
import { Tool } from "../../generic/toolbar/tool"
import cursor from "../../../styles/cursor.module.css"
import mood from "../../../styles/mood.module.css"
import { Dispatch } from "react"
export type ToolToggleEditingProps = {
editing: boolean,
setEditing: Dispatch<boolean>,
save: () => Promise<void>,
}
/**
* ToolBar {@link Tool} which switches between {@link EditingMode}s of the surrounding context.
*
* 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.
* ToolBar {@link Tool} which allows the user to start editing an event and then save their changes.
*/
export function ToolToggleEditing(props: ToolToggleEditingProps) {
export function ToolToggleEditing({ editing, setEditing, save: upperSave }: ToolToggleEditingProps) {
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) {
return (
@ -36,13 +34,13 @@ export function ToolToggleEditing(props: ToolToggleEditingProps) {
</Tool>
)
}
else if (editing === EditingMode.EDIT) {
else if (editing) {
return (
<Tool
aria-label={t("toolToggleEditingSave")}
onClick={() => {
save()
setEditing(EditingMode.VIEW)
setEditing(false)
}}
className={mood.positive}
>
@ -55,7 +53,7 @@ export function ToolToggleEditing(props: ToolToggleEditingProps) {
<Tool
aria-label={t("toolToggleEditingEdit")}
onClick={() => {
setEditing(EditingMode.EDIT)
setEditing(true)
}}
>
<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 useSWR from 'swr'
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 { ToolBar } from '../../components/generic/toolbar/bar'
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 { AuthContext } from '../../components/auth/base'
import { useDefinedContext } from '../../utils/definedContext'
import { ViewContent } from '../../components/generic/views/content'
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 { EventsActionEdit } from '../../components/events/actions/edit'
import { EventsActionView } from '../../components/events/actions/view'
export async function getServerSideProps(context: NextPageContext) {
@ -34,7 +25,7 @@ export async function getServerSideProps(context: NextPageContext) {
return {
props: {
slug,
event: await database.event.findUnique({ where: { slug } }),
fallbackData: await database.event.findUnique({ where: { slug } }),
...(await serverSideTranslations(context.locale ?? "it-IT", ["common"]))
}
}
@ -43,87 +34,51 @@ export async function getServerSideProps(context: NextPageContext) {
type PageEventProps = {
slug: string,
event: Event,
fallbackData: Event,
}
const PageEvent: NextPage<PageEventProps> = ({ slug, event }) => {
const PageEvent: NextPage<PageEventProps> = ({ slug, fallbackData }) => {
const { t } = useTranslation()
const swrHook = useSWR<Event>(`/api/events/${slug}`, { fallback: event })
const [auth,] = useDefinedContext(AuthContext)
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(
async () => {
if (swrHook.data === undefined) {
console.warn("[PageEvent] Tried to save while no data was available.")
return
}
await axios.patch(`/api/events/${slug}`, swrHook.data)
swrHook.mutate(swrHook.data)
console.debug("[PageEvent] Saved updated data successfully!")
const response = await axios.patch<Event>(`/api/events/${slug}`, data!)
mutate(response.data, { revalidate: false })
},
[axios, swrHook]
[axios, data]
)
const eventName = data?.name ?? slug
const eventPostcard = data?.postcard || defaultPostcard
const eventCanEdit = auth && data && auth.userId === data.creatorId
return <>
<Head>
<title key="title">{swrHook.data?.name ?? slug} - {t("siteTitle")}</title>
<title key="title">{eventName} - {t("siteTitle")}</title>
</Head>
<Postcard
src={swrHook.data?.postcard || defaultPostcard}
/>
<Postcard src={eventPostcard} />
<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">
<ToolToggleVisibility />
{swrHook.data && auth?.userId === swrHook.data?.creatorId &&
<ToolToggleEditing
save={save}
/>
}
</ToolBar>
</EditingContextProvider>
<ToolBar vertical="vadapt" horizontal="right">
<ToolToggleVisibility />
{eventCanEdit &&
<ToolToggleEditing
editing={eventEditing}
setEditing={eventSetEditing}
save={save}
/>
}
</ToolBar>
{eventEditing ?
<EventsActionEdit data={data!} mutate={mutate} />
:
<EventsActionView data={data!} />
}
</>
}