aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRuben Beltran del Rio <ruben@unlimited.pizza>2021-03-13 15:28:29 +0100
committerRuben Beltran del Rio <ruben@unlimited.pizza>2021-03-13 15:28:29 +0100
commitc1bc5993a694f6fd047a3881351827058042483b (patch)
tree3d64130e1e6a2f86f70981df28cf22cd535bf2b2 /src
parentbd8e98d7e24c4dbaee7db6ec7955f7c2f6d396a6 (diff)
Use routify and GraphQL server
Diffstat (limited to 'src')
-rw-r--r--src/app.html23
-rw-r--r--src/components/forum/forum.svelte27
-rw-r--r--src/components/forum_list/forum_list.svelte9
-rw-r--r--src/components/glyph/glyph.svelte2
-rw-r--r--src/components/topic_index/topic_index.svelte8
-rw-r--r--src/components/topic_summary/topic_summary.svelte17
-rw-r--r--src/config/apollo.js9
-rw-r--r--src/config/config.js8
-rw-r--r--src/config/env.dist2
-rw-r--r--src/data/queries.js29
-rw-r--r--src/forum.js6
-rw-r--r--src/forum.svelte63
-rw-r--r--src/pages/_fallback.svelte (renamed from src/routes/$error.svelte)0
-rw-r--r--src/pages/_layout.svelte (renamed from src/routes/$layout.svelte)12
-rw-r--r--src/pages/a/[id].svelte5
-rw-r--r--src/pages/f/[id].svelte6
-rw-r--r--src/pages/g/[id].svelte6
-rw-r--r--src/pages/index.svelte (renamed from src/routes/index.svelte)0
-rw-r--r--src/pages/p/[id].svelte5
-rw-r--r--src/pages/t/[id].svelte5
-rw-r--r--src/routes/a/[id].svelte10
-rw-r--r--src/routes/f/[id].svelte9
-rw-r--r--src/routes/g/[id].svelte9
-rw-r--r--src/routes/p/[id].svelte11
-rw-r--r--src/routes/t/[id].svelte10
-rw-r--r--src/stores/forum.js39
-rw-r--r--src/stores/forums.js48
-rw-r--r--src/utils/readable_time.js38
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');
+ }
+};