mirror of
https://github.com/Discreetly/frontend.git
synced 2026-01-08 04:23:56 -05:00
checkpoint with overscroll issue
This commit is contained in:
5812
package-lock.json
generated
5812
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@@ -1,17 +1,21 @@
|
||||
{
|
||||
"name": "discreetly",
|
||||
"version": "0.0.1",
|
||||
"version": "0.3.0",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "playwright test",
|
||||
"test": "npm run test:unit && npm run test:e2e",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch --output human --threshold error",
|
||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||
"format": "prettier --plugin-search-dir . --write .",
|
||||
"prepare": "svelte-kit sync"
|
||||
"test:e2e": "playwright test",
|
||||
"test:unit": "vitest",
|
||||
"test:unit:ui": "vitest --ui --open --watch",
|
||||
"test:unit:coverage": "vitest run --coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
@@ -22,36 +26,45 @@
|
||||
"@sveltejs/adapter-cloudflare": "^2.3.0",
|
||||
"@sveltejs/adapter-static": "^2.0.3",
|
||||
"@sveltejs/kit": "^1.25.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.4.6",
|
||||
"@tailwindcss/forms": "^0.5.6",
|
||||
"@tailwindcss/typography": "0.5.10",
|
||||
"@testing-library/svelte": "^4.0.3",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/node": "^20.6.3",
|
||||
"@types/qrcode": "^1.5.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"@vitest/ui": "^0.34.6",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-svelte": "^2.30.0",
|
||||
"eslint": "^8.28.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"lightningcss": "^1.21.7",
|
||||
"postcss": "^8.4.24",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.8.0",
|
||||
"postcss": "^8.4.24",
|
||||
"prettier-plugin-svelte": "^2.10.1",
|
||||
"rollup-plugin-sizes": "^1.0.5",
|
||||
"svelte": "^4.0.5",
|
||||
"prettier": "^2.8.0",
|
||||
"svelte-check": "^3.4.5",
|
||||
"svelte-kit": "^1.2.0",
|
||||
"svelte": "^4.0.5",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"terser": "^5.19.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.3.6"
|
||||
"vite-plugin-tailwind-purgecss": "0.1.3",
|
||||
"vite": "^4.4.11",
|
||||
"vitest": "0.32.2"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@ethersproject/bytes": "^5.7.0",
|
||||
"@ethersproject/keccak256": "^5.7.0",
|
||||
"@ethersproject/strings": "^5.7.0",
|
||||
"@floating-ui/dom": "1.5.3",
|
||||
"@personaelabs/spartan-ecdsa": "^2.3.0",
|
||||
"@rainbow-me/rainbowkit": "^1.1.1",
|
||||
"@semaphore-protocol/group": "^3.10.1",
|
||||
"@semaphore-protocol/identity": "^3.10.1",
|
||||
"autolinker": "^4.0.0",
|
||||
@@ -65,6 +78,7 @@
|
||||
"qr-scanner": "^1.4.2",
|
||||
"qrcode": "^1.5.3",
|
||||
"socket.io-client": "^4.7.1",
|
||||
"svelte-material-icons": "^3.0.5"
|
||||
"svelte-material-icons": "^3.0.5",
|
||||
"wagmi": "^1.4.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
14
playwright.config.ts
Normal file
14
playwright.config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
webServer: {
|
||||
command: 'npm run build && npm run preview',
|
||||
port: 4173,
|
||||
stdout: 'ignore',
|
||||
stderr: 'ignore'
|
||||
},
|
||||
testDir: 'tests',
|
||||
testMatch: /(.+\.)?(test|spec)\.[jt]s/
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,13 +1,6 @@
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
|
||||
const config = {
|
||||
plugins: [
|
||||
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
|
||||
tailwindcss(),
|
||||
//But others, like autoprefixer, need to run after,
|
||||
autoprefixer
|
||||
]
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
||||
5
src/+error.svelte
Normal file
5
src/+error.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
</script>
|
||||
|
||||
<h1 class="h3">{$page.error.message}</h1>
|
||||
@@ -8,6 +8,6 @@
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover" data-theme="discreetly-theme">
|
||||
<div>%sveltekit.body%</div>
|
||||
<div style="display: contents" class="h-full overflow-hidden">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -91,3 +91,7 @@ input:focus-visible {
|
||||
font-family: 'Nippo';
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.rail-icon {
|
||||
@apply w-full h3;
|
||||
}
|
||||
|
||||
12
src/error.html
Normal file
12
src/error.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>%sveltekit.error.message%</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Something didn't work right</h1>
|
||||
<p>Status: %sveltekit.status%</p>
|
||||
<p>Message: %sveltekit.error.message%</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,5 @@
|
||||
// FIXME: This is a potential hack to get proofs to generate on the front end
|
||||
export async function handle({ event, resolve }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export async function handle({ event, resolve }: { event: unknown; resolve: any }) {
|
||||
return resolve(event, { ssr: false });
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Ratings } from '@skeletonlabs/skeleton';
|
||||
import FullCircle from 'svelte-material-icons/Circle.svelte';
|
||||
import CircleEmpty from 'svelte-material-icons/CircleOutline.svelte';
|
||||
|
||||
@@ -9,11 +10,11 @@
|
||||
$: emptycircles = maxHealth - health;
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row ms-2 place-items-center">
|
||||
{#each { length: fullcircles } as _, i}
|
||||
<FullCircle class="w-4 h-4 text-green-500" />
|
||||
{/each}
|
||||
{#each { length: emptycircles } as _, i}
|
||||
<Ratings value={health} max={maxHealth} spacing="space-x-0">
|
||||
<svelte:fragment slot="empty">
|
||||
<CircleEmpty class="w-4 h-4 text-surface-600-300-token" />
|
||||
{/each}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="full">
|
||||
<FullCircle class="w-4 h-4 text-green-500" />
|
||||
</svelte:fragment>
|
||||
</Ratings>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import FullShield from 'svelte-material-icons/Shield.svelte';
|
||||
import ShieldHalfFullHalfEmpty from 'svelte-material-icons/ShieldHalfFull.svelte';
|
||||
import ShieldEmpty from 'svelte-material-icons/ShieldOutline.svelte';
|
||||
import ShieldHalfFull from 'svelte-material-icons/ShieldHalf.svelte';
|
||||
|
||||
export let health: number;
|
||||
export let maxHealth: number;
|
||||
|
||||
@@ -1,27 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { getIdentityBackup } from '$lib/utils/';
|
||||
|
||||
$: id = getIdentityBackup();
|
||||
$: identityBackupExists = id ? true : false;
|
||||
$: encodedIdentity = 'data:text/json;charset=utf-8,' + encodeURIComponent(id!);
|
||||
|
||||
let revealIdentity = false;
|
||||
let id = '';
|
||||
|
||||
function reveal() {
|
||||
id = getIdentityBackup();
|
||||
if (revealIdentity == false) {
|
||||
revealIdentity = true;
|
||||
setTimeout(() => {
|
||||
revealIdentity = false;
|
||||
id = '';
|
||||
}, 60000);
|
||||
if (id === undefined || id === null) {
|
||||
id = 'No Identity Backup Found';
|
||||
} else {
|
||||
revealIdentity = false;
|
||||
if (revealIdentity == false) {
|
||||
revealIdentity = true;
|
||||
setTimeout(() => {
|
||||
revealIdentity = false;
|
||||
id = '';
|
||||
}, 60000);
|
||||
} else {
|
||||
revealIdentity = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="m-2 sm:m-3 flex flex-col gap-4">
|
||||
<a
|
||||
class="btn variant-ghost-success"
|
||||
href={'data:text/json;charset=utf-8,' + encodeURIComponent(getIdentityBackup())}
|
||||
download="Discreetly_Identity.json">Download Identity Backup as JSON</a
|
||||
>
|
||||
{#if identityBackupExists}
|
||||
<a class="btn variant-ghost-success" href={encodedIdentity} download="Discreetly_Identity.json"
|
||||
>Download Identity Backup as JSON</a
|
||||
>
|
||||
{:else}
|
||||
<div class="text-sm text-primary-500">
|
||||
Error getting your identity backup. Please contact the developers for help.
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !revealIdentity}
|
||||
<div class="btn variant-ghost-success" on:click={reveal}>Show Identity</div>
|
||||
{:else}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import SelectServer from '$lib/components/SelectServer.svelte';
|
||||
import { alertAll } from '$lib/utils';
|
||||
import { inviteCode } from '$lib/utils/inviteCode';
|
||||
|
||||
export let code = '';
|
||||
@@ -12,13 +13,13 @@
|
||||
inviteCode(code)
|
||||
.then(({ acceptedRoomNames, err }) => {
|
||||
if (err) {
|
||||
alert(err);
|
||||
alertAll(err);
|
||||
} else {
|
||||
acceptedRoomNames = acceptedRoomNames;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err);
|
||||
alertAll(err);
|
||||
})
|
||||
.finally(() => {
|
||||
loading = false;
|
||||
|
||||
@@ -7,21 +7,23 @@
|
||||
import { getModalStore, type ModalSettings } from '@skeletonlabs/skeleton';
|
||||
import { deriveKey, hashPassword } from '$lib/crypto/crypto';
|
||||
import { onMount } from 'svelte';
|
||||
import { setPassword } from '$lib/utils';
|
||||
|
||||
import { alertAll, setPassword } from '$lib/utils';
|
||||
const modalStore = getModalStore();
|
||||
|
||||
export let cls: string = '';
|
||||
|
||||
let minPasswordLength = 3;
|
||||
|
||||
function setPasswordModal() {
|
||||
const modal: ModalSettings = {
|
||||
type: 'prompt',
|
||||
title: 'Set a Password',
|
||||
body: 'Set a password to encrypt your identity and room passwords',
|
||||
body: 'Set a password or pin to encrypt your identity and room passwords',
|
||||
value: '',
|
||||
valueAttr: { type: 'password', minlength: 3, required: true },
|
||||
valueAttr: { type: 'password', minlength: minPasswordLength, required: true },
|
||||
response: async (r: string) => {
|
||||
setPassword(r);
|
||||
if (r != '' && r != null && r != undefined && r.length >= minPasswordLength) {
|
||||
setPassword(r);
|
||||
}
|
||||
}
|
||||
};
|
||||
modalStore.trigger(modal);
|
||||
@@ -33,13 +35,14 @@
|
||||
title: 'Unlock',
|
||||
body: 'Enter your password to unlock your keystores',
|
||||
value: '',
|
||||
valueAttr: { type: 'password', minlength: 3, required: true },
|
||||
valueAttr: { type: 'password', minlength: 4, required: true },
|
||||
response: async (r: string) => {
|
||||
if (r != 'false') {
|
||||
if (r != 'false' && r != '' && r != null && r != undefined) {
|
||||
const hashedPassword = await hashPassword(r);
|
||||
if ($configStore.hashedPwd == hashedPassword) {
|
||||
$keyStore = await deriveKey(r);
|
||||
} else {
|
||||
alertAll('Incorrect Password');
|
||||
$keyStore = null;
|
||||
}
|
||||
}
|
||||
@@ -53,7 +56,7 @@
|
||||
}
|
||||
onMount(() => {
|
||||
console.debug(
|
||||
'PasswordLock: ',
|
||||
'PadLock:',
|
||||
$passwordSet ? 'password set,' : 'password not set,',
|
||||
$keyStore !== null && $keyStore !== undefined ? 'unlocked' : 'locked'
|
||||
);
|
||||
@@ -64,14 +67,14 @@
|
||||
{#if $passwordSet}
|
||||
{#if $keyStore instanceof CryptoKey}
|
||||
<div on:click={lock} title="Unlocked, click to lock">
|
||||
<LockOpen class="text-warning-300-600-token" />
|
||||
<LockOpen class="w-full text-warning-300-600-token" />
|
||||
</div>
|
||||
{:else}
|
||||
<div on:click={unlock} title="Locked" class="text-success-500"><Lock /></div>
|
||||
<div on:click={unlock} title="Locked" class="w-full text-success-500"><Lock /></div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div on:click={setPasswordModal} title="Password not set">
|
||||
<NoPassword class="text-primary-500" />
|
||||
<NoPassword class="w-full text-error-500" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
24
src/lib/components/Welcome.svelte
Normal file
24
src/lib/components/Welcome.svelte
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/button.svelte';
|
||||
import { identityExists } from '$lib/stores';
|
||||
</script>
|
||||
|
||||
{#if !$identityExists}
|
||||
<p class="card py-2 px-4 md:px-5 mb-3">
|
||||
If you have an <code class="code">invite code</code> head over to the <Button
|
||||
link="/signup"
|
||||
cls="variant-ghost-primary btn-sm m-2 sm:m-3">Sign Up</Button
|
||||
>
|
||||
</p>
|
||||
<p class="card py-2 px-4 md:px-5">
|
||||
Or if you want to request an invite code, join our <Button
|
||||
link="https://discord.gg/brJQ36KVxk"
|
||||
cls="variant-ghost-tertiary btn-sm m-2 sm:m-3">Discord</Button
|
||||
>
|
||||
</p>
|
||||
{:else}
|
||||
<p>
|
||||
It looks like you are already signed up!
|
||||
<Button link="/chat" cls="variant-ghost-success">Go Chat</Button>
|
||||
</p>
|
||||
{/if}
|
||||
@@ -10,8 +10,11 @@ export const defaultServers = {
|
||||
|
||||
export const configDefaults: ConfigurationI = {
|
||||
signUpStatus: {
|
||||
inviteAccepted: false,
|
||||
completedSignup: false,
|
||||
identityBackedUp: false
|
||||
},
|
||||
identityStore: IdentityStoreE.NO_IDENTITY
|
||||
identityStore: IdentityStoreE.NO_IDENTITY,
|
||||
numMessagesToSave: 500,
|
||||
hashedPwd: undefined,
|
||||
beta: false
|
||||
};
|
||||
|
||||
@@ -23,6 +23,14 @@ export async function getMessages(serverUrl: string, roomId: string) {
|
||||
return get([serverUrl, `api/room/${roomId}/messages`]) as Promise<MessageI[]>;
|
||||
}
|
||||
|
||||
interface CreateInviteData {
|
||||
numCodes: number;
|
||||
expiresAt?: number;
|
||||
usesLeft?: number;
|
||||
roomIds?: string[];
|
||||
all?: boolean;
|
||||
}
|
||||
|
||||
export async function createInvite(
|
||||
serverUrl: string,
|
||||
username: string,
|
||||
@@ -32,7 +40,7 @@ export async function createInvite(
|
||||
expiresAt?: number,
|
||||
usesLeft?: number
|
||||
) {
|
||||
const data = { numCodes, expiresAt, usesLeft };
|
||||
const data: CreateInviteData = { numCodes, expiresAt, usesLeft };
|
||||
if (roomIds.length > 0) {
|
||||
data['roomIds'] = roomIds;
|
||||
} else {
|
||||
|
||||
@@ -129,3 +129,14 @@ export const identityStore = storable({} as IdentityStoreI, 'identity');
|
||||
* @description Identity store, this is the user's identity ENCRYPTED
|
||||
*/
|
||||
export const identityKeyStore = encryptable({} as IdentityStoreI, 'identityencrypted');
|
||||
|
||||
export const identityExists = derived(
|
||||
[identityStore, identityKeyStore],
|
||||
([$identityStore, $identityKeyStore]) => {
|
||||
if ($identityStore._commitment || $identityKeyStore._commitment) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ export enum Experiences {
|
||||
export enum IdentityStoreE {
|
||||
'NO_IDENTITY',
|
||||
'localStorage',
|
||||
'localStorageEncrypted',
|
||||
'cryptKeeper',
|
||||
'PCDPass'
|
||||
}
|
||||
|
||||
@@ -36,10 +36,8 @@ export interface JoinResponseI {
|
||||
roomIds: string[];
|
||||
}
|
||||
|
||||
// Keyed by roomId
|
||||
|
||||
export interface SignUpStatusI {
|
||||
inviteAccepted: boolean;
|
||||
completedSignup: boolean;
|
||||
identityBackedUp: boolean;
|
||||
inviteCode?: string;
|
||||
}
|
||||
|
||||
@@ -30,3 +30,12 @@ export const clearConsoleMessages = () => {
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
|
||||
export function isInputFieldFocused() {
|
||||
const activeElement = document.activeElement;
|
||||
return (
|
||||
activeElement &&
|
||||
(activeElement.tagName.toLowerCase() === 'input' ||
|
||||
activeElement.tagName.toLowerCase() === 'textarea')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,35 +65,35 @@ export function getIdentity(): IdentityStoreI {
|
||||
|
||||
export function getCommitment() {
|
||||
const id = get(identityKeyStore) as IdentityStoreI;
|
||||
const id_old = get(identityStore);
|
||||
const id_ = get(identityStore);
|
||||
if (id !== null && id !== undefined) {
|
||||
return id._commitment;
|
||||
}
|
||||
if (id_old !== null && id_old !== undefined) {
|
||||
if (id_ !== null && id_ !== undefined) {
|
||||
console.warn('PLEASE ADD A PASSWORD!');
|
||||
return id_old._commitment;
|
||||
return id_._commitment;
|
||||
}
|
||||
}
|
||||
|
||||
export function getIdentityBackup() {
|
||||
const id = get(identityKeyStore);
|
||||
const id_old = get(identityStore);
|
||||
const id_ = get(identityStore);
|
||||
if (id !== null && id !== undefined) {
|
||||
return JSON.stringify(id);
|
||||
}
|
||||
if (id_old !== null && id_old !== undefined) {
|
||||
if (id_ !== null && id_ !== undefined) {
|
||||
console.warn('PLEASE ADD A PASSWORD!');
|
||||
return JSON.stringify(id_old);
|
||||
return JSON.stringify(id_);
|
||||
}
|
||||
}
|
||||
|
||||
export function doesIdentityExist(): 'safe' | 'unsafe' | 'none' {
|
||||
const id = get(identityKeyStore);
|
||||
const id_old = get(identityStore);
|
||||
const id_ = get(identityStore);
|
||||
if (id._commitment !== null && id._commitment !== undefined) {
|
||||
return 'safe';
|
||||
}
|
||||
if (id_old._commitment !== null && id_old._commitment !== undefined) {
|
||||
if (id_._commitment !== null && id_._commitment !== undefined) {
|
||||
console.warn('PLEASE ADD A PASSWORD');
|
||||
return 'unsafe';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getCommitment, updateRooms } from '$lib/utils/';
|
||||
import { alertAll, getCommitment, updateRooms } from '$lib/utils/';
|
||||
import { postInviteCode } from '$lib/services/server';
|
||||
import { selectedServer, configStore } from '$lib/stores';
|
||||
import { get } from 'svelte/store';
|
||||
@@ -10,6 +10,11 @@ export async function inviteCode(newCode: string) {
|
||||
const server = get(selectedServer);
|
||||
try {
|
||||
const idc = getCommitment();
|
||||
if (!idc) {
|
||||
// TODO convert this to alertAll at some point
|
||||
alertAll('No identity commitment found');
|
||||
throw new Error('No identity commitment found');
|
||||
}
|
||||
const result = (await postInviteCode(server, {
|
||||
code: newCode.toLowerCase(),
|
||||
idc
|
||||
@@ -20,7 +25,7 @@ export async function inviteCode(newCode: string) {
|
||||
acceptedRoomNames = await updateRooms(server, result.roomIds);
|
||||
console.log(`Added to rooms: ${acceptedRoomNames}`);
|
||||
configStore.update((store) => {
|
||||
store['signUpStatus']['inviteAccepted'] = true;
|
||||
store['signUpStatus']['completedSignup'] = true;
|
||||
store['signUpStatus']['inviteCode'] = '';
|
||||
return store;
|
||||
});
|
||||
|
||||
@@ -20,9 +20,11 @@ export function getEpochFromTimestamp(
|
||||
let relative = '';
|
||||
try {
|
||||
relative = formatRelative(new Date(timestamp), new Date());
|
||||
} catch (err) {
|
||||
} catch (err: unknown) {
|
||||
let message = 'Unknown Error';
|
||||
if (err instanceof Error) message = err.message;
|
||||
relative = 'Unknown';
|
||||
console.debug(`${err.message}: ${epoch} * ${ratelimit} = ${timestamp}`);
|
||||
console.debug(`${message}: ${epoch} * ${ratelimit} = ${timestamp}`);
|
||||
}
|
||||
return { epoch, relative, timestamp };
|
||||
}
|
||||
|
||||
@@ -43,6 +43,11 @@ function updateRoomStore(rooms: RoomI[], serverURL: string = get(selectedServer)
|
||||
async function getRoomIdsIfEmpty(server: string, roomIds: string[]): Promise<string[]> {
|
||||
if (roomIds.length < 1) {
|
||||
const idc = getCommitment();
|
||||
if (!idc) {
|
||||
// TODO convert this to alertAll at some point
|
||||
console.error('No identity commitment found');
|
||||
throw new Error('No identity commitment found');
|
||||
}
|
||||
return await getRoomIdsByIdentityCommitment(server, idc);
|
||||
}
|
||||
return roomIds;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get, type Writable } from 'svelte/store';
|
||||
import type { serverStoreI } from '../stores';
|
||||
import type { serverStoreI } from '../types/stores';
|
||||
import { serverStore, roomsStore, selectedServer } from '../stores';
|
||||
import { getServerData } from '$lib/services/server';
|
||||
import type { RoomI } from '$lib/types';
|
||||
@@ -19,7 +19,7 @@ export function getServerRooms(url: string, store: Writable<serverStoreI> = serv
|
||||
if (!roomIds) {
|
||||
roomIds = [];
|
||||
}
|
||||
return roomIds.map((roomId) => {
|
||||
return roomIds.map((roomId: string) => {
|
||||
return get(roomsStore)[roomId];
|
||||
}) as RoomI[];
|
||||
}
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { AppShell, Modal, initializeStores } from '@skeletonlabs/skeleton';
|
||||
import { Modal, initializeStores } from '@skeletonlabs/skeleton';
|
||||
import { Toast } from '@skeletonlabs/skeleton';
|
||||
import '../app.postcss';
|
||||
import { onMount } from 'svelte';
|
||||
import AppHeader from './AppHeader.svelte';
|
||||
import Loading from '$lib/components/loading.svelte';
|
||||
import { selectedServer } from '$lib/stores';
|
||||
import { getServerList, setDefaultServers } from '$lib/utils/';
|
||||
import { getServerList, isInputFieldFocused, setDefaultServers } from '$lib/utils/';
|
||||
import { updateServer } from '$lib/utils/';
|
||||
import { Drawer } from '@skeletonlabs/skeleton';
|
||||
import { Drawer, getDrawerStore } from '@skeletonlabs/skeleton';
|
||||
import SelectServer from '$lib/components/SelectServer.svelte';
|
||||
import SelectRoom from '$lib/components/SelectRoom.svelte';
|
||||
import Console from './console/Console.svelte';
|
||||
import Sidebar from './Sidebar.svelte';
|
||||
import AppFooter from './AppFooter.svelte';
|
||||
|
||||
initializeStores();
|
||||
|
||||
const drawerStore = getDrawerStore();
|
||||
// Hack to get BigInt <-> JSON compatibility
|
||||
(BigInt.prototype as any).toJSON = function () {
|
||||
return this.toString();
|
||||
@@ -25,19 +29,48 @@
|
||||
setDefaultServers();
|
||||
}
|
||||
updateServer($selectedServer);
|
||||
document.addEventListener('keydown', function (event) {
|
||||
if (event.key === '`') {
|
||||
if (!isInputFieldFocused()) {
|
||||
event.preventDefault();
|
||||
if ($drawerStore.open !== true) {
|
||||
drawerStore.open({ id: 'console' });
|
||||
} else {
|
||||
drawerStore.close();
|
||||
}
|
||||
} else {
|
||||
console.log('Input field focused, not opening console');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<Modal />
|
||||
<Toast position="t" background="variant-filled-primary" />
|
||||
<Drawer position="top" padding="p-4" rounded="rounded-token">
|
||||
<SelectServer />
|
||||
<SelectRoom />
|
||||
{#if $drawerStore.id === 'roomselect'}
|
||||
<SelectServer />
|
||||
<SelectRoom />
|
||||
{:else if $drawerStore.id === 'console'}
|
||||
<Console />
|
||||
{/if}
|
||||
</Drawer>
|
||||
|
||||
<AppShell>
|
||||
<svelte:fragment slot="header"><AppHeader /></svelte:fragment>
|
||||
<slot>
|
||||
<Loading />
|
||||
</slot>
|
||||
</AppShell>
|
||||
<div class="w-full h-screen flex flex-col overflow-hidden">
|
||||
<div class="flex-none z-10"><AppHeader /></div>
|
||||
<div class="grid grid-cols-[1fr,auto] h-full min-w-full justify-between">
|
||||
<main class="flex flex-col justify-between">
|
||||
<slot class="flex flex-col justify-center">
|
||||
<Loading />
|
||||
</slot>
|
||||
<div class="block lg:hidden">
|
||||
<AppFooter />
|
||||
</div>
|
||||
</main>
|
||||
<div class="hidden lg:block"><Sidebar /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/button.svelte';
|
||||
import { identityStore } from '$lib/stores';
|
||||
$: identityExists = !!$identityStore._commitment;
|
||||
import Welcome from '$lib/components/Welcome.svelte';
|
||||
|
||||
console.info(
|
||||
'I see you are checking out the logs, let us know what you think on our discord: https://discord.gg/brJQ36KVxk'
|
||||
@@ -10,21 +8,6 @@
|
||||
|
||||
<div class="mx-5 lg:mx-auto mt-10 max-w-[80ch]">
|
||||
<h2 class="h2 mb-5">Welcome to Discreetly!</h2>
|
||||
{#if !identityExists}
|
||||
<p class="card py-2 px-4 md:px-5 mb-3">
|
||||
If you have an <code class="code">invite code</code> head over to the <Button
|
||||
link="/signup"
|
||||
cls="variant-ghost-primary btn-sm m-2 sm:m-3">Sign Up</Button
|
||||
>
|
||||
</p>
|
||||
<p class="card py-2 px-4 md:px-5">
|
||||
Or if you want to request an invite code, join our <Button
|
||||
link="https://discord.gg/brJQ36KVxk"
|
||||
cls="variant-ghost-tertiary btn-sm m-2 sm:m-3">Discord</Button
|
||||
>
|
||||
</p>
|
||||
{:else}
|
||||
<Button link="/chat" cls="variant-ghost-success">Go Chat</Button>
|
||||
{/if}
|
||||
<Welcome />
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -1,27 +1,97 @@
|
||||
<script lang="ts">
|
||||
import { identityStore } from '$lib/stores';
|
||||
import { AppBar } from '@skeletonlabs/skeleton';
|
||||
$: identityExists = !!$identityStore._commitment;
|
||||
import { page } from '$app/stores';
|
||||
import { passwordSet, configStore, keyStore } from '$lib/stores';
|
||||
import { getModalStore, type ModalSettings } from '@skeletonlabs/skeleton';
|
||||
import { deriveKey, hashPassword } from '$lib/crypto/crypto';
|
||||
import { onMount } from 'svelte';
|
||||
import { TabAnchor, TabGroup } from '@skeletonlabs/skeleton';
|
||||
|
||||
import Chat from 'svelte-material-icons/Chat.svelte';
|
||||
import Settings from 'svelte-material-icons/TuneVariant.svelte';
|
||||
import Information from 'svelte-material-icons/Information.svelte';
|
||||
import Lock from 'svelte-material-icons/Lock.svelte';
|
||||
import LockOpen from 'svelte-material-icons/LockOpenVariant.svelte';
|
||||
import NoPassword from 'svelte-material-icons/LockOff.svelte';
|
||||
import { alertAll } from '$lib/utils';
|
||||
|
||||
const modalStore = getModalStore();
|
||||
|
||||
function unlock() {
|
||||
const modal: ModalSettings = {
|
||||
type: 'prompt',
|
||||
title: 'Unlock',
|
||||
body: 'Enter your password to unlock your keystores',
|
||||
value: '',
|
||||
valueAttr: { type: 'password', minlength: 4, required: true },
|
||||
response: async (r: string) => {
|
||||
if (r != 'false' && r != '' && r != null && r != undefined) {
|
||||
const hashedPassword = await hashPassword(r);
|
||||
if ($configStore.hashedPwd == hashedPassword) {
|
||||
$keyStore = await deriveKey(r);
|
||||
} else {
|
||||
alertAll('Incorrect Password');
|
||||
$keyStore = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
modalStore.trigger(modal);
|
||||
}
|
||||
|
||||
function lock() {
|
||||
$keyStore = null;
|
||||
}
|
||||
onMount(() => {
|
||||
console.debug(
|
||||
'PadLock:',
|
||||
$passwordSet ? 'password set,' : 'password not set,',
|
||||
$keyStore !== null && $keyStore !== undefined ? 'unlocked' : 'locked'
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<AppBar class="hidden md:block" padding="py-3 px-8">
|
||||
<svelte:fragment slot="lead">
|
||||
<a href="/">Home</a>
|
||||
{#if identityExists}
|
||||
<a href="/chat">Chat</a>
|
||||
{:else}
|
||||
<a href="/signup">Sign Up</a>
|
||||
{/if}
|
||||
<a href="/about">About</a>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="trail">
|
||||
<p class="hidden sm:inline me-2 text-primary-500">Alpha Version!</p>
|
||||
<p><a href="/testing" style="margin:0 !important">© 2023 Privacy & Scaling Explorations</a></p>
|
||||
</svelte:fragment>
|
||||
</AppBar>
|
||||
<TabGroup
|
||||
justify="justify-center"
|
||||
active="variant-filled-primary"
|
||||
hover="hover:variant-soft-primary"
|
||||
flex="flex-1 lg:flex-none"
|
||||
class="bg-surface-100-800-token w-full"
|
||||
>
|
||||
<TabAnchor href="/about" selected={$page.url.pathname === '/about'} title="About">
|
||||
<svelte:fragment slot="lead"><Information class="rail-icon" /></svelte:fragment>
|
||||
<span>About</span>
|
||||
</TabAnchor>
|
||||
|
||||
<style>
|
||||
a {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
<TabAnchor href="/chat" selected={$page.url.pathname === '/chat'} title="Chat">
|
||||
<svelte:fragment slot="lead"><Chat class="rail-icon" /></svelte:fragment>
|
||||
<span>Chat</span>
|
||||
</TabAnchor>
|
||||
{#if $passwordSet}
|
||||
{#if $keyStore instanceof CryptoKey}
|
||||
<TabAnchor on:click={lock} title="Unlocked, click to lock">
|
||||
<svelte:fragment slot="lead">
|
||||
<LockOpen class="rail-icon text-warning-300-600-token" />
|
||||
</svelte:fragment>
|
||||
<span>Lock</span>
|
||||
</TabAnchor>
|
||||
{:else}
|
||||
<TabAnchor on:click={unlock} title="Locked">
|
||||
<svelte:fragment slot="lead">
|
||||
<Lock class="rail-icon text-success-500" />
|
||||
</svelte:fragment>
|
||||
<span>Unlock</span>
|
||||
</TabAnchor>
|
||||
{/if}
|
||||
{:else}
|
||||
<TabAnchor href="/settings/security" title="Password not set">
|
||||
<svelte:fragment slot="lead">
|
||||
<NoPassword class="rail-icon text-error-500" />
|
||||
</svelte:fragment>
|
||||
<span>Secure</span>
|
||||
</TabAnchor>
|
||||
{/if}
|
||||
<TabAnchor href="/settings" selected={$page.url.pathname === '/settings'} title="Settings">
|
||||
<svelte:fragment slot="lead"><Settings class="rail-icon" /></svelte:fragment>
|
||||
<span>Settings</span>
|
||||
</TabAnchor>
|
||||
</TabGroup>
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { AppBar } from '@skeletonlabs/skeleton';
|
||||
import { LightSwitch } from '@skeletonlabs/skeleton';
|
||||
import { page } from '$app/stores';
|
||||
import { configStore, currentSelectedRoom, identityStore, keyStore } from '$lib/stores';
|
||||
import { AppBar, type DrawerSettings } from '@skeletonlabs/skeleton';
|
||||
import { currentSelectedRoom, identityExists } from '$lib/stores';
|
||||
import { getDrawerStore } from '@skeletonlabs/skeleton';
|
||||
import Settings from 'svelte-material-icons/TuneVariant.svelte';
|
||||
import Information from 'svelte-material-icons/Information.svelte';
|
||||
import Console from 'svelte-material-icons/Console.svelte';
|
||||
import PasswordLock from '$lib/components/Padlock.svelte';
|
||||
import Chat from 'svelte-material-icons/Chat.svelte';
|
||||
$: identityExists = !!$identityStore._commitment;
|
||||
|
||||
$: roomName = $currentSelectedRoom?.name ?? 'Select Room';
|
||||
|
||||
const drawerStore = getDrawerStore();
|
||||
|
||||
const settings: DrawerSettings = { id: 'roomselect' };
|
||||
|
||||
// Open the drawer:
|
||||
function drawerOpen(): void {
|
||||
drawerStore.open();
|
||||
drawerStore.open(settings);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -29,43 +25,43 @@
|
||||
>
|
||||
<svelte:fragment slot="lead">
|
||||
<h1 class="h4 text-primary-500">
|
||||
{#if identityExists}
|
||||
<a href="/chat"><img class="max-h-7" src="/logo-text.png" alt="discreetly" /></a>
|
||||
{#if $identityExists}
|
||||
<a href="/chat" role="button" tabindex="0"
|
||||
><img class="max-h-7" src="/logo-text.png" alt="discreetly" /></a
|
||||
>
|
||||
{:else}
|
||||
<a href="/"><img class="max-h-7" src="/logo-text.png" alt="discreetly" /></a>
|
||||
<a href="/" role="button" tabindex="0"
|
||||
><img class="max-h-7" src="/logo-text.png" alt="discreetly" /></a
|
||||
>
|
||||
{/if}
|
||||
</h1>
|
||||
</svelte:fragment>
|
||||
|
||||
<a href="/about" class="btn btn-sm variant-ringed-secondary hidden sm:inline">About</a>
|
||||
{#if identityExists}
|
||||
<a href="/chat" class="hidden btn btn-sm variant-ringed-secondary sm:inline">Chat</a>
|
||||
{#if $identityExists}
|
||||
<a
|
||||
href="/chat"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class="hidden btn btn-sm variant-ringed-secondary sm:inline">Chat</a
|
||||
>
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<a
|
||||
class="btn btn-sm variant-ringed-secondary font-medium text-sm inline sm:hidden"
|
||||
on:click={drawerOpen}>{roomName}</a
|
||||
on:click={drawerOpen}
|
||||
on:keypress={() => {
|
||||
drawerOpen();
|
||||
}}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
{roomName}
|
||||
</a>
|
||||
{:else}
|
||||
<a class="btn btn-sm variant-ringed-secondary" href="/signup">Sign Up</a>
|
||||
<a class="btn btn-sm variant-ringed-secondary" href="/signup" role="button" tabindex="0"
|
||||
>Sign Up</a
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
class="btn btn-sm variant-ringed-secondary font-medium text-sm hidden sm:inline"
|
||||
href="/console">Console</a
|
||||
>
|
||||
<svelte:fragment slot="trail">
|
||||
<a href="/about" class="hidden sm:inline"><Information size="1.2em" /></a>
|
||||
{#if identityExists && $page.url.pathname !== '/chat'}
|
||||
<a href="/chat" class="inline"><Chat size="1.2em" /></a>
|
||||
{/if}
|
||||
{#if $page.url.pathname !== '/console'}
|
||||
<a href="/console" class="inline"><Console size="1.2em" /></a>
|
||||
{/if}
|
||||
|
||||
<PasswordLock cls="inline" />
|
||||
{#if identityExists}
|
||||
<a href="/settings"><Settings size="1.2em" /></a>
|
||||
{/if}
|
||||
<div class="hidden sm:inline">
|
||||
<LightSwitch />
|
||||
</div>
|
||||
<div class="hidden sm:inline text-primary-500">Alpha Version!</div>
|
||||
</svelte:fragment>
|
||||
</AppBar>
|
||||
|
||||
104
src/routes/Sidebar.svelte
Normal file
104
src/routes/Sidebar.svelte
Normal file
@@ -0,0 +1,104 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import {
|
||||
AppRail,
|
||||
AppRailAnchor,
|
||||
getModalStore,
|
||||
type ModalSettings
|
||||
} from '@skeletonlabs/skeleton';
|
||||
import Chat from 'svelte-material-icons/Chat.svelte';
|
||||
import Settings from 'svelte-material-icons/TuneVariant.svelte';
|
||||
import Information from 'svelte-material-icons/Information.svelte';
|
||||
import Console from 'svelte-material-icons/Console.svelte';
|
||||
import Lock from 'svelte-material-icons/Lock.svelte';
|
||||
import LockOpen from 'svelte-material-icons/LockOpenVariant.svelte';
|
||||
import NoPassword from 'svelte-material-icons/LockOff.svelte';
|
||||
import { hashPassword, deriveKey } from '$lib/crypto/crypto';
|
||||
import { configStore, keyStore, passwordSet } from '$lib/stores';
|
||||
import { alertAll } from '$lib/utils';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const modalStore = getModalStore();
|
||||
|
||||
function unlock() {
|
||||
const modal: ModalSettings = {
|
||||
type: 'prompt',
|
||||
title: 'Unlock',
|
||||
body: 'Enter your password to unlock your keystores',
|
||||
value: '',
|
||||
valueAttr: { type: 'password', minlength: 4, required: true },
|
||||
response: async (r: string) => {
|
||||
if (r != 'false' && r != '' && r != null && r != undefined) {
|
||||
const hashedPassword = await hashPassword(r);
|
||||
if ($configStore.hashedPwd == hashedPassword) {
|
||||
$keyStore = await deriveKey(r);
|
||||
} else {
|
||||
alertAll('Incorrect Password');
|
||||
$keyStore = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
modalStore.trigger(modal);
|
||||
}
|
||||
|
||||
function lock() {
|
||||
$keyStore = null;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
console.debug(
|
||||
'PadLock:',
|
||||
$passwordSet ? 'password set,' : 'password not set,',
|
||||
$keyStore !== null && $keyStore !== undefined ? 'unlocked' : 'locked'
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<AppRail height="h-full">
|
||||
<AppRailAnchor href="/chat" selected={$page.url.pathname === '/chat'} title="Chat">
|
||||
<svelte:fragment slot="lead"><Chat class="rail-icon" /></svelte:fragment>
|
||||
<span>Chat</span>
|
||||
</AppRailAnchor>
|
||||
|
||||
<AppRailAnchor href="/about" selected={$page.url.pathname === '/about'} title="About">
|
||||
<svelte:fragment slot="lead"><Information class="rail-icon" /></svelte:fragment>
|
||||
<span>About</span>
|
||||
</AppRailAnchor>
|
||||
|
||||
<AppRailAnchor href="/console" selected={$page.url.pathname === '/console'} title="About">
|
||||
<svelte:fragment slot="lead"><Console class="rail-icon" /></svelte:fragment>
|
||||
<span>Console</span>
|
||||
</AppRailAnchor>
|
||||
|
||||
<svelte:fragment slot="trail">
|
||||
{#if $passwordSet}
|
||||
{#if $keyStore instanceof CryptoKey}
|
||||
<AppRailAnchor on:click={lock} title="Unlocked, click to lock">
|
||||
<svelte:fragment slot="lead">
|
||||
<LockOpen class="rail-icon text-warning-300-600-token" />
|
||||
</svelte:fragment>
|
||||
<span>Lock</span>
|
||||
</AppRailAnchor>
|
||||
{:else}
|
||||
<AppRailAnchor on:click={unlock} title="Locked">
|
||||
<svelte:fragment slot="lead">
|
||||
<Lock class="rail-icon text-success-500" />
|
||||
</svelte:fragment>
|
||||
<span>Unlock</span>
|
||||
</AppRailAnchor>
|
||||
{/if}
|
||||
{:else}
|
||||
<AppRailAnchor href="/settings/security" title="Password not set">
|
||||
<svelte:fragment slot="lead">
|
||||
<NoPassword class="rail-icon text-error-500" />
|
||||
</svelte:fragment>
|
||||
<span>Secure</span>
|
||||
</AppRailAnchor>
|
||||
{/if}
|
||||
<AppRailAnchor href="/settings" selected={$page.url.pathname === '/settings'} title="Settings">
|
||||
<svelte:fragment slot="lead"><Settings class="rail-icon" /></svelte:fragment>
|
||||
<span>Settings</span>
|
||||
</AppRailAnchor>
|
||||
</svelte:fragment>
|
||||
</AppRail>
|
||||
@@ -1,13 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { identityStore, serverStore } from '$lib/stores';
|
||||
import { identityExists, serverStore } from '$lib/stores';
|
||||
import { updateServer } from '$lib/utils';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
$: identityExists = !!$identityStore._commitment;
|
||||
|
||||
onMount(() => {
|
||||
if (!identityExists) {
|
||||
if (!$identityExists) {
|
||||
goto('/signup');
|
||||
}
|
||||
if (!Object.keys($serverStore).length) {
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
import Console from './Console.svelte';
|
||||
</script>
|
||||
|
||||
<Console />
|
||||
<div class="mx-2 h-full">
|
||||
<Console />
|
||||
</div>
|
||||
|
||||
@@ -3,30 +3,39 @@
|
||||
import { consoleStore } from '$lib/stores';
|
||||
import { clearConsoleMessages } from '$lib/utils';
|
||||
import TrashCan from 'svelte-material-icons/TrashCanOutline.svelte';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
let elemChat: HTMLElement;
|
||||
export let placeholder: string = 'Enter / Command';
|
||||
|
||||
function scrollChatBottom(behavior?: ScrollBehavior): void {
|
||||
setTimeout(() => {
|
||||
elemChat.scrollTo({ top: elemChat.scrollHeight, behavior });
|
||||
}, 0);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
scrollChatBottom('smooth');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="mx-3 my-2">
|
||||
<h3 class="h3 mb-1 small:mb-3">Console</h3>
|
||||
<div class="card variant-ghost-surface">
|
||||
<header class="card-header">
|
||||
<button
|
||||
class="btn btn-sm variant-ghost-primary float-right"
|
||||
on:click={() => clearConsoleMessages()}
|
||||
>
|
||||
<TrashCan />
|
||||
</button>
|
||||
</header>
|
||||
<section class="p-4">
|
||||
{#each $consoleStore.messages as line, idx}
|
||||
<p class={line.type}>{line.message}</p>
|
||||
{/each}
|
||||
</section>
|
||||
<footer class="card-footer">
|
||||
<InputPrompt {placeholder} />
|
||||
</footer>
|
||||
</div>
|
||||
<div class="p-4 small:p-2 h-full overflow-y-hidden grid grid-rows-[auto,1fr,auto]">
|
||||
<header class="flex flex-row justify-between px-2">
|
||||
<h6 class="h4">Console</h6>
|
||||
<button
|
||||
class="btn btn-sm variant-ghost-primary float-right"
|
||||
on:click={() => clearConsoleMessages()}
|
||||
>
|
||||
<TrashCan />
|
||||
</button>
|
||||
</header>
|
||||
<section class="p-4 overflow-y-scroll" bind:this={elemChat}>
|
||||
{#each $consoleStore.messages as line, idx}
|
||||
<p class={line.type}>{line.message}</p>
|
||||
{/each}
|
||||
</section>
|
||||
<footer>
|
||||
<InputPrompt {placeholder} />
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
addConsoleMessage('`/clear` Clears the console');
|
||||
addConsoleMessage('`/join invite-code` Joins a room via invite code, Example:');
|
||||
addConsoleMessage('`/password Password`');
|
||||
addConsoleMessage('`/clearPassword`');
|
||||
addConsoleMessage('`/unlock Password`');
|
||||
addConsoleMessage('`/lock`');
|
||||
addConsoleMessage('`/backup`');
|
||||
@@ -75,10 +74,6 @@
|
||||
addConsoleMessage('/password OLDPASSWORD NEWPASSWORD', 'warning');
|
||||
}
|
||||
break;
|
||||
case '/clearPassword':
|
||||
$configStore.hashedPwd = null;
|
||||
addConsoleMessage('Password Cleared');
|
||||
break;
|
||||
case '/lock':
|
||||
$keyStore = null;
|
||||
addConsoleMessage('Locked!');
|
||||
|
||||
5
src/routes/gateways/+page.svelte
Normal file
5
src/routes/gateways/+page.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import JoinMore from './JoinMore.svelte';
|
||||
</script>
|
||||
|
||||
<JoinMore />
|
||||
@@ -1,7 +1,3 @@
|
||||
<div class=" mx-2 md:mx-4">
|
||||
<div class="mx-auto mt-10 max-w-[80ch]">
|
||||
<h2 class="h2 mb-8 text-center">Manage Your Identity</h2>
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
<div class="px-2 sm:px-5 mt-3 sm:mt-8 overflow-scroll h-100">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -1,67 +1,49 @@
|
||||
<script lang="ts">
|
||||
import JoinMore from './JoinMore.svelte';
|
||||
import { identityStore } from '$lib/stores';
|
||||
import { page } from '$app/stores';
|
||||
import DeleteIdentity from './DeleteIdentity.svelte';
|
||||
import BackupIdentity from './BackupIdentity.svelte';
|
||||
import RestoreIdentity from './RestoreIdentity.svelte';
|
||||
import { identityExists } from '$lib/stores';
|
||||
import DeleteIdentity from './identity/DeleteIdentity.svelte';
|
||||
import BackupIdentity from './identity/BackupIdentity.svelte';
|
||||
import RestoreIdentity from './identity/RestoreIdentity.svelte';
|
||||
import { createIdentity } from '$lib/utils/';
|
||||
import { Tab, TabGroup } from '@skeletonlabs/skeleton';
|
||||
import ActionRepresentation from './ActionRepresentation.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
$: identityExists = !!$identityStore._commitment;
|
||||
let tabSet: number = 0;
|
||||
onMount(() => {
|
||||
if ($page.url.hash) {
|
||||
const hash = $page.url.hash.replace('#', '');
|
||||
if (hash === 'join-more') {
|
||||
tabSet = 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
import ActionRepresentation from './ui/ActionRepresentation.svelte';
|
||||
import IdentityIcon from 'svelte-material-icons/Account.svelte';
|
||||
import Eye from 'svelte-material-icons/Eye.svelte';
|
||||
</script>
|
||||
|
||||
{#if !identityExists}
|
||||
<div class="mb-8 text-center">
|
||||
<h2 class="h2 mb-3 sm:mb-5 text-center">Manage Settings</h2>
|
||||
{#if !$identityExists}
|
||||
<div class="mb-3 sm:mb-8 text-center">
|
||||
<span class="text-base italic px-4 py-2 font-mono badge variant-outline-error"
|
||||
>Identity Not Found!</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid grid-flow-rows gap-5 my-5 max-w-md mx-auto">
|
||||
{#if !identityExists}
|
||||
<div class="flex flex-row flex-wrap gap-5 my-5 mx-auto justify-center">
|
||||
{#if !$identityExists}
|
||||
<button
|
||||
on:click={() => createIdentity()}
|
||||
class="btn variant-ghost-primary font-medium"
|
||||
type="button"
|
||||
>
|
||||
Generate Identity
|
||||
Generate New Identity
|
||||
</button>
|
||||
<RestoreIdentity />
|
||||
{:else}
|
||||
<TabGroup
|
||||
justify="justify-around"
|
||||
active="variant-soft-secondary"
|
||||
flex="flex-1 lg:flex-none"
|
||||
class="w-full"
|
||||
>
|
||||
<Tab bind:group={tabSet} name="id" value={0} class="center">
|
||||
<span>Identity</span>
|
||||
</Tab>
|
||||
<Tab bind:group={tabSet} name="server" value={1}>Server</Tab>
|
||||
<Tab bind:group={tabSet} name="misc" value={2}>Settings</Tab>
|
||||
<!-- Tab Panels --->
|
||||
<svelte:fragment slot="panel">
|
||||
{#if tabSet === 0}
|
||||
<BackupIdentity />
|
||||
<DeleteIdentity />
|
||||
<RestoreIdentity />
|
||||
{:else if tabSet === 1}
|
||||
<JoinMore />
|
||||
{:else if tabSet === 2}
|
||||
<ActionRepresentation />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</TabGroup>
|
||||
<div>
|
||||
<h3 class="h3 flex flex-row gap-2 items-center"><IdentityIcon /> Identity</h3>
|
||||
<div class="flex flex-col gap-3 sm:gap-5 items-stretch">
|
||||
<BackupIdentity />
|
||||
<DeleteIdentity />
|
||||
<RestoreIdentity />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="h3 flex flex-row gap-2 items-center"><Eye /> UI</h3>
|
||||
<div class="flex flex-col gap-3 sm:gap-5 items-stretch">
|
||||
<ActionRepresentation />
|
||||
<BackupIdentity />
|
||||
<DeleteIdentity />
|
||||
<RestoreIdentity />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
9
src/routes/settings/identity/+page.svelte
Normal file
9
src/routes/settings/identity/+page.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import BackupIdentity from './BackupIdentity.svelte';
|
||||
import DeleteIdentity from './DeleteIdentity.svelte';
|
||||
import RestoreIdentity from './RestoreIdentity.svelte';
|
||||
</script>
|
||||
|
||||
<BackupIdentity />
|
||||
<DeleteIdentity />
|
||||
<RestoreIdentity />
|
||||
@@ -2,9 +2,9 @@
|
||||
import BackupIdentity from '$lib/components/BackupIdentity.svelte';
|
||||
</script>
|
||||
|
||||
<div class="card variant-ghost-secondary mb-5">
|
||||
<div class="card variant-ghost-secondary">
|
||||
<header class="card-header">
|
||||
<h4 class="h4">Backup Your Identity</h4>
|
||||
<h3 class="h4">Backup Your Identity</h3>
|
||||
</header>
|
||||
<section class="p-2 sm:p-4">
|
||||
<BackupIdentity />
|
||||
@@ -32,7 +32,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="card variant-ghost-error mb-5">
|
||||
<div class="card variant-ghost-error">
|
||||
<header class="card-header">
|
||||
<h3 class="h4">Delete Your Identity & Reset Application</h3>
|
||||
</header>
|
||||
@@ -42,7 +42,7 @@
|
||||
>I promise I backed up my identity, or I really want to destroy it forever.</span
|
||||
>
|
||||
</section>
|
||||
<footer class="card-footer text-center mb-2">
|
||||
<footer class="card-footer text-center">
|
||||
{#if !isButtonDisabled}
|
||||
<button
|
||||
id="delete-identity"
|
||||
5
src/routes/settings/identity/backup/+page.svelte
Normal file
5
src/routes/settings/identity/backup/+page.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import BackupIdentity from '../BackupIdentity.svelte';
|
||||
</script>
|
||||
|
||||
<BackupIdentity />
|
||||
5
src/routes/settings/identity/restore/+page.svelte
Normal file
5
src/routes/settings/identity/restore/+page.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import RestoreIdentity from '../RestoreIdentity.svelte';
|
||||
</script>
|
||||
|
||||
<RestoreIdentity />
|
||||
47
src/routes/settings/security/+page.svelte
Normal file
47
src/routes/settings/security/+page.svelte
Normal file
@@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
import { setPassword } from '$lib/utils';
|
||||
import { passwordSet } from '$lib/stores';
|
||||
|
||||
const minPasswordLength = 4;
|
||||
let r = '';
|
||||
|
||||
function onSubmit() {
|
||||
if (r != '' && r != null && r != undefined && r.length >= minPasswordLength) {
|
||||
setPassword(r);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="max-w-lg mx-auto">
|
||||
{#if !$passwordSet}
|
||||
<h2 class="h3 mb-3">Set A Password!</h2>
|
||||
<p class="mt-3">
|
||||
In order to secure your identity and other sensitive data that is stored in your browser, we
|
||||
need you to set a password.
|
||||
</p>
|
||||
<p class="my-3">
|
||||
This password is only used locally, there is not username and password for your account, so
|
||||
don't forget to <a href="/settings/backup" title="Backup Identity">backup your identity</a>.
|
||||
</p>
|
||||
<form on:submit|preventDefault={() => onSubmit()} class="flex flex-col w-full">
|
||||
<label for="setPasswordInput" class="label" />
|
||||
<input
|
||||
id="setPasswordInput"
|
||||
type="password"
|
||||
name="password"
|
||||
class="input"
|
||||
bind:value={r}
|
||||
minlength={minPasswordLength}
|
||||
required
|
||||
/>
|
||||
<small>Minimum password length: {minPasswordLength}</small>
|
||||
<button
|
||||
class="btn variant-filled-primary mt-3"
|
||||
disabled={r.length < minPasswordLength}
|
||||
type="submit">Set Password</button
|
||||
>
|
||||
</form>
|
||||
{:else}
|
||||
<h2 class="h3">Password Set!</h2>
|
||||
{/if}
|
||||
</div>
|
||||
39
src/routes/settings/status/+page.svelte
Normal file
39
src/routes/settings/status/+page.svelte
Normal file
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { configStore, identityKeyStore, identityStore, serverStore } from '$lib/stores';
|
||||
import { IdentityStoreE } from '$lib/types';
|
||||
</script>
|
||||
|
||||
<div id="status" class="flex flex-col gap-5">
|
||||
<div>
|
||||
<h2 class="h3">configStore</h2>
|
||||
<div>Completed Signup: {JSON.stringify($configStore.signUpStatus.completedSignup)}</div>
|
||||
<div>Identity Backedup: {JSON.stringify($configStore.signUpStatus.identityBackedUp)}</div>
|
||||
<div>IdentityStore Type: {IdentityStoreE[$configStore.identityStore]}</div>
|
||||
<div>Beta: {JSON.stringify($configStore.beta)}</div>
|
||||
<div>Hashed Password: {JSON.stringify($configStore.hashedPwd)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="h3">Identity Data</h2>
|
||||
{#each Object.keys($identityStore) as key}
|
||||
<div>Unprotected {key}: {JSON.stringify($identityStore[key])}</div>
|
||||
{/each}
|
||||
{#each Object.keys($identityKeyStore) as key}
|
||||
<div>Protected {key}: {JSON.stringify($identityKeyStore[key])}</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="h3">Server Data</h2>
|
||||
{#each Object.keys($serverStore) as key}
|
||||
<div>{$serverStore[key].name}:</div>
|
||||
<div class="ps-5">id: {$serverStore[key].id}</div>
|
||||
<div class="ps-5">version: {$serverStore[key].version}</div>
|
||||
<div class="ps-5">url: {key}</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#status div div {
|
||||
margin-left: 1.25rem;
|
||||
}
|
||||
</style>
|
||||
5
src/routes/settings/ui/+page.svelte
Normal file
5
src/routes/settings/ui/+page.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<script>
|
||||
import ActionRepresentation from './ActionRepresentation.svelte';
|
||||
</script>
|
||||
|
||||
<ActionRepresentation />
|
||||
@@ -2,6 +2,8 @@
|
||||
import AP from '$lib/components/AP.svelte';
|
||||
import { configStore } from '$lib/stores';
|
||||
import { ActionRepresentationE } from '$lib/types';
|
||||
import { RangeSlider } from '@skeletonlabs/skeleton';
|
||||
import { max } from 'date-fns';
|
||||
$: if ($configStore.actionRepresentation == undefined) {
|
||||
$configStore.actionRepresentation = ActionRepresentationE.AP;
|
||||
}
|
||||
@@ -12,7 +14,7 @@
|
||||
|
||||
<div class="card variant-soft-secondary">
|
||||
<header class="card-header">
|
||||
<h4 class="h4">Action Representation</h4>
|
||||
<h3 class="h4">Action Representation</h3>
|
||||
</header>
|
||||
<section class="p-2 mb-2 sm:p-4 sm:mb-4">
|
||||
<div class="flex flex-col">
|
||||
@@ -31,14 +33,18 @@
|
||||
<div class="border-t mt-2 pt-2 mb-2 sm:mb-4 flex flex-col place-items-center">
|
||||
<h6 class="h6">Demo</h6>
|
||||
<AP {health} {maxHealth} />
|
||||
<label
|
||||
><div>Health: {health}</div>
|
||||
<input type="range" min="0" max={maxHealth} bind:value={health} />
|
||||
</label>
|
||||
<label
|
||||
><div>Max Health: {maxHealth}</div>
|
||||
<input type="range" min="1" max={10} bind:value={maxHealth} />
|
||||
</label>
|
||||
<RangeSlider name="range-slider" bind:value={health} max={maxHealth} step={1} ticked>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="font-bold">Health</div>
|
||||
<div class="text-xs">{health} / {maxHealth}</div>
|
||||
</div>
|
||||
</RangeSlider>
|
||||
<RangeSlider name="range-slider" bind:value={maxHealth} max={10} step={1} ticked>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="font-bold">Max Health</div>
|
||||
<div class="text-xs">{maxHealth} / {10}</div>
|
||||
</div>
|
||||
</RangeSlider>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -46,7 +46,6 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Console placeholder="/join invite-code-goes-here" />
|
||||
<div>
|
||||
<a href="/settings" class="btn btn-sm variant-ghost-primary">
|
||||
<span>Restore Identity</span>
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
# Installing Webfonts
|
||||
|
||||
Follow these simple Steps.
|
||||
|
||||
## 1.
|
||||
|
||||
Put `nippo/` Folder into a Folder called `fonts/`.
|
||||
|
||||
## 2.
|
||||
|
||||
Put `nippo.css` into your `css/` Folder.
|
||||
|
||||
## 3. (Optional)
|
||||
|
||||
You may adapt the `url('path')` in `nippo.css` depends on your Website Filesystem.
|
||||
|
||||
## 4.
|
||||
|
||||
Import `nippo.css` at the top of you main Stylesheet.
|
||||
|
||||
```
|
||||
@@ -19,7 +24,6 @@ Import `nippo.css` at the top of you main Stylesheet.
|
||||
|
||||
## 5.
|
||||
|
||||
|
||||
```
|
||||
font-family: 'Nippo-Variable';
|
||||
font-family: 'Nippo-Extralight';
|
||||
@@ -28,4 +32,3 @@ font-family: 'Nippo-Regular';
|
||||
font-family: 'Nippo-Medium';
|
||||
font-family: 'Nippo-Bold';
|
||||
```
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* This is a variable font
|
||||
* You can controll variable axes as shown below:
|
||||
@@ -29,67 +28,61 @@
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nippo-Variable';
|
||||
src: url('../fonts/Nippo-Variable.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Variable.woff') format('woff'),
|
||||
url('../fonts/Nippo-Variable.ttf') format('truetype');
|
||||
font-weight: 200 700;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-family: 'Nippo-Variable';
|
||||
src: url('../fonts/Nippo-Variable.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Variable.woff') format('woff'),
|
||||
url('../fonts/Nippo-Variable.ttf') format('truetype');
|
||||
font-weight: 200 700;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nippo-Extralight';
|
||||
src: url('../fonts/Nippo-Extralight.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Extralight.woff') format('woff'),
|
||||
url('../fonts/Nippo-Extralight.ttf') format('truetype');
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-family: 'Nippo-Extralight';
|
||||
src: url('../fonts/Nippo-Extralight.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Extralight.woff') format('woff'),
|
||||
url('../fonts/Nippo-Extralight.ttf') format('truetype');
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nippo-Light';
|
||||
src: url('../fonts/Nippo-Light.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Light.woff') format('woff'),
|
||||
url('../fonts/Nippo-Light.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-family: 'Nippo-Light';
|
||||
src: url('../fonts/Nippo-Light.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Light.woff') format('woff'),
|
||||
url('../fonts/Nippo-Light.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nippo-Regular';
|
||||
src: url('../fonts/Nippo-Regular.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Regular.woff') format('woff'),
|
||||
url('../fonts/Nippo-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-family: 'Nippo-Regular';
|
||||
src: url('../fonts/Nippo-Regular.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Regular.woff') format('woff'),
|
||||
url('../fonts/Nippo-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nippo-Medium';
|
||||
src: url('../fonts/Nippo-Medium.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Medium.woff') format('woff'),
|
||||
url('../fonts/Nippo-Medium.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-family: 'Nippo-Medium';
|
||||
src: url('../fonts/Nippo-Medium.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Medium.woff') format('woff'),
|
||||
url('../fonts/Nippo-Medium.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nippo-Bold';
|
||||
src: url('../fonts/Nippo-Bold.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Bold.woff') format('woff'),
|
||||
url('../fonts/Nippo-Bold.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-family: 'Nippo-Bold';
|
||||
src: url('../fonts/Nippo-Bold.woff2') format('woff2'),
|
||||
url('../fonts/Nippo-Bold.woff') format('woff'),
|
||||
url('../fonts/Nippo-Bold.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
{
|
||||
"name": "App",
|
||||
"icons": [
|
||||
{
|
||||
"src": "\/android-icon-36x36.png",
|
||||
"sizes": "36x36",
|
||||
"type": "image\/png",
|
||||
"density": "0.75"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image\/png",
|
||||
"density": "1.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image\/png",
|
||||
"density": "1.5"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image\/png",
|
||||
"density": "2.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image\/png",
|
||||
"density": "3.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image\/png",
|
||||
"density": "4.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
"name": "App",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-icon-36x36.png",
|
||||
"sizes": "36x36",
|
||||
"type": "image/png",
|
||||
"density": "0.75"
|
||||
},
|
||||
{
|
||||
"src": "/android-icon-48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image/png",
|
||||
"density": "1.0"
|
||||
},
|
||||
{
|
||||
"src": "/android-icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"density": "1.5"
|
||||
},
|
||||
{
|
||||
"src": "/android-icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"density": "2.0"
|
||||
},
|
||||
{
|
||||
"src": "/android-icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"density": "3.0"
|
||||
},
|
||||
{
|
||||
"src": "/android-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"density": "4.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,114 +1,105 @@
|
||||
{
|
||||
"protocol": "groth16",
|
||||
"curve": "bn128",
|
||||
"nPublic": 5,
|
||||
"vk_alpha_1": [
|
||||
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
||||
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
||||
"1"
|
||||
],
|
||||
"vk_beta_2": [
|
||||
[
|
||||
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
||||
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
||||
],
|
||||
[
|
||||
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
||||
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"vk_gamma_2": [
|
||||
[
|
||||
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
||||
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
||||
],
|
||||
[
|
||||
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
||||
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"vk_delta_2": [
|
||||
[
|
||||
"3817638527075299341910923013750491829772293424386698324526317429044327254930",
|
||||
"16817041674734304676071688134655235316477394149172545683919861305510126014325"
|
||||
],
|
||||
[
|
||||
"15321616806892601534034512759307826486280009165298528953245727688557811743473",
|
||||
"6325777744419158965546141708181579937852352378408429766464847802592525079966"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"vk_alphabeta_12": [
|
||||
[
|
||||
[
|
||||
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
||||
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
||||
],
|
||||
[
|
||||
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
||||
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
||||
],
|
||||
[
|
||||
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
||||
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
||||
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
||||
],
|
||||
[
|
||||
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
||||
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
||||
],
|
||||
[
|
||||
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
||||
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
||||
]
|
||||
]
|
||||
],
|
||||
"IC": [
|
||||
[
|
||||
"4920513730204767532050733107749276406754520419375654722016092399980613788208",
|
||||
"10950491564509418434657706642388934308456795265036074733953533582377584967294",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"6815064660695497986531118446154820702646540722664044216580897159556261271171",
|
||||
"17838140936832571103329556013529166877877534025488014783346458943575275015438",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"16364982450206976302246609763791333525052810246590359380676749324389440643932",
|
||||
"17092624338100676284548565502349491320314889021833923882585524649862570629227",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"3679639231485547795420532910726924727560917141402837495597760107842698404034",
|
||||
"16213191511474848247596810551723578773353083440353745908057321946068926848382",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"9215428431027260354679105025212521481930206886203677270216204485256690813172",
|
||||
"934602510541226149881779979217731465262250233587980565969044391353665291792",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"8935861545794299876685457331391349387048184820319250771243971382360441890897",
|
||||
"4993459033694759724715904486381952906869986989682015547152342336961693234616",
|
||||
"1"
|
||||
]
|
||||
]
|
||||
}
|
||||
"protocol": "groth16",
|
||||
"curve": "bn128",
|
||||
"nPublic": 5,
|
||||
"vk_alpha_1": [
|
||||
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
||||
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
||||
"1"
|
||||
],
|
||||
"vk_beta_2": [
|
||||
[
|
||||
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
||||
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
||||
],
|
||||
[
|
||||
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
||||
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
||||
],
|
||||
["1", "0"]
|
||||
],
|
||||
"vk_gamma_2": [
|
||||
[
|
||||
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
||||
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
||||
],
|
||||
[
|
||||
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
||||
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
||||
],
|
||||
["1", "0"]
|
||||
],
|
||||
"vk_delta_2": [
|
||||
[
|
||||
"3817638527075299341910923013750491829772293424386698324526317429044327254930",
|
||||
"16817041674734304676071688134655235316477394149172545683919861305510126014325"
|
||||
],
|
||||
[
|
||||
"15321616806892601534034512759307826486280009165298528953245727688557811743473",
|
||||
"6325777744419158965546141708181579937852352378408429766464847802592525079966"
|
||||
],
|
||||
["1", "0"]
|
||||
],
|
||||
"vk_alphabeta_12": [
|
||||
[
|
||||
[
|
||||
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
||||
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
||||
],
|
||||
[
|
||||
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
||||
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
||||
],
|
||||
[
|
||||
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
||||
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
||||
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
||||
],
|
||||
[
|
||||
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
||||
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
||||
],
|
||||
[
|
||||
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
||||
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
||||
]
|
||||
]
|
||||
],
|
||||
"IC": [
|
||||
[
|
||||
"4920513730204767532050733107749276406754520419375654722016092399980613788208",
|
||||
"10950491564509418434657706642388934308456795265036074733953533582377584967294",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"6815064660695497986531118446154820702646540722664044216580897159556261271171",
|
||||
"17838140936832571103329556013529166877877534025488014783346458943575275015438",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"16364982450206976302246609763791333525052810246590359380676749324389440643932",
|
||||
"17092624338100676284548565502349491320314889021833923882585524649862570629227",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"3679639231485547795420532910726924727560917141402837495597760107842698404034",
|
||||
"16213191511474848247596810551723578773353083440353745908057321946068926848382",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"9215428431027260354679105025212521481930206886203677270216204485256690813172",
|
||||
"934602510541226149881779979217731465262250233587980565969044391353665291792",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"8935861545794299876685457331391349387048184820319250771243971382360441890897",
|
||||
"4993459033694759724715904486381952906869986989682015547152342336961693234616",
|
||||
"1"
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,99 +1,90 @@
|
||||
{
|
||||
"protocol": "groth16",
|
||||
"curve": "bn128",
|
||||
"nPublic": 2,
|
||||
"vk_alpha_1": [
|
||||
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
||||
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
||||
"1"
|
||||
],
|
||||
"vk_beta_2": [
|
||||
[
|
||||
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
||||
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
||||
],
|
||||
[
|
||||
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
||||
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"vk_gamma_2": [
|
||||
[
|
||||
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
||||
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
||||
],
|
||||
[
|
||||
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
||||
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"vk_delta_2": [
|
||||
[
|
||||
"11983007890138271431646631339128325393343025477496193427884097571341594637227",
|
||||
"7369399572723042520302573420729576910581887995143697476970345965043452827346"
|
||||
],
|
||||
[
|
||||
"18498623948725074905368922260006856461276039836160071250761949723295691187028",
|
||||
"20253812994897318378765322974844822348210001364477588185294466023467389614920"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"vk_alphabeta_12": [
|
||||
[
|
||||
[
|
||||
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
||||
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
||||
],
|
||||
[
|
||||
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
||||
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
||||
],
|
||||
[
|
||||
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
||||
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
||||
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
||||
],
|
||||
[
|
||||
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
||||
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
||||
],
|
||||
[
|
||||
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
||||
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
||||
]
|
||||
]
|
||||
],
|
||||
"IC": [
|
||||
[
|
||||
"19490069286251317200471893224761952280235157078692599655063040494106083015102",
|
||||
"15613730057977833735664106983317680013118142165231654768046521650638333652991",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"1563543155852853229359605494188815884199915022658219002707722789976065966419",
|
||||
"858819375930654753672617171465307097688802650498051619587167586479724200799",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"3808889614445935800597561392085733302718838702771107544944545050886958022904",
|
||||
"13293649293049947010793838294353767499934999769633605908974566715226392122400",
|
||||
"1"
|
||||
]
|
||||
]
|
||||
}
|
||||
"protocol": "groth16",
|
||||
"curve": "bn128",
|
||||
"nPublic": 2,
|
||||
"vk_alpha_1": [
|
||||
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
||||
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
||||
"1"
|
||||
],
|
||||
"vk_beta_2": [
|
||||
[
|
||||
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
||||
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
||||
],
|
||||
[
|
||||
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
||||
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
||||
],
|
||||
["1", "0"]
|
||||
],
|
||||
"vk_gamma_2": [
|
||||
[
|
||||
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
||||
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
||||
],
|
||||
[
|
||||
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
||||
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
||||
],
|
||||
["1", "0"]
|
||||
],
|
||||
"vk_delta_2": [
|
||||
[
|
||||
"11983007890138271431646631339128325393343025477496193427884097571341594637227",
|
||||
"7369399572723042520302573420729576910581887995143697476970345965043452827346"
|
||||
],
|
||||
[
|
||||
"18498623948725074905368922260006856461276039836160071250761949723295691187028",
|
||||
"20253812994897318378765322974844822348210001364477588185294466023467389614920"
|
||||
],
|
||||
["1", "0"]
|
||||
],
|
||||
"vk_alphabeta_12": [
|
||||
[
|
||||
[
|
||||
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
||||
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
||||
],
|
||||
[
|
||||
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
||||
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
||||
],
|
||||
[
|
||||
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
||||
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
||||
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
||||
],
|
||||
[
|
||||
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
||||
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
||||
],
|
||||
[
|
||||
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
||||
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
||||
]
|
||||
]
|
||||
],
|
||||
"IC": [
|
||||
[
|
||||
"19490069286251317200471893224761952280235157078692599655063040494106083015102",
|
||||
"15613730057977833735664106983317680013118142165231654768046521650638333652991",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"1563543155852853229359605494188815884199915022658219002707722789976065966419",
|
||||
"858819375930654753672617171465307097688802650498051619587167586479724200799",
|
||||
"1"
|
||||
],
|
||||
[
|
||||
"3808889614445935800597561392085733302718838702771107544944545050886958022904",
|
||||
"13293649293049947010793838294353767499934999769633605908974566715226392122400",
|
||||
"1"
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,17 +4,10 @@ import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||
// for more information about preprocessors
|
||||
preprocess: [vitePreprocess({})],
|
||||
|
||||
preprocess: [vitePreprocess({})],
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter({
|
||||
// default options are shown. On some platforms
|
||||
// these options are set automatically — see below
|
||||
pages: 'build',
|
||||
assets: 'build',
|
||||
fallback: 'index.html',
|
||||
@@ -25,5 +18,3 @@ const config = {
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { join } from 'path';
|
||||
import type { Config } from 'tailwindcss';
|
||||
import { discreetlyTheme } from './theme';
|
||||
import { skeleton } from '@skeletonlabs/tw-plugin';
|
||||
import forms from '@tailwindcss/forms';
|
||||
import typography from '@tailwindcss/typography';
|
||||
import { skeleton } from '@skeletonlabs/tw-plugin';
|
||||
import { discreetlyTheme } from './theme';
|
||||
|
||||
const config = {
|
||||
export default {
|
||||
darkMode: 'class',
|
||||
content: [
|
||||
'./src/**/*.{html,js,svelte,ts}',
|
||||
@@ -14,11 +15,11 @@ const config = {
|
||||
extend: {}
|
||||
},
|
||||
plugins: [
|
||||
forms,
|
||||
typography,
|
||||
skeleton({
|
||||
themes: { custom: [discreetlyTheme] }
|
||||
}),
|
||||
forms
|
||||
]
|
||||
} satisfies Config;
|
||||
|
||||
export default config;
|
||||
|
||||
28
tests/app.test.ts
Normal file
28
tests/app.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { getLocalStorage } from './utils';
|
||||
|
||||
test.describe('/', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('mount', async ({ page }) => {
|
||||
await expect(page.getByRole('heading', { name: 'Welcome to Discreetly' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('default servers should be set', async ({ page }) => {
|
||||
const localStorage = await getLocalStorage(page);
|
||||
localStorage.forEach((object) => {
|
||||
switch (object.name) {
|
||||
case 'selectedServer':
|
||||
expect(object.value).toBe('"https://server.discreetly.chat/"');
|
||||
break;
|
||||
case 'serverData':
|
||||
expect(object.value).toBe(
|
||||
'{"https://server.discreetly.chat/":{"name":"Discreetly Server","url":"https://server.discreetly.chat/"}}'
|
||||
);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
test('index page has expected h1', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible();
|
||||
});
|
||||
6
tests/utils.ts
Normal file
6
tests/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
export async function getLocalStorage(page: Page) {
|
||||
const state = await page.context().storageState();
|
||||
return state.origins[0].localStorage;
|
||||
}
|
||||
@@ -1,17 +1,10 @@
|
||||
import { purgeCss } from 'vite-plugin-tailwind-purgecss';
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import sizes from 'rollup-plugin-sizes';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
build: {
|
||||
minify: 'terser',
|
||||
cssMinify: 'lightningcss',
|
||||
rollupOptions: {
|
||||
plugins: [sizes()],
|
||||
output: {
|
||||
compact: true
|
||||
}
|
||||
}
|
||||
plugins: [sveltekit(), purgeCss()],
|
||||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user