diff --git a/.gitignore b/.gitignore index 79518f7..a8b5a49 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,9 @@ Thumbs.db # Vite vite.config.js.timestamp-* vite.config.ts.timestamp-* + +# Intellij +.idea + +# PocketBase +/pb_data diff --git a/package.json b/package.json index c387dcd..f0a2212 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "niko", + "name": "nikk", "version": "0.0.1", "private": true, "scripts": { @@ -20,21 +20,24 @@ "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@types/eslint": "^8.56.7", + "bulma": "^1.0.1", "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.36.0", "globals": "^15.0.0", "prettier": "^3.1.1", "prettier-plugin-svelte": "^3.1.2", + "sass": "^1.77.5", "svelte": "^4.2.7", "svelte-check": "^3.6.0", "tslib": "^2.4.1", "typescript": "^5.0.0", "typescript-eslint": "^8.0.0-alpha.20", "vite": "^5.0.3", - "vitest": "^1.2.0", - "bulma": "^1.0.1", - "sass": "^1.77.5" + "vitest": "^1.2.0" }, - "type": "module" + "type": "module", + "dependencies": { + "pocketbase": "^0.21.3" + } } diff --git a/pb_migrations/1718834233_created_profiles.js b/pb_migrations/1718834233_created_profiles.js new file mode 100644 index 0000000..b3b40e3 --- /dev/null +++ b/pb_migrations/1718834233_created_profiles.js @@ -0,0 +1,60 @@ +/// +migrate( + (db) => { + const collection = new Collection({ + id: 'etdl54k56vwybrn', + created: '2024-06-19 21:57:13.774Z', + updated: '2024-06-19 21:57:13.774Z', + name: 'profiles', + type: 'base', + system: false, + schema: [ + { + system: false, + id: 'vodgbchi', + name: 'user', + type: 'relation', + required: true, + presentable: false, + unique: false, + options: { + collectionId: '_pb_users_auth_', + cascadeDelete: true, + minSelect: null, + maxSelect: 1, + displayFields: null + } + }, + { + system: false, + id: 'ykt3zolr', + name: 'name', + type: 'text', + required: true, + presentable: false, + unique: false, + options: { + min: null, + max: null, + pattern: '' + } + } + ], + indexes: ['CREATE INDEX `idx_VTowXf1` ON `profiles` (`name`)'], + listRule: null, + viewRule: null, + createRule: null, + updateRule: null, + deleteRule: null, + options: {} + }); + + return Dao(db).saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn'); + + return dao.deleteCollection(collection); + } +); diff --git a/pb_migrations/1718834453_created_characters.js b/pb_migrations/1718834453_created_characters.js new file mode 100644 index 0000000..fb76f37 --- /dev/null +++ b/pb_migrations/1718834453_created_characters.js @@ -0,0 +1,81 @@ +/// +migrate( + (db) => { + const collection = new Collection({ + id: 'i7mafqbep52hivh', + created: '2024-06-19 22:00:53.918Z', + updated: '2024-06-19 22:00:53.918Z', + name: 'characters', + type: 'base', + system: false, + schema: [ + { + system: false, + id: 'mn4unycx', + name: 'name', + type: 'text', + required: true, + presentable: false, + unique: false, + options: { + min: null, + max: null, + pattern: '' + } + }, + { + system: false, + id: '47sgeg6w', + name: 'displayName', + type: 'text', + required: true, + presentable: false, + unique: false, + options: { + min: null, + max: null, + pattern: '' + } + }, + { + system: false, + id: 'urnacitl', + name: 'element', + type: 'select', + required: true, + presentable: false, + unique: false, + options: { + maxSelect: 1, + values: ['pyro', 'hydro', 'anemo', 'electro', 'dendro', 'cryo', 'geo'] + } + }, + { + system: false, + id: 'mhmjqqet', + name: 'visible', + type: 'bool', + required: false, + presentable: false, + unique: false, + options: {} + } + ], + indexes: ['CREATE UNIQUE INDEX `idx_Fd6ZMqS` ON `characters` (`name`)'], + listRule: 'visible = true', + viewRule: 'visible = true', + createRule: null, + updateRule: null, + deleteRule: null, + options: {} + }); + + return Dao(db).saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('i7mafqbep52hivh'); + + return dao.deleteCollection(collection); + } +); diff --git a/pb_migrations/1718834489_updated_profiles.js b/pb_migrations/1718834489_updated_profiles.js new file mode 100644 index 0000000..326d677 --- /dev/null +++ b/pb_migrations/1718834489_updated_profiles.js @@ -0,0 +1,27 @@ +/// +migrate( + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn'); + + collection.listRule = '@request.auth.id = user.id'; + collection.viewRule = '@request.auth.id = user.id'; + collection.createRule = '@request.auth.id = user.id'; + collection.updateRule = '@request.auth.id = user.id'; + collection.deleteRule = '@request.auth.id = user.id'; + + return dao.saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn'); + + collection.listRule = null; + collection.viewRule = null; + collection.createRule = null; + collection.updateRule = null; + collection.deleteRule = null; + + return dao.saveCollection(collection); + } +); diff --git a/pb_migrations/1718834637_created_votes.js b/pb_migrations/1718834637_created_votes.js new file mode 100644 index 0000000..262107c --- /dev/null +++ b/pb_migrations/1718834637_created_votes.js @@ -0,0 +1,76 @@ +/// +migrate( + (db) => { + const collection = new Collection({ + id: '2q5l1u64zseldm9', + created: '2024-06-19 22:03:57.192Z', + updated: '2024-06-19 22:03:57.192Z', + name: 'votes', + type: 'base', + system: false, + schema: [ + { + system: false, + id: 'reelx3nj', + name: 'profile', + type: 'relation', + required: true, + presentable: false, + unique: false, + options: { + collectionId: 'etdl54k56vwybrn', + cascadeDelete: true, + minSelect: null, + maxSelect: 1, + displayFields: null + } + }, + { + system: false, + id: 'oryeao7u', + name: 'character', + type: 'relation', + required: true, + presentable: false, + unique: false, + options: { + collectionId: 'i7mafqbep52hivh', + cascadeDelete: true, + minSelect: null, + maxSelect: 1, + displayFields: null + } + }, + { + system: false, + id: 'sllown1l', + name: 'nick', + type: 'text', + required: true, + presentable: false, + unique: false, + options: { + min: null, + max: null, + pattern: '' + } + } + ], + indexes: ['CREATE UNIQUE INDEX `idx_TdQeCRY` ON `votes` (\n `profile`,\n `character`\n)'], + listRule: '@request.auth.id != ""', + viewRule: '@request.auth.id != ""', + createRule: '@request.auth.id = profile.user.id', + updateRule: '@request.auth.id = profile.user.id', + deleteRule: '@request.auth.id = profile.user.id', + options: {} + }); + + return Dao(db).saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9'); + + return dao.deleteCollection(collection); + } +); diff --git a/pb_migrations/1718834662_updated_profiles.js b/pb_migrations/1718834662_updated_profiles.js new file mode 100644 index 0000000..6d7f268 --- /dev/null +++ b/pb_migrations/1718834662_updated_profiles.js @@ -0,0 +1,21 @@ +/// +migrate( + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn'); + + collection.listRule = '@request.auth.id != ""'; + collection.viewRule = '@request.auth.id != ""'; + + return dao.saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn'); + + collection.listRule = '@request.auth.id = user.id'; + collection.viewRule = '@request.auth.id = user.id'; + + return dao.saveCollection(collection); + } +); diff --git a/pb_migrations/1718834688_updated_characters.js b/pb_migrations/1718834688_updated_characters.js new file mode 100644 index 0000000..3ede090 --- /dev/null +++ b/pb_migrations/1718834688_updated_characters.js @@ -0,0 +1,21 @@ +/// +migrate( + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('i7mafqbep52hivh'); + + collection.listRule = '@request.auth.id != "" && visible = true'; + collection.viewRule = '@request.auth.id != "" && visible = true'; + + return dao.saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('i7mafqbep52hivh'); + + collection.listRule = 'visible = true'; + collection.viewRule = 'visible = true'; + + return dao.saveCollection(collection); + } +); diff --git a/pb_migrations/1718838136_updated_votes.js b/pb_migrations/1718838136_updated_votes.js new file mode 100644 index 0000000..c8b4c42 --- /dev/null +++ b/pb_migrations/1718838136_updated_votes.js @@ -0,0 +1,25 @@ +/// +migrate( + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9'); + + collection.name = 'nicks'; + collection.indexes = [ + 'CREATE UNIQUE INDEX `idx_TdQeCRY` ON `nicks` (\n `profile`,\n `character`\n)' + ]; + + return dao.saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9'); + + collection.name = 'votes'; + collection.indexes = [ + 'CREATE UNIQUE INDEX `idx_TdQeCRY` ON `votes` (\n `profile`,\n `character`\n)' + ]; + + return dao.saveCollection(collection); + } +); diff --git a/pb_migrations/1718839919_updated_nicks.js b/pb_migrations/1718839919_updated_nicks.js new file mode 100644 index 0000000..80850dd --- /dev/null +++ b/pb_migrations/1718839919_updated_nicks.js @@ -0,0 +1,51 @@ +/// +migrate( + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9'); + + // update + collection.schema.addField( + new SchemaField({ + system: false, + id: 'sllown1l', + name: 'name', + type: 'text', + required: true, + presentable: false, + unique: false, + options: { + min: null, + max: null, + pattern: '' + } + }) + ); + + return dao.saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9'); + + // update + collection.schema.addField( + new SchemaField({ + system: false, + id: 'sllown1l', + name: 'nick', + type: 'text', + required: true, + presentable: false, + unique: false, + options: { + min: null, + max: null, + pattern: '' + } + }) + ); + + return dao.saveCollection(collection); + } +); diff --git a/pb_migrations/1719252581_updated_profiles.js b/pb_migrations/1719252581_updated_profiles.js new file mode 100644 index 0000000..ecc124e --- /dev/null +++ b/pb_migrations/1719252581_updated_profiles.js @@ -0,0 +1,55 @@ +/// +migrate( + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn'); + + collection.indexes = ['CREATE UNIQUE INDEX `idx_VTowXf1` ON `profiles` (`name`)']; + + // update + collection.schema.addField( + new SchemaField({ + system: false, + id: 'ykt3zolr', + name: 'name', + type: 'text', + required: true, + presentable: false, + unique: false, + options: { + min: 2, + max: null, + pattern: '' + } + }) + ); + + return dao.saveCollection(collection); + }, + (db) => { + const dao = new Dao(db); + const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn'); + + collection.indexes = ['CREATE INDEX `idx_VTowXf1` ON `profiles` (`name`)']; + + // update + collection.schema.addField( + new SchemaField({ + system: false, + id: 'ykt3zolr', + name: 'name', + type: 'text', + required: true, + presentable: false, + unique: false, + options: { + min: null, + max: null, + pattern: '' + } + }) + ); + + return dao.saveCollection(collection); + } +); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f39a356..30912e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + pocketbase: + specifier: ^0.21.3 + version: 0.21.3 devDependencies: '@playwright/test': specifier: ^1.28.1 @@ -1095,6 +1099,9 @@ packages: engines: {node: '>=16'} hasBin: true + pocketbase@0.21.3: + resolution: {integrity: sha512-bsQRZ1mj4dhPJ4P5iSrDqWQtBX2NtuBkf6IOZwTv27zZOw6zrISgo963i5JD/99qKKD5aMSMDPGk1BE9ZyI7Cg==} + postcss-load-config@3.1.4: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} @@ -2469,6 +2476,8 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + pocketbase@0.21.3: {} + postcss-load-config@3.1.4(postcss@8.4.38): dependencies: lilconfig: 2.1.0 diff --git a/src/app.html b/src/app.html index 77a5ff5..9df6a79 100644 --- a/src/app.html +++ b/src/app.html @@ -1,9 +1,36 @@ - + + + nikk - nicknames for your favorite characters + + + + + + + + + + + + + %sveltekit.head% diff --git a/src/app.scss b/src/app.scss index bd5f8df..df81920 100644 --- a/src/app.scss +++ b/src/app.scss @@ -1,15 +1,15 @@ /* Override global Sass variables from the /utilities folder */ -@use "bulma/sass/utilities" with ( -$link: $pink, -);; -@use "bulma/sass/base" with ( -$body-overflow-y: auto +@use 'bulma/sass/utilities' with ( + $link: $pink +); +@use 'bulma/sass/base' with ( + $body-overflow-y: auto ); /* Import the components you need */ -@use "bulma/sass/elements"; -@use "bulma/sass/form"; -@use "bulma/sass/components"; -@use "bulma/sass/grid"; -@use "bulma/sass/helpers"; -@use "bulma/sass/layout"; -@use "bulma/sass/themes"; \ No newline at end of file +@use 'bulma/sass/elements'; +@use 'bulma/sass/form'; +@use 'bulma/sass/components'; +@use 'bulma/sass/grid'; +@use 'bulma/sass/helpers'; +@use 'bulma/sass/layout'; +@use 'bulma/sass/themes'; diff --git a/src/lib/components/ui/navbar.svelte b/src/lib/components/ui/navbar.svelte new file mode 100644 index 0000000..41248a8 --- /dev/null +++ b/src/lib/components/ui/navbar.svelte @@ -0,0 +1,138 @@ + + +
+ + +
+ + diff --git a/src/lib/index.ts b/src/lib/index.ts deleted file mode 100644 index 856f2b6..0000000 --- a/src/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/pocketbase.ts b/src/lib/pocketbase.ts new file mode 100644 index 0000000..7005037 --- /dev/null +++ b/src/lib/pocketbase.ts @@ -0,0 +1,54 @@ +import PocketBase, { type AuthModel, type RecordModel } from 'pocketbase'; +import { env } from '$env/dynamic/public'; +import { writable, readonly } from 'svelte/store'; + +export const pb = new PocketBase(env.PUBLIC_POCKETBASE_URL); + +const _user = writable(pb.authStore.model); + +pb.authStore.onChange(() => { + _user.set(pb.authStore.model); +}); + +export const user = readonly(_user); + +export const getAllStuff = async () => { + const profiles = await pb.collection('profiles').getFullList({ + sort: '+created', + fields: 'id, name, user' + }); + const characters = await pb.collection('characters').getFullList({ + sort: '+created', + fields: 'id, name, displayName, element' + }); + const nicks = await pb.collection('nicks').getFullList({ + sort: '+created', + fields: 'id, profile, character, name' + }); + return { profiles, characters, nicks }; +}; + +export const saveNewNick = async (profileId: string, characterId: string, name: string) => { + let existingNick: RecordModel | null = null; + try { + existingNick = await pb.collection('nicks').getFirstListItem( + pb.filter('profile = {:profile} && character = {:character}', { + profile: profileId, + character: characterId + }) + ); + } catch (e) {} + if (existingNick && name.trim() === '') { + await pb.collection('nicks').delete(existingNick.id); + return; + } + if (existingNick) { + await pb.collection('nicks').update(existingNick.id, { name }); + } else { + await pb.collection('nicks').create({ + profile: profileId, + character: characterId, + name + }); + } +}; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index d23c24f..9fb1615 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1 +1,27 @@ - \ No newline at end of file + + + + +
+
+ +
+
+ + diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..3900880 --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,27 @@ +import type { LayoutLoad } from './$types'; +import { pb } from '$lib/pocketbase'; +import type { RecordModel } from 'pocketbase'; + +export const load: LayoutLoad = async ({ setHeaders }) => { + pb.authStore.loadFromCookie(document.cookie); + + try { + pb.authStore.isValid && (await pb.collection('users').authRefresh()); + } catch (e) { + console.log('authRefresh error', e); + // do nothing + } + + const user = pb.authStore.model; + let profile: RecordModel | null = null; + try { + profile = + user && + (await pb + .collection('profiles') + .getFirstListItem(pb.filter('user = {:user}', { user: user.id }))); + } catch (e) {} + return { user, profile }; +}; + +export const ssr = false; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 5982b0a..85c7906 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,2 +1,201 @@ -

Welcome to SvelteKit

-

Visit kit.svelte.dev to read the documentation

+ + + + + +{#if !profile} +
+ ganyu oh no +

You must be logged in to view this page

+
+{:else} +
+ + +
+ +
+ {#each $characters as character (character.id)} +
+
+
+
+
+ {`${character.displayName}'s +
+
+ {#if profile} + + {/if} +
+
+
+

+ {character.displayName} +

+
+ {#each $nicks + .filter((nick) => nick.character === character.id) + .sort( (a, b) => (a.profile === profile?.id ? -1 : b.profile === profile?.id ? 1 : 0) ) + .map( (nick) => ({ id: nick.id, byMe: nick.profile === profile?.id, name: nick.name, user: $profiles.find((profile) => profile.id === nick.profile)?.name }) ) as nick (nick.id)} +
+ + {nick.user} + + + {nick.name} + +
+ {/each} +
+
+
+
+
+ {/each} +
+{/if} + + diff --git a/src/routes/+page.ts b/src/routes/+page.ts new file mode 100644 index 0000000..0bd84ac --- /dev/null +++ b/src/routes/+page.ts @@ -0,0 +1,6 @@ +import type { PageLoad } from './$types'; +import { getAllStuff } from '$lib/pocketbase'; + +export const load: PageLoad = async () => { + return getAllStuff(); +}; diff --git a/src/routes/auth/callback/+page.svelte b/src/routes/auth/callback/+page.svelte new file mode 100644 index 0000000..08d1f7c --- /dev/null +++ b/src/routes/auth/callback/+page.svelte @@ -0,0 +1,43 @@ + diff --git a/src/routes/auth/login/+page.svelte b/src/routes/auth/login/+page.svelte new file mode 100644 index 0000000..c2ecf30 --- /dev/null +++ b/src/routes/auth/login/+page.svelte @@ -0,0 +1,17 @@ + diff --git a/src/routes/auth/logout/+page.svelte b/src/routes/auth/logout/+page.svelte new file mode 100644 index 0000000..f225e52 --- /dev/null +++ b/src/routes/auth/logout/+page.svelte @@ -0,0 +1,15 @@ + diff --git a/src/variables.scss b/src/variables.scss index a63d8cb..3469193 100644 --- a/src/variables.scss +++ b/src/variables.scss @@ -1,2 +1,2 @@ /* Set your brand colors */ -$pink: pink; \ No newline at end of file +$pink: pink; diff --git a/static/assets/images/fail.png b/static/assets/images/fail.png new file mode 100644 index 0000000..cf2b4e5 Binary files /dev/null and b/static/assets/images/fail.png differ diff --git a/static/favicon.png b/static/favicon.png index 825b9e6..ad3b5d1 100644 Binary files a/static/favicon.png and b/static/favicon.png differ diff --git a/vite.config.ts b/vite.config.ts index dc1598f..2a8dca3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,18 +1,18 @@ -import { sveltekit } from "@sveltejs/kit/vite"; -import { defineConfig } from "vitest/config"; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vitest/config'; export default defineConfig({ - plugins: [sveltekit()], + plugins: [sveltekit()], - test: { - include: ["src/**/*.{test,spec}.{js,ts}"] - }, + test: { + include: ['src/**/*.{test,spec}.{js,ts}'] + }, - css: { - preprocessorOptions: { - scss: { - additionalData: "@use \"src/variables.scss\" as *;" - } - } - } -}); \ No newline at end of file + css: { + preprocessorOptions: { + scss: { + additionalData: '@use "src/variables.scss" as *;' + } + } + } +});