|
import {JSX} from "preact"; |
|
import {useState, useEffect, useCallback} from "preact/hooks"; |
|
import "./styles.scss"; |
|
import style from "./style.module.scss"; |
|
import {Container} from "./components/container"; |
|
import {loadTopics, saveTopics} from "./utils/topics"; |
|
import {Topic} from "./utils/topics"; |
|
import {Topics} from "./components/topics"; |
|
import {Topic as TopicComponent} from "./components/topic"; |
|
import {Route, routes, RouteSetter} from "./utils/route"; |
|
import {Settings} from "./components/settings"; |
|
import {Layout} from "./components/layout"; |
|
import {fetchSettings, resetSettings, saveSettings} from "./utils/settings"; |
|
import {generateTopic, generatePosts} from "./utils/oobabooga"; |
|
import {tokenizeTopic} from "./utils/model"; |
|
|
|
export function App(): JSX.Element { |
|
const [route, _setRoute] = useState<Route>(routes.home); |
|
const [page, setPage] = useState<number>(0); |
|
const [topicId, setTopicId] = useState<Topic["id"] | null>(null); |
|
|
|
|
|
const [topics, setTopics] = useState<Topic[]>(loadTopics); |
|
const [latestGeneratedTopicId, setLatestGeneratedTopicId] = useState<string|null>(null); |
|
const [pendingGeneration, setPendingGeneration] = useState<boolean>(false); |
|
|
|
useEffect(() => { |
|
console.log("save !") |
|
saveTopics(topics); |
|
}, [topics]); |
|
|
|
const _generateTopic = async (postsCount: number) => { |
|
setPendingGeneration(true); |
|
const topic = await generateTopic(settings, postsCount); |
|
setLatestGeneratedTopicId(topic.id); |
|
setTopics(topics => [...topics, topic]); |
|
setPendingGeneration(false); |
|
} |
|
|
|
const addPosts = async (topicId: string, postsCount: number) => { |
|
setPendingGeneration(true); |
|
const posts = await generatePosts(settings, postsCount, topics.find(t => t.id === topicId)); |
|
const newTopics = [...topics]; |
|
const foundIndex = newTopics.findIndex(t => t.id === topicId); |
|
newTopics[foundIndex].posts = newTopics[foundIndex].posts.concat(posts); |
|
setTopics(newTopics) |
|
setPendingGeneration(false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
const [settings, setSettings] = useState(fetchSettings) |
|
|
|
useEffect(() => { |
|
saveSettings(settings); |
|
}, [settings]); |
|
|
|
const resetApp = () => { |
|
resetSettings(); |
|
setSettings(fetchSettings); |
|
setTopics([]); |
|
} |
|
|
|
const updateRoute = useCallback(() => { |
|
const url = new URL(window.location.href); |
|
const route = url.searchParams.get("route"); |
|
if (route && route in routes) { |
|
_setRoute(route as keyof typeof routes); |
|
} |
|
const page = url.searchParams.get("page"); |
|
if (page) { |
|
setPage(parseInt(page)); |
|
} |
|
const id = url.searchParams.get("id"); |
|
if (id) { |
|
setTopicId(id); |
|
} |
|
}, []); |
|
|
|
|
|
useEffect(() => { |
|
updateRoute() |
|
}, []); |
|
|
|
|
|
useEffect(() => { |
|
function listener() { |
|
updateRoute() |
|
} |
|
|
|
window.addEventListener('popstate', listener); |
|
|
|
return () => { |
|
window.removeEventListener('popstate', listener) |
|
} |
|
}, []); |
|
|
|
|
|
|
|
const setRoute: RouteSetter = useCallback((route: Route, page?: number, id?: string) => { |
|
const url = new URL(window.location.href); |
|
url.searchParams.set("route", route); |
|
_setRoute(route); |
|
|
|
if (page !== undefined) { |
|
url.searchParams.set("page", String(page)); |
|
setPage(page); |
|
} else { |
|
url.searchParams.delete("page"); |
|
setPage(0); |
|
} |
|
|
|
if (id !== undefined) { |
|
url.searchParams.set("id", id); |
|
setTopicId(id); |
|
} else { |
|
url.searchParams.delete("id"); |
|
setTopicId(null); |
|
} |
|
|
|
const newUrl = url.toString(); |
|
|
|
|
|
if (newUrl !== window.location.href) { |
|
window.history.pushState({}, "", newUrl); |
|
} |
|
}, []); |
|
|
|
let routeComponent: JSX.Element = undefined; |
|
let breadcrumbs: string = undefined; |
|
let title: string = undefined; |
|
|
|
switch (route) { |
|
case routes.home: |
|
routeComponent = <Topics |
|
topics={topics} |
|
setRoute={setRoute} |
|
settings={settings} |
|
setSettings={setSettings} |
|
generateTopic={_generateTopic} |
|
pendingGeneration={pendingGeneration} |
|
latestGeneratedTopicId={latestGeneratedTopicId} |
|
/> |
|
breadcrumbs = "accueil" |
|
title = "Liste des sujets" |
|
break; |
|
case routes.topic: |
|
if (topicId === null) { |
|
routeComponent = <div>Impossible d'afficher le sujet</div> |
|
breadcrumbs = "accueil" |
|
title = "Sujet" |
|
} else { |
|
if (topics === null) { |
|
routeComponent = <div>Chargement...</div> |
|
breadcrumbs = `accueil / sujet` |
|
title = `Chargement...` |
|
} else { |
|
const topic = topics.find(t => t.id === topicId); |
|
routeComponent = <TopicComponent |
|
topic={topic} |
|
settings={settings} |
|
setSettings={setSettings} |
|
addPosts={addPosts} |
|
pendingGeneration={pendingGeneration} |
|
/> |
|
breadcrumbs = `accueil / ${topic.title}` |
|
title = `Sujet : ${topic.title}` |
|
} |
|
} |
|
|
|
break; |
|
case routes.settings: |
|
routeComponent = <Settings settings={settings} setSettings={setSettings} resetApp={resetApp}/> |
|
breadcrumbs = "accueil / paramètres" |
|
title = "Paramètres" |
|
break; |
|
} |
|
|
|
return ( |
|
<> |
|
<header className={style.header}> |
|
<Container> |
|
<h1 className={style.logo}> |
|
<a href="#" onClick={e => { |
|
e.preventDefault(); |
|
setRoute(routes.home); |
|
}}> |
|
JVCGPT |
|
</a> |
|
</h1> |
|
</Container> |
|
</header> |
|
<main> |
|
<Container> |
|
<Layout |
|
breadcrumbs={breadcrumbs} |
|
title={title} |
|
setRoute={setRoute} |
|
> |
|
{routeComponent} |
|
</Layout> |
|
</Container> |
|
</main> |
|
</> |
|
); |
|
} |