--- /dev/null
+module.exports = {
+ root: true,
+ extends: ['@hapi/eslint-config-hapi'],
+ plugins: ['svelte3'],
+ overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
+ parserOptions: {
+ sourceType: 'module',
+ ecmaVersion: 2019
+ },
+ env: {
+ browser: true,
+ es2017: true,
+ node: true
+ },
+ rules: {
+ indent: [2, 2],
+ 'no-undef': 2,
+ 'require-yield': 0
+ }
+};
--- /dev/null
+engine-strict=true
const halfWidth = originalWidth / 2;
const halfHeight = originalHeight / 2;
// const padding = t < 0.8 ? halfWidth * (1 - t) / 0.8 : halfWidth / 2 + 1;
- const height = t <= 0.2 ? (originalHeight * t / 0.2) : originalHeight;
- const marginY = t <= 0.2 ? (halfHeight * (1 - t / 0.2)) : 0;
+ const height = t <= 0.2 ? (originalHeight * t) / 0.2 : originalHeight;
+ const marginY = t <= 0.2 ? halfHeight * (1 - t / 0.2) : 0;
const width = t > 0.2 ? ((t - 0.2) / 0.8) * originalWidth : 0;
- const marginX = t > 0.2 ? (1 - ((t - 0.2) / 0.8)) * halfWidth : halfWidth;
+ const marginX = t > 0.2 ? (1 - (t - 0.2) / 0.8) * halfWidth : halfWidth;
return `width: ${width}px; height: ${height}px; margin: ${marginY}px ${marginX}px`;
}
--- /dev/null
+:root {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
+ 'Open Sans', 'Helvetica Neue', sans-serif;
+}
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link rel="icon" href="/favicon.ico" />
+ <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" />
+
+ %svelte.head%
+
+ <link rel="manifest" href="/manifest.webmanifest" />
+ <link rel="stylesheet" href="/global.css" />
+ </head>
+ <body>
+ <noscript>
+ <h1>Javascript Required.</h1>
+ <p>Please enable Javascript to use this forum.</p>
+ </noscript>
+ <div id="forum">%svelte.body%</div>
+ </body>
+</html>
<script>
- import { _ } from 'svelte-i18n';
- export let message;
+ import { _ } from 'svelte-i18n';
+ export let message;
- import { blink } from '$/animations/blink';
+ import { blink } from '$/animations/blink';
</script>
<div transition:blink>
- <h2>{$_('error.generic.title')}</h2>
- <p>{message || $_('error.generic.message')}</p>
+ <h2>{$_('error.generic.title')}</h2>
+ <p>{message || $_('error.generic.message')}</p>
</div>
<style>
- div {
- background: repeating-linear-gradient( -45deg, red, red 5px, black 5px, black 10px );
- border: 5px solid red;
- color: yellow;
- font-family: 'ヒラギノ角ゴ ProN' , 'Hiragino Kaku Gothic ProN' , '游ゴシック' , '游ゴシック体' , YuGothic , 'Yu Gothic' , 'メイリオ' , Meiryo , 'MS ゴシック' , 'MS Gothic' , HiraKakuProN-W3 , 'TakaoExゴシック' , TakaoExGothic , 'MotoyaLCedar' , 'Droid Sans Japanese' , sans-serif;
- margin: 0 10px 0 0;
- text-align: center;
- overflow: hidden;
- }
+ div {
+ background: repeating-linear-gradient(-45deg, red, red 5px, black 5px, black 10px);
+ border: 5px solid red;
+ color: yellow;
+ font-family: 'ヒラギノ角ゴ ProN', 'Hiragino Kaku Gothic ProN', '游ゴシック', '游ゴシック体',
+ YuGothic, 'Yu Gothic', 'メイリオ', Meiryo, 'MS ゴシック', 'MS Gothic', HiraKakuProN-W3,
+ 'TakaoExゴシック', TakaoExGothic, 'MotoyaLCedar', 'Droid Sans Japanese', sans-serif;
+ margin: 0 10px 0 0;
+ text-align: center;
+ overflow: hidden;
+ }
- h2, p {
- background-color: black;
- font-size: 1em;
- }
+ h2,
+ p {
+ background-color: black;
+ font-size: 1em;
+ }
- h2 {
- text-transform: uppercase;
- margin: 100px 5px 10px;
- }
+ h2 {
+ text-transform: uppercase;
+ margin: 100px 5px 10px;
+ }
- p {
- margin: 10px 5px 100px;
- }
+ p {
+ margin: 10px 5px 100px;
+ }
</style>
-
<script>
- import { _ } from 'svelte-i18n';
+ import { _ } from 'svelte-i18n';
- import LanguageSelector from '$/components/language_selector/language_selector.svelte';
+ import LanguageSelector from '$/components/language_selector/language_selector.svelte';
- const licenseUrl = 'https://gitlab.com/rbdr/forum/';
+ const licenseUrl = 'https://gitlab.com/rbdr/forum/';
</script>
-<footer title="{$_('footer.title')}">
- <ul>
- <li>{@html $_('footer.license', { values: { licenseUrl } })}</li>
- <li>{$_('footer.choose_language')}: <LanguageSelector /></li>
- </ul>
+<footer title={$_('footer.title')}>
+ <ul>
+ <li>{@html $_('footer.license', { values: { licenseUrl } })}</li>
+ <li>{$_('footer.choose_language')}: <LanguageSelector /></li>
+ </ul>
</footer>
<style>
- footer {
- grid-column: col-start 1 / span 12;
- border-top: 1px solid black;
- }
+ footer {
+ grid-column: col-start 1 / span 12;
+ border-top: 1px solid black;
+ }
- ul {
- padding: 0;
- }
+ ul {
+ padding: 0;
+ }
- li {
- display: inline;
- margin: 5px;
- }
-
- a {
- text-decoration: none;
- line-height: 3em;
- display: inline-block;
- }
+ li {
+ display: inline;
+ margin: 5px;
+ }
</style>
<script>
- export let id;
+ export let forum;
- import { _ } from 'svelte-i18n';
- import { getForum } from '$/stores/forum';
- import ErrorBlock from '$/components/error_block/error_block.svelte';
- import Loader from '$/components/loader/loader.svelte';
-
- import TopicSummary from '$/components/topic_summary/topic_summary.svelte';
-
- $: store = getForum(id);
- $: forum = $store.data;
+ import { _ } from 'svelte-i18n';
+ import TopicSummary from '$/components/topic_summary/topic_summary.svelte';
</script>
-{#if $store.loading}
- <Loader />
-{/if}
-{#if $store.error}
- <ErrorBlock message={$_('forum.error.unavailable')} />
-{/if}
-{#if forum}
- <h1>{forum.glyph} {$_(forum.label)}</h1>
- <ul>
- {#each forum.topics as topic}
- <TopicSummary topic={topic} />
- {/each}
- </ul>
-{/if}
+<h1>{forum.glyph} {$_(forum.label)}</h1>
+<ul>
+ {#each forum.topics as topic}
+ <TopicSummary {topic} />
+ {/each}
+</ul>
<script>
- import { _ } from 'svelte-i18n';
- import { forums } from '$/stores/forums';
- import Loader from '$/components/loader/loader.svelte';
- import ErrorBlock from '$/components/error_block/error_block.svelte';
+ import { _ } from 'svelte-i18n';
+ import { forums } from '$/stores/forums';
+ import Loader from '$/components/loader/loader.svelte';
+ import ErrorBlock from '$/components/error_block/error_block.svelte';
</script>
-<nav title="{$_('forum_list.title')}">
- {#if $forums.loading}
- <Loader />
- {/if}
- {#if $forums.error}
- <ErrorBlock message={$_('forum_list.error.unavailable')} />
- {/if}
- <ul>
- {#each $forums.data as forum}
- <li>
- <a href="/f/{forum.id}">
- <span aria-hidden="true" class="navigation-glyph {forum.glyph}">{forum.glyph}</span>
- <span class="navigation-label">{$_(forum.label)}</span>
- </a>
- </li>
- {/each}
- </ul>
+<nav title={$_('forum_list.title')}>
+ {#if $forums.loading}
+ <Loader />
+ {/if}
+ {#if $forums.error}
+ <ErrorBlock message={$_('forum_list.error.unavailable')} />
+ {/if}
+ <ul>
+ {#each $forums.data as forum}
+ <li>
+ <a href="/f/{forum.id}">
+ <span aria-hidden="true" class="navigation-glyph {forum.glyph}">{forum.glyph}</span>
+ <span class="navigation-label">{$_(forum.label)}</span>
+ </a>
+ </li>
+ {/each}
+ </ul>
</nav>
<style>
- nav {
- grid-column: col-start 1;
- grid-row: 2;
- border-right: 1px solid black;
- }
+ nav {
+ grid-column: col-start 1;
+ grid-row: 2;
+ border-right: 1px solid black;
+ }
- ul {
- padding: 0;
- }
+ ul {
+ padding: 0;
+ }
- li {
- display: block;
- text-align: left;
- margin-bottom: 20px;
- }
+ li {
+ display: block;
+ text-align: left;
+ margin-bottom: 20px;
+ }
- .navigation-glyph {
- font-size: 1.5rem;
- display: block;
- }
+ .navigation-glyph {
+ font-size: 1.5rem;
+ display: block;
+ }
- .☽ {
- font-size: 2rem;
- }
+ .☽ {
+ font-size: 2rem;
+ }
- a {
- text-decoration: none;
- }
+ a {
+ text-decoration: none;
+ }
</style>
<script>
- import { _ } from 'svelte-i18n';
- import { getGlyphHash } from '$/utils/glyph_hash';
+ import { _ } from 'svelte-i18n';
+ import { getGlyphHash } from '$/utils/glyph_hash';
- export let uuid;
+ export let uuid;
</script>
-<div class="glyphicon" aria-hidden="true" title="{$_('glyph.title')}">
- {#each getGlyphHash(uuid) as fragment}
- <span class="{fragment.glyph}" style="color: {fragment.color} ">
- {fragment.glyph}
- </span>
- {/each}
+<div class="glyphicon" aria-hidden="true" title={$_('glyph.title')}>
+ {#each getGlyphHash(uuid) as fragment}
+ <span class={fragment.glyph} style="color: {fragment.color} ">
+ {fragment.glyph}
+ </span>
+ {/each}
</div>
<style>
- .glyphicon {
- border: 1px solid black;
- display: inline-block;
- font-size: 1.4rem;
- height: 48px;
- margin-top: 5px;
- width: 48px;
- background-color: white;
- padding: 2px;
- }
+ .glyphicon {
+ border: 1px solid black;
+ display: inline-block;
+ font-size: 1.4rem;
+ height: 48px;
+ margin-top: 5px;
+ width: 48px;
+ background-color: white;
+ padding: 2px;
+ }
- span {
- display: block;
- float: left;
- width: 24px;
- height: 24px;
- text-align: center;
- line-height: 24px;
- }
+ span {
+ display: block;
+ float: left;
+ width: 24px;
+ height: 24px;
+ text-align: center;
+ line-height: 24px;
+ }
- .☽ {
- font-size: 2rem;
- line-height: 19px;
- }
+ .☽ {
+ font-size: 2rem;
+ line-height: 19px;
+ }
</style>
<script>
- import { _ } from 'svelte-i18n';
+ import { _ } from 'svelte-i18n';
+ import { version } from '$/config/config';
+ import { actions } from '$/stores/actions';
- import { params } from '@roxi/routify';
- import { version } from '$/config/config';
+ $: topic_id = $actions.topic_id;
</script>
-<header title="{$_('header.title')}">
- <ul>
- <li><strong><a href="/" aria-label="{$_('header.long_version', { values: { version } })}">{$_('header.short_version', { values: { version } })}</a></strong></li>
- <li><a href="/new" aria-label="{$_('header.action.new.title')}">{@html $_('header.action.new.display')}</a></li>
- {#if $params.topic_id}
- <li><a href="/reply/{$params.topic_id}" aria-label="{$_('header.action.reply.title')}">{@html $_('header.action.reply.display')}</a></li>
- {/if}
- <li><a href="/search" aria-label="{$_('header.action.search.title')}">{@html $_('header.action.search.display')}</a></li>
- <li><a href="/logout" aria-label="{$_('header.action.log_out.title')}">{@html $_('header.action.log_out.display')}</a></li>
- </ul>
+<header title={$_('header.title')}>
+ <ul>
+ <li>
+ <strong
+ ><a href="/" aria-label={$_('header.long_version', { values: { version } })}
+ >{$_('header.short_version', { values: { version } })}</a
+ ></strong
+ >
+ </li>
+ <li>
+ <a href="/new" aria-label={$_('header.action.new.title')}
+ >{@html $_('header.action.new.display')}</a
+ >
+ </li>
+ {#if topic_id}
+ <li>
+ <a href="/reply/{topic_id}" aria-label={$_('header.action.reply.title')}
+ >{@html $_('header.action.reply.display')}</a
+ >
+ </li>
+ {/if}
+ <li>
+ <a href="/search" aria-label={$_('header.action.search.title')}
+ >{@html $_('header.action.search.display')}</a
+ >
+ </li>
+ <li>
+ <a href="/logout" aria-label={$_('header.action.log_out.title')}
+ >{@html $_('header.action.log_out.display')}</a
+ >
+ </li>
+ </ul>
</header>
<style>
- header {
- grid-column: col-start 1 / span 12;
- border-bottom: 1px solid black;
- }
+ header {
+ grid-column: col-start 1 / span 12;
+ border-bottom: 1px solid black;
+ }
- ul {
- padding: 0;
- }
+ ul {
+ padding: 0;
+ }
- .action-key {
- font-weight: bold;
- text-decoration: underline;
- }
+ li {
+ display: inline;
+ margin: 5px;
+ }
- li {
- display: inline;
- margin: 5px;
- }
-
- a {
- text-decoration: none;
- line-height: 3em;
- display: inline-block;
- }
-
- strong a {
- color: blue;
- text-decoration: underline;
- }
+ a {
+ text-decoration: none;
+ line-height: 3em;
+ display: inline-block;
+ }
+ strong a {
+ color: blue;
+ text-decoration: underline;
+ }
</style>
<script>
- import { _ } from 'svelte-i18n';
+ import { _ } from 'svelte-i18n';
</script>
<h1>{$_('home.title')}</h1>
<script>
- import { _ } from 'svelte-i18n';
+ import { _ } from 'svelte-i18n';
</script>
<h1>{$_('error.invalid_url.title')}</h1>
<script>
- import { locale, locales } from 'svelte-i18n';
- import { getLangNameFromCode } from 'language-name-map';
+ import { locale, locales } from 'svelte-i18n';
+ import { getLangNameFromCode } from 'language-name-map';
- $: namedLocales = $locales
- .map((code) => ({
- code,
- ...getLangNameFromCode(code)
- }))
- .sort((a, b) => a.native - b.native);
+ $: namedLocales = $locales
+ .map((code) => ({
+ code,
+ ...getLangNameFromCode(code)
+ }))
+ .sort((a, b) => a.native - b.native);
- let selected = $locale;
+ let selected = $locale;
- $: {
- console.log(`the current locale is ${selected}`);
- locale.set(selected);
- }
+ $: {
+ console.log(`the current locale is ${selected}`);
+ locale.set(selected);
+ }
</script>
<select bind:value={selected}>
- {#each namedLocales as namedLocale}
- <option value="{ namedLocale.code }">{ namedLocale.native }</option>
- {/each}
+ {#each namedLocales as namedLocale}
+ <option value={namedLocale.code}>{namedLocale.native}</option>
+ {/each}
</select>
<style>
</style>
-
<script>
- import { _ } from 'svelte-i18n';
+ import { _ } from 'svelte-i18n';
</script>
<p>{$_('loader.message')}</p>
<script>
- export let id;
+ export let post;
+ export let index = 0;
+ export let count = 1;
- import { _ } from 'svelte-i18n';
+ import { _ } from 'svelte-i18n';
+ import Glyph from '$/components/glyph/glyph.svelte';
- import { getPost } from '$/stores/post';
- import PostContent from '$/components/post_content/post_content.svelte';
- import ErrorBlock from '$/components/error_block/error_block.svelte';
- import Loader from '$/components/loader/loader.svelte';
-
- $: store = getPost(id);
- $: post = $store.data;
+ const timestampToISO = (timestamp) => new Date(timestamp).toISOString();
</script>
-{#if $store.loading}
- <Loader />
-{/if}
-{#if $store.error}
- <ErrorBlock message={$_('post.error.unavailable')} />
-{/if}
-{#if post}
- <PostContent post={post} />
-{/if}
+<aside
+ class="post-meta"
+ title={$_('post.metadata_title', { values: { count: index + 1, total: count } })}
+>
+ <Glyph uuid={post.author.id} />
+ <span class="h-card">
+ {$_('post.author_credit')}
+ <a href="/a/{post.author.handle}" class="p-nickname u-url">{post.author.handle}</a>.
+ </span>
+ <time role="presentation" class="dt-published" datetime={timestampToISO(post.created_at)}>
+ <a title={$_('post.permalink_title')} href="/p/{post.id}">
+ {timestampToISO(post.created_at)}
+ </a>
+ </time>
+ {#if post.topic}
+ <span class="h-card">
+ ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.)
+ </span>
+ {/if}
+</aside>
+<article
+ class="e-content"
+ title={$_('post.title', {
+ values: { count: index + 1, total: count, author: post.author.handle }
+ })}
+>
+ {post.text}
+</article>
+<hr />
+
+<style>
+ .post-meta * {
+ vertical-align: top;
+ }
+
+ article {
+ white-space: pre;
+ }
+</style>
<script>
- export let post;
- export let index = 0;
- export let count = 1;
+ export let post;
+ export let index = 0;
+ export let count = 1;
- import { _ } from 'svelte-i18n';
- import Glyph from '$/components/glyph/glyph.svelte';
+ import { _ } from 'svelte-i18n';
+ import Glyph from '$/components/glyph/glyph.svelte';
- const timestampToISO = (timestamp) => (new Date(timestamp)).toISOString();
+ const timestampToISO = (timestamp) => new Date(timestamp).toISOString();
</script>
- <aside class="post-meta" title="{$_('post.metadata_title', { values: { count: index + 1, total: count } })}">
- <Glyph uuid={post.author.id} />
- <span class="h-card">
- {$_('post.author_credit')} <a href="/a/{post.author.handle}" class="p-nickname u-url">{post.author.handle}</a>.
- </span>
- <time role="presentation" class="dt-published" datetime="{timestampToISO(post.created_at)}">
- <a title="{$_('post.permalink_title')}" href="/p/{post.id}">
- {timestampToISO(post.created_at)}
- </a>
- </time>
- {#if post.topic}
- <span class="h-card">
- ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.)
- </span>
- {/if}
- </aside>
- <article class="e-content" title="{$_('post.title', { values: { count: index + 1, total: count, author: post.author.handle } })}">
- {post.text}
- </article>
- <hr/>
+
+<aside
+ class="post-meta"
+ title={$_('post.metadata_title', { values: { count: index + 1, total: count } })}
+>
+ <Glyph uuid={post.author.id} />
+ <span class="h-card">
+ {$_('post.author_credit')}
+ <a href="/a/{post.author.handle}" class="p-nickname u-url">{post.author.handle}</a>.
+ </span>
+ <time role="presentation" class="dt-published" datetime={timestampToISO(post.created_at)}>
+ <a title={$_('post.permalink_title')} href="/p/{post.id}">
+ {timestampToISO(post.created_at)}
+ </a>
+ </time>
+ {#if post.topic}
+ <span class="h-card">
+ ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.)
+ </span>
+ {/if}
+</aside>
+<article
+ class="e-content"
+ title={$_('post.title', {
+ values: { count: index + 1, total: count, author: post.author.handle }
+ })}
+>
+ {post.text}
+</article>
+<hr />
<style>
- .post-meta * {
- vertical-align: top;
- }
+ .post-meta * {
+ vertical-align: top;
+ }
- article {
- white-space: pre;
- }
+ article {
+ white-space: pre;
+ }
</style>
<script>
- export let id;
+ export let tag;
- import { _ } from 'svelte-i18n';
- import { getTag } from '$/stores/tag';
- import ErrorBlock from '$/components/error_block/error_block.svelte';
- import Loader from '$/components/loader/loader.svelte';
- import TopicSummary from '$/components/topic_summary/topic_summary.svelte';
-
- $: store = getTag(id);
- $: tag = $store.data;
+ import { _ } from 'svelte-i18n';
+ import TopicSummary from '$/components/topic_summary/topic_summary.svelte';
</script>
-{#if $store.loading}
- <Loader />
-{/if}
-{#if $store.error}
- <ErrorBlock message={$_('tag.error.unavailable')} />
-{/if}
-{#if tag}
- <h1>{$_('tag.title')} {tag.id}</h1>
- <ul>
- {#each tag.topics as topic}
- <TopicSummary topic={topic} />
- {/each}
- </ul>
-{/if}
+<h1>{$_('tag.title')}: {tag.id}</h1>
+<ul>
+ {#each tag.topics as topic}
+ <TopicSummary {topic} />
+ {/each}
+</ul>
<script>
- export let id;
+ export let topic;
- import { _ } from 'svelte-i18n';
- import { getTopic } from '$/stores/topic';
+ import { _ } from 'svelte-i18n';
+ import Post from '$/components/post/post.svelte';
+ import { readableTime } from '$/utils/readable_time.js';
- import ErrorBlock from '$/components/error_block/error_block.svelte';
- import Loader from '$/components/loader/loader.svelte';
- import PostContent from '$/components/post_content/post_content.svelte';
- import { readableTime } from '$/utils/readable_time.js';
-
- $: store = getTopic(id);
- $: topic = $store.data;
- $: remainingTime = topic ? (topic.updated_at + topic.ttl) - Date.now() : 0;
- $: remaining = readableTime(remainingTime);
+ $: remainingTime = topic ? topic.updated_at + topic.ttl - Date.now() : 0;
+ $: remaining = readableTime(remainingTime);
</script>
-{#if $store.loading}
- <Loader />
-{/if}
-{#if $store.error}
- <ErrorBlock message={$_('topic.error.unavailable')} />
-{/if}
-{#if topic}
- <div class="h-entry" title="{$_('topic.title')}">
- <h1 class="p-name">{topic.title}</h1>
- <aside class="topic-meta" title="{$_('topic.metadata_title')}">
- {#if topic.forum}
- <span class="topic-location">{$_('topic.category_location')} <a href="/f/{topic.forum.id}"
- class="p-category">{topic.forum.glyph} {$_(topic.forum.label)}</a>.</span>
- {/if}
- <span class="topic-ttl"><a class="u-url u-uid" title="{$_('topic.permalink_title')}" href="/t/{topic.id}">({$_('topic.remaining_time', { values: { remaining: $_(remaining.label, { values: { count: remaining.count } }) } })})</a>.</span>
- </aside>
- {#if topic.tags.length > 0}
- <aside class="topic-tags" title="{$_('topic.tags_title')}">
- {$_('topic.tags_location')}
- {#each topic.tags as tag}
- <a href="/g/{tag.id}" class="p-category">{tag.id}<span class="tag-weight">({tag.weight})</span></a>{' '}
- {/each}
- </aside>
- {/if}
- {#each topic.posts as post, index}
- <PostContent post={post} index={index} count={topic.posts.length} />
- {/each}
- </div>
-{/if}
+<div class="h-entry" title={$_('topic.title')}>
+ <h1 class="p-name">{topic.title}</h1>
+ <aside class="topic-meta" title={$_('topic.metadata_title')}>
+ {#if topic.forum}
+ <span class="topic-location"
+ >{$_('topic.category_location')}
+ <a href="/f/{topic.forum.id}" class="p-category"
+ >{topic.forum.glyph} {$_(topic.forum.label)}</a
+ >.</span
+ >
+ {/if}
+ <span class="topic-ttl"
+ ><a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}"
+ >({$_('topic.remaining_time', {
+ values: { remaining: $_(remaining.label, { values: { count: remaining.count } }) }
+ })})</a
+ >.</span
+ >
+ </aside>
+ {#if topic.tags.length > 0}
+ <aside class="topic-tags" title={$_('topic.tags_title')}>
+ {$_('topic.tags_location')}
+ {#each topic.tags as tag}
+ <a href="/g/{tag.id}" class="p-category"
+ >{tag.id}<span class="tag-weight">({tag.weight})</span></a
+ >{' '}
+ {/each}
+ </aside>
+ {/if}
+ {#each topic.posts as post, index}
+ <Post {post} {index} count={topic.posts.length} />
+ {/each}
+</div>
<script>
- export let topic;
+ export let topic;
- import { _ } from 'svelte-i18n';
- import { readableTime } from '$/utils/readable_time.js';
+ import { _ } from 'svelte-i18n';
+ import { readableTime } from '$/utils/readable_time.js';
- $: remainingTime = (topic.updated_at + topic.ttl) - Date.now();
- $: remaining = readableTime(remainingTime);
+ $: 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 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>
-import { ApolloClient, InMemoryCache } from '@apollo/client/core';
+import fetch from 'cross-fetch';
+import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core';
import { apollo as apolloConfig } from './config';
const cache = new InMemoryCache();
export const client = new ApolloClient({
cache,
+ link: new HttpLink({
+ uri: apolloConfig.uri,
+ fetch
+ }),
...apolloConfig
});
*/
export const apollo = {
- uri: import.meta.env.VITE_APOLLO_SERVER,
- name,
- version: packageVersion
+ uri: import.meta.env.VITE_APOLLO_SERVER,
+ name,
+ version: packageVersion
};
export const socketServer = import.meta.env.FORUM_SOCKET_SERVER;
{
- "error": {
- "generic": {
- "title": "Error!",
- "message": "Unknown error has occurred. Panic!"
- },
- "invalid_url": {
- "title": "This is not right.",
- "message": "This URL is not valid. Try another one maybe?"
- }
- },
- "footer": {
- "choose_language": "Choose your language",
- "license": "Forum is <a href=\"{licenseUrl}\">open source.</a>",
- "title": "Footer"
- },
- "forum": {
- "name": {
- "everything": "Everything",
- "us": "Us",
- "words": "Words",
- "sound": "Sounds",
- "structure": "Structure",
- "interaction": "Interaction",
- "emotion": "Emotion",
- "movement": "Movement",
- "belief": "Belief",
- "experience": "Experience",
- "online": "Online",
- "the_world": "The World",
- "life": "Life"
- },
- "error": {
- "unavailable": "Forum topics unavailable."
- }
- },
- "forum_list": {
- "title": "List of forums",
- "error": {
- "unavailable": "Forum list unavailable."
- }
- },
- "glyph": {
- "title": "User avatar"
- },
- "header": {
- "action": {
- "new": {
- "title": "New",
- "display": "<u>N</u>ew"
- },
- "reply": {
- "title": "Reply",
- "display": "<u>R</u>eply"
- },
- "search": {
- "title": "Search",
- "display": "<u>S</u>earch"
- },
- "log_out": {
- "title": "Log Out",
- "display": "Log <u>O</u>ut"
- }
- },
- "long_version": "Forum version {version}",
- "title": "Toolbar",
- "short_version": "Forum v{version}"
- },
- "home": {
- "title": "Hello.",
- "content": "You are now in a forum. Select a category from the left or input a valid URL."
- },
- "loader": {
- "message": "Loading."
- },
- "post": {
- "author_credit": "By:",
- "metadata_title": "Post {count} of {total} metadata",
- "permalink_title": "Permalink to post",
- "title": "Post {count} of {total} by {author}",
- "topic_location": "In",
- "error": {
- "unavailable": "Post unavailable."
- }
- },
- "tag": {
- "title": "Tag:",
- "error": {
- "unavailable": "Tag topics unavailable."
- }
- },
- "topic": {
- "category_location": "Posted on",
- "metadata_title": "Topic metadata",
- "permalink_title": "Permalink to topic",
- "remaining_time": "{remaining} remaining",
- "tags_location": "Tags:",
- "tags_title": "Topic tags",
- "title": "Topic",
- "error": {
- "unavailable": "Topic unavailable."
- }
- },
- "time": {
- "days": "{count, plural, =1 {# day} other {# days}}",
- "hours": "{count, plural, =1 {# hour} other {# hours}}",
- "minutes": "{count, plural, =1 {# minute} other {# minutes}}",
- "seconds": "{count, plural, =1 {# second} other {# seconds}}"
- }
+ "error": {
+ "generic": {
+ "title": "Error!",
+ "message": "Unknown error has occurred. Panic!"
+ },
+ "invalid_url": {
+ "title": "This is not right.",
+ "message": "This URL is not valid. Try another one maybe?"
+ }
+ },
+ "footer": {
+ "choose_language": "Choose your language",
+ "license": "Forum is <a href=\"{licenseUrl}\">open source.</a>",
+ "title": "Footer"
+ },
+ "forum": {
+ "forum": "forum",
+ "name": {
+ "everything": "Everything",
+ "us": "Us",
+ "words": "Words",
+ "sound": "Sounds",
+ "structure": "Structure",
+ "interaction": "Interaction",
+ "emotion": "Emotion",
+ "movement": "Movement",
+ "belief": "Belief",
+ "experience": "Experience",
+ "online": "Online",
+ "the_world": "The World",
+ "life": "Life"
+ },
+ "error": {
+ "unavailable": "Forum topics unavailable."
+ }
+ },
+ "forum_list": {
+ "title": "List of forums",
+ "error": {
+ "unavailable": "Forum list unavailable."
+ }
+ },
+ "glyph": {
+ "title": "User avatar"
+ },
+ "header": {
+ "action": {
+ "new": {
+ "title": "New",
+ "display": "<u>N</u>ew"
+ },
+ "reply": {
+ "title": "Reply",
+ "display": "<u>R</u>eply"
+ },
+ "search": {
+ "title": "Search",
+ "display": "<u>S</u>earch"
+ },
+ "log_out": {
+ "title": "Log Out",
+ "display": "Log <u>O</u>ut"
+ }
+ },
+ "long_version": "Forum version {version}",
+ "title": "Toolbar",
+ "short_version": "Forum v{version}"
+ },
+ "home": {
+ "title": "Hello.",
+ "content": "You are now in a forum. Select a category from the left or input a valid URL."
+ },
+ "loader": {
+ "message": "Loading."
+ },
+ "post": {
+ "author_credit": "By:",
+ "metadata_title": "Post {count} of {total} metadata",
+ "permalink_title": "Permalink to post",
+ "post": "Post",
+ "title": "Post {count} of {total} by {author}",
+ "topic_location": "In",
+ "error": {
+ "unavailable": "Post unavailable."
+ }
+ },
+ "tag": {
+ "title": "Tag",
+ "error": {
+ "unavailable": "Tag topics unavailable."
+ }
+ },
+ "topic": {
+ "category_location": "Posted on",
+ "metadata_title": "Topic metadata",
+ "permalink_title": "Permalink to topic",
+ "remaining_time": "{remaining} remaining",
+ "tags_location": "Tags:",
+ "tags_title": "Topic tags",
+ "title": "Topic",
+ "error": {
+ "unavailable": "Topic unavailable."
+ }
+ },
+ "time": {
+ "days": "{count, plural, =1 {# day} other {# days}}",
+ "hours": "{count, plural, =1 {# hour} other {# hours}}",
+ "minutes": "{count, plural, =1 {# minute} other {# minutes}}",
+ "seconds": "{count, plural, =1 {# second} other {# seconds}}"
+ }
}
{
- "error": {
- "generic": {
- "title": "Error!",
- "message": "Hubo un error desconocido. ¡Entra en pánico!"
- },
- "invalid_url": {
- "title": "Esto no está bien.",
- "message": "Este URL no es válido. Intenta otro, ¿Tal vez?"
- }
- },
- "footer": {
- "choose_language": "Escoge un lenguaje",
- "license": "Forum es <a href=\"{licenseUrl}\">software libre.</a>",
- "title": "Pie de página"
- },
- "forum": {
- "name": {
- "everything": "Todo",
- "us": "Nosotros",
- "words": "Palabras",
- "sound": "Sonidos",
- "structure": "Estructura",
- "interaction": "Interacción",
- "emotion": "Emoción",
- "movement": "Movimiento",
- "belief": "Creencia",
- "experience": "Experiencia",
- "online": "En Línea",
- "the_world": "El Mundo",
- "life": "Vida"
- },
- "error": {
- "unavailable": "Temas del foro no disponibles."
- }
- },
- "forum_list": {
- "title": "Lista de foros",
- "error": {
- "unavailable": "Lista de foros no disponible."
- }
- },
- "glyph": {
- "title": "Avatar del usuario"
- },
- "header": {
- "action": {
- "new": {
- "title": "Nuevo",
- "display": "<u>N</u>uevo"
- },
- "reply": {
- "title": "Responder",
- "display": "<u>R</u>esponder"
- },
- "search": {
- "title": "Buscar",
- "display": "Bu<u>s</u>car"
- },
- "log_out": {
- "title": "Cerrar Sesión",
- "display": "Cerrar Sesi<u>ó</u>n"
- }
- },
- "long_version": "Forum versión {version}",
- "title": "Barra de herramientas",
- "short_version": "Forum v{version}"
- },
- "home": {
- "title": "Hola.",
- "content": "Ahora estás en un foro. Elige una categoría de la izquierda o escribe un URL válido."
- },
- "loader": {
- "message": "Cargando."
- },
- "post": {
- "author_credit": "Por:",
- "metadata_title": "Metadatos de entrada {count} de {total}",
- "permalink_title": "Permalink a entrada",
- "title": "Entrada {count} de {total}, por {author}",
- "topic_location": "En",
- "error": {
- "unavailable": "Entrada no disponible."
- }
- },
- "tag": {
- "title": "Etiqueta:",
- "error": {
- "unavailable": "Temas de la etiqueta no disponibles."
- }
- },
- "topic": {
- "category_location": "Agregado a",
- "metadata_title": "Metadatos del tema",
- "permalink_title": "Permalink al tema",
- "remaining_time": "Quedan {remaining}",
- "tags_location": "Etiquetas:",
- "tags_title": "Etiquetas del tema",
- "title": "Tema",
- "error": {
- "unavailable": "Tema no disponible."
- }
- },
- "time": {
- "days": "{count, plural, =1 {# día} other {# días}}",
- "hours": "{count, plural, =1 {# hora} other {# horas}}",
- "minutes": "{count, plural, =1 {# minuto} other {# minutos}}",
- "seconds": "{count, plural, =1 {# segundo} other {# segundos}}"
- }
+ "error": {
+ "generic": {
+ "title": "Error!",
+ "message": "Hubo un error desconocido. ¡Entra en pánico!"
+ },
+ "invalid_url": {
+ "title": "Esto no está bien.",
+ "message": "Este URL no es válido. Intenta otro, ¿Tal vez?"
+ }
+ },
+ "footer": {
+ "choose_language": "Escoge un lenguaje",
+ "license": "Forum es <a href=\"{licenseUrl}\">software libre.</a>",
+ "title": "Pie de página"
+ },
+ "forum": {
+ "forum": "foro",
+ "name": {
+ "everything": "Todo",
+ "us": "Nosotros",
+ "words": "Palabras",
+ "sound": "Sonidos",
+ "structure": "Estructura",
+ "interaction": "Interacción",
+ "emotion": "Emoción",
+ "movement": "Movimiento",
+ "belief": "Creencia",
+ "experience": "Experiencia",
+ "online": "En Línea",
+ "the_world": "El Mundo",
+ "life": "Vida"
+ },
+ "error": {
+ "unavailable": "Temas del foro no disponibles."
+ }
+ },
+ "forum_list": {
+ "title": "Lista de foros",
+ "error": {
+ "unavailable": "Lista de foros no disponible."
+ }
+ },
+ "glyph": {
+ "title": "Avatar del usuario"
+ },
+ "header": {
+ "action": {
+ "new": {
+ "title": "Nuevo",
+ "display": "<u>N</u>uevo"
+ },
+ "reply": {
+ "title": "Responder",
+ "display": "<u>R</u>esponder"
+ },
+ "search": {
+ "title": "Buscar",
+ "display": "Bu<u>s</u>car"
+ },
+ "log_out": {
+ "title": "Cerrar Sesión",
+ "display": "Cerrar Sesi<u>ó</u>n"
+ }
+ },
+ "long_version": "Forum versión {version}",
+ "title": "Barra de herramientas",
+ "short_version": "Forum v{version}"
+ },
+ "home": {
+ "title": "Hola.",
+ "content": "Ahora estás en un foro. Elige una categoría de la izquierda o escribe un URL válido."
+ },
+ "loader": {
+ "message": "Cargando."
+ },
+ "post": {
+ "author_credit": "Por:",
+ "metadata_title": "Metadatos de entrada {count} de {total}",
+ "permalink_title": "Permalink a entrada",
+ "post": "Entrada",
+ "title": "Entrada {count} de {total}, por {author}",
+ "topic_location": "En",
+ "error": {
+ "unavailable": "Entrada no disponible."
+ }
+ },
+ "tag": {
+ "title": "Etiqueta",
+ "error": {
+ "unavailable": "Temas de la etiqueta no disponibles."
+ }
+ },
+ "topic": {
+ "category_location": "Agregado a",
+ "metadata_title": "Metadatos del tema",
+ "permalink_title": "Permalink al tema",
+ "remaining_time": "Quedan {remaining}",
+ "tags_location": "Etiquetas:",
+ "tags_title": "Etiquetas del tema",
+ "title": "Tema",
+ "error": {
+ "unavailable": "Tema no disponible."
+ }
+ },
+ "time": {
+ "days": "{count, plural, =1 {# día} other {# días}}",
+ "hours": "{count, plural, =1 {# hora} other {# horas}}",
+ "minutes": "{count, plural, =1 {# minuto} other {# minutos}}",
+ "seconds": "{count, plural, =1 {# segundo} other {# segundos}}"
+ }
}
import { gql } from '@apollo/client/core';
export const GET_FORUMS = gql`
- query GetForums {
- forums {
- id
- glyph
- label
- position
- }
- }
+ 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
- }
- }
- }
+ query GetForum($id: ID!) {
+ forum(id: $id) {
+ id
+ glyph
+ label
+ position
+ topics {
+ id
+ title
+ updated_at
+ ttl
+ }
+ }
+ }
`;
export const GET_TAG = gql`
- query GetTag($id: ID!) {
- tag(id: $id) {
- id
- topics {
- id
- title
- updated_at
- ttl
- }
- }
- }
+ query GetTag($id: ID!) {
+ tag(id: $id) {
+ id
+ topics {
+ id
+ title
+ updated_at
+ ttl
+ }
+ }
+ }
`;
export const GET_TOPIC = gql`
- query GetTopic($id: ID!) {
- topic(id: $id) {
- id
- title
- updated_at
- ttl
- forum {
- id
- glyph
- label
- }
- tags {
- id
- weight
- }
- posts {
- id
- text
- created_at
- author {
- id
- handle
- }
- }
- }
- }
+ query GetTopic($id: ID!) {
+ topic(id: $id) {
+ id
+ title
+ updated_at
+ ttl
+ forum {
+ id
+ glyph
+ label
+ }
+ tags {
+ id
+ weight
+ }
+ posts {
+ id
+ text
+ created_at
+ author {
+ id
+ handle
+ }
+ }
+ }
+ }
`;
export const GET_POST = gql`
- query GetPost($id: ID!) {
- post(id: $id) {
- id
- text
- created_at
- author {
- id
- handle
- }
- topic {
- id
- title
- }
- }
- }
+ query GetPost($id: ID!) {
+ post(id: $id) {
+ id
+ text
+ created_at
+ author {
+ id
+ handle
+ }
+ topic {
+ id
+ title
+ }
+ }
+ }
`;
+++ /dev/null
-import HMR from '@roxi/routify/hmr';
-import Forum from './Forum.svelte';
-
-const app = HMR(Forum, { target: document.body }, 'forum');
-
-export default app;
+++ /dev/null
-<script>
- import { Router } from '@roxi/routify';
- import { routes } from '../.routify/routes';
-</script>
-
-<Router {routes} />
--- /dev/null
+/// <reference types="@sveltejs/kit" />
+/// <reference types="svelte" />
+/// <reference types="vite/client" />
+++ /dev/null
-<script>
- import InvalidRoute from '$/components/invalid_route/invalid_route.svelte';
-</script>
-
-<InvalidRoute />
+++ /dev/null
-<script>
- import '$/config/i18n';
- import { isLoading } from 'svelte-i18n';
- import ForumList from '$/components/forum_list/forum_list.svelte';
- import Header from '$/components/header/header.svelte';
- import Loader from '$/components/loader/loader.svelte';
- import Footer from '$/components/footer/footer.svelte';
-</script>
-
-{#if $isLoading}
- <Loader />
-{:else}
- <Header />
- <main>
- <slot></slot>
- </main>
- <ForumList />
- <Footer />
-{/if}
-
-<style>
- main {
- grid-column: col-start 2 / span 11;
- }
-
- :global(#forum) {
- display: grid;
- font-family : 'ヒラギノ明朝 ProN' , 'Hiragino Mincho ProN' , '游明朝','游明朝体',YuMincho,'Yu Mincho' , 'MS 明朝' , 'MS Mincho' , HiraMinProN-W3 , 'TakaoEx明朝' , TakaoExMincho , 'MotoyaLCedar' , 'Droid Sans Japanese' , serif;
- grid-template-columns: repeat(12, [col-start] 1fr);
- grid-gap: 20px;
- grid-auto-rows: minmax(24px, auto);
- }
-
- :global(body) {
- background-color: whitesmoke;
- }
-</style>
+++ /dev/null
-<script>
- import Author from '$/components/author/author.svelte';
-</script>
-
-<Author/>
+++ /dev/null
-<script>
- import Forum from '$/components/forum/forum.svelte';
- export let id;
-</script>
-
-<Forum id={id}/>
+++ /dev/null
-<script>
- import Tag from '$/components/tag/tag.svelte';
- export let id;
-</script>
-
-<Tag id={id}/>
+++ /dev/null
-<script>
- import { _ } from 'svelte-i18n';
- import Loader from '$/components/loader/loader.svelte';
-</script>
-
-<Loader />
-<h1>{$_('home.title')}</h1>
-<p>{$_('home.content')}</p>
+++ /dev/null
-<script>
- import Post from '$/components/post/post.svelte';
- export let id;
-</script>
-
-<Post id={id}/>
+++ /dev/null
-<script>
- import Topic from '$/components/topic/topic.svelte';
- export let topic_id;
-</script>
-
-<Topic id={topic_id}/>
--- /dev/null
+<script>
+ import InvalidRoute from '$/components/invalid_route/invalid_route.svelte';
+</script>
+
+<InvalidRoute />
--- /dev/null
+<script>
+ import '$/config/i18n';
+ import { isLoading } from 'svelte-i18n';
+ import ForumList from '$/components/forum_list/forum_list.svelte';
+ import Header from '$/components/header/header.svelte';
+ import Loader from '$/components/loader/loader.svelte';
+ import Footer from '$/components/footer/footer.svelte';
+</script>
+
+{#if $isLoading}
+ <Loader />
+{:else}
+ <Header />
+ <main>
+ <slot />
+ </main>
+ <ForumList />
+ <Footer />
+{/if}
--- /dev/null
+<script context="module">
+ export const load = ({
+ page: {
+ params: { id }
+ }
+ }) => ({ props: { id } });
+</script>
+
+<script>
+ import Author from '$/components/author/author.svelte';
+</script>
+
+<Author />
--- /dev/null
+<script context="module">
+ export const load = ({
+ page: {
+ params: { id }
+ }
+ }) => ({ props: { id } });
+</script>
+
+<script>
+ import { _ } from 'svelte-i18n';
+ import Forum from '$/components/forum/forum.svelte';
+ import ErrorBlock from '$/components/error_block/error_block.svelte';
+ import Loader from '$/components/loader/loader.svelte';
+
+ export let id;
+
+ import { getForum } from '$/stores/forum';
+ $: store = getForum(id);
+ $: forum = $store.data;
+</script>
+
+<svelte:head>
+ <title>{$_(`forum.name.${id}`)}, {$_('forum.forum')}</title>
+</svelte:head>
+
+{#if $store.loading}
+ <Loader />
+{/if}
+{#if $store.error}
+ <ErrorBlock message={$_('forum.error.unavailable')} />
+{/if}
+{#if forum}
+ <Forum {forum} />
+{/if}
--- /dev/null
+<script context="module">
+ export const load = ({
+ page: {
+ params: { id }
+ }
+ }) => ({ props: { id } });
+</script>
+
+<script>
+ import { _ } from 'svelte-i18n';
+ import { getTag } from '$/stores/tag';
+ import ErrorBlock from '$/components/error_block/error_block.svelte';
+ import Loader from '$/components/loader/loader.svelte';
+ import Tag from '$/components/tag/tag.svelte';
+ export let id;
+
+ $: store = getTag(id);
+ $: tag = $store.data;
+</script>
+
+<svelte:head>
+ <title>{id}, {$_('tag.title')}</title>
+</svelte:head>
+
+{#if $store.loading}
+ <Loader />
+{/if}
+{#if $store.error}
+ <ErrorBlock message={$_('tag.error.unavailable')} />
+{/if}
+{#if tag}
+ <Tag {tag} />
+{/if}
--- /dev/null
+<script>
+ import { _ } from 'svelte-i18n';
+ import Loader from '$/components/loader/loader.svelte';
+</script>
+
+<svelte:head>
+ <title>Forum.</title>
+</svelte:head>
+
+<Loader />
+<h1>{$_('home.title')}</h1>
+<p>{$_('home.content')}</p>
--- /dev/null
+<script context="module">
+ export const load = ({
+ page: {
+ params: { id }
+ }
+ }) => ({ props: { id } });
+</script>
+
+<script>
+ import { _ } from 'svelte-i18n';
+ import { getPost } from '$/stores/post';
+ import Post from '$/components/post/post.svelte';
+ import ErrorBlock from '$/components/error_block/error_block.svelte';
+ import Loader from '$/components/loader/loader.svelte';
+
+ export let id;
+
+ $: store = getPost(id);
+ $: post = $store.data;
+</script>
+
+<svelte:head>
+ <title>{$_('post.post')}}</title>
+</svelte:head>
+
+{#if $store.loading}
+ <Loader />
+{/if}
+{#if $store.error}
+ <ErrorBlock message={$_('post.error.unavailable')} />
+{/if}
+{#if post}
+ <Post {post} />
+{/if}
--- /dev/null
+<script context="module">
+ export const load = ({
+ page: {
+ params: { id }
+ }
+ }) => ({ props: { id } });
+</script>
+
+<script>
+ import { onDestroy } from 'svelte';
+ import { _ } from 'svelte-i18n';
+ import { getTopic } from '$/stores/topic';
+ import { disableTopicActions, enableTopicActions } from '$/stores/actions';
+
+ import Topic from '$/components/topic/topic.svelte';
+ import ErrorBlock from '$/components/error_block/error_block.svelte';
+ import Loader from '$/components/loader/loader.svelte';
+
+ export let id;
+
+ $: store = getTopic(id);
+ $: topic = $store.data;
+
+ enableTopicActions(id);
+ onDestroy(() => disableTopicActions());
+</script>
+
+<svelte:head>
+ {#if $store.loading}
+ <title>{$_('loader.message')}, {$_('topic.title')}</title>
+ {/if}
+ {#if $store.error}
+ <title>{$_('error.generic.title')}, {$_('topic.title')}</title>
+ {/if}
+ {#if topic}
+ <title>{topic.title}, {$_('topic.title')}</title>
+ {/if}
+</svelte:head>
+
+{#if $store.loading}
+ <Loader />
+{/if}
+{#if $store.error}
+ <ErrorBlock message={$_('topic.error.unavailable')} />
+{/if}
+{#if topic}
+ <Topic {topic} />
+{/if}
+++ /dev/null
-import EventEmitter from 'eventemitter3';
-import { socketServer } from './config/config';
-
-const internals = {
-
- kReconnectInterval: 3000, // How often we attempt to reconnect
-
- eventEmitter: new EventEmitter(), // internal event emitter
- socket: null, // stores the socket connection
- retry: null, // stores the retry operation
-
- connect() {
-
- console.debug('Connecting socket.');
- internals.socket = new WebSocket(socketServer);
-
- internals.socket.addEventListener('message', internals.onMessage);
- internals.socket.addEventListener('error', internals.onError);
- internals.socket.addEventListener('close', internals.onClose);
- },
-
- // Handles socket errors.
-
- onError(event) {
-
- console.error('Socket error. Closing connection');
- internals.socket.close();
- },
-
- // Handles socket errors.
-
- onClose(event) {
-
- console.debug(`Connection closed: ${event.reason || 'Unknown reason'}. Retrying in ${internals.kReconnectInterval}ms`);
-
- internals.retry && clearTimeout(internals.retry);
- internals.retry = setTimeout(() => {
-
- console.debug('Reconnecting socket.');
- internals.retry = null;
- internals.connect();
- }, internals.kReconnectInterval);
- },
-
- // Forwards events from the socket to our internal event emitter.
-
- onMessage(event) {
-
- internals.eventEmitter.emit('message', event);
- }
-};
-
-export const onMessage = function (listener) {
-
- if (!internals.socket) {
- internals.connect();
- }
-
- internals.eventEmitter.on('message', (message) => {
-
- listener(JSON.parse(message.data));
- });
-};
--- /dev/null
+import { writable } from 'svelte/store';
+
+/*
+ * This is a store to set the actions in the top header.
+ */
+
+export const actions = writable({});
+
+export const enableTopicActions = (id) => {
+
+ actions.update((actionsValue) => {
+
+ actionsValue.topic_id = id;
+ return actionsValue;
+ });
+};
+
+export const disableTopicActions = () => {
+
+ actions.update((actionsValue) => {
+
+ delete actionsValue.id;
+ return actionsValue;
+ });
+};
export const store = function store({ key, query, initialValue = null, variables = {} }) {
- return readable({
- loading: true,
- data: initialValue,
- error: undefined
- }, (set) => {
-
- const handleError = function (error) {
-
- return set({
- loading: false,
- data: initialValue,
- error
- });
- };
-
- client.watchQuery({ query, variables }).subscribe((result) => {
-
- if (result.errors) {
- const error = new ApolloError({ graphQLErrors: result.errors });
- return handleError(error);
- }
-
- set({
- loading: false,
- data: result.data[key],
- error: undefined
- });
- }, (error) => handleError(error));
- });
+ return readable(
+ {
+ loading: true,
+ data: initialValue,
+ error: undefined
+ },
+ (set) => {
+
+ const handleError = function (error) {
+
+ return set({
+ loading: false,
+ data: initialValue,
+ error
+ });
+ };
+
+ client.watchQuery({ query, variables }).subscribe(
+ (result) => {
+
+ if (result.errors) {
+ const error = new ApolloError({ graphQLErrors: result.errors });
+ return handleError(error);
+ }
+
+ set({
+ loading: false,
+ data: result.data[key],
+ error: undefined
+ });
+ },
+ (error) => handleError(error)
+ );
+ }
+ );
};
const internals = {
kSplitterRegex: /.{1,8}/g,
- kGlyphs: [
- '☽',
- '☆',
- '♢',
- '♡',
- '╱',
- '╲',
- '╳',
- '〰',
- '▷',
- '⏊',
- '〒',
- '▢',
- '◯',
- '⏃',
- '⏀',
- '⏆'
- ]
+ kGlyphs: ['☽', '☆', '♢', '♡', '╱', '╲', '╳', '〰', '▷', '⏊', '〒', '▢', '◯', '⏃', '⏀', '⏆']
};
// Return a glyph with color based on a 4 byte fragment of a UUIDv4
const getGlyphHashFragment = function (uuidFragment) {
- const glyphIndex = parseInt(uuidFragment.substring(0,2), 16) % 16;
+ const glyphIndex = parseInt(uuidFragment.substring(0, 2), 16) % 16;
return {
glyph: internals.kGlyphs[glyphIndex],
- color: `#${uuidFragment.substring(2,8)}`
+ color: `#${uuidFragment.substring(2, 8)}`
};
};
// Return an array of glyphs based on a UUIDv4
export const getGlyphHash = function (uuid) {
- const hashFragments = uuid.replace(/[-]/g,'').match(internals.kSplitterRegex);
+ const hashFragments = uuid.replace(/[-]/g, '').match(internals.kSplitterRegex);
return hashFragments.map(getGlyphHashFragment);
};