feat: update svelte UI to sveltekit SPA

This commit is contained in:
Damillora 2023-08-09 00:26:37 +07:00
parent d6189fa3dc
commit 79f0616532
52 changed files with 2249 additions and 2508 deletions

View File

@ -6,7 +6,7 @@ COPY . .
RUN apk add vips-dev pkgconfig gcc musl-dev RUN apk add vips-dev pkgconfig gcc musl-dev
RUN go get -d -v ./... RUN go get -d -v ./...
RUN go build -o /shioriko RUN go build -o /shioriko
RUN mkdir -p /web && cp -r web/static web/template /web RUN mkdir -p /web && cp -r web/static /web
FROM node:14-alpine AS node_build FROM node:14-alpine AS node_build
WORKDIR /src WORKDIR /src
@ -20,6 +20,5 @@ WORKDIR /app
RUN apk add vips RUN apk add vips
COPY --from=build /shioriko /app/ COPY --from=build /shioriko /app/
COPY --from=node_build /src/web/static/ /app/web/static/ COPY --from=node_build /src/web/static/ /app/web/static/
COPY --from=node_build /src/web/template/ /app/web/template/
ENTRYPOINT ["/app/shioriko"] ENTRYPOINT ["/app/shioriko"]

View File

@ -49,11 +49,10 @@ func Initialize() {
func Start() { func Start() {
g := gin.Default() g := gin.Default()
g.Static("/static", "./web/static") g.StaticFile("/", "./web/static/index.html")
g.Static("/_app", "./web/static/_app")
g.Static("/data", config.CurrentConfig.DataDirectory) g.Static("/data", config.CurrentConfig.DataDirectory)
g.LoadHTMLGlob("web/template/**/*")
g.Use(cors.Default()) g.Use(cors.Default())
InitializeRoutes(g) InitializeRoutes(g)

View File

@ -3,7 +3,6 @@ package app
import ( import (
"net/http" "net/http"
"github.com/Damillora/Shioriko/pkg/config"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -12,9 +11,5 @@ func InitializeFrontendRoutes(g *gin.Engine) {
} }
func frontendHome(c *gin.Context) { func frontendHome(c *gin.Context) {
baseURL := config.CurrentConfig.BaseURL c.Redirect(http.StatusFound, "/")
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "Shioriko",
"base_url": baseURL,
})
} }

13
web/app/.eslintignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

29
web/app/.eslintrc.cjs Normal file
View File

@ -0,0 +1,29 @@
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
};

12
web/app/.gitignore vendored
View File

@ -1,4 +1,10 @@
/node_modules/
/public/build/
.DS_Store .DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

2
web/app/.npmrc Normal file
View File

@ -0,0 +1,2 @@
engine-strict=true
resolution-mode=highest

View File

@ -1,105 +1,38 @@
*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)* # create-svelte
--- Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
# svelte app ## Creating a project
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template. If you're seeing this, you've probably already done this step. Congrats!
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
```bash ```bash
npx degit sveltejs/template svelte-app # create a new project in the current directory
cd svelte-app npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
``` ```
*Note that you will need to have [Node.js](https://nodejs.org) installed.* ## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
## Get started
Install the dependencies...
```bash
cd svelte-app
npm install
```
...then start [Rollup](https://rollupjs.org):
```bash ```bash
npm run dev npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
``` ```
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes. ## Building
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`. To create a production version of your app:
If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense.
## Building and running in production mode
To create an optimised version of the app:
```bash ```bash
npm run build npm run build
``` ```
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com). You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
## Single-page app mode
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
```js
"start": "sirv public --single"
```
## Using TypeScript
This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with:
```bash
node scripts/setupTypeScript.js
```
Or remove the script via:
```bash
rm scripts/setupTypeScript.js
```
## Deploying to the web
### With [Vercel](https://vercel.com)
Install `vercel` if you haven't already:
```bash
npm install -g vercel
```
Then, from within your project folder:
```bash
cd public
vercel deploy --name my-project
```
### With [surge](https://surge.sh/)
Install `surge` if you haven't already:
```bash
npm install -g surge
```
Then, from within your project folder:
```bash
npm run build
surge public my-project.surge.sh
```

View File

@ -1,32 +1,36 @@
{ {
"name": "svelte-app", "name": "Shioriko",
"version": "1.0.0", "version": "0.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "rollup -c", "dev": "vite dev",
"dev": "rollup -c -w", "build": "vite build",
"start": "sirv public --no-clear" "preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "eslint ."
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0", "@sveltejs/adapter-auto": "^2.0.0",
"@rollup/plugin-node-resolve": "^11.0.0", "@sveltejs/kit": "^1.20.4",
"rollup": "^2.3.4", "@typescript-eslint/eslint-plugin": "^5.45.0",
"rollup-plugin-css-only": "^3.1.0", "@typescript-eslint/parser": "^5.45.0",
"rollup-plugin-livereload": "^2.0.0", "eslint": "^8.28.0",
"rollup-plugin-svelte": "^7.0.0", "eslint-plugin-svelte": "^2.30.0",
"rollup-plugin-terser": "^7.0.0", "sass": "^1.63.6",
"svelte": "^3.0.0" "svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"svelte-tags-input": "^5.0.0",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.4.2"
}, },
"type": "module",
"dependencies": { "dependencies": {
"@rollup/plugin-json": "^4.1.0", "@sveltejs/adapter-static": "^2.0.3",
"axios": "^0.21.1", "axios": "^1.4.0",
"bulma": "^0.9.2", "bulma": "^0.9.4",
"node-sass": "^6.0.0", "query-string": "^8.1.0",
"postcss": "^8.2.14", "sass": "^1.64.2"
"query-string": "^7.0.0",
"sirv-cli": "^1.0.0",
"svelte-preprocess": "^4.7.3",
"svelte-routing": "^1.6.0",
"svelte-tags-input": "^2.7.1"
} }
} }

1927
web/app/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +0,0 @@
import json from '@rollup/plugin-json';
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import css from 'rollup-plugin-css-only';
import sveltePreprocess from 'svelte-preprocess'
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
dir: "../static",
assetFileNames: 'bundle.css',
entryFileNames: 'bundle.js'
},
plugins: [
json(),
svelte({
compilerOptions: {
// enable run-time checks when not in production
dev: !production
},
preprocess: sveltePreprocess({
sourceMap: !production,
scss: {
includePaths: [
'node_modules',
'src'
]
},
}),
}),
// we'll extract any component CSS out into
// a separate file - better for performance
css({ name: "bundle" }),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};

View File

@ -1,68 +0,0 @@
<script>
import { Router, Link, Route } from "svelte-routing";
import Navbar from "./Navbar.svelte";
import Home from "./routes/Home.svelte";
import Login from "./routes/Auth/Login.svelte";
import Logout from "./routes/Auth/Logout.svelte";
import Register from "./routes/Auth/Register.svelte";
import Posts from "./routes/Post/Posts.svelte";
import Post from "./routes/Post/Post.svelte";
import Upload from "./routes/Post/Upload.svelte";
import Tags from "./routes/Tags/Tags.svelte";
import Tag from "./routes/Tags/Tag.svelte";
import Profile from "./routes/User/Profile.svelte";
export let url = "";
let baseURL = window.BASE_URL;
</script>
<Router {url}>
<Navbar />
<div>
<Route path="/" component={Home} />
<Route path="/posts" component={Posts} />
<Route path="/post/:id" component={Post} />
<Route path="/auth/login" component={Login} />
<Route path="/auth/logout" component={Logout} />
<Route path="/upload" component={Upload} />
<Route path="/tags" component={Tags} />
<Route path="/tags/:tag" component={Tag} />
<Route path="/auth/register" component={Register} />
<Route path="/user/profile" component={Profile} />
</div>
</Router>
<style global lang="scss">
@import "./main.scss";
#tags .svelte-tags-input-tag {
background: $primary;
color: $text-invert;
}
#tags .svelte-tags-input-layout {
@extend .input;
height: inherit;
& .svelte-tags-input {
margin-top: 0 !important;
font-size: 13.3333px;
}
}
#tags .svelte-tags-input-matchs {
z-index: 200;
&-parent {
z-index: 200;
}
& li:hover {
background: $primary;
}
}
</style>

12
web/app/src/app.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {};

12
web/app/src/app.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@ -1,3 +1,4 @@
/* Write your global styles here, in SCSS syntax. Variables and mixins from the src/variables.scss file are available here without importing */
@import "../node_modules/bulma/sass/utilities/_all"; @import "../node_modules/bulma/sass/utilities/_all";
@import "../node_modules/bulma/sass/base/_all"; @import "../node_modules/bulma/sass/base/_all";
@import "../node_modules/bulma/sass/elements/_all"; @import "../node_modules/bulma/sass/elements/_all";
@ -6,7 +7,6 @@
@import "../node_modules/bulma/sass/grid/_all"; @import "../node_modules/bulma/sass/grid/_all";
@import "../node_modules/bulma/sass/helpers/_all"; @import "../node_modules/bulma/sass/helpers/_all";
@import "../node_modules/bulma/sass/layout/_all"; @import "../node_modules/bulma/sass/layout/_all";
@import "../node_modules/bytemd/dist/index.css";
.tile.is-multiline { .tile.is-multiline {
flex-wrap: wrap; flex-wrap: wrap;

View File

@ -1,7 +1,8 @@
import { token } from "./stores.js" import { token } from "./stores.js"
import { browser } from "$app/environment";
import axios from "axios"; import axios from "axios";
let url = window.BASE_URL; let url = browser && window.location.origin || "";
let current_token; let current_token;
token.subscribe(value => { token.subscribe(value => {
current_token = value; current_token = value;

View File

@ -1,6 +1,6 @@
<script> <script>
import { token } from "./stores.js"; import { token } from "$lib/stores";
import { isTokenExpired } from "./login-check.js"; import { isTokenExpired } from "$lib/login-check";
let loggedIn = false; let loggedIn = false;
token.subscribe((value) => { token.subscribe((value) => {

View File

@ -1,8 +1,8 @@
<script> <script>
import { token } from "./stores.js"; import { token } from "$lib/stores";
import { navigate } from "svelte-routing"; import { goto } from "$app/navigation";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { isTokenExpired } from "./login-check.js"; import { isTokenExpired } from "$lib/login-check";
let loggedIn = false; let loggedIn = false;
token.subscribe((value) => { token.subscribe((value) => {
@ -11,7 +11,7 @@
onMount(() => { onMount(() => {
if (loggedIn === false) { if (loggedIn === false) {
navigate("/auth/login"); goto("/auth/login");
} }
}); });
</script> </script>

View File

@ -1,7 +1,7 @@
<script> <script>
import Tags from "svelte-tags-input"; import Tags from "svelte-tags-input";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { getPost, postUpdate, getTagAutocomplete } from "./api.js"; import { getPost, postUpdate, getTagAutocomplete } from "$lib/api";
export let isActive = false; export let isActive = false;
export let post; export let post;

View File

@ -1,7 +1,6 @@
<script> <script>
import { Link } from "svelte-routing"; import { token } from "$lib/stores";
import { token } from "./stores.js"; import { isTokenExpired } from "$lib/login-check";
import { isTokenExpired } from "./login-check.js";
let menu_shown = false; let menu_shown = false;
@ -17,7 +16,7 @@
<nav class="navbar" role="navigation" aria-label="main navigation"> <nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand"> <div class="navbar-brand">
<Link class="navbar-item" to="/">Shioriko</Link> <a class="navbar-item" href="/">Shioriko</a>
<a <a
href={"#"} href={"#"}
@ -35,10 +34,10 @@
<div class="navbar-menu" class:is-active={menu_shown}> <div class="navbar-menu" class:is-active={menu_shown}>
<div class="navbar-start"> <div class="navbar-start">
<Link class="navbar-item" to="/posts">Posts</Link> <a class="navbar-item" href="/posts">Posts</a>
<Link class="navbar-item" to="/tags">Tags</Link> <a class="navbar-item" href="/tags">Tags</a>
{#if loggedIn} {#if loggedIn}
<Link class="navbar-item" to="/upload">Upload</Link> <a class="navbar-item" href="/upload">Upload</a>
{/if} {/if}
</div> </div>
@ -46,23 +45,23 @@
{#if loggedIn} {#if loggedIn}
<div class="navbar-item"> <div class="navbar-item">
<div class="buttons"> <div class="buttons">
<Link to="/user/profile" class="button is-primary"> <a href="/user/profile" class="button is-primary">
Profile Profile
</Link> </a>
<Link to="/auth/logout" class="button is-light"> <a href="/auth/logout" class="button is-light">
Log out Log out
</Link> </a>
</div> </div>
</div> </div>
{:else} {:else}
<div class="navbar-item"> <div class="navbar-item">
<div class="buttons"> <div class="buttons">
<Link to="/auth/register" class="button is-primary"> <a href="/auth/register" class="button is-primary">
<strong>Register</strong> <strong>Register</strong>
</Link> </a>
<Link to="/auth/login" class="button is-light"> <a href="/auth/login" class="button is-light">
Log in Log in
</Link> </a>
</div> </div>
</div> </div>
{/if} {/if}

View File

@ -1,6 +1,5 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { Link } from "svelte-routing";
export let posts = []; export let posts = [];
</script> </script>
@ -12,9 +11,9 @@
<div class="card"> <div class="card">
<div class="card-image"> <div class="card-image">
<figure class="image"> <figure class="image">
<Link to="/post/{post.id}"> <a href="/post/{post.id}">
<img alt={post.id} src={post.thumbnail_path} /> <img alt={post.id} src={post.thumbnail_path} />
</Link> </a>
</figure> </figure>
</div> </div>
</div> </div>

View File

@ -1,8 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { Link, navigate } from "svelte-routing"; import { getTagTypes, updateTag } from "$lib/api";
import { getTagTypes, updateTag } from "../../api";
export let tag; export let tag;
export let data; export let data;
@ -25,7 +24,7 @@
const onFormSubmit = async () => { const onFormSubmit = async () => {
await updateTag(tag, form); await updateTag(tag, form);
navigate("/tags/"+form.name); goto("/tags/" + form.name);
toggleRenameMenu(); toggleRenameMenu();
onSubmit(form.name); onSubmit(form.name);
@ -81,7 +80,7 @@
<strong>Posts:</strong> <strong>Posts:</strong>
</div> </div>
<div class="row"> <div class="row">
{data.postCount} (<Link to="/posts?tags={tag}">Browse</Link>) {data.postCount} (<a href="/posts?tags={tag}">Browse</a>)
</div> </div>
</div> </div>
<div class="panel-block column"> <div class="panel-block column">

View File

@ -1,10 +1,9 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { Link } from "svelte-routing"; import { getRelatedTags } from "$lib/api";
import { getRelatedTags } from "../../api.js"; import AuthCheck from "$lib/components/AuthCheck.svelte";
import AuthCheck from "../../AuthCheck.svelte"; import TagLinkNumbered from "$lib/components/TagLinkNumbered.svelte";
import TagLinkNumbered from "../../TagLinkNumbered.svelte";
export let tag; export let tag;
export let data; export let data;
@ -40,7 +39,7 @@
<strong>Posts:</strong> <strong>Posts:</strong>
</div> </div>
<div class="row"> <div class="row">
{data.postCount} (<Link to="/posts?tags={tag}">Browse</Link>) {data.postCount} (<a href="/posts?tags={tag}">Browse</a>)
</div> </div>
</div> </div>
<div class="panel-block column"> <div class="panel-block column">

View File

@ -1,6 +1,4 @@
<script> <script>
import { Link } from "svelte-routing";
export let tag; export let tag;
export let num; export let num;
@ -9,6 +7,6 @@
let tagDisplay = tagName.split("_").join(" "); let tagDisplay = tagName.split("_").join(" ");
</script> </script>
<Link to="/posts?tags={tagName}" <a href="/posts?tags={tagName}"
>{tagDisplay} <span class="is-pulled-right">{num}</span></Link >{tagDisplay} <span class="is-pulled-right">{num}</span></a
> >

View File

@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { updateTagNotes } from "../../api"; import { updateTagNotes } from "$lib/api";
export let tag; export let tag;
export let data; export let data;

View File

@ -1,5 +1,5 @@
<script> <script>
import AuthCheck from "../../AuthCheck.svelte"; import AuthCheck from "$lib/components/AuthCheck.svelte";
export let data; export let data;
export let toggleEditMenu; export let toggleEditMenu;

1
web/app/src/lib/index.ts Normal file
View File

@ -0,0 +1 @@
// place files you want to import through the `$lib` alias in this folder.

10
web/app/src/lib/stores.ts Normal file
View File

@ -0,0 +1,10 @@
import { writable } from "svelte/store";
import { browser } from "$app/environment"
export const token = writable(browser && localStorage.getItem("apiToken") || "");
token.subscribe(value => {
if (value != null) {
browser && localStorage.setItem("apiToken", value);
}
});

View File

@ -1,8 +0,0 @@
import App from './App.svelte';
const app = new App({
target: document.body,
hydrate: false,
});
export default app;

View File

@ -0,0 +1,14 @@
<script>
import "../app.scss";
import Navbar from "$lib/components/Navbar.svelte";
export const ssr = false;
</script>
<svelte:head>
<title>Shioriko</title>
</svelte:head>
<Navbar />
<slot />

View File

@ -0,0 +1 @@
export const ssr = false;

View File

@ -1,9 +1,10 @@
<script> <script lang="ts">
import Tags from "svelte-tags-input"; import Tags from "svelte-tags-input";
import { getTagAutocomplete } from "../api.js"; import { getTagAutocomplete } from "$lib/api";
import { navigate } from "svelte-routing";
let searchTerms = []; import { goto } from '$app/navigation';
let searchTerms: string[] = [];
const onTagChange = (value) => { const onTagChange = (value) => {
searchTerms = value.detail.tags; searchTerms = value.detail.tags;
@ -16,9 +17,9 @@
const onSearch = (i) => { const onSearch = (i) => {
if (searchTerms.length > 0) { if (searchTerms.length > 0) {
navigate(`/posts?tags=${searchTerms.join("+")}`); goto(`/posts?tags=${searchTerms.join("+")}`);
} else { } else {
navigate(`/posts`); goto(`/posts`);
} }
}; };
</script> </script>

View File

@ -1,10 +0,0 @@
<script>
import { token } from "../../stores.js";
import { navigate } from "svelte-routing";
import { onMount } from "svelte";
onMount(() => {
token.set("");
navigate("/");
});
</script>

View File

@ -1,6 +1,6 @@
<script> <script>
import { login } from "../../api.js"; import { login } from "$lib/api";
import { navigate } from "svelte-routing"; import { goto } from "$app/navigation";
let username = ""; let username = "";
let password = ""; let password = "";
@ -10,7 +10,7 @@
error = ""; error = "";
try { try {
const tokenData = await login({ username, password }); const tokenData = await login({ username, password });
navigate("/"); goto("/");
} catch (e) { } catch (e) {
error = "We had trouble logging you in"; error = "We had trouble logging you in";
return; return;

View File

@ -0,0 +1,10 @@
<script>
import { token } from "$lib/stores";
import { goto } from "$app/navigation";
import { onMount } from "svelte";
onMount(() => {
token.set("");
goto("/");
});
</script>

View File

@ -1,6 +1,6 @@
<script> <script>
import { register } from "../../api.js"; import { register } from "$lib/api";
import { navigate } from "svelte-routing"; import { goto } from "$app/navigation";
let username = ""; let username = "";
let password = ""; let password = "";
@ -10,7 +10,7 @@
const doRegister = async () => { const doRegister = async () => {
try { try {
const tokenData = await register({ email, username, password }); const tokenData = await register({ email, username, password });
navigate("/"); goto("/");
} catch (error) { } catch (error) {
error = "We had trouble registering you"; error = "We had trouble registering you";
} }

View File

@ -1,11 +1,14 @@
<script> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import { getPost, postDelete } from "../../api.js"; import { getPost, postDelete } from "$lib/api";
import { navigate } from "svelte-routing"; import { goto } from "$app/navigation";
import EditPostPanel from "../../EditPostPanel.svelte"; import EditPostPanel from "$lib/components/EditPostPanel.svelte";
import ViewPostPanel from "../../ViewPostPanel.svelte"; import ViewPostPanel from "$lib/components/ViewPostPanel.svelte";
export let id;
let post; import { page } from "$app/stores";
const { id } = $page.params;
let post: any;
const getData = async () => { const getData = async () => {
const data = await getPost({ id }); const data = await getPost({ id });
post = data; post = data;
@ -28,7 +31,7 @@
toggleDeleteMenu(); toggleDeleteMenu();
const success = await postDelete({ id }); const success = await postDelete({ id });
if (success) { if (success) {
navigate("/posts"); goto("/posts");
} }
}; };
const toggleDeleteMenu = () => { const toggleDeleteMenu = () => {

View File

@ -1,13 +1,14 @@
<script> <script lang="ts">
import { getPostSearchTag, getTag, getTagAutocomplete } from "../../api.js"; import { getPostSearchTag, getTag, getTagAutocomplete } from "$lib/api";
import { Link, navigate } from "svelte-routing"; import TagLinkNumbered from "$lib/components/TagLinkNumbered.svelte";
import TagLinkNumbered from "../../TagLinkNumbered.svelte"; import PostGallery from "$lib/components/Post/PostGallery.svelte";
import PostGallery from "../../components/Post/PostGallery.svelte";
import queryString from "query-string"; import queryString from "query-string";
import Tags from "svelte-tags-input"; import Tags from "svelte-tags-input";
import { paginate } from "../../simple-pagination.js"; import { paginate } from "$lib/simple-pagination";
import { goto } from "$app/navigation";
import { page as currentPage } from '$app/stores';
export let location; const url = $currentPage.url;
let searchTerms = []; let searchTerms = [];
@ -46,7 +47,7 @@
tagInfo = await getTag({ tag: searchTerms[0] }); tagInfo = await getTag({ tag: searchTerms[0] });
} }
}; };
let queryParams; let tagQuery;
const onTagChange = (value) => { const onTagChange = (value) => {
searchTerms = value.detail.tags; searchTerms = value.detail.tags;
@ -58,9 +59,9 @@
}; };
$: { $: {
queryParams = queryString.parse(location.search); tagQuery = url.searchParams.get('tags');
if (queryParams.tags) { if (tagQuery) {
searchTerms = queryParams.tags.split(" "); searchTerms = tagQuery.split(" ");
} else { } else {
searchTerms = []; searchTerms = [];
tagInfo = null; tagInfo = null;
@ -71,9 +72,9 @@
} }
const onSearch = (i) => { const onSearch = (i) => {
if (searchTerms.length > 0) { if (searchTerms.length > 0) {
navigate(`/posts?tags=${searchTerms.join("+")}`); goto(`/posts?tags=${searchTerms.join("+")}`);
} else { } else {
navigate(`/posts`); goto(`/posts`);
} }
}; };
@ -124,16 +125,16 @@
{tagInfo.tagName.split("_").join(" ")} {tagInfo.tagName.split("_").join(" ")}
</p> </p>
{#if tagInfo.tagNote} {#if tagInfo.tagNote}
<div class="panel-block column "> <div class="panel-block column">
<div class="content pre-line"> <div class="content pre-line">
{tagInfo.tagNote} {tagInfo.tagNote}
</div> </div>
</div> </div>
{/if} {/if}
<div class="panel-block column"> <div class="panel-block column">
<Link <a
class="button is-primary" class="button is-primary"
to="/tags/{tagInfo.tagName}">View Tag</Link href="/tags/{tagInfo.tagName}">View Tag</a
> >
</div> </div>
</div> </div>

View File

@ -1,6 +1,5 @@
<script> <script>
import { getTags } from "../../api"; import { getTags } from "$lib/api";
import { Link } from "svelte-routing";
let tags = []; let tags = [];
@ -29,7 +28,7 @@
{#each tags as tag} {#each tags as tag}
<tr> <tr>
<td> <td>
<Link to="/tags/{tag.tagName}">{tag.tagName}</Link> <a href="/tags/{tag.tagName}">{tag.tagName}</a>
</td> </td>
<td>{tag.tagType}</td> <td>{tag.tagType}</td>
<td>{tag.postCount}</td> <td>{tag.postCount}</td>

View File

@ -1,16 +1,17 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { getTag, getPostSearchTag } from "../../api"; import { getTag, getPostSearchTag } from "$lib/api";
import EditTagNotesPanel from "../../components/TagNotes/EditTagNotesPanel.svelte"; import EditTagNotesPanel from "$lib/components/TagNotes/EditTagNotesPanel.svelte";
import ViewTagNotesPanel from "../../components/TagNotes/ViewTagNotesPanel.svelte"; import ViewTagNotesPanel from "$lib/components/TagNotes/ViewTagNotesPanel.svelte";
import ViewTagPanel from "../../components/Tag/ViewTagPanel.svelte"; import ViewTagPanel from "$lib/components/Tag/ViewTagPanel.svelte";
import EditTagPanel from "../../components/Tag/EditTagPanel.svelte"; import EditTagPanel from "$lib/components/Tag/EditTagPanel.svelte";
import PostGallery from "../../components/Post/PostGallery.svelte"; import PostGallery from "$lib/components/Post/PostGallery.svelte";
import { Link } from "svelte-routing";
export let tag; import { page } from "$app/stores";
let { tag } = $page.params;
let data; let data;
let posts = []; let posts = [];

View File

@ -1,8 +1,8 @@
<script> <script>
import { uploadBlob, postCreate, getTagAutocomplete } from "../../api.js"; import { uploadBlob, postCreate, getTagAutocomplete } from "$lib/api";
import { navigate, Link } from "svelte-routing"; import { goto } from "$app/navigation";
import Tags from "svelte-tags-input"; import Tags from "svelte-tags-input";
import AuthRequired from "../../AuthRequired.svelte"; import AuthRequired from "$lib/components/AuthRequired.svelte";
let currentProgress = 0; let currentProgress = 0;
@ -45,7 +45,7 @@
const onSubmit = async () => { const onSubmit = async () => {
const response = await postCreate(form); const response = await postCreate(form);
navigate(`/post/${response.id}`); goto(`/post/${response.id}`);
}; };
</script> </script>
@ -84,7 +84,7 @@
<p class="help"> <p class="help">
Similar posts: Similar posts:
{#each similar as post, i} {#each similar as post, i}
<Link to="/post/{post.id}">{post.id}</Link> <a href="/post/{post.id}">{post.id}</a>
{#if i < similar.length - 1} {#if i < similar.length - 1}
, ,
{/if} {/if}
@ -107,7 +107,12 @@
<div class="field"> <div class="field">
<label for="tags" class="label">Tags</label> <label for="tags" class="label">Tags</label>
<div class="control" id="tags"> <div class="control" id="tags">
<Tags addKeys={[9, 32]} on:tags={onTagChange} autoComplete={onAutocomplete} autoCompleteFilter={false}/> <Tags
addKeys={[9, 32]}
on:tags={onTagChange}
autoComplete={onAutocomplete}
autoCompleteFilter={false}
/>
</div> </div>
</div> </div>
<div class="control"> <div class="control">

View File

@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { getUserProfile } from "../../api.js"; import { getUserProfile } from "$lib/api";
import AuthRequired from "../../AuthRequired.svelte"; import AuthRequired from "$lib/components/AuthRequired.svelte";
let user; let user;

View File

@ -1,10 +0,0 @@
import { writable } from "svelte/store";
const storedToken = localStorage.getItem("apiToken") ?? "";
export const token = writable(storedToken);
token.subscribe(value => {
if (value != null) {
localStorage.setItem("apiToken", value);
}
});

View File

@ -0,0 +1 @@
/* Variables and mixins declared here will be available in all other SCSS files */

2
web/app/static/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

21
web/app/svelte.config.js Normal file
View File

@ -0,0 +1,21 @@
import adapter from "@sveltejs/adapter-static";
import { vitePreprocess } from "@sveltejs/kit/vite";
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [vitePreprocess({})],
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter({
fallback: 'index.html', // may differ from host to host,
pages: '../static',
})
},
};
export default config;

17
web/app/tsconfig.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

14
web/app/vite.config.ts Normal file
View File

@ -0,0 +1,14 @@
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [sveltekit()],
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "src/variables.scss" as *;',
},
},
},
});

File diff suppressed because it is too large Load Diff