diff options
Diffstat (limited to 'src')
28 files changed, 240 insertions, 176 deletions
diff --git a/src/app.html b/src/app.html deleted file mode 100644 index 26d754f..0000000 --- a/src/app.html +++ /dev/null @@ -1,23 +0,0 @@ -<!doctype html> -<html lang="en"> - <head> - <meta charset="utf-8"> - <meta name="description" content="A forum for the year 3000"> - <meta name="robots" content="noindex, nofollow" /> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <meta name="theme-color" content="#ffffff"> - - <title>Forum.</title> - - <link rel="manifest" href="/manifest.webmanifest"> - - %svelte.head% - </head> - <body> - <div id="forum">%svelte.body%</div> - <noscript> - <h1>Javascript Required.</h1> - <p>Please enable Javascript to use this forum.</p> - </noscript> - </body> -</html> diff --git a/src/components/forum/forum.svelte b/src/components/forum/forum.svelte new file mode 100644 index 0000000..7008e66 --- /dev/null +++ b/src/components/forum/forum.svelte @@ -0,0 +1,27 @@ +<script> + export let id; + + import { _ } from 'svelte-i18n'; + import { getForum } from '$stores/forum'; + import ErrorBlock from '../error_block/error_block.svelte'; + + import TopicSummary from '$components/topic_summary/topic_summary.svelte'; + + $: store = getForum(id); + $: forum = $store.data; +</script> + +{#if $store.loading} + <p>Loading...</p> +{/if} +{#if $store.error} + <ErrorBlock message={$_('forum_list.error.unavailable')} /> +{/if} +{#if forum} + <h1>{forum.glyph} {$_(forum.label)}</h1> + <ul> + {#each forum.topics as topic} + <TopicSummary topic={topic} /> + {/each} + </ul> +{/if} diff --git a/src/components/forum_list/forum_list.svelte b/src/components/forum_list/forum_list.svelte index 174ae25..46f60f0 100644 --- a/src/components/forum_list/forum_list.svelte +++ b/src/components/forum_list/forum_list.svelte @@ -1,15 +1,18 @@ <script> import { _ } from 'svelte-i18n'; - import { forums } from '../../stores/forums.js'; + import { forums } from '$stores/forums'; import ErrorBlock from '../error_block/error_block.svelte'; </script> <nav title="{$_('forum_list.title')}"> - {#if !$forums.length} + {#if $forums.loading} + <p>Loading...</p> + {/if} + {#if $forums.error} <ErrorBlock message={$_('forum_list.error.unavailable')} /> {/if} <ul> - {#each $forums as forum} + {#each $forums.data as forum} <li> <a href="/f/{forum.id}"> <span aria-hidden="true" class="navigation-glyph {forum.glyph}">{forum.glyph}</span> diff --git a/src/components/glyph/glyph.svelte b/src/components/glyph/glyph.svelte index e4b9166..98d1c6e 100644 --- a/src/components/glyph/glyph.svelte +++ b/src/components/glyph/glyph.svelte @@ -21,6 +21,8 @@ height: 48px; margin-top: 5px; width: 48px; + background-color: white; + padding: 2px; } span { diff --git a/src/components/topic_index/topic_index.svelte b/src/components/topic_index/topic_index.svelte deleted file mode 100644 index 3de83bb..0000000 --- a/src/components/topic_index/topic_index.svelte +++ /dev/null @@ -1,8 +0,0 @@ -<script> - export let params; - import ErrorBlock from '../error_block/error_block.svelte'; -</script> - -<ErrorBlock /> -<h1>Topic Index.</h1> -<p>This component lists topics for category or tag with id: {params.id}</p> diff --git a/src/components/topic_summary/topic_summary.svelte b/src/components/topic_summary/topic_summary.svelte new file mode 100644 index 0000000..deef3e2 --- /dev/null +++ b/src/components/topic_summary/topic_summary.svelte @@ -0,0 +1,17 @@ +<script> + export let topic; + + import { _ } from 'svelte-i18n'; + import { readableTime } from '../../utils/readable_time.js'; + + $: remainingTime = (topic.updated_at + topic.ttl) - Date.now(); + $: remaining = readableTime(remainingTime); +</script> + +<li class="h-entry" title="{$_('topic.title')}"> + <span class="p-name"><a class="u-url u-uid" title="{$_('topic.permalink_title')}" href="/t/{topic.id}">{topic.title}</a></span> + <span class="topic-ttl">({$_('topic.remaining_time', { values: { remaining: $_(remaining.label, { values: { count: remaining.count } }) } })})</span> +</li> + +<style> +</style> diff --git a/src/config/apollo.js b/src/config/apollo.js new file mode 100644 index 0000000..4cfca79 --- /dev/null +++ b/src/config/apollo.js @@ -0,0 +1,9 @@ +import { ApolloClient, InMemoryCache } from '@apollo/client/core'; +import { apollo as apolloConfig } from './config'; + +const cache = new InMemoryCache(); + +export const client = new ApolloClient({ + cache, + ...apolloConfig +}); diff --git a/src/config/config.js b/src/config/config.js index 6d819a3..0570e1c 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -1,4 +1,4 @@ -import { version as packageVersion } from '../../package.json'; +import { name, version as packageVersion } from '../../package.json'; /* * The main configuration object for the Forum frontend. These values @@ -6,6 +6,12 @@ import { version as packageVersion } from '../../package.json'; * file, otherwise it won't work. */ +export const apollo = { + uri: import.meta.env.VITE_APOLLO_SERVER, + name, + version: packageVersion +}; + export const socketServer = import.meta.env.FORUM_SOCKET_SERVER; export const version = packageVersion; diff --git a/src/config/env.dist b/src/config/env.dist index 0bcd782..1f3ff0c 100644 --- a/src/config/env.dist +++ b/src/config/env.dist @@ -1 +1 @@ -FORUM_SOCKET_SERVER=ws://location_of_socket_server +VITE_APOLLO_SERVER=http://location_of_apollo_server diff --git a/src/data/queries.js b/src/data/queries.js new file mode 100644 index 0000000..de223cb --- /dev/null +++ b/src/data/queries.js @@ -0,0 +1,29 @@ +import { gql } from '@apollo/client/core'; + +export const GET_FORUMS = gql` + query GetForums { + forums { + id + glyph + label + position + } + } +`; + +export const GET_FORUM = gql` + query GetForum($id: ID!) { + forum(id: $id) { + id + glyph + label + position + topics { + id + title, + updated_at, + ttl + } + } + } +`; diff --git a/src/forum.js b/src/forum.js new file mode 100644 index 0000000..1e8ec25 --- /dev/null +++ b/src/forum.js @@ -0,0 +1,6 @@ +import HMR from '@roxi/routify/hmr'; +import Forum from './Forum.svelte'; + +const app = HMR(Forum, { target: document.body }, 'forum'); + +export default app; diff --git a/src/forum.svelte b/src/forum.svelte index d2a6e1b..0eb4b90 100644 --- a/src/forum.svelte +++ b/src/forum.svelte @@ -1,63 +1,6 @@ <script> - import LightRouter from 'lightrouter'; - - // Initialize localization - - import './config/i18n'; - - // Global components - - import ForumList from './components/forum_list/forum_list.svelte'; - import Header from './components/header/header.svelte'; - import Footer from './components/footer/footer.svelte'; - - // Routed Components - import Author from './components/author/author.svelte'; - import Home from './components/home/home.svelte'; - import InvalidRoute from './components/invalid_route/invalid_route.svelte'; - import Post from './components/post/post.svelte'; - import Topic from './components/topic/topic.svelte'; - import TopicIndex from './components/topic_index/topic_index.svelte'; - - let page; - let params; - - // sets the route params and current page. - - const setRoute = function setRoute(targetPage) { - - return function (routerParams) { - - params = routerParams; - page = targetPage; - }; - }; - - const router = new LightRouter({ - routes: { - '': () => (page = Home) && true, - 'f/{id}': setRoute(TopicIndex), - 'g/{id}': setRoute(TopicIndex), - 'a/{id}': setRoute(Author), - 't/{id}': setRoute(Topic), - 'p/{id}': setRoute(Post), - '.*': setRoute(InvalidRoute) - } - }); - - router.run(); - + import { Router } from '@roxi/routify'; + import { routes } from '../.routify/routes'; </script> -<Header /> -<main> - <svelte:component this={ page } params={ params } /> -</main> -<ForumList /> -<Footer /> - -<style> - main { - grid-column: col-start 2 / span 11; - } -</style> +<Router {routes} /> diff --git a/src/routes/$error.svelte b/src/pages/_fallback.svelte index df7da3c..df7da3c 100644 --- a/src/routes/$error.svelte +++ b/src/pages/_fallback.svelte diff --git a/src/routes/$layout.svelte b/src/pages/_layout.svelte index a848a59..80cc29e 100644 --- a/src/routes/$layout.svelte +++ b/src/pages/_layout.svelte @@ -1,12 +1,6 @@ <script> import '$config/i18n'; - import { isLoading, waitLocale } from 'svelte-i18n'; - - export const load = function load() { - - return waitLocale; - }; - + import { isLoading } from 'svelte-i18n'; import ForumList from '$components/forum_list/forum_list.svelte'; import Header from '$components/header/header.svelte'; import Footer from '$components/footer/footer.svelte'; @@ -36,4 +30,8 @@ grid-gap: 20px; grid-auto-rows: minmax(24px, auto); } + + :global(body) { + background-color: whitesmoke; + } </style> diff --git a/src/pages/a/[id].svelte b/src/pages/a/[id].svelte new file mode 100644 index 0000000..9e6f184 --- /dev/null +++ b/src/pages/a/[id].svelte @@ -0,0 +1,5 @@ +<script> + import Author from '$components/author/author.svelte'; +</script> + +<Author/> diff --git a/src/pages/f/[id].svelte b/src/pages/f/[id].svelte new file mode 100644 index 0000000..0251694 --- /dev/null +++ b/src/pages/f/[id].svelte @@ -0,0 +1,6 @@ +<script> + import Forum from '$components/forum/forum.svelte'; + export let id; +</script> + +<Forum id={id}/> diff --git a/src/pages/g/[id].svelte b/src/pages/g/[id].svelte new file mode 100644 index 0000000..04fda92 --- /dev/null +++ b/src/pages/g/[id].svelte @@ -0,0 +1,6 @@ +<script> + export let id; +</script> + +<h1>Tag Index.</h1> +<p>This component lists topics for tag with id: {id}</p> diff --git a/src/routes/index.svelte b/src/pages/index.svelte index 7000137..7000137 100644 --- a/src/routes/index.svelte +++ b/src/pages/index.svelte diff --git a/src/pages/p/[id].svelte b/src/pages/p/[id].svelte new file mode 100644 index 0000000..9caab6f --- /dev/null +++ b/src/pages/p/[id].svelte @@ -0,0 +1,5 @@ +<script> + import Post from '$components/post/post.svelte'; +</script> + +<Post/> diff --git a/src/pages/t/[id].svelte b/src/pages/t/[id].svelte new file mode 100644 index 0000000..3fd9560 --- /dev/null +++ b/src/pages/t/[id].svelte @@ -0,0 +1,5 @@ +<script> + import Topic from '$components/topic/topic.svelte'; +</script> + +<Topic/> diff --git a/src/routes/a/[id].svelte b/src/routes/a/[id].svelte deleted file mode 100644 index 1b6c705..0000000 --- a/src/routes/a/[id].svelte +++ /dev/null @@ -1,10 +0,0 @@ -<script> - export async function load(ctx) { - let id = ctx.page.params.id - return { props: { id }} - } - - import Author from '$components/author/author.svelte'; -</script> - -<Author/> diff --git a/src/routes/f/[id].svelte b/src/routes/f/[id].svelte deleted file mode 100644 index d1fae4c..0000000 --- a/src/routes/f/[id].svelte +++ /dev/null @@ -1,9 +0,0 @@ -<script> - export async function load(ctx) { - let id = ctx.page.params.id - return { props: { id }} - } -</script> - -<h1>Forum Index.</h1> -<p>This component lists topics for forum with id: {id}</p> diff --git a/src/routes/g/[id].svelte b/src/routes/g/[id].svelte deleted file mode 100644 index d036c36..0000000 --- a/src/routes/g/[id].svelte +++ /dev/null @@ -1,9 +0,0 @@ -<script> - export async function load(ctx) { - let id = ctx.page.params.id - return { props: { id }} - } -</script> - -<h1>Tag Index.</h1> -<p>This component lists topics for tag with id: {id}</p> diff --git a/src/routes/p/[id].svelte b/src/routes/p/[id].svelte deleted file mode 100644 index b8a8165..0000000 --- a/src/routes/p/[id].svelte +++ /dev/null @@ -1,11 +0,0 @@ -<script> - export const load = function load(ctx) { - - const id = ctx.page.params.id; - return { props: { id } }; - }; - - import Post from '$components/post/post.svelte'; -</script> - -<Post/> diff --git a/src/routes/t/[id].svelte b/src/routes/t/[id].svelte deleted file mode 100644 index 8775cec..0000000 --- a/src/routes/t/[id].svelte +++ /dev/null @@ -1,10 +0,0 @@ -<script> - export async function load(ctx) { - let id = ctx.page.params.id - return { props: { id }} - } - - import Topic from '$components/topic/topic.svelte'; -</script> - -<Topic/> diff --git a/src/stores/forum.js b/src/stores/forum.js new file mode 100644 index 0000000..e7d335e --- /dev/null +++ b/src/stores/forum.js @@ -0,0 +1,39 @@ +import { ApolloError } from '@apollo/client/core'; +import { readable } from 'svelte/store'; +import { client } from '$config/apollo'; +import { GET_FORUM } from '$data/queries'; + +const internals = { + + // The exported data structure + + initialValue: { + loading: true, + data: null, + error: undefined + } +}; + +export const getForum = function forum(id) { + + return readable(internals.initialValue, (set) => { + + client.watchQuery({ query: GET_FORUM, variables: { id } }).subscribe((result) => { + + if (result.errors) { + const error = new ApolloError({ graphQLErrors: result.errors }); + return set({ + loading: false, + data: null, + error + }); + } + + set({ + loading: false, + data: result.data.forum, + error: undefined + }); + }); + }); +}; diff --git a/src/stores/forums.js b/src/stores/forums.js index e7acb60..ade01a7 100644 --- a/src/stores/forums.js +++ b/src/stores/forums.js @@ -1,36 +1,36 @@ +import { ApolloError } from '@apollo/client/core'; import { readable } from 'svelte/store'; +import { client } from '$config/apollo'; +import { GET_FORUMS } from '$data/queries'; const internals = { - // Constants - - kChangeFeedEventName: 'changefeed:forums', - // The exported data structure - forums: [], - - // Handles messages from the event - - handleChangeFeed(data) { - - // No old value == add - if (!data.old_val) { - return internals.forums.push(data.new_val); - } + initialValue: { + loading: true, + data: [], + error: undefined + } +}; - // We have an old value, let's find it. - const index = internals.forums.findIndex((element) => element.id === data.old_val.id); +export const forums = readable(internals.initialValue, (set) => { - if (index > -1) { - if (data.new_val) { - return internals.forums.splice(index, 1, data.new_val || undefined); - } + client.watchQuery({ query: GET_FORUMS }).subscribe((result) => { - return internals.forums.splice(index, 1); + if (result.errors) { + const error = new ApolloError({ graphQLErrors: result.errors }); + return set({ + loading: false, + data: [], + error + }); } - } -}; -export const forums = readable(internals.forums, (set) => { + set({ + loading: false, + data: result.data.forums, + error: undefined + }); + }); }); diff --git a/src/utils/readable_time.js b/src/utils/readable_time.js new file mode 100644 index 0000000..4476952 --- /dev/null +++ b/src/utils/readable_time.js @@ -0,0 +1,38 @@ +const internals = { + magnitudes: { + day: 86400000, + hour: 3600000, + minute: 60000, + second: 1000 + }, + labels: { + day: 'time.days', + hour: 'time.hours', + minute: 'time.minutes', + second: 'time.seconds' + }, + + makeTimeReadable(time, magnitude) { + + return { + count: Math.floor(time / internals.magnitudes[magnitude]), + label: internals.labels[magnitude] + }; + } +}; + +export const readableTime = function readableTime(time) { + + switch (true) { + case time >= internals.magnitudes.day: + return internals.makeTimeReadable(time, 'day'); + case time >= internals.magnitudes.hour: + return internals.makeTimeReadable(time, 'hour'); + case time >= internals.magnitudes.minute: + return internals.makeTimeReadable(time, 'minute'); + case time < 0: + return internals.makeTimeReadable(0, 'second'); + default: + return internals.makeTimeReadable(time, 'second'); + } +}; |