diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml deleted file mode 100644 index 52c98bcb..00000000 --- a/.github/workflows/client.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Client CI - -on: - push: - branches: - - main - pull_request: - -concurrency: - group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' - cancel-in-progress: true - -jobs: - build: - name: Build - runs-on: ubuntu-latest - env: - workdir: ./client - - steps: - - name: Checkout Source - uses: actions/checkout@v3 - - - name: Setup Bun - uses: oven-sh/setup-bun@v1 - with: - bun-version: latest - - - name: Install Dependencies - working-directory: ${{ env.workdir }} - run: bun i - - - name: Build Check - working-directory: ${{ env.workdir }} - env: - NODE_ENV: production - UNSPLASH_ACCESS_KEY: testing - run: bun run build - - - name: Run Lint Check - working-directory: ${{ env.workdir }} - run: bun run lint diff --git a/README.md b/README.md index 942cd16f..5e82744a 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@
- [![Discord](https://img.shields.io/discord/1011702194925490186?color=blue&label=discord&logo=discord)](https://discord.gg/yde6mcgs2C) - ![Build](https://github.com/TownHall-HQ/TownHall/workflows/build/badge.svg) - ![Clippy](https://github.com/TownHall-HQ/TownHall/workflows/clippy/badge.svg) - ![Formatter](https://github.com/TownHall-HQ/TownHall/workflows/fmt/badge.svg) +[![Discord](https://img.shields.io/discord/1011702194925490186?color=blue&label=discord&logo=discord)](https://discord.gg/yde6mcgs2C) +![Build](https://github.com/TownHall-HQ/TownHall/workflows/build/badge.svg) +![Clippy](https://github.com/TownHall-HQ/TownHall/workflows/clippy/badge.svg) +![Formatter](https://github.com/TownHall-HQ/TownHall/workflows/fmt/badge.svg)
@@ -53,6 +53,8 @@ cargo run serve > Note: As of today migrations runs when bootstrapping the server automatically +### To read the documentation of the web [here](crates/web/README.md) + ## Software Architecture ### Layers @@ -63,7 +65,7 @@ the services.
- ![softarq](https://github.com/TownHall-HQ/TownHall/assets/34756077/86abfb8d-8e96-4e93-9677-4e0864f53da6) +![softarq](https://github.com/TownHall-HQ/TownHall/assets/34756077/86abfb8d-8e96-4e93-9677-4e0864f53da6)
@@ -77,17 +79,17 @@ present but are not 100% accurate to the original Domain Driven Design architect The client and server solution is available in this repository. -Directory | Description ---- | --- -`client` | Web Front-End, written in SvelteKit & TypeScript -`crates/` | Contains GraphQL Server Logic, CLI and Domain libraries. Rust is the predominant language. -`crates/cli` | CLI used to manage the Server instance. run database migrations and other developer tasks -`crates/core` | Domain Logic, includes Models, Value Objects, Repositories and Services -`crates/entity` | Entities generated from database -`crates/migration` | Database migrations -`crates/server` | HTTP Server Logic, uses Axum and GraphQL -`crates/test` | E2E Tests for the GraphQL Server -`crates/web` | Web UI, written in Leptos +| Directory | Description | +| ------------------ | ------------------------------------------------------------------------------------------ | +| `client` | Web Front-End, written in SvelteKit & TypeScript | +| `crates/` | Contains GraphQL Server Logic, CLI and Domain libraries. Rust is the predominant language. | +| `crates/cli` | CLI used to manage the Server instance. run database migrations and other developer tasks | +| `crates/core` | Domain Logic, includes Models, Value Objects, Repositories and Services | +| `crates/entity` | Entities generated from database | +| `crates/migration` | Database migrations | +| `crates/server` | HTTP Server Logic, uses Axum and GraphQL | +| `crates/test` | E2E Tests for the GraphQL Server | +| `crates/web` | Web UI, written in Leptos | ## Testing @@ -114,7 +116,6 @@ just e2e_test Teardown containers using `just undev`. - ## Contributors diff --git a/client/.env.example b/client/.env.example deleted file mode 100644 index 24d35f4d..00000000 --- a/client/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -VITE_GRAPHQL_URL=http://127.0.0.1:7878/graphql -UNSPLASH_ACCESS_KEY= \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore deleted file mode 100644 index 38972655..00000000 --- a/client/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -.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 diff --git a/client/.npmrc b/client/.npmrc deleted file mode 100644 index b6f27f13..00000000 --- a/client/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/client/LICENSE b/client/LICENSE deleted file mode 100644 index e995fdca..00000000 --- a/client/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2023 Whizzes Contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/client/README.md b/client/README.md deleted file mode 100644 index 1a8c0fad..00000000 --- a/client/README.md +++ /dev/null @@ -1,50 +0,0 @@ - -
-

townhall Frontend

-

- A "Host Yourself" Chat powered by Rust and Whizzes Contributors -

-
- -
- -[![Discord](https://img.shields.io/discord/1011702194925490186?color=blue&label=discord&logo=discord)](https://discord.gg/yde6mcgs2C) -![Build](https://github.com/TownHall-HQ/TownHall/workflows/build/badge.svg) -![Tests](https://github.com/TownHall-HQ/TownHall/workflows/test/badge.svg) -![Lint](https://github.com/TownHall-HQ/TownHall/workflows/lint/badge.svg) - -
- -## Development - -> We use [Bun.sh][1] to run this project. We recommend you install it to -> contribute w/o any issues. `npm i -g bun` - -```bash -# clone repository -git clone git@github.com:whizzes/townhall.git - -# cd into the new directory -cd ./townhall/client - -# create a `.env` file by copying contents from `.env.example` -cp .env.example .env - -# install dependencies -bun i - -# optional: make sure townhall server is running -lsof -i -P -n | grep LISTEN - -# run on development mode -bun run dev:open - -# optional: if you don't want the browser to open-up automatically run -# "bun run dev" instead -``` - -## License - -Licensed under the MIT License - -[1]: https://bun.sh diff --git a/client/biome.json b/client/biome.json deleted file mode 100644 index aafea9c0..00000000 --- a/client/biome.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "organizeImports": { - "enabled": false - }, - "linter": { - "enabled": true, - "ignore": [ - "./src/lib/graphql/schema.ts" - ], - "rules": { - "recommended": true - } - }, - "formatter": { - "enabled": true, - "ignore": [ - "./src/lib/graphql/schema.ts" - ], - "indentStyle": "space" - }, - "javascript": { - "formatter": { - "quoteStyle": "single" - } - } -} diff --git a/client/bun.lockb b/client/bun.lockb deleted file mode 100755 index 2a4d2e9e..00000000 Binary files a/client/bun.lockb and /dev/null differ diff --git a/client/codegen.yml b/client/codegen.yml deleted file mode 100644 index 977d02d0..00000000 --- a/client/codegen.yml +++ /dev/null @@ -1,13 +0,0 @@ -overwrite: true -schema: ${VITE_GRAPHQL_URL} -documents: - - 'src/**/*.gql' -generates: - src/lib/graphql/schema.ts: - config: - urqlImportFrom: '@urql/svelte' - withHooks: false - plugins: - - 'typescript' - - 'typescript-operations' - - 'typescript-urql' diff --git a/client/package.json b/client/package.json deleted file mode 100644 index 2c36b9ad..00000000 --- a/client/package.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "townhall-web", - "version": "0.1.0", - "private": true, - "scripts": { - "coverage": "vitest run --coverage", - "dev": "vite dev", - "dev:open": "vite dev --open", - "dev:host": "vite dev --host", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "generate": "graphql-codegen --config codegen.yml -r dotenv/config", - "lint": "biome format ./src && biome check ./src", - "format": "biome format --write ./src && biome check --apply ./src", - "format:unsafe": "biome format --write ./src && biome check --apply-unsafe ./src", - "test": "vitest", - "test:watch": "vitest --watch", - "generate-pwa-assets": "pwa-assets-generator" - }, - "devDependencies": { - "@biomejs/biome": "1.1.2", - "@fontsource/inter": "^5.0.8", - "@graphql-codegen/cli": "^5.0.0", - "@graphql-codegen/typescript": "^4.0.1", - "@graphql-codegen/typescript-operations": "^4.0.1", - "@graphql-codegen/typescript-urql": "^3.7.3", - "@sveltejs/adapter-auto": "^2.1.0", - "@sveltejs/kit": "^1.22.4", - "@sveltejs/vite-plugin-svelte": "^2.4.3", - "@tailwindcss/forms": "^0.5.4", - "@testing-library/svelte": "^4.0.3", - "@urql/core": "^4.1.1", - "@urql/exchange-auth": "^2.1.6", - "@urql/svelte": "^4.0.4", - "@vite-pwa/sveltekit": "^0.2.7", - "@whizzes/svelte-forms": "^0.2.3", - "@whizzes/svelte-notifications": "^0.1.4", - "@whizzes/svelte-placeholder": "^0.1.0", - "autoprefixer": "^10.4.14", - "classnames": "^2.3.2", - "graphql": "^16.7.1", - "graphql-tag": "^2.12.6", - "jsdom": "^22.1.0", - "postcss": "^8.4.27", - "svelte": "^4.1.2", - "svelte-check": "^3.4.6", - "tailwindcss": "^3.3.3", - "tslib": "^2.6.1", - "typescript": "^5.1.6", - "unplugin-icons": "^0.16.5", - "unsplash-js": "^7.0.18", - "vite": "^4.4.8", - "vitest": "^0.34.1", - "yup": "^1.2.0", - "vite-plugin-pwa": "^0.16.5" - }, - "type": "module", - "dependencies": {} -} diff --git a/client/postcss.config.cjs b/client/postcss.config.cjs deleted file mode 100644 index 5cbc2c7d..00000000 --- a/client/postcss.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {} - } -}; diff --git a/client/src/app.css b/client/src/app.css deleted file mode 100644 index 8adff87f..00000000 --- a/client/src/app.css +++ /dev/null @@ -1,7 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -* { - font-family: 'Inter', sans-serif; -} diff --git a/client/src/app.d.ts b/client/src/app.d.ts deleted file mode 100644 index a2f9bf99..00000000 --- a/client/src/app.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces - -import type { - CurrentUserFragmentFragment, - PostEdge, -} from '$lib/graphql/schema'; - -declare global { - namespace App { - // interface Error {} - // interface Platform {} - - interface PageData { - accessToken?: string | null; - user?: CurrentUserFragmentFragment | null; - posts?: PostEdge[] | null; - } - - interface Locals { - accessToken?: string | null; - user?: CurrentUserFragmentFragment | null; - } - } -} diff --git a/client/src/app.html b/client/src/app.html deleted file mode 100644 index a8f350bf..00000000 --- a/client/src/app.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - %sveltekit.head% - - -
%sveltekit.body%
- - diff --git a/client/src/hooks.server.ts b/client/src/hooks.server.ts deleted file mode 100644 index 54633f18..00000000 --- a/client/src/hooks.server.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createClient, cacheExchange, fetchExchange } from '@urql/core'; - -import type { Handle } from '@sveltejs/kit'; -import { AuthService } from '$lib/services/AuthService'; - -export const handle: Handle = async ({ event, resolve }) => { - try { - const accessToken = event.cookies.get('accessToken'); - - if (!accessToken) { - return await resolve(event); - } - - if (event.locals.accessToken && event.locals.user) { - return await resolve(event); - } - - const urqlClient = createClient({ - url: import.meta.env.VITE_GRAPHQL_URL, - exchanges: [cacheExchange, fetchExchange], - }); - - const userDetials = await AuthService.whoami(urqlClient, accessToken); - - if (userDetials && accessToken) { - event.locals.accessToken = accessToken; - event.locals.user = userDetials; - } - - return await resolve(event); - } catch (err) { - event.locals.user = null; - event.locals.accessToken = null; - - return await resolve(event); - } -}; diff --git a/client/src/lib/actions/click-outside.ts b/client/src/lib/actions/click-outside.ts deleted file mode 100644 index dd24710d..00000000 --- a/client/src/lib/actions/click-outside.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** Dispatch event on click outside of node */ -// biome-ignore lint/suspicious/noExplicitAny: -export function clickOutside(node: HTMLElement): any { - const handleClick = (event: MouseEvent) => { - if ( - node && - !node.contains(event.target as Node) && - !event.defaultPrevented - ) { - node.dispatchEvent(new CustomEvent('clickOutside')); - } - }; - - document.addEventListener('click', handleClick, true); - - return { - destroy() { - document.removeEventListener('click', handleClick, true); - }, - on: { - clickOutside: handleClick, - }, - }; -} diff --git a/client/src/lib/actions/intersection-observer.ts b/client/src/lib/actions/intersection-observer.ts deleted file mode 100644 index ecd92e95..00000000 --- a/client/src/lib/actions/intersection-observer.ts +++ /dev/null @@ -1,17 +0,0 @@ -export default function intersectionObserver({ - fetch, - element, -}: { fetch: Function; element: Element }) { - if (element) { - const observer = new IntersectionObserver( - (entries) => { - const first = entries[0]; - if (first.isIntersecting) { - fetch(); - } - }, - { threshold: 1 }, - ); - observer.observe(element); - } -} diff --git a/client/src/lib/components/Button.svelte b/client/src/lib/components/Button.svelte deleted file mode 100644 index 3bc9facf..00000000 --- a/client/src/lib/components/Button.svelte +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/client/src/lib/components/Card.svelte b/client/src/lib/components/Card.svelte deleted file mode 100644 index b318951d..00000000 --- a/client/src/lib/components/Card.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - -
- -
diff --git a/client/src/lib/components/Divider.svelte b/client/src/lib/components/Divider.svelte deleted file mode 100644 index e9cab0f8..00000000 --- a/client/src/lib/components/Divider.svelte +++ /dev/null @@ -1 +0,0 @@ - diff --git a/client/src/lib/components/Feed/Feed.svelte b/client/src/lib/components/Feed/Feed.svelte deleted file mode 100644 index f598995d..00000000 --- a/client/src/lib/components/Feed/Feed.svelte +++ /dev/null @@ -1,76 +0,0 @@ - - -
- {#each posts as post} - - {/each} - - {#if loadingPosts} - {#each Array.from({ length: 6 }) as _} -
- - - - - - - - - -
- {/each} - {/if} -
diff --git a/client/src/lib/components/Feed/Post.svelte b/client/src/lib/components/Feed/Post.svelte deleted file mode 100644 index 2936be4c..00000000 --- a/client/src/lib/components/Feed/Post.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - - -
- -
-

- {post.author.name} - {post.author.surname} - @{post.author.username} -

- -
-
-
-

- {post.title} -

- {#if post.content} -

{post.content}

- {/if} -
-
diff --git a/client/src/lib/components/Feed/index.ts b/client/src/lib/components/Feed/index.ts deleted file mode 100644 index 24b3d152..00000000 --- a/client/src/lib/components/Feed/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as Feed } from './Feed.svelte'; -export { default as Post } from './Post.svelte'; diff --git a/client/src/lib/components/Notification/Notification.svelte b/client/src/lib/components/Notification/Notification.svelte deleted file mode 100644 index ed2a2b00..00000000 --- a/client/src/lib/components/Notification/Notification.svelte +++ /dev/null @@ -1,51 +0,0 @@ - - -
  • -
    -
    - {#if notification.kind === NotificationKind.Success} - - {:else if notification.kind === NotificationKind.Warning} - - {:else} - - {/if} -
    -
    -
    - {notification.title} -

    {notification.message}

    -
    -
  • diff --git a/client/src/lib/components/PostBox.svelte b/client/src/lib/components/PostBox.svelte deleted file mode 100644 index f1e57cf6..00000000 --- a/client/src/lib/components/PostBox.svelte +++ /dev/null @@ -1,72 +0,0 @@ - - -
    - -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    diff --git a/client/src/lib/components/ReloadPrompt.svelte b/client/src/lib/components/ReloadPrompt.svelte deleted file mode 100644 index 1c437c01..00000000 --- a/client/src/lib/components/ReloadPrompt.svelte +++ /dev/null @@ -1,48 +0,0 @@ - - -{#if toast} - -{/if} diff --git a/client/src/lib/components/TextField.svelte b/client/src/lib/components/TextField.svelte deleted file mode 100644 index 88b1bd57..00000000 --- a/client/src/lib/components/TextField.svelte +++ /dev/null @@ -1,94 +0,0 @@ - - -{#if label} - -{/if} - -
    -
    - - {#if error} -
    - -
    - {/if} -
    - {#if error} -

    {error}

    - {/if} -
    - - diff --git a/client/src/lib/components/ui/modal.svelte b/client/src/lib/components/ui/modal.svelte deleted file mode 100644 index c64bf10a..00000000 --- a/client/src/lib/components/ui/modal.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - - - (show = false)} - on:click|self={() => dialog.close()} - class="max-w-lg w-full rounded-md dark:backdrop:bg-neutral-800/60 dark:bg-neutral-700 dark:text-white" -> - -
    - {#if $$slots.header} -
    - -
    - {/if} -
    - -
    - -
    - -
    -
    -
    diff --git a/client/src/lib/components/upload-modal/action-card.svelte b/client/src/lib/components/upload-modal/action-card.svelte deleted file mode 100644 index 8c3a1809..00000000 --- a/client/src/lib/components/upload-modal/action-card.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/client/src/lib/components/upload-modal/file-form.svelte b/client/src/lib/components/upload-modal/file-form.svelte deleted file mode 100644 index 5ae22975..00000000 --- a/client/src/lib/components/upload-modal/file-form.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/client/src/lib/components/upload-modal/index.svelte b/client/src/lib/components/upload-modal/index.svelte deleted file mode 100644 index 30a0d6b6..00000000 --- a/client/src/lib/components/upload-modal/index.svelte +++ /dev/null @@ -1,99 +0,0 @@ - - - - {#if currentCard !== undefined} -
    - -
    - {/if} - {#if currentCard === "url"} - { - destroyLocalBlob(); - preview = { - ...preview, - url: ev.detail.url, - type: "url", - }; - currentCard = "preview"; - }} - /> - {:else if currentCard === "local"} - - {:else if currentCard === "preview"} - - {:else} -
    - (currentCard = ev.detail.cardType)} - > -
    - -
    -
    - (currentCard = ev.detail.cardType)} - text="Upload from Local File" - > -
    - -
    -
    -
    - {/if} -
    diff --git a/client/src/lib/components/upload-modal/preview.svelte b/client/src/lib/components/upload-modal/preview.svelte deleted file mode 100644 index b9113265..00000000 --- a/client/src/lib/components/upload-modal/preview.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - -
    - Preview -
    - -
    - -
    diff --git a/client/src/lib/components/upload-modal/url-form.svelte b/client/src/lib/components/upload-modal/url-form.svelte deleted file mode 100644 index e3123f8c..00000000 --- a/client/src/lib/components/upload-modal/url-form.svelte +++ /dev/null @@ -1,29 +0,0 @@ - - -
    -
    - -
    -
    - - - -
    -
    diff --git a/client/src/lib/graphql/schema.ts b/client/src/lib/graphql/schema.ts deleted file mode 100644 index d88a686d..00000000 --- a/client/src/lib/graphql/schema.ts +++ /dev/null @@ -1,550 +0,0 @@ -import gql from 'graphql-tag'; -export type Maybe = T | null; -export type InputMaybe = Maybe; -export type Exact = { [K in keyof T]: T[K] }; -export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; -export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -export type MakeEmpty = { [_ in K]?: never }; -export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; -export type Omit = Pick>; -/** All built-in and custom scalars, mapped to their actual values */ -export type Scalars = { - ID: { input: string; output: string; } - String: { input: string; output: string; } - Boolean: { input: boolean; output: boolean; } - Int: { input: number; output: number; } - Float: { input: number; output: number; } - DateTime: { input: any; output: any; } - Email: { input: any; output: any; } - Pxid: { input: any; output: any; } - Upload: { input: any; output: any; } -}; - -export type AccessToken = { - __typename?: 'AccessToken'; - accessToken: Scalars['String']['output']; -}; - -export type AuthError = { - __typename?: 'AuthError'; - code: AuthErrorCode; - message: Scalars['String']['output']; -}; - -export enum AuthErrorCode { - EmailParseError = 'EMAIL_PARSE_ERROR', - EmailTaken = 'EMAIL_TAKEN', - Internal = 'INTERNAL', - Unauthorized = 'UNAUTHORIZED' -} - -export type Image = { - __typename?: 'Image'; - id: Scalars['Pxid']['output']; - url: Scalars['String']['output']; -}; - -export type Me = { - __typename?: 'Me'; - error?: Maybe; - user?: Maybe; -}; - -export type MutationRoot = { - __typename?: 'MutationRoot'; - /** Creates a post authored by the user identified by the provided token */ - postCreate: PostCreate; - tokenCreate: TokenCreate; - userAvatarUpdate: UserAvatarUpdate; - userFollow: UserFollow; - userRegister: UserRegister; - userUnfollow: UserUnfollow; - userUpdate: UserUpdate; -}; - - -export type MutationRootPostCreateArgs = { - input: PostCreateInput; -}; - - -export type MutationRootTokenCreateArgs = { - email: Scalars['Email']['input']; - password: Scalars['String']['input']; -}; - - -export type MutationRootUserAvatarUpdateArgs = { - file: Scalars['Upload']['input']; -}; - - -export type MutationRootUserFollowArgs = { - followeeId: Scalars['Pxid']['input']; -}; - - -export type MutationRootUserRegisterArgs = { - input: UserRegisterInput; -}; - - -export type MutationRootUserUnfollowArgs = { - followeeId: Scalars['Pxid']['input']; -}; - - -export type MutationRootUserUpdateArgs = { - input: UserUpdateInput; -}; - -/** Information about pagination in a connection */ -export type PageInfo = { - __typename?: 'PageInfo'; - /** When paginating forwards, the cursor to continue. */ - endCursor?: Maybe; - /** When paginating forwards, are there more items? */ - hasNextPage: Scalars['Boolean']['output']; - /** When paginating backwards, are there more items? */ - hasPreviousPage: Scalars['Boolean']['output']; - /** When paginating backwards, the cursor to continue. */ - startCursor?: Maybe; -}; - -export type Post = { - __typename?: 'Post'; - author: User; - authorId: Scalars['Pxid']['output']; - content?: Maybe; - createdAt: Scalars['DateTime']['output']; - head: Scalars['Boolean']['output']; - id: Scalars['Pxid']['output']; - parentId?: Maybe; - title: Scalars['String']['output']; - updatedAt: Scalars['DateTime']['output']; -}; - -export type PostConnection = { - __typename?: 'PostConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - totalCount: Scalars['Int']['output']; -}; - -export type PostCreate = { - __typename?: 'PostCreate'; - error?: Maybe; - post?: Maybe; -}; - -export type PostCreateInput = { - content?: InputMaybe; - parentId?: InputMaybe; - title: Scalars['String']['input']; -}; - -/** An edge in a connection. */ -export type PostEdge = { - __typename?: 'PostEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: Post; -}; - -export type PostError = { - __typename?: 'PostError'; - code: PostErrorCode; - message: Scalars['String']['output']; -}; - -export enum PostErrorCode { - InvalidParentId = 'INVALID_PARENT_ID', - Unauthorized = 'UNAUTHORIZED', - Unknown = 'UNKNOWN' -} - -export type QueryRoot = { - __typename?: 'QueryRoot'; - me: Me; - posts: PostConnection; - user: UserConnection; -}; - - -export type QueryRootPostsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; -}; - - -export type QueryRootUserArgs = { - after?: InputMaybe; - before?: InputMaybe; - filter?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; -}; - -export type TokenCreate = { - __typename?: 'TokenCreate'; - error?: Maybe; - token?: Maybe; -}; - -/** A Platform's User */ -export type User = { - __typename?: 'User'; - avatar?: Maybe; - createdAt: Scalars['DateTime']['output']; - email: Scalars['String']['output']; - id: Scalars['Pxid']['output']; - name: Scalars['String']['output']; - /** Lists posts authored by this user */ - posts: PostConnection; - surname: Scalars['String']['output']; - updatedAt: Scalars['DateTime']['output']; - username: Scalars['String']['output']; -}; - - -/** A Platform's User */ -export type UserPostsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; -}; - -export type UserAvatarUpdate = { - __typename?: 'UserAvatarUpdate'; - error?: Maybe; - user?: Maybe; -}; - -export type UserConnection = { - __typename?: 'UserConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type UserEdge = { - __typename?: 'UserEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: User; -}; - -export type UserError = { - __typename?: 'UserError'; - code: UserErrorCode; - message: Scalars['String']['output']; -}; - -export enum UserErrorCode { - AvatarUploadError = 'AVATAR_UPLOAD_ERROR', - EmailTaken = 'EMAIL_TAKEN', - Internal = 'INTERNAL', - InvalidEmail = 'INVALID_EMAIL', - Unauthorized = 'UNAUTHORIZED' -} - -export type UserFilterInput = { - email?: InputMaybe; - id?: InputMaybe; - username?: InputMaybe; -}; - -export type UserFollow = { - __typename?: 'UserFollow'; - error?: Maybe; -}; - -export type UserRegister = { - __typename?: 'UserRegister'; - error?: Maybe; - user?: Maybe; -}; - -export type UserRegisterInput = { - email: Scalars['Email']['input']; - name: Scalars['String']['input']; - password: Scalars['String']['input']; - surname: Scalars['String']['input']; - username: Scalars['String']['input']; -}; - -export type UserUnfollow = { - __typename?: 'UserUnfollow'; - error?: Maybe; -}; - -export type UserUpdate = { - __typename?: 'UserUpdate'; - error?: Maybe; - user?: Maybe; -}; - -export type UserUpdateInput = { - name?: InputMaybe; - surname?: InputMaybe; -}; - -export type TokenCreateMutationVariables = Exact<{ - email: Scalars['Email']['input']; - password: Scalars['String']['input']; -}>; - - -export type TokenCreateMutation = { __typename?: 'MutationRoot', tokenCreate: { __typename?: 'TokenCreate', token?: { __typename?: 'AccessToken', accessToken: string } | null, error?: { __typename?: 'AuthError', code: AuthErrorCode, message: string } | null } }; - -export type AuthorFragment = { __typename?: 'User', id: any, name: string, surname: string, username: string, email: string, createdAt: any, updatedAt: any }; - -export type PostCreateMutationVariables = Exact<{ - input: PostCreateInput; -}>; - - -export type PostCreateMutation = { __typename?: 'MutationRoot', postCreate: { __typename?: 'PostCreate', post?: { __typename?: 'Post', id: any, authorId: any, parentId?: any | null, head: boolean, title: string, content?: string | null, createdAt: any, updatedAt: any } | null, error?: { __typename?: 'PostError', code: PostErrorCode, message: string } | null } }; - -export type PostCreateFieldsFragment = { __typename?: 'Post', id: any, authorId: any, parentId?: any | null, head: boolean, title: string, content?: string | null, createdAt: any, updatedAt: any }; - -export type PostListQueryVariables = Exact<{ - first?: InputMaybe; - last?: InputMaybe; - after?: InputMaybe; - before?: InputMaybe; -}>; - - -export type PostListQuery = { __typename?: 'QueryRoot', posts: { __typename?: 'PostConnection', edges: Array<{ __typename?: 'PostEdge', node: { __typename?: 'Post', id: any, authorId: any, parentId?: any | null, head: boolean, title: string, content?: string | null, createdAt: any, updatedAt: any, author: { __typename?: 'User', id: any, name: string, surname: string, username: string, email: string, createdAt: any, updatedAt: any } } }>, pageInfo: { __typename?: 'PageInfo', hasPreviousPage: boolean, hasNextPage: boolean, startCursor?: string | null, endCursor?: string | null } } }; - -export type PostListFieldsFragment = { __typename?: 'Post', id: any, authorId: any, parentId?: any | null, head: boolean, title: string, content?: string | null, createdAt: any, updatedAt: any, author: { __typename?: 'User', id: any, name: string, surname: string, username: string, email: string, createdAt: any, updatedAt: any } }; - -export type CurrentUserFragment = { __typename?: 'User', id: any, name: string, surname: string, email: string, username: string, createdAt: any, updatedAt: any, avatar?: { __typename?: 'Image', id: any, url: string } | null }; - -export type MeQueryVariables = Exact<{ [key: string]: never; }>; - - -export type MeQuery = { __typename?: 'QueryRoot', me: { __typename?: 'Me', user?: { __typename?: 'User', id: any, name: string, surname: string, email: string, username: string, createdAt: any, updatedAt: any, avatar?: { __typename?: 'Image', id: any, url: string } | null } | null } }; - -export type UserAvatarUpdateMutationVariables = Exact<{ - file: Scalars['Upload']['input']; -}>; - - -export type UserAvatarUpdateMutation = { __typename?: 'MutationRoot', userAvatarUpdate: { __typename?: 'UserAvatarUpdate', user?: { __typename?: 'User', id: any, name: string, surname: string, email: string, username: string, createdAt: any, updatedAt: any, avatar?: { __typename?: 'Image', id: any, url: string } | null } | null, error?: { __typename?: 'UserError', code: UserErrorCode, message: string } | null } }; - -export type UserPostsListQueryVariables = Exact<{ - username?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - after?: InputMaybe; - before?: InputMaybe; -}>; - - -export type UserPostsListQuery = { __typename?: 'QueryRoot', user: { __typename?: 'UserConnection', edges: Array<{ __typename?: 'UserEdge', node: { __typename?: 'User', id: any, name: string, surname: string, username: string, email: string, createdAt: any, updatedAt: any, posts: { __typename?: 'PostConnection', edges: Array<{ __typename?: 'PostEdge', node: { __typename?: 'Post', id: any, authorId: any, parentId?: any | null, head: boolean, title: string, content?: string | null, createdAt: any, updatedAt: any, author: { __typename?: 'User', id: any, name: string, surname: string, username: string, email: string, createdAt: any, updatedAt: any } } }>, pageInfo: { __typename?: 'PageInfo', hasPreviousPage: boolean, hasNextPage: boolean, startCursor?: string | null, endCursor?: string | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasPreviousPage: boolean, hasNextPage: boolean, startCursor?: string | null, endCursor?: string | null } } }; - -export type UserRegisterMutationVariables = Exact<{ - input: UserRegisterInput; -}>; - - -export type UserRegisterMutation = { __typename?: 'MutationRoot', userRegister: { __typename?: 'UserRegister', user?: { __typename?: 'User', id: any, name: string, surname: string, email: string, username: string, createdAt: any, updatedAt: any, avatar?: { __typename?: 'Image', id: any, url: string } | null } | null, error?: { __typename?: 'UserError', code: UserErrorCode, message: string } | null } }; - -export type UserUpdateMutationVariables = Exact<{ - input: UserUpdateInput; -}>; - - -export type UserUpdateMutation = { __typename?: 'MutationRoot', userUpdate: { __typename?: 'UserUpdate', user?: { __typename?: 'User', id: any, name: string, surname: string, email: string, username: string, createdAt: any, updatedAt: any, avatar?: { __typename?: 'Image', id: any, url: string } | null } | null, error?: { __typename?: 'UserError', code: UserErrorCode, message: string } | null } }; - -export const PostCreateFieldsFragmentDoc = gql` - fragment PostCreateFields on Post { - id - authorId - parentId - head - title - content - createdAt - updatedAt -} - `; -export const AuthorFragmentDoc = gql` - fragment Author on User { - id - name - surname - username - email - createdAt - updatedAt -} - `; -export const PostListFieldsFragmentDoc = gql` - fragment PostListFields on Post { - id - authorId - parentId - head - title - content - createdAt - updatedAt - author { - ...Author - } -} - ${AuthorFragmentDoc}`; -export const CurrentUserFragmentDoc = gql` - fragment CurrentUser on User { - id - name - surname - email - username - avatar { - id - url - } - createdAt - updatedAt -} - `; -export const TokenCreateDocument = gql` - mutation TokenCreate($email: Email!, $password: String!) { - tokenCreate(email: $email, password: $password) { - token { - accessToken - } - error { - code - message - } - } -} - `; -export const PostCreateDocument = gql` - mutation PostCreate($input: PostCreateInput!) { - postCreate(input: $input) { - post { - ...PostCreateFields - } - error { - code - message - } - } -} - ${PostCreateFieldsFragmentDoc}`; -export const PostListDocument = gql` - query PostList($first: Int, $last: Int, $after: Pxid, $before: Pxid) { - posts(first: $first, last: $last, after: $after, before: $before) { - edges { - node { - ...PostListFields - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor - } - } -} - ${PostListFieldsFragmentDoc}`; -export const MeDocument = gql` - query Me { - me { - user { - ...CurrentUser - } - } -} - ${CurrentUserFragmentDoc}`; -export const UserAvatarUpdateDocument = gql` - mutation UserAvatarUpdate($file: Upload!) { - userAvatarUpdate(file: $file) { - user { - ...CurrentUser - } - error { - code - message - } - } -} - ${CurrentUserFragmentDoc}`; -export const UserPostsListDocument = gql` - query UserPostsList($username: String, $first: Int, $last: Int, $after: Pxid, $before: Pxid) { - user(filter: {username: $username}) { - edges { - node { - ...Author - posts(first: $first, last: $last, after: $after, before: $before) { - edges { - node { - ...PostListFields - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor - } - } - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor - } - } -} - ${AuthorFragmentDoc} -${PostListFieldsFragmentDoc}`; -export const UserRegisterDocument = gql` - mutation UserRegister($input: UserRegisterInput!) { - userRegister(input: $input) { - user { - ...CurrentUser - } - error { - code - message - } - } -} - ${CurrentUserFragmentDoc}`; -export const UserUpdateDocument = gql` - mutation UserUpdate($input: UserUpdateInput!) { - userUpdate(input: $input) { - user { - ...CurrentUser - } - error { - code - message - } - } -} - ${CurrentUserFragmentDoc}`; \ No newline at end of file diff --git a/client/src/lib/icons/desktop-icon.svelte b/client/src/lib/icons/desktop-icon.svelte deleted file mode 100644 index 177a3e0f..00000000 --- a/client/src/lib/icons/desktop-icon.svelte +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/client/src/lib/icons/url-icon.svelte b/client/src/lib/icons/url-icon.svelte deleted file mode 100644 index 47403be1..00000000 --- a/client/src/lib/icons/url-icon.svelte +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/client/src/lib/services/AuthService/graphql/TokenCreate.gql b/client/src/lib/services/AuthService/graphql/TokenCreate.gql deleted file mode 100644 index ba53ce06..00000000 --- a/client/src/lib/services/AuthService/graphql/TokenCreate.gql +++ /dev/null @@ -1,11 +0,0 @@ -mutation TokenCreate($email: Email!, $password: String!) { - tokenCreate(email: $email, password: $password) { - token { - accessToken - } - error { - code - message - } - } -} diff --git a/client/src/lib/services/AuthService/index.ts b/client/src/lib/services/AuthService/index.ts deleted file mode 100644 index b60d4ef1..00000000 --- a/client/src/lib/services/AuthService/index.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { MeDocument, TokenCreateDocument } from '$lib/graphql/schema'; - -import type { Client } from '@urql/core'; -import type { - AccessToken, - AuthErrorCode, - CurrentUserFragment, -} from '$lib/graphql/schema'; -import { GraphQLError } from '$lib/utils/graphql'; - -export class AuthError extends GraphQLError {} - -export class AuthService { - static async tokenCreate( - urqlClient: Client, - email: string, - password: string, - ): Promise { - const response = await urqlClient - .mutation( - TokenCreateDocument, - { - email, - password, - }, - { - requestPolicy: 'network-only', // We dont want to cache this request - }, - ) - .toPromise(); - - if (response?.error || response?.data?.tokenCreate?.error) { - if (response?.data?.tokenCreate?.error) { - const error: AuthError = response.data.tokenCreate.error; - - throw AuthError.new(error.code, error.message); - } - - throw response?.error; - } - - return response.data.tokenCreate.token; - } - - static async whoami( - urqlClient: Client, - accessToken: string, - ): Promise { - const response = await urqlClient - .query( - MeDocument, - {}, - { - requestPolicy: 'network-only', - fetchOptions: { - headers: { - Authorization: `JWT ${accessToken}`, - }, - }, - }, - ) - .toPromise(); - - if (response?.error || response?.data?.me?.error) { - throw new Error('Failed'); - } - - return response?.data?.me?.user; - } -} diff --git a/client/src/lib/services/PostService/graphql/Author.Fragment.gql b/client/src/lib/services/PostService/graphql/Author.Fragment.gql deleted file mode 100644 index bd21680d..00000000 --- a/client/src/lib/services/PostService/graphql/Author.Fragment.gql +++ /dev/null @@ -1,9 +0,0 @@ -fragment Author on User { - id - name - surname - username - email - createdAt - updatedAt -} diff --git a/client/src/lib/services/PostService/graphql/PostCreate.gql b/client/src/lib/services/PostService/graphql/PostCreate.gql deleted file mode 100644 index 89b8239a..00000000 --- a/client/src/lib/services/PostService/graphql/PostCreate.gql +++ /dev/null @@ -1,22 +0,0 @@ -mutation PostCreate($input: PostCreateInput!) { - postCreate(input: $input) { - post { - ...PostCreateFields - } - error { - code - message - } - } -} - -fragment PostCreateFields on Post { - id - authorId - parentId - head - title - content - createdAt - updatedAt -} diff --git a/client/src/lib/services/PostService/graphql/PostList.gql b/client/src/lib/services/PostService/graphql/PostList.gql deleted file mode 100644 index 96ac9ce7..00000000 --- a/client/src/lib/services/PostService/graphql/PostList.gql +++ /dev/null @@ -1,15 +0,0 @@ -query PostList($first: Int, $last: Int, $after: Pxid, $before: Pxid) { - posts (first: $first, last: $last, after: $after, before: $before){ - edges { - node { - ...PostListFields - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor - } - } -} diff --git a/client/src/lib/services/PostService/graphql/PostListFields.Fragment.gql b/client/src/lib/services/PostService/graphql/PostListFields.Fragment.gql deleted file mode 100644 index fcc1e9a7..00000000 --- a/client/src/lib/services/PostService/graphql/PostListFields.Fragment.gql +++ /dev/null @@ -1,13 +0,0 @@ -fragment PostListFields on Post { - id - authorId - parentId - head - title - content - createdAt - updatedAt - author { - ...Author - } -} diff --git a/client/src/lib/services/PostService/index.ts b/client/src/lib/services/PostService/index.ts deleted file mode 100644 index 29b16454..00000000 --- a/client/src/lib/services/PostService/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { PostCreateDocument } from '$lib/graphql/schema'; - -import type { Client } from '@urql/core'; -import type { - PostCreateFieldsFragment, - PostCreateInput, - PostErrorCode, -} from '$lib/graphql/schema'; - -export class PostError extends Error { - constructor(code: PostErrorCode, message: string) { - super(`PostError: ${code} - ${message}`); - this.name = 'PostError'; - } -} - -export class PostService { - static async create( - urqlClient: Client, - input: PostCreateInput, - ): Promise { - const response = await urqlClient - .mutation(PostCreateDocument, { - input, - }) - .toPromise(); - - if (response.error) { - throw new Error(response.error.message); - } - - if (response.data?.postCreate?.post) { - return response.data?.postCreate?.post; - } - - const { code, message } = response.data?.postCreate?.error || { - code: 'UNKNOWN', - message: 'Unknown error', - }; - - throw new PostError(code, message); - } -} diff --git a/client/src/lib/services/Unsplash.ts b/client/src/lib/services/Unsplash.ts deleted file mode 100644 index e942678e..00000000 --- a/client/src/lib/services/Unsplash.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { UnsplashImage } from '$routes/api/unsplash/+server'; - -export enum UnsplashErrorCode { - MissingApiToken = 0, - Unknown = 1, -} - -export class UnsplashService { - /** - * Fetches a random background image from Unsplash. - * - * This method relies on the `UNSPLASH_ACCESS_KEY` environment variable - * to be set. - */ - static async fetchUnsplashBackground(): Promise { - const response = await fetch('/api/unsplash', { - method: 'GET', - }); - - if (response.ok) { - return response.json(); - } - - return null; - } -} diff --git a/client/src/lib/services/UserService/graphql/CurrentUser.Fragment.gql b/client/src/lib/services/UserService/graphql/CurrentUser.Fragment.gql deleted file mode 100644 index 3a6b86fc..00000000 --- a/client/src/lib/services/UserService/graphql/CurrentUser.Fragment.gql +++ /dev/null @@ -1,13 +0,0 @@ -fragment CurrentUser on User { - id - name - surname - email - username - avatar { - id - url - } - createdAt - updatedAt -} diff --git a/client/src/lib/services/UserService/graphql/Me.gql b/client/src/lib/services/UserService/graphql/Me.gql deleted file mode 100644 index 61650688..00000000 --- a/client/src/lib/services/UserService/graphql/Me.gql +++ /dev/null @@ -1,7 +0,0 @@ -query Me { - me { - user { - ...CurrentUser - } - } -} diff --git a/client/src/lib/services/UserService/graphql/UserAvatarUpdate.gql b/client/src/lib/services/UserService/graphql/UserAvatarUpdate.gql deleted file mode 100644 index c1672ad0..00000000 --- a/client/src/lib/services/UserService/graphql/UserAvatarUpdate.gql +++ /dev/null @@ -1,11 +0,0 @@ -mutation UserAvatarUpdate($file: Upload!) { - userAvatarUpdate(file: $file) { - user { - ...CurrentUser - } - error { - code - message - } - } -} diff --git a/client/src/lib/services/UserService/graphql/UserPostsList.gql b/client/src/lib/services/UserService/graphql/UserPostsList.gql deleted file mode 100644 index b82c0764..00000000 --- a/client/src/lib/services/UserService/graphql/UserPostsList.gql +++ /dev/null @@ -1,28 +0,0 @@ -query UserPostsList($username: String, $first: Int, $last: Int, $after: Pxid, $before: Pxid) { - user(filter: {username: $username}) { - edges { - node { - ...Author - posts(first: $first, last: $last, after: $after, before: $before) { - edges { - node { - ...PostListFields - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor - } - } - } - } - pageInfo { - hasPreviousPage - hasNextPage - startCursor - endCursor - } - } -} diff --git a/client/src/lib/services/UserService/graphql/UserRegister.gql b/client/src/lib/services/UserService/graphql/UserRegister.gql deleted file mode 100644 index 03c689ae..00000000 --- a/client/src/lib/services/UserService/graphql/UserRegister.gql +++ /dev/null @@ -1,11 +0,0 @@ -mutation UserRegister($input: UserRegisterInput!) { - userRegister(input: $input) { - user { - ...CurrentUser - } - error { - code - message - } - } -} diff --git a/client/src/lib/services/UserService/graphql/UserUpdate.gql b/client/src/lib/services/UserService/graphql/UserUpdate.gql deleted file mode 100644 index ec675fac..00000000 --- a/client/src/lib/services/UserService/graphql/UserUpdate.gql +++ /dev/null @@ -1,11 +0,0 @@ -mutation UserUpdate($input: UserUpdateInput!) { - userUpdate(input: $input) { - user { - ...CurrentUser - } - error { - code - message - } - } -} diff --git a/client/src/lib/services/UserService/index.ts b/client/src/lib/services/UserService/index.ts deleted file mode 100644 index 4450f9eb..00000000 --- a/client/src/lib/services/UserService/index.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - UserAvatarUpdateDocument, - UserRegisterDocument, - UserUpdateDocument, -} from '$lib/graphql/schema'; - -import type { Client } from '@urql/core'; -import type { - CurrentUserFragment, - UserErrorCode, - UserRegisterInput, - UserUpdateInput, -} from '$lib/graphql/schema'; -import { GraphQLError } from '$lib/utils/graphql'; - -export class UserError extends GraphQLError {} - -export class UserService { - static async userRegister( - urqlClient: Client, - input: UserRegisterInput, - ): Promise { - const response = await urqlClient - .mutation( - UserRegisterDocument, - { - input, - }, - { - requestPolicy: 'network-only', // We dont want to cache this request - }, - ) - .toPromise(); - - if (response?.error || response?.data?.userRegister?.error) { - if (response?.data?.userRegister?.error) { - const error: UserError = response.data.userRegister.error; - - throw UserError.new(error.code, error.message); - } - - throw response?.error; - } - - return response.data.userRegister.user; - } - - static async userAvatarUpdate( - urqlClient: Client, - file: File, - ): Promise { - const response = await urqlClient - .mutation(UserAvatarUpdateDocument, { - file, - }) - .toPromise(); - - if (response?.error || response?.data?.userAvatarUpdate?.error) { - if (response?.data?.userAvatarUpdate?.error) { - const error: UserError = response.data.userAvatarUpdate.error; - - throw UserError.new(error.code, error.message); - } - - throw response?.error; - } - - return response.data.userAvatarUpdate.user; - } - - static async userUpdate( - urqlClient: Client, - input: UserUpdateInput, - ): Promise { - const response = await urqlClient - .mutation(UserUpdateDocument, { - input, - }) - .toPromise(); - - if (response.error) { - throw new Error(response.error.message); - } - - if (response.data?.userUpdate?.user) { - return response.data?.userUpdate?.user; - } - - const { code, message } = response.data?.userUpdate?.error || { - code: 'UNKNOWN', - message: 'Unknown error', - }; - - throw UserError.new(code, message); - } -} diff --git a/client/src/lib/stores/ui.ts b/client/src/lib/stores/ui.ts deleted file mode 100644 index 6f31e29b..00000000 --- a/client/src/lib/stores/ui.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { browser } from '$app/environment'; -import { writable, type Readable } from 'svelte/store'; - -const PREFERS_COLOR_SCHEME_DARK_MEDIA_QUERY = '(prefers-color-scheme:dark)'; - -export type UIStoreMethods = { - openSidebar(): void; - closeSidebar(): void; - setPreferred(colorScheme: ColorScheme): void; - setDarkColorScheme(): void; - setLightColorScheme(): void; - syncPreferredScheme(): void; -}; - -export enum ColorScheme { - Dark = 'dark', - Light = 'light', -} - -export type UIStore = { - isSidebarOpen: boolean; - colorScheme: ColorScheme; -}; - -/** - * Returns the preferred color scheme based on user's operative system - */ -export function getPreferredScheme(): ColorScheme { - if (browser) { - const localColorScheme = localStorage.getItem('colorScheme'); - - if (localColorScheme) { - return localColorScheme as ColorScheme; - } - } - - if (typeof window !== 'undefined') { - return window?.matchMedia?.(PREFERS_COLOR_SCHEME_DARK_MEDIA_QUERY)?.matches - ? ColorScheme.Dark - : ColorScheme.Light; - } - - return ColorScheme.Light; -} - -export function createUIStore() { - const { subscribe, update } = writable({ - isSidebarOpen: false, - colorScheme: getPreferredScheme(), - }); - - const syncPreferredScheme = () => { - const preferredScheme = getPreferredScheme(); - - if (preferredScheme === ColorScheme.Dark) { - setDarkColorScheme(); - return; - } - - setLightColorScheme(); - }; - - const closeSidebar = () => - update((current) => ({ - ...current, - isSidebarOpen: false, - })); - - const openSidebar = () => - update((current) => ({ - ...current, - isSidebarOpen: true, - })); - - const setDarkColorScheme = () => { - if (typeof document !== 'undefined') { - document.documentElement.classList.add('dark'); - - update((current) => ({ - ...current, - colorScheme: ColorScheme.Dark, - })); - } - }; - - const setLightColorScheme = () => { - if (typeof document !== 'undefined') { - document.documentElement.classList.remove('dark'); - - update((current) => ({ - ...current, - colorScheme: ColorScheme.Light, - })); - } - }; - - const setPreferred = (scheme: ColorScheme) => { - if (browser) { - localStorage.setItem('colorScheme', scheme); - - if (scheme === ColorScheme.Dark) { - setDarkColorScheme(); - return; - } - - setLightColorScheme(); - } - }; - - setPreferred(ColorScheme.Light); - - return { - subscribe, - openSidebar, - closeSidebar, - setDarkColorScheme, - setLightColorScheme, - setPreferred, - syncPreferredScheme, - }; -} - -const uiStore = createUIStore() as unknown as Readable & - UIStoreMethods; -export default uiStore; diff --git a/client/src/lib/stores/user.ts b/client/src/lib/stores/user.ts deleted file mode 100644 index 41ee6d2b..00000000 --- a/client/src/lib/stores/user.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { writable } from 'svelte/store'; - -import type { CurrentUserFragment, UserUpdateInput } from '$lib/graphql/schema'; -import type { Unsubscriber, Writable } from 'svelte/store'; -import type { Readable, Subscriber } from 'svelte/motion'; -import type { Client } from '@urql/core'; -import { UserService } from '$lib/services/UserService'; - -export interface UserStoreMethods { - /** - * Sets the initial state for the `UserStore` - */ - init(user: CurrentUserFragment): void; - - /** - * - * @param urqlClient - * @param file - * - * Updates the avatar of this user - */ - updateAvatar(urqlClient: Client, file: File): Promise; - - /** - * - * @param urqlClient - * @param input - * - * Updates the user - */ - update(urqlClient: Client, input: UserUpdateInput): Promise; -} - -export class UserStore implements UserStoreMethods { - private inner: Writable; - - constructor() { - this.inner = writable(null); - } - - public subscribe(run: Subscriber): Unsubscriber { - return this.inner.subscribe(run); - } - - public init(user: CurrentUserFragment): void { - this.inner.set(user); - } - - public async updateAvatar(urqlClient: Client, file: File): Promise { - const user = await UserService.userAvatarUpdate(urqlClient, file); - this.inner.set(user); - } - - public async update( - urqlClient: Client, - input: UserUpdateInput, - ): Promise { - const user = await UserService.userUpdate(urqlClient, input); - this.inner.set(user); - } -} - -export const userStore = - new UserStore() as unknown as Readable & - UserStoreMethods; diff --git a/client/src/lib/utils/basic-auth.ts b/client/src/lib/utils/basic-auth.ts deleted file mode 100644 index 1c39c0e3..00000000 --- a/client/src/lib/utils/basic-auth.ts +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Credentials for a HTTP Basic Authentication instance - */ -export type Credentials = { - username: string; - password: string; -}; - -export const CREDENTIALS_REGEXP = - /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/; -export const CRED_PAIR_REGEXP = /^([^:]*):(.*)$/; - -/** - * Creates a HTTP Basic compliant Header Value. - * - * This is only compatible on Browser Environment due to the fact that uses - * the `Window`'s object to access `btoa`. - * - * @param username - * @param password - * @returns - */ -export function createHeader(username: string, password: string): string { - const credentials = window.btoa(`${username}:${password}`); - - return `Basic ${credentials}`; -} - -/** - * Parses a `Request`'s object `Authorization` header to retrieve the - * basic authentication credentials. - * - * @param request - * @returns - */ -export function parseHeader(request: Request): Credentials { - if (!request) { - throw new TypeError('"request" argument is required'); - } - - if (typeof request !== 'object') { - throw new TypeError('"request" must be an object'); - } - - const header = getAuthorizationHeader(request); - - if (!header) { - throw new TypeError( - '"request" headers doesn\'t include an Authorization key pair', - ); - } - - return parse(header); -} - -export function decodeBase64(str: string) { - return Buffer.from(str, 'base64').toString(); -} - -export function getAuthorizationHeader(request: Request): string | null { - if (!request.headers || typeof request.headers !== 'object') { - throw new TypeError( - 'argument "request" must be an object with the "headers" field', - ); - } - - if (typeof request.headers.get !== 'function') { - throw new TypeError( - 'Missing "get" function for "Headers" on "Request.headers"', - ); - } - - return request.headers.get('authorization') || null; -} - -function parse(authorization: string): Credentials { - if (typeof authorization !== 'string') { - throw new TypeError('"authorization" in not of type "string"'); - } - - const matches = CREDENTIALS_REGEXP.exec(authorization); - - if (!matches) { - throw new TypeError( - "The provided Authorization Header doesn't follows HTTP Basic Authentication", - ); - } - - const creds = CRED_PAIR_REGEXP.exec(decodeBase64(matches[1])); - - if (!creds) { - throw new TypeError('Failed to retrieve user credentials'); - } - - return { - username: creds[1], - password: creds[2], - }; -} diff --git a/client/src/lib/utils/graphql.ts b/client/src/lib/utils/graphql.ts deleted file mode 100644 index 2a424a91..00000000 --- a/client/src/lib/utils/graphql.ts +++ /dev/null @@ -1,40 +0,0 @@ -export class GraphQLError extends Error { - private readonly _code: C; - private readonly _message: string; - - protected constructor(name: string, code: C, message: string) { - super(`${code}: ${message}`); - - this.name = name; - this._code = code; - this._message = message; - } - - get code(): C { - return this._code; - } - - get message(): string { - return this._message; - } - - public static new(code: C, message?: string): GraphQLError { - return new GraphQLError( - this.name, - code, - message || `${this.name}: ${code}`, - ); - } - - public toObject(): { error: string; code: C; message: string } { - return { - error: this.name, - code: this._code, - message: this._message, - }; - } - - public toString(): string { - return JSON.stringify(this.toObject()); - } -} diff --git a/client/src/lib/utils/http.ts b/client/src/lib/utils/http.ts deleted file mode 100644 index 5c473ab8..00000000 --- a/client/src/lib/utils/http.ts +++ /dev/null @@ -1,39 +0,0 @@ -export enum StatusCode { - Ok = 200, - BadRequest = 400, - Unauthorized = 401, - Forbidden = 403, - NotFound = 404, - InternalServerError = 500, -} - -export type HttpErrorResponse = { - statusCode: StatusCode; - message: string | C; - code: C; -}; - -export class JsonResponse { - static error(statusCode: StatusCode, code: C, message?: string): Response { - const object: HttpErrorResponse = { - statusCode, - message: message ?? code, - code, - }; - - return new Response(JSON.stringify(object), { - status: statusCode, - }); - } - - static success(data: T, statusCode: StatusCode = StatusCode.Ok): Response { - const object = { - statusCode, - data, - }; - - return new Response(JSON.stringify(object), { - status: statusCode, - }); - } -} diff --git a/client/src/routes/(app)/+layout.server.ts b/client/src/routes/(app)/+layout.server.ts deleted file mode 100644 index 9a8e01b0..00000000 --- a/client/src/routes/(app)/+layout.server.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { redirect } from '@sveltejs/kit'; - -import type { CurrentUserFragment } from '$lib/graphql/schema'; -import type { LayoutServerLoad } from './$types'; - -export const load: LayoutServerLoad = ({ - locals, -}: { - locals: { - accessToken?: string; - user?: CurrentUserFragment; - }; -}) => { - if (!locals.user || !locals.accessToken) { - throw redirect(302, '/login'); - } - - return { - accessToken: locals.accessToken, - user: locals.user, - }; -}; diff --git a/client/src/routes/(app)/+layout.svelte b/client/src/routes/(app)/+layout.svelte deleted file mode 100644 index 4665fb14..00000000 --- a/client/src/routes/(app)/+layout.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -
    - -
    -
    - -
    -
    -
    diff --git a/client/src/routes/(app)/+page.svelte b/client/src/routes/(app)/+page.svelte deleted file mode 100644 index db59ba9f..00000000 --- a/client/src/routes/(app)/+page.svelte +++ /dev/null @@ -1,4 +0,0 @@ -
    - -
    diff --git a/client/src/routes/(app)/components/Navbar/Navbar.svelte b/client/src/routes/(app)/components/Navbar/Navbar.svelte deleted file mode 100644 index 9cc47553..00000000 --- a/client/src/routes/(app)/components/Navbar/Navbar.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -
    - - 🏖️ - townhall - -
    - {#if $userStore} - - {/if} -
    -
    diff --git a/client/src/routes/(app)/components/Navbar/UserSummary.svelte b/client/src/routes/(app)/components/Navbar/UserSummary.svelte deleted file mode 100644 index 3e30f0e5..00000000 --- a/client/src/routes/(app)/components/Navbar/UserSummary.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -
    -
    - {$userStore?.username} avatar -
    -
    diff --git a/client/src/routes/(app)/components/Navbar/index.ts b/client/src/routes/(app)/components/Navbar/index.ts deleted file mode 100644 index d329d716..00000000 --- a/client/src/routes/(app)/components/Navbar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Navbar.svelte'; diff --git a/client/src/routes/(app)/settings/+page.svelte b/client/src/routes/(app)/settings/+page.svelte deleted file mode 100644 index e69de29b..00000000 diff --git a/client/src/routes/(app)/settings/profile/+page.svelte b/client/src/routes/(app)/settings/profile/+page.svelte deleted file mode 100644 index f8f47fc2..00000000 --- a/client/src/routes/(app)/settings/profile/+page.svelte +++ /dev/null @@ -1,110 +0,0 @@ - - - - Your Profile | {$userStore?.name} - - - - -
    -

    Edit profile

    -
    - -
    -
    -
    - -
    -
    - - -
    -
    -
    - -
    - diff --git a/client/src/routes/(auth)/+layout.server.ts b/client/src/routes/(auth)/+layout.server.ts deleted file mode 100644 index 2d98a8ff..00000000 --- a/client/src/routes/(auth)/+layout.server.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { redirect } from '@sveltejs/kit'; - -import type { CurrentUserFragment } from '$lib/graphql/schema'; -import type { LayoutServerLoad } from './$types'; - -export const load: LayoutServerLoad = ({ - locals, -}: { - locals: { - accessToken?: string; - user?: CurrentUserFragment; - }; -}) => { - if (locals.user || locals.accessToken) { - throw redirect(302, '/'); - } - - return { - accessToken: locals.accessToken, - user: locals.user, - }; -}; diff --git a/client/src/routes/(auth)/+layout.svelte b/client/src/routes/(auth)/+layout.svelte deleted file mode 100644 index b4c38464..00000000 --- a/client/src/routes/(auth)/+layout.svelte +++ /dev/null @@ -1,95 +0,0 @@ - - -
    - - {#if backgroutdAuthor} - - {/if} -
    - - diff --git a/client/src/routes/(auth)/login/+page.svelte b/client/src/routes/(auth)/login/+page.svelte deleted file mode 100644 index 2517eab1..00000000 --- a/client/src/routes/(auth)/login/+page.svelte +++ /dev/null @@ -1,87 +0,0 @@ - - -
    -
    -

    Welcome Back!

    - Log in to your account to continue -
    -
    - - -
    - -
    - - - - - Don't have an account? Sign up - -
    diff --git a/client/src/routes/(auth)/login/+server.ts b/client/src/routes/(auth)/login/+server.ts deleted file mode 100644 index a308ecff..00000000 --- a/client/src/routes/(auth)/login/+server.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { createClient, cacheExchange, fetchExchange } from '@urql/core'; - -import { parseHeader } from '$lib/utils/basic-auth'; - -import type { Cookies } from '@sveltejs/kit'; -import { JsonResponse, StatusCode } from '$lib/utils/http'; -import { AuthError, AuthService } from '$lib/services/AuthService'; - -const AUTH_TOKEN_COOKIE_MAX_AGE_DAYS = 30; - -export enum LoginError { - MissingCredentials = 'MISSING_CREDENTIALS', - InvalidCredentials = 'INVALID_CREDENTIALS', - Unknown = 'UNKNOWN', -} - -export const POST = async ({ - cookies, - request, -}: { - cookies: Cookies; - request: Request; -}) => { - try { - const { username, password } = parseHeader(request); - - if (!username || !password) { - return JsonResponse.error( - StatusCode.BadRequest, - LoginError.MissingCredentials, - ); - } - - const urqlClient = createClient({ - url: import.meta.env.VITE_GRAPHQL_URL, - exchanges: [cacheExchange, fetchExchange], - }); - const tokens = await AuthService.tokenCreate( - urqlClient, - username, - password, - ); - - cookies.set('accessToken', tokens.accessToken, { - path: '/', - httpOnly: true, - sameSite: 'strict', - secure: process.env.NODE_ENV === 'production', - maxAge: 60 * 60 * 24 * AUTH_TOKEN_COOKIE_MAX_AGE_DAYS, - }); - - return new Response(null, { - status: 201, - }); - } catch (err) { - if (err instanceof AuthError) { - return JsonResponse.error( - StatusCode.Unauthorized, - LoginError.InvalidCredentials, - ); - } - - return JsonResponse.error( - StatusCode.InternalServerError, - LoginError.Unknown, - ); - } -}; diff --git a/client/src/routes/(auth)/logout/+server.ts b/client/src/routes/(auth)/logout/+server.ts deleted file mode 100644 index 5f03900c..00000000 --- a/client/src/routes/(auth)/logout/+server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { redirect } from '@sveltejs/kit'; - -import type { RequestHandler } from './$types'; - -export const GET = (({ cookies }) => { - cookies.delete('accessToken'); - - throw redirect(302, '/login'); -}) satisfies RequestHandler; diff --git a/client/src/routes/(auth)/signup/+page.svelte b/client/src/routes/(auth)/signup/+page.svelte deleted file mode 100644 index 6805e765..00000000 --- a/client/src/routes/(auth)/signup/+page.svelte +++ /dev/null @@ -1,119 +0,0 @@ - - -{#if registeredUser} -
    -
    -

    Welcome to {registeredUser.name}!

    - Visit the Login Page to continue -
    -
    -{:else} -
    -
    -

    Welcome to townhall!

    - Create an account to continue -
    -
    - - - - - - - - - Already have an account? Log in - -
    -{/if} diff --git a/client/src/routes/+layout.svelte b/client/src/routes/+layout.svelte deleted file mode 100644 index 97a297d2..00000000 --- a/client/src/routes/+layout.svelte +++ /dev/null @@ -1,104 +0,0 @@ - - - - {@html webManifest} - - -
    - -
    - - - - - -{#await import('$lib/components/ReloadPrompt.svelte') then { default: ReloadPrompt }} - -{/await} diff --git a/client/src/routes/api/unsplash/+server.ts b/client/src/routes/api/unsplash/+server.ts deleted file mode 100644 index c619c975..00000000 --- a/client/src/routes/api/unsplash/+server.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { createApi } from 'unsplash-js'; - -import { UNSPLASH_ACCESS_KEY } from '$env/static/private'; -import { JsonResponse, StatusCode } from '$lib/utils/http'; - -import type { RequestEvent } from './$types'; -import { UnsplashErrorCode } from '$lib/services/Unsplash'; - -export type UnsplashAuthor = { - id: string; - name: string; - username: string; - avatar: string; -}; - -export type UnsplashImage = { - url: URL; - author: UnsplashAuthor; -}; - -const UNSPLASH_DAILY_BACKGROUND_COOKIE = 'UNSPLASH_DAILY_BACKGROUND'; - -export async function GET(event: RequestEvent) { - const UNSPLASH_DAILY_BACKGROUND = event.cookies.get( - UNSPLASH_DAILY_BACKGROUND_COOKIE, - ); - - if (UNSPLASH_DAILY_BACKGROUND) { - const body = JSON.parse(UNSPLASH_DAILY_BACKGROUND); - - return JsonResponse.success(body); - } - - if (!UNSPLASH_ACCESS_KEY) { - return JsonResponse.error( - StatusCode.InternalServerError, - UnsplashErrorCode.MissingApiToken, - 'UNSPLASH_ACCESS_KEY environment variable is not set', - ); - } - - const unsplash = createApi({ - accessKey: UNSPLASH_ACCESS_KEY, - }); - - const result = await unsplash.photos.getRandom({ - query: 'night lights', - }); - - if (result.status === 200) { - const image = Array.isArray(result?.response) - ? result?.response[0] - : result?.response; - - if (image) { - const today = new Date(); - - today.setDate(today.getDate() + 1); - - const body: UnsplashImage = { - url: new URL(image.urls.regular), - author: { - id: image.user.id, - username: image.user.username, - name: image.user.name, - avatar: image.user.profile_image?.medium, - }, - }; - - event.cookies.set('UNSPLASH_DAILY_BACKGROUND', JSON.stringify(body), { - expires: today, - path: '/', - httpOnly: true, - sameSite: 'strict', - secure: process.env.NODE_ENV === 'production', - }); - - return JsonResponse.success(body); - } - } - - return JsonResponse.error( - StatusCode.InternalServerError, - UnsplashErrorCode.Unknown, - 'Failed to fetch Unsplash background', - ); -} diff --git a/client/static/android-chrome-192x192.png b/client/static/android-chrome-192x192.png deleted file mode 100644 index aab6791a..00000000 Binary files a/client/static/android-chrome-192x192.png and /dev/null differ diff --git a/client/static/android-chrome-512x512.png b/client/static/android-chrome-512x512.png deleted file mode 100644 index 8d4f7d81..00000000 Binary files a/client/static/android-chrome-512x512.png and /dev/null differ diff --git a/client/static/apple-touch-icon.png b/client/static/apple-touch-icon.png deleted file mode 100644 index 2138a652..00000000 Binary files a/client/static/apple-touch-icon.png and /dev/null differ diff --git a/client/static/favicon-16x16.png b/client/static/favicon-16x16.png deleted file mode 100644 index d39e6394..00000000 Binary files a/client/static/favicon-16x16.png and /dev/null differ diff --git a/client/static/favicon-32x32.png b/client/static/favicon-32x32.png deleted file mode 100644 index 7be3c6cc..00000000 Binary files a/client/static/favicon-32x32.png and /dev/null differ diff --git a/client/static/favicon.ico b/client/static/favicon.ico deleted file mode 100644 index b54d0d6f..00000000 Binary files a/client/static/favicon.ico and /dev/null differ diff --git a/client/static/favicon.png b/client/static/favicon.png deleted file mode 100644 index 825b9e65..00000000 Binary files a/client/static/favicon.png and /dev/null differ diff --git a/client/static/icons/bell.svg b/client/static/icons/bell.svg deleted file mode 100644 index 3d05be91..00000000 --- a/client/static/icons/bell.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/client/static/icons/check.svg b/client/static/icons/check.svg deleted file mode 100644 index dcf72c4d..00000000 --- a/client/static/icons/check.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/client/static/icons/close.svg b/client/static/icons/close.svg deleted file mode 100644 index 4e38b844..00000000 --- a/client/static/icons/close.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/client/static/icons/hamburger.svg b/client/static/icons/hamburger.svg deleted file mode 100644 index f47ea4fc..00000000 --- a/client/static/icons/hamburger.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/client/static/icons/home.svg b/client/static/icons/home.svg deleted file mode 100644 index b09bbeab..00000000 --- a/client/static/icons/home.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/client/static/icons/search.svg b/client/static/icons/search.svg deleted file mode 100644 index d2b30a2f..00000000 --- a/client/static/icons/search.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/client/static/icons/warning.svg b/client/static/icons/warning.svg deleted file mode 100644 index f7f6b35a..00000000 --- a/client/static/icons/warning.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/client/static/img/auth-fallback-bg.jpg b/client/static/img/auth-fallback-bg.jpg deleted file mode 100644 index 819c178f..00000000 Binary files a/client/static/img/auth-fallback-bg.jpg and /dev/null differ diff --git a/client/static/maskable_icon.png b/client/static/maskable_icon.png deleted file mode 100644 index 810b7423..00000000 Binary files a/client/static/maskable_icon.png and /dev/null differ diff --git a/client/svelte.config.js b/client/svelte.config.js deleted file mode 100644 index 332718fb..00000000 --- a/client/svelte.config.js +++ /dev/null @@ -1,15 +0,0 @@ -import adapter from "@sveltejs/adapter-auto"; -import { vitePreprocess } from "@sveltejs/kit/vite"; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter(), - serviceWorker: { - register: false, - }, - }, - preprocess: vitePreprocess(), -}; - -export default config; diff --git a/client/tailwind.config.cjs b/client/tailwind.config.cjs deleted file mode 100644 index d66db02b..00000000 --- a/client/tailwind.config.cjs +++ /dev/null @@ -1,25 +0,0 @@ -const defaultTheme = require('tailwindcss/defaultTheme'); - -/** @type {import('tailwindcss').Config} */ -module.exports = { - darkMode: 'class', - content: ['./src/**/*.{html,js,svelte,ts}'], - theme: { - extend: { - colors: { - base: { - dark: '#0D1117', - light: '#F5F5F5' - }, - inner: { - dark: '#161B22', - light: '#0F1419' - } - }, - fontFamily: { - sans: ['Inter', ...defaultTheme.fontFamily.sans] - } - }, - plugins: [require('@tailwindcss/forms')] - } -}; diff --git a/client/test/Button.test.ts b/client/test/Button.test.ts deleted file mode 100644 index a5f53cf8..00000000 --- a/client/test/Button.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { afterEach, describe, it, expect, vi, beforeEach } from 'vitest'; -import { cleanup, fireEvent, render } from '@testing-library/svelte'; - -import Button from '../src/lib/components/Button.svelte'; - -const BUTTON_DEFAULT_CLASSES = - 'bg-black px-5 py-3 text-base font-medium text-center text-white bg-primary-700 rounded-lg hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 sm:w-auto dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800'; - -describe('$lib/components/Button.svelte', () => { - beforeEach(() => cleanup()); - - it('creates a button with default props', () => { - const { container } = render(Button); - const buttonElement = container - .getElementsByTagName('button') - .item(0) as HTMLButtonElement; - - expect(buttonElement).toBeTruthy(); - expect(buttonElement.getAttribute('type')).toStrictEqual('button'); - expect(buttonElement.getAttribute('disabled')).toBeNull(); - }); - - it('creates a disabled button', () => { - const { container } = render(Button, { type: 'button', disabled: true }); - const buttonElement = container - .getElementsByTagName('button') - .item(0) as HTMLButtonElement; - - expect(buttonElement).toBeTruthy(); - expect(buttonElement.getAttribute('type')).toStrictEqual('button'); - expect(buttonElement.getAttribute('disabled')).toBeDefined(); - }); - - it('creates a submit type button', () => { - const { container } = render(Button, { type: 'submit' }); - const buttonElement = container - .getElementsByTagName('button') - .item(0) as HTMLButtonElement; - - expect(buttonElement).toBeTruthy(); - expect(buttonElement.getAttribute('type')).toStrictEqual('submit'); - }); - - it('holds classes by default', () => { - const { container } = render(Button); - const buttonElement = container - .getElementsByTagName('button') - .item(0) as HTMLButtonElement; - - expect(buttonElement).toBeTruthy(); - expect(buttonElement.getAttribute('class')).toStrictEqual( - [BUTTON_DEFAULT_CLASSES].join(' ') - ); - }); - - it('extends default classes when disabled', () => { - const { container } = render(Button, { disabled: true }); - const buttonElement = container - .getElementsByTagName('button') - .item(0) as HTMLButtonElement; - - expect(buttonElement).toBeTruthy(); - expect(buttonElement.getAttribute('class')).toStrictEqual( - [BUTTON_DEFAULT_CLASSES].join(' ') - ); - }); - - it('extends default classes to have full width', () => { - const { container } = render(Button, { fullWidth: true }); - const buttonElement = container - .getElementsByTagName('button') - .item(0) as HTMLButtonElement; - - expect(buttonElement).toBeTruthy(); - expect(buttonElement.getAttribute('class')).toStrictEqual( - [BUTTON_DEFAULT_CLASSES].join(' ') - ); - }); - - it('handles click events', async () => { - const { container, component } = render(Button); - const buttonElement = container - .getElementsByTagName('button') - .item(0) as HTMLButtonElement; - const mockFn = vi.fn(); - - component.$on('click', mockFn); - - expect(buttonElement).toBeTruthy(); - await fireEvent.click(buttonElement); - - expect(mockFn.mock.calls.length).toEqual(1); - }); -}); diff --git a/client/test/TextField.test.ts b/client/test/TextField.test.ts deleted file mode 100644 index 49a49f47..00000000 --- a/client/test/TextField.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { render } from '@testing-library/svelte'; - -import TextField from '../src/lib/components/TextField.svelte'; - -const INPUT_DEFAULT_CLASSES = - 'bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500'; - -describe('Test for TextField component', () => { - it('Create input with default props', () => { - const { container } = render(TextField); - const inputElement = container - .getElementsByTagName('input') - .item(0) as HTMLElement; - const paragraphErrorElement = container - .getElementsByTagName('p') - .item(0) as HTMLParagraphElement; - const labelElement = container - .getElementsByTagName('label') - .item(0) as HTMLLabelElement; - - expect(inputElement).toBeTruthy(); - expect(inputElement.getAttribute('type')).toBe('text'); - expect(inputElement.getAttribute('value')).toBe(null); - expect(inputElement.getAttribute('placeholder')).toBe(null); - expect(inputElement.getAttribute('autocomplete')).toBe(null); - expect(paragraphErrorElement).toBeTruthy(); - expect(labelElement).toBeFalsy(); - }); - - it('should render a label when the `label` prop is passed', () => { - const { container } = render(TextField, { props: { label: 'Name' } }); - - const labelElement = container.querySelector('label'); - expect(labelElement).toBeTruthy(); - - expect(labelElement?.textContent).toBe('Name'); - }); - - it('applies custom classes to the input', () => { - const { container } = render(TextField, { - props: { - class: 'custom-class-1' - } - }); - - const inputElement = container - .getElementsByTagName('input') - .item(0) as HTMLInputElement; - - expect(inputElement.classList.toString()).toBe( - 'custom-class-1 ' + INPUT_DEFAULT_CLASSES - ); - }); - - it('shows error message when error prop is passed', async () => { - const errorMessage = 'This field is required'; - const { container } = render(TextField, { - error: errorMessage - }); - const textField = container - .getElementsByTagName('p') - .item(0) as HTMLParagraphElement; - - expect(textField.textContent).toBe(errorMessage); - }); -}); diff --git a/client/tsconfig.json b/client/tsconfig.json deleted file mode 100644 index 92f32306..00000000 --- a/client/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "baseUrl": ".", - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "types": ["unplugin-icons/types/svelte", "vite-plugin-pwa/info"], - "paths": { - "unplugin-icons/loaders": ["node_modules/unplugin-icons/dist/loaders"], - "unplugin-icons/vite": ["node_modules/unplugin-icons/dist/vite"], - "$app": ["src/routes/(app)/"], - "$app/*": ["src/routes/(app)/*"], - "$auth": ["src/routes/(auth)"], - "$auth/*": ["src/routes/(auth)/*"], - "$lib": ["src/lib"], - "$lib/*": ["src/lib/*"], - "$routes": ["src/routes"], - "$routes/*": ["src/routes/*"] - } - } - // 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 -} diff --git a/client/vite.config.ts b/client/vite.config.ts deleted file mode 100644 index 340cdad5..00000000 --- a/client/vite.config.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { sveltekit } from "@sveltejs/kit/vite"; -import path from "path"; -import { FileSystemIconLoader } from "unplugin-icons/loaders"; -import Icons from "unplugin-icons/vite"; -import { SvelteKitPWA } from "@vite-pwa/sveltekit"; -import type { UserConfig } from "vite"; - -const config: UserConfig = { - resolve: { - alias: { - $app: path.resolve("./src/routes/(app)"), - $auth: path.resolve("./src/routes/(auth)"), - $routes: path.resolve("./src/routes"), - }, - }, - plugins: [ - sveltekit(), - SvelteKitPWA({ - manifest: { - name: "townhall", - short_name: "townhall", - background_color: "#fff", - theme_color: "#448aff", - start_url: "/", - display: "standalone", - prefer_related_applications: true, - icons: [ - { - src: "/maskable_icon.png", - sizes: "196x196", - type: "image/png", - purpose: "any maskable", - }, - { - src: "/android-chrome-192x192.png", - type: "image/png", - sizes: "192x192", - }, - { - src: "/android-chrome-512x512.png", - type: "image/png", - sizes: "512x512", - }, - ], - }, - }), - Icons({ - compiler: "svelte", - customCollections: { - custom: FileSystemIconLoader("./static/icons"), - }, - }), - ], -}; - -export default config; diff --git a/client/vitest.config.ts b/client/vitest.config.ts deleted file mode 100644 index aeb9479d..00000000 --- a/client/vitest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineConfig } from 'vitest/config'; -import { svelte } from '@sveltejs/vite-plugin-svelte'; - -export default defineConfig({ - plugins: [svelte()], - test: { - globals: true, - environment: 'jsdom', - watch: false - } -});