start user page
This commit is contained in:
parent
90205a1243
commit
27cec4e77e
17 changed files with 401 additions and 82 deletions
10
package.json
10
package.json
|
@ -16,6 +16,8 @@
|
|||
"@sveltejs/kit": "^1.5.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/marked": "^4.0.8",
|
||||
"@types/sanitize-html": "^2.8.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
|
@ -34,7 +36,11 @@
|
|||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@steeze-ui/heroicons": "^2.2.2",
|
||||
"@steeze-ui/svelte-icon": "^1.3.2"
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"bootstrap": "5.3.0-alpha1",
|
||||
"bootstrap-icons": "^1.10.3",
|
||||
"marked": "^4.2.12",
|
||||
"sanitize-html": "^2.10.0",
|
||||
"sveltestrap": "^5.10.0"
|
||||
}
|
||||
}
|
||||
|
|
127
pnpm-lock.yaml
127
pnpm-lock.yaml
|
@ -1,37 +1,49 @@
|
|||
lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@steeze-ui/heroicons': ^2.2.2
|
||||
'@steeze-ui/svelte-icon': ^1.3.2
|
||||
'@popperjs/core': ^2.11.6
|
||||
'@sveltejs/adapter-auto': ^2.0.0
|
||||
'@sveltejs/kit': ^1.5.0
|
||||
'@tailwindcss/forms': ^0.5.3
|
||||
'@tailwindcss/typography': ^0.5.9
|
||||
'@types/marked': ^4.0.8
|
||||
'@types/sanitize-html': ^2.8.1
|
||||
'@typescript-eslint/eslint-plugin': ^5.45.0
|
||||
'@typescript-eslint/parser': ^5.45.0
|
||||
autoprefixer: ^10.4.13
|
||||
bootstrap: 5.3.0-alpha1
|
||||
bootstrap-icons: ^1.10.3
|
||||
eslint: ^8.28.0
|
||||
eslint-config-prettier: ^8.5.0
|
||||
eslint-plugin-svelte3: ^4.0.0
|
||||
marked: ^4.2.12
|
||||
postcss: ^8.4.21
|
||||
prettier: ^2.8.0
|
||||
prettier-plugin-svelte: ^2.8.1
|
||||
sanitize-html: ^2.10.0
|
||||
svelte: ^3.54.0
|
||||
svelte-check: ^3.0.1
|
||||
sveltestrap: ^5.10.0
|
||||
tailwindcss: ^3.2.7
|
||||
tslib: ^2.4.1
|
||||
typescript: ^4.9.3
|
||||
vite: ^4.0.0
|
||||
|
||||
dependencies:
|
||||
'@steeze-ui/heroicons': 2.2.2
|
||||
'@steeze-ui/svelte-icon': 1.3.2
|
||||
'@popperjs/core': 2.11.6
|
||||
bootstrap: 5.3.0-alpha1_@popperjs+core@2.11.6
|
||||
bootstrap-icons: 1.10.3
|
||||
marked: 4.2.12
|
||||
sanitize-html: 2.10.0
|
||||
sveltestrap: 5.10.0_svelte@3.55.1
|
||||
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-auto': 2.0.0_@sveltejs+kit@1.11.0
|
||||
'@sveltejs/kit': 1.11.0_svelte@3.55.1+vite@4.1.4
|
||||
'@tailwindcss/forms': 0.5.3_tailwindcss@3.2.7
|
||||
'@tailwindcss/typography': 0.5.9_tailwindcss@3.2.7
|
||||
'@types/marked': 4.0.8
|
||||
'@types/sanitize-html': 2.8.1
|
||||
'@typescript-eslint/eslint-plugin': 5.54.1_mlk7dnz565t663n4razh6a6v6i
|
||||
'@typescript-eslint/parser': 5.54.1_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
autoprefixer: 10.4.13_postcss@8.4.21
|
||||
|
@ -331,12 +343,8 @@ packages:
|
|||
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
||||
dev: true
|
||||
|
||||
/@steeze-ui/heroicons/2.2.2:
|
||||
resolution: {integrity: sha512-VUkgSAbjLzsEuF4kDQDESANb/NNxqhkOMnrzD1VhB3tSGr7wVZ5FKS7aM2QZcKXjziwR6dbt7Nhs3fpZhqOi1A==}
|
||||
dev: false
|
||||
|
||||
/@steeze-ui/svelte-icon/1.3.2:
|
||||
resolution: {integrity: sha512-Y7tH9UKeDYLfBicWLw4n4mhgfuSwgEdtq9wog7H+DjvfpTvea5SEqjHI8onoKZonJqAaUBGVjH02LAiqRWu+cw==}
|
||||
/@popperjs/core/2.11.6:
|
||||
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
|
||||
dev: false
|
||||
|
||||
/@sveltejs/adapter-auto/2.0.0_@sveltejs+kit@1.11.0:
|
||||
|
@ -424,10 +432,20 @@ packages:
|
|||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||
dev: true
|
||||
|
||||
/@types/marked/4.0.8:
|
||||
resolution: {integrity: sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==}
|
||||
dev: true
|
||||
|
||||
/@types/pug/2.0.6:
|
||||
resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
|
||||
dev: true
|
||||
|
||||
/@types/sanitize-html/2.8.1:
|
||||
resolution: {integrity: sha512-Q6kMAbBBaXA5IagoipeSr4Y/zuGyh4BZ5lewgb3cYe3OYqy0k/d67iMsC4O895eks676bVAe9G+0y1i0k2ZlnA==}
|
||||
dependencies:
|
||||
htmlparser2: 8.0.1
|
||||
dev: true
|
||||
|
||||
/@types/sass/1.45.0:
|
||||
resolution: {integrity: sha512-jn7qwGFmJHwUSphV8zZneO3GmtlgLsmhs/LQyVvQbIIa+fzGMUiHI4HXJZL3FT8MJmgXWbLGiVVY7ElvHq6vDA==}
|
||||
deprecated: This is a stub types definition. sass provides its own type definitions, so you do not need this installed.
|
||||
|
@ -669,6 +687,18 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/bootstrap-icons/1.10.3:
|
||||
resolution: {integrity: sha512-7Qvj0j0idEm/DdX9Q0CpxAnJYqBCFCiUI6qzSPYfERMcokVuV9Mdm/AJiVZI8+Gawe4h/l6zFcOzvV7oXCZArw==}
|
||||
dev: false
|
||||
|
||||
/bootstrap/5.3.0-alpha1_@popperjs+core@2.11.6:
|
||||
resolution: {integrity: sha512-ABZpKK4ObS3kKlIqH+ZVDqoy5t/bhFG0oHTAzByUdon7YIom0lpCeTqRniDzJmbtcWkNe800VVPBiJgxSYTYew==}
|
||||
peerDependencies:
|
||||
'@popperjs/core': ^2.11.6
|
||||
dependencies:
|
||||
'@popperjs/core': 2.11.6
|
||||
dev: false
|
||||
|
||||
/brace-expansion/1.1.11:
|
||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||
dependencies:
|
||||
|
@ -796,7 +826,6 @@ packages:
|
|||
/deepmerge/4.3.0:
|
||||
resolution: {integrity: sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/defined/1.0.1:
|
||||
resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==}
|
||||
|
@ -843,10 +872,37 @@ packages:
|
|||
esutils: 2.0.3
|
||||
dev: true
|
||||
|
||||
/dom-serializer/2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
entities: 4.4.0
|
||||
|
||||
/domelementtype/2.3.0:
|
||||
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
||||
|
||||
/domhandler/5.0.3:
|
||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||
engines: {node: '>= 4'}
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
/domutils/3.0.1:
|
||||
resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==}
|
||||
dependencies:
|
||||
dom-serializer: 2.0.0
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
|
||||
/electron-to-chromium/1.4.325:
|
||||
resolution: {integrity: sha512-K1C03NT4I7BuzsRdCU5RWkgZxtswnKDYM6/eMhkEXqKu4e5T+ck610x3FPzu1y7HVFSiQKZqP16gnJzPpji1TQ==}
|
||||
dev: true
|
||||
|
||||
/entities/4.4.0:
|
||||
resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
/es6-promise/3.3.1:
|
||||
resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
|
||||
dev: true
|
||||
|
@ -889,7 +945,6 @@ packages:
|
|||
/escape-string-regexp/4.0.0:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/eslint-config-prettier/8.7.0_eslint@8.35.0:
|
||||
resolution: {integrity: sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==}
|
||||
|
@ -1192,6 +1247,14 @@ packages:
|
|||
function-bind: 1.1.1
|
||||
dev: true
|
||||
|
||||
/htmlparser2/8.0.1:
|
||||
resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
domutils: 3.0.1
|
||||
entities: 4.4.0
|
||||
|
||||
/ignore/5.2.4:
|
||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||
engines: {node: '>= 4'}
|
||||
|
@ -1264,6 +1327,11 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-plain-object/5.0.0:
|
||||
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/isexe/2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
dev: true
|
||||
|
@ -1352,6 +1420,12 @@ packages:
|
|||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: true
|
||||
|
||||
/marked/4.2.12:
|
||||
resolution: {integrity: sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==}
|
||||
engines: {node: '>= 12'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/merge2/1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -1416,7 +1490,6 @@ packages:
|
|||
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/natural-compare-lite/1.4.0:
|
||||
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
||||
|
@ -1484,6 +1557,10 @@ packages:
|
|||
callsites: 3.1.0
|
||||
dev: true
|
||||
|
||||
/parse-srcset/1.0.2:
|
||||
resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
|
||||
dev: false
|
||||
|
||||
/path-exists/4.0.0:
|
||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -1510,7 +1587,6 @@ packages:
|
|||
|
||||
/picocolors/1.0.0:
|
||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||
dev: true
|
||||
|
||||
/picomatch/2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
|
@ -1598,7 +1674,6 @@ packages:
|
|||
nanoid: 3.3.4
|
||||
picocolors: 1.0.0
|
||||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/prelude-ls/1.2.1:
|
||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||
|
@ -1716,6 +1791,17 @@ packages:
|
|||
rimraf: 2.7.1
|
||||
dev: true
|
||||
|
||||
/sanitize-html/2.10.0:
|
||||
resolution: {integrity: sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==}
|
||||
dependencies:
|
||||
deepmerge: 4.3.0
|
||||
escape-string-regexp: 4.0.0
|
||||
htmlparser2: 8.0.1
|
||||
is-plain-object: 5.0.0
|
||||
parse-srcset: 1.0.2
|
||||
postcss: 8.4.21
|
||||
dev: false
|
||||
|
||||
/sass/1.58.3:
|
||||
resolution: {integrity: sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
@ -1777,7 +1863,6 @@ packages:
|
|||
/source-map-js/1.0.2:
|
||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/streamsearch/1.1.0:
|
||||
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
|
||||
|
@ -1903,7 +1988,15 @@ packages:
|
|||
/svelte/3.55.1:
|
||||
resolution: {integrity: sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==}
|
||||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/sveltestrap/5.10.0_svelte@3.55.1:
|
||||
resolution: {integrity: sha512-k6Ob+6G2AMYvBidXHBKM9W28fJqFHbmosqCe/NC8pv6TV7K+v47Yw+zmnLWkjqCzzmjkSLkL48SrHZrlWc9mYQ==}
|
||||
peerDependencies:
|
||||
svelte: ^3.29.0
|
||||
dependencies:
|
||||
'@popperjs/core': 2.11.6
|
||||
svelte: 3.55.1
|
||||
dev: false
|
||||
|
||||
/tailwindcss/3.2.7_postcss@8.4.21:
|
||||
resolution: {integrity: sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body class="bg-white dark:bg-slate-800 text-black dark:text-white" data-sveltekit-preload-data="hover">
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
export interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
name: string;
|
||||
display_name: string | null;
|
||||
bio: string | null;
|
||||
avatar_urls: string[] | null;
|
||||
links: string[] | null;
|
||||
|
||||
names: FieldEntry[];
|
||||
pronouns: Pronoun[];
|
||||
members: PartialMember[];
|
||||
fields: Field[];
|
||||
}
|
||||
|
||||
export interface MeUser extends User {
|
||||
|
@ -12,6 +17,38 @@ export interface MeUser extends User {
|
|||
discord_username: string | null;
|
||||
}
|
||||
|
||||
export interface Field {
|
||||
name: string;
|
||||
entries: FieldEntry[];
|
||||
}
|
||||
|
||||
export interface FieldEntry {
|
||||
value: string;
|
||||
status: WordStatus;
|
||||
}
|
||||
|
||||
export interface Pronoun {
|
||||
pronouns: string;
|
||||
display_text: string | null;
|
||||
status: WordStatus;
|
||||
}
|
||||
|
||||
export enum WordStatus {
|
||||
Unknown = 0,
|
||||
Favourite = 1,
|
||||
Okay = 2,
|
||||
Jokingly = 3,
|
||||
FriendsOnly = 4,
|
||||
Avoid = 5,
|
||||
}
|
||||
|
||||
export interface PartialMember {
|
||||
id: string;
|
||||
name: string;
|
||||
display_name: string | null;
|
||||
avatar_urls: string[] | null;
|
||||
}
|
||||
|
||||
export interface APIError {
|
||||
code: ErrorCode;
|
||||
message?: string;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import type { APIError } from "./entities";
|
||||
|
||||
export async function fetchAPI<T>(
|
||||
export async function apiFetch<T>(
|
||||
path: string,
|
||||
{ method, body, token }: { method?: string; body?: any; token?: string },
|
||||
) {
|
||||
const resp = await fetch(`${process.env.ORIGIN}/api/v1${path}`, {
|
||||
method,
|
||||
const apiBase = typeof process !== "undefined" ? process.env.ORIGIN : "";
|
||||
|
||||
const resp = await fetch(`${apiBase}/api/v1${path}`, {
|
||||
method: method || "GET",
|
||||
headers: {
|
||||
...(token ? { Authorization: token } : {}),
|
||||
"Content-Type": "application/json",
|
||||
|
@ -17,3 +19,6 @@ export async function fetchAPI<T>(
|
|||
if (resp.status < 200 || resp.status >= 300) throw data as APIError;
|
||||
return data as T;
|
||||
}
|
||||
|
||||
export const apiFetchClient = async <T>(path: string, method: string = "GET", body: any = null) =>
|
||||
apiFetch<T>(path, { method, body, token: localStorage.getItem("pronouns-token") || undefined });
|
||||
|
|
18
src/lib/components/FieldCard.svelte
Normal file
18
src/lib/components/FieldCard.svelte
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { Card, CardHeader, CardTitle, ListGroup, ListGroupItem } from "sveltestrap";
|
||||
|
||||
import type { Field } from "$lib/api/entities";
|
||||
|
||||
import StatusIcon from "./StatusIcon.svelte";
|
||||
|
||||
export let field: Field;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h5>{field.name}</h5>
|
||||
<ul class="list-unstyled">
|
||||
{#each field.entries as entry}
|
||||
<li><StatusIcon status={entry.status} /> {entry.value}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
16
src/lib/components/PronounLink.svelte
Normal file
16
src/lib/components/PronounLink.svelte
Normal file
|
@ -0,0 +1,16 @@
|
|||
<script lang="ts">
|
||||
import type { Pronoun } from "$lib/api/entities";
|
||||
|
||||
export let pronouns: Pronoun;
|
||||
|
||||
let pronounText: string;
|
||||
if (pronouns.display_text) {
|
||||
pronounText = pronouns.display_text;
|
||||
} else {
|
||||
const split = pronouns.pronouns.split("/");
|
||||
if (split.length < 2) pronounText = split.join("/");
|
||||
else pronounText = split.slice(0, 2).join("/")
|
||||
}
|
||||
</script>
|
||||
|
||||
<a href="/pronouns/{pronouns.pronouns}">{pronounText}</a>
|
52
src/lib/components/StatusIcon.svelte
Normal file
52
src/lib/components/StatusIcon.svelte
Normal file
|
@ -0,0 +1,52 @@
|
|||
<script lang="ts">
|
||||
import { Icon, Tooltip } from "sveltestrap";
|
||||
|
||||
import { WordStatus } from "$lib/api/entities";
|
||||
|
||||
export let status: WordStatus;
|
||||
|
||||
const iconFor = (wordStatus: WordStatus) => {
|
||||
switch (wordStatus) {
|
||||
case WordStatus.Favourite:
|
||||
return "heart-fill";
|
||||
case WordStatus.Okay:
|
||||
return "hand-thumbs-up";
|
||||
case WordStatus.Jokingly:
|
||||
return "emoji-laughing";
|
||||
case WordStatus.FriendsOnly:
|
||||
return "people";
|
||||
case WordStatus.Avoid:
|
||||
return "hand-thumbs-down";
|
||||
default:
|
||||
return "hand-thumbs-up";
|
||||
}
|
||||
};
|
||||
|
||||
const textFor = (wordStatus: WordStatus) => {
|
||||
switch (wordStatus) {
|
||||
case WordStatus.Favourite:
|
||||
return "Favourite";
|
||||
case WordStatus.Okay:
|
||||
return "Okay";
|
||||
case WordStatus.Jokingly:
|
||||
return "Jokingly";
|
||||
case WordStatus.FriendsOnly:
|
||||
return "Friends only";
|
||||
case WordStatus.Avoid:
|
||||
return "Avoid";
|
||||
default:
|
||||
return "Okay";
|
||||
}
|
||||
};
|
||||
|
||||
let statusIcon: string;
|
||||
$: statusIcon = iconFor(status);
|
||||
|
||||
let statusText: string;
|
||||
$: statusText = textFor(status);
|
||||
|
||||
let iconElement: HTMLElement;
|
||||
</script>
|
||||
|
||||
<span bind:this={iconElement} tabindex={0}><Icon name={statusIcon} /></span>
|
||||
<Tooltip target={iconElement} placement="top">{statusText}</Tooltip>
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import "../app.css";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "bootstrap-icons/font/bootstrap-icons.css";
|
||||
|
||||
import Navigation from "./nav/Navigation.svelte";
|
||||
</script>
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
<svelte:head>
|
||||
<title>pronouns.cc</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
||||
|
|
10
src/routes/@[username]/+page.server.ts
Normal file
10
src/routes/@[username]/+page.server.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { apiFetch } from "$lib/api/fetch";
|
||||
import type { User } from "$lib/api/entities";
|
||||
|
||||
export const load = async ({ params }) => {
|
||||
const resp = await apiFetch<User>(`/users/${params.username}`, {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
return resp;
|
||||
};
|
84
src/routes/@[username]/+page.svelte
Normal file
84
src/routes/@[username]/+page.svelte
Normal file
|
@ -0,0 +1,84 @@
|
|||
<script lang="ts">
|
||||
import { marked } from "marked";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
|
||||
import FieldCard from "$lib/components/FieldCard.svelte";
|
||||
|
||||
import type { PageData } from "./$types";
|
||||
import StatusIcon from "$lib/components/StatusIcon.svelte";
|
||||
import PronounLink from "$lib/components/PronounLink.svelte";
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let bio: string | null;
|
||||
$: bio = data.bio ? sanitizeHtml(marked.parse(data.bio)) : null;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>@{data.name} - pronouns.cc</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="container">
|
||||
<div class="grid">
|
||||
<div class="row">
|
||||
{#if data.avatar_urls}
|
||||
<div class="col-md" />
|
||||
{/if}
|
||||
<div class="col-md">
|
||||
{#if data.display_name}
|
||||
<h2>{data.display_name}</h2>
|
||||
<h4>@{data.name}</h4>
|
||||
{:else}
|
||||
<h2>@{data.name}</h2>
|
||||
{/if}
|
||||
<hr />
|
||||
{#if bio}
|
||||
<p>{@html bio}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if data.links}
|
||||
<div class="col-md">
|
||||
<ul>
|
||||
{#each data.links as link}
|
||||
<li><a href={link}>{link}</a></li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="row">
|
||||
{#if data.names}
|
||||
<div class="col-md">
|
||||
<h4>Names</h4>
|
||||
<ul class="list-unstyled">
|
||||
{#each data.names as name}
|
||||
<li><StatusIcon status={name.status} /> {name.value}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
{#if data.pronouns}
|
||||
<div class="col-md">
|
||||
<h4>Pronouns</h4>
|
||||
<ul class="list-unstyled">
|
||||
{#each data.pronouns as pronouns}
|
||||
<li>
|
||||
<StatusIcon status={pronouns.status} />
|
||||
<PronounLink pronouns={pronouns} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if data.fields}
|
||||
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3">
|
||||
{#each data.fields as field}
|
||||
<div class="col">
|
||||
<FieldCard {field} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
|
@ -1,7 +1,7 @@
|
|||
import { fetchAPI } from "$lib/api/fetch";
|
||||
import { apiFetch } from "$lib/api/fetch";
|
||||
|
||||
export const load = async () => {
|
||||
const resp = await fetchAPI<UrlsResponse>("/auth/urls", {
|
||||
const resp = await apiFetch<UrlsResponse>("/auth/urls", {
|
||||
method: "POST",
|
||||
body: {
|
||||
callback_domain: process.env.ORIGIN,
|
||||
|
|
|
@ -7,6 +7,17 @@
|
|||
<svelte:head>
|
||||
<title>Login - pronouns.cc</title>
|
||||
</svelte:head>
|
||||
<div>
|
||||
<a href={data.discord}>Login with Discord</a>
|
||||
|
||||
<div class="container">
|
||||
<h1>Log in or sign up</h1>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p>
|
||||
<a class="btn btn-primary" href={data.discord} role="button">Log in with Discord</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
0
src/routes/login/discord/+page.svelte
Normal file
0
src/routes/login/discord/+page.svelte
Normal file
|
@ -2,10 +2,8 @@
|
|||
import { onMount } from "svelte";
|
||||
import { browser } from "$app/environment";
|
||||
|
||||
import { Icon } from "@steeze-ui/svelte-icon";
|
||||
import { Moon, Sun, EllipsisVertical } from "@steeze-ui/heroicons";
|
||||
import { Collapse,Icon, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from "sveltestrap";
|
||||
|
||||
import NavItem from "./NavItem.svelte";
|
||||
import Logo from "./Logo.svelte";
|
||||
|
||||
let darkTheme: boolean = false;
|
||||
|
@ -23,9 +21,9 @@
|
|||
if (!browser) return;
|
||||
|
||||
if (isDark) {
|
||||
document.documentElement.classList.remove("dark");
|
||||
document.documentElement.setAttribute("data-bs-theme", "dark");
|
||||
} else {
|
||||
document.documentElement.classList.add("dark");
|
||||
document.documentElement.setAttribute("data-bs-theme", "light");
|
||||
}
|
||||
localStorage.setItem("theme", isDark ? "dark" : "light");
|
||||
};
|
||||
|
@ -33,53 +31,34 @@
|
|||
const toggleTheme = () => {
|
||||
darkTheme = !darkTheme;
|
||||
};
|
||||
const toggleMenu = () => {
|
||||
showMenu = !showMenu;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="bg-white/75 dark:bg-slate-800/75 w-full backdrop-blur border-slate-200 dark:border-slate-700 border-b"
|
||||
<Navbar
|
||||
color={darkTheme ? "dark" : "light"}
|
||||
light={!darkTheme}
|
||||
dark={darkTheme}
|
||||
expand="lg"
|
||||
class="mb-4"
|
||||
>
|
||||
<div class="max-w-8xl mx-auto">
|
||||
<div class="py-4 mx-4">
|
||||
<div class="flex items-center">
|
||||
<a href="/"><Logo /></a>
|
||||
<div class="ml-auto flex items-center">
|
||||
<nav class="hidden lg:flex">
|
||||
<ul class="flex space-x-4 font-bold">
|
||||
<NavItem href="/login">Log in</NavItem>
|
||||
</ul>
|
||||
</nav>
|
||||
<div
|
||||
class="flex border-l border-slate-200 ml-4 pl-4 lg:ml-6 lg:pl-6 lg:mr-2 dark:border-slate-700 space-x-2 lg:space-x-4"
|
||||
>
|
||||
<div
|
||||
class="cursor-pointer flex"
|
||||
on:click={() => toggleTheme()}
|
||||
on:keypress={() => toggleTheme()}
|
||||
title={darkTheme ? "Switch to light mode" : "Switch to dark mode"}
|
||||
>
|
||||
<Icon src={darkTheme ? Sun : Moon} class="hover:text-sky-400" size="32" />
|
||||
</div>
|
||||
<div
|
||||
class="cursor-pointer flex lg:hidden"
|
||||
on:click={() => (showMenu = !showMenu)}
|
||||
on:keypress={() => (showMenu = !showMenu)}
|
||||
title={showMenu ? "Hide menu" : "Show menu"}
|
||||
>
|
||||
<Icon src={EllipsisVertical} class="hover:text-sky-400" size="32" />
|
||||
</div>
|
||||
<!-- <div
|
||||
onClick={() => setShowMenu(!showMenu)}
|
||||
title="Show menu"
|
||||
className="cursor-pointer flex lg:hidden"
|
||||
>
|
||||
<List
|
||||
className="dark:hover:text-sky-400 hover:text-sky-500"
|
||||
size={24}
|
||||
/>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NavbarBrand href="/"><Logo /></NavbarBrand>
|
||||
<NavbarToggler on:click={toggleMenu} />
|
||||
<Collapse isOpen={showMenu} navbar expand="lg">
|
||||
<Nav class="ms-auto" navbar>
|
||||
<NavItem>
|
||||
<NavLink href="/login">Log in</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
on:click={() => toggleTheme()}
|
||||
title={darkTheme ? "Switch to light mode" : "Switch to dark mode"}
|
||||
>
|
||||
<Icon name={darkTheme ? "sun" : "moon-stars"} height="24" />
|
||||
{darkTheme ? "Light mode" : "Dark mode"}
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</Collapse>
|
||||
</Navbar>
|
||||
|
|
|
@ -13,8 +13,11 @@ export default defineConfig({
|
|||
"/media": {
|
||||
target: "http://localhost:9000",
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/media/, "/pronouns.cc")
|
||||
rewrite: (path) => path.replace(/^\/media/, "/pronouns.cc"),
|
||||
},
|
||||
},
|
||||
},
|
||||
ssr: {
|
||||
noExternal: ["@popperjs/core"],
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue