diff --git a/README.md b/README.md index 020090a..4c2bea4 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,32 @@ Identity is an open-source application that helps you save your most relevant me ## Rationale -Identity is a project that initially started as an app whose purpose was to store music you like -(or liked) for future use in treatment for diseases like dementia. Over time, the idea evolved -and is now general-purpose. +Identity is a project that initially started as an app whose purpose was to store music you like (or used to like) for future use in treatment +for conditions such as dementia. Over time, the idea evolved and is now a general-purpose memory-saving app. ## Projects * `identity-web`. The web app that interacts with the Identiy API. -* `identity-api`. The Identity API, also takes care of storing data. -* `identity-format`. The specification for the Identity file format. +* `identity-api`. The Identity API, takes care of storing user data. +* `asset-api`. The Asset API, takes care of storing user-generated assets. + +## Installation and building + +The Identity project is composed by a web-app and two servers. In the future, Docker containers may be built to ease the installation of this project. + +### Building and running + +#### Building `identity-web` + +1. Copy and update the `env.example` file: `cp .env.example .env` +2. Run `yarn` to install the dependencies. + * You may need to [enable Corepack](https://nodejs.org/api/corepack.html). +3. Run `yarn preview` to check that everything works properly. +4. Modify the `svelte.config.js` file to deploy to your desired environment. +5. Run `yarn build` to generate the SPA build. + * The build will be placed at the `build/` folder. ## Citations -1. Van de Winckel, A., Feys, H., De Weerdt, W., & Dom, R. (2004). Cognitive and behavioural effects of music-based exercises in patients with dementia. Clinical Rehabilitation, 18(3), 253-260. https://doi.org/10.1191/0269215504cr750oa \ No newline at end of file +1. Van de Winckel, A., Feys, H., De Weerdt, W., & Dom, R. (2004). Cognitive and behavioural effects of music-based exercises in patients with dementia. Clinical Rehabilitation, 18(3), 253-260. https://doi.org/10.1191/0269215504cr750oa +2. The dementia guide: Living well after your diagnosis. (2021, April 16). Alzheimer’s Society. https://www.alzheimers.org.uk/get-support/publications-factsheets/the-dementia-guide \ No newline at end of file diff --git a/identity-web/.env.example b/identity-web/.env.example new file mode 100644 index 0000000..ce138a2 --- /dev/null +++ b/identity-web/.env.example @@ -0,0 +1,3 @@ +VITE_IDENTITY_API_ENDPOINT="http://localhost:3000/" +VITE_ASSET_API_ENDPOINT="http://localhost:3001/" +VITE_SUPPORT_PAGE="mailto:support@example.com" \ No newline at end of file diff --git a/identity-web/.prettierrc b/identity-web/.prettierrc index 9573023..cff6b1d 100644 --- a/identity-web/.prettierrc +++ b/identity-web/.prettierrc @@ -3,6 +3,7 @@ "singleQuote": true, "trailingComma": "none", "printWidth": 100, - "plugins": ["prettier-plugin-svelte"], + "htmlWhitespaceSensitivity": "ignore", + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] } diff --git a/identity-web/package.json b/identity-web/package.json index 8ea9ace..6a9cd3f 100644 --- a/identity-web/package.json +++ b/identity-web/package.json @@ -14,6 +14,7 @@ }, "devDependencies": { "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/adapter-static": "^3.0.2", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@tailwindcss/forms": "^0.5.7", @@ -26,6 +27,7 @@ "postcss": "^8.4.38", "prettier": "^3.1.1", "prettier-plugin-svelte": "^3.1.2", + "prettier-plugin-tailwindcss": "^0.6.5", "svelte": "^4.2.7", "svelte-check": "^3.6.0", "tailwindcss": "^3.4.4", diff --git a/identity-web/postcss.config.js b/identity-web/postcss.config.js index 2e7af2b..0f77216 100644 --- a/identity-web/postcss.config.js +++ b/identity-web/postcss.config.js @@ -1,6 +1,6 @@ export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} + plugins: { + tailwindcss: {}, + autoprefixer: {} + } +}; diff --git a/identity-web/src/app.css b/identity-web/src/app.css index bd6213e..2bc0696 100644 --- a/identity-web/src/app.css +++ b/identity-web/src/app.css @@ -1,3 +1,19 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.3em; + vertical-align: super; +} +sub { + bottom: -0.25em; + vertical-align: sub; +} diff --git a/identity-web/src/lib/api.ts b/identity-web/src/lib/api.ts index 24f8d49..29749f8 100644 --- a/identity-web/src/lib/api.ts +++ b/identity-web/src/lib/api.ts @@ -1,122 +1,140 @@ -import type { Entry, IdlessEntry } from "./entry" +import type { Entry, IdlessEntry } from './entry'; +import { ENV_VARIABLES } from './variables'; -const ENDPOINT = 'http://localhost:3000/' -const ASSET_API_ENDPOINT = 'http://localhost:3001/' +const ENDPOINT = ENV_VARIABLES.IDENTITY_API_ENDPOINT; +const ASSET_API_ENDPOINT = ENV_VARIABLES.ASSET_API_ENDPOINT; export type Credentials = { - token: string, -} + token: string; +}; export type AccountHeir = { - contactMethod: "email", - name: string, - value: string, -} + contactMethod: 'email'; + name: string; + value: string; +}; export type Account = { - uid: string, - name: string, - heirs: AccountHeir[], -} + uid: string; + name: string; + heirs: AccountHeir[]; +}; -function sendRequest(path: string, credentials?: Credentials, request: RequestInit = {}, params: string = "") { - if (typeof request !== "string" && credentials != null) { - request.headers = { 'Authorization': `Bearer ${credentials.token}`, ...request.headers } - } +function sendRequest( + path: string, + credentials?: Credentials, + request: RequestInit = {}, + params: string = '' +) { + if (typeof request !== 'string' && credentials != null) { + request.headers = { Authorization: `Bearer ${credentials.token}`, ...request.headers }; + } - let url = new URL(ENDPOINT); - url.pathname = path; - url.search = params + let url = new URL(ENDPOINT); + url.pathname = path; + url.search = params; - return fetch(url, request) + return fetch(url, request); } /// **Safety:** The caller must enforce that the given request in progress must return the type `R` async function asJson(request: Promise): Promise { - let req = await request; - return (await req.json() as R) + let req = await request; + return (await req.json()) as R; } export function login(credentials: { - email: string, - password: string, -}): Promise<{ token: string, } | { error: string, }> { - return asJson(sendRequest('/auth/login', undefined, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(credentials), - })) + email: string; + password: string; +}): Promise<{ token: string } | { error: string }> { + return asJson( + sendRequest('/auth/login', undefined, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(credentials) + }) + ); } export function register(credentials: { - name: string, - email: string, - password: string, -}): Promise<{ token: string, } | { error: string, }> { - return asJson(sendRequest('/auth/register', undefined, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(credentials), - })) + name: string; + email: string; + password: string; +}): Promise<{ token: string } | { error: string }> { + return asJson( + sendRequest('/auth/register', undefined, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(credentials) + }) + ); } export function accountData(credentials: Credentials): Promise { - return asJson(sendRequest('/auth/account', credentials)) + return asJson(sendRequest('/auth/account', credentials)); } -export function genSessionKey(credentials: Credentials): Promise<{ session_key: string } | { error: string }> { - return asJson(sendRequest('/auth/genkey', credentials)) +export function genSessionKey( + credentials: Credentials +): Promise<{ session_key: string } | { error: string }> { + return asJson(sendRequest('/auth/genkey', credentials)); } export async function assetEndpoint(): Promise { - let res = await sendRequest("/asset/endpoint") - return res.text() + let res = await sendRequest('/asset/endpoint'); + return res.text(); } -export async function entryPage(credentials: Credentials, offset: number, limit: number): Promise { - return asJson(sendRequest('/entry/list', credentials, undefined, `?offset=${offset}&limit=${limit}`)) +export async function entryPage( + credentials: Credentials, + offset: number, + limit: number +): Promise { + return asJson( + sendRequest('/entry/list', credentials, undefined, `?offset=${offset}&limit=${limit}`) + ); } export async function addEntry(credentials: Credentials, entry: IdlessEntry): Promise { - await sendRequest('/entry', credentials, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({entry}), - }) + await sendRequest('/entry', credentials, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ entry }) + }); } export async function deleteEntry(credentials: Credentials, entry_id: string): Promise { - await sendRequest('/entry', credentials, { method: 'DELETE' }, `?entry_id=${entry_id}`) + await sendRequest('/entry', credentials, { method: 'DELETE' }, `?entry_id=${entry_id}`); } export async function updateHeirs(credentials: Credentials, heirs: AccountHeir[]): Promise { - await sendRequest('/auth/heirs', credentials, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(heirs), - }); + await sendRequest('/auth/heirs', credentials, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(heirs) + }); } export async function uploadAsset(session_key: string, file: File): Promise { - let url = new URL('/asset', ASSET_API_ENDPOINT); - url.search = `?session_key=${session_key}` - - let form = new FormData() - form.append("file", file); + let url = new URL('/asset', ASSET_API_ENDPOINT); + url.search = `?session_key=${session_key}`; - let res = await fetch(url, { - method: "PUT", - body: form, - }) + let form = new FormData(); + form.append('file', file); - let { asset_id } = await res.json(); - return asset_id; -} \ No newline at end of file + let res = await fetch(url, { + method: 'PUT', + body: form + }); + + let { asset_id } = await res.json(); + return asset_id; +} diff --git a/identity-web/src/lib/assets/ladies.jpg b/identity-web/src/lib/assets/ladies.jpg new file mode 100644 index 0000000..890d89b Binary files /dev/null and b/identity-web/src/lib/assets/ladies.jpg differ diff --git a/identity-web/src/lib/assets/memory-photos.jpg b/identity-web/src/lib/assets/memory-photos.jpg new file mode 100644 index 0000000..ca28611 Binary files /dev/null and b/identity-web/src/lib/assets/memory-photos.jpg differ diff --git a/identity-web/src/lib/components/FeelingsChooser.svelte b/identity-web/src/lib/components/FeelingsChooser.svelte index 4a513fb..48bd8a1 100644 --- a/identity-web/src/lib/components/FeelingsChooser.svelte +++ b/identity-web/src/lib/components/FeelingsChooser.svelte @@ -1,74 +1,84 @@
- {#if displayText} - Feelings - {/if} + {#if displayText} + Feelings + {/if} -
- -
- {#if chosenFeelings.length > 0} -
- Chosen: - {#each chosenFeelings as feeling (feeling)} -
- - -
- {/each} -
- {:else} - No feelings chosen. - {#if required} - You need to choose at least one feeling. - {/if} - {/if} -
-
-
- {#each feelingsToChoose as feeling (feeling)} - - {/each} -
-
\ No newline at end of file +
+ +
+ {#if chosenFeelings.length > 0} +
+ Chosen: + {#each chosenFeelings as feeling (feeling)} +
+ + +
+ {/each} +
+ {:else} + No feelings chosen. + {#if required} + You need to choose at least one feeling. + {/if} + {/if} +
+
+
+ {#each feelingsToChoose as feeling (feeling)} + + {/each} +
+ diff --git a/identity-web/src/lib/entry.ts b/identity-web/src/lib/entry.ts index cc32dd0..9854fc8 100644 --- a/identity-web/src/lib/entry.ts +++ b/identity-web/src/lib/entry.ts @@ -1,80 +1,149 @@ -export const TITLED_ENTRIES = ["event", "environment", "memory"]; -export const FEELINGS = ["relaxed", "afraid", "angry", "bad", "bored", "confused", "excited", "fine", "happy", "hurt", "in love", "mad", "nervous", "okay", "sad", "scared", "shy", "sleepy", "active", "surprised", "tired", "upset", "worried"]; +export const TITLED_ENTRIES = ['event', 'environment', 'memory']; +export const FEELINGS = [ + 'relaxed', + 'afraid', + 'angry', + 'bad', + 'bored', + 'confused', + 'excited', + 'fine', + 'happy', + 'hurt', + 'in love', + 'mad', + 'nervous', + 'okay', + 'sad', + 'scared', + 'shy', + 'sleepy', + 'active', + 'surprised', + 'tired', + 'upset', + 'worried' +]; -export type KnownFeeling = "relaxed" | "afraid" | "angry" | "bad" | "bored" | "confused" | "excited" | "fine" | "happy" | "hurt" | "in love" | "mad" | "nervous" | "okay" | "sad" | "scared" | "shy" | "sleepy" | "active" | "surprised" | "tired" | "upset" | "worried"; -export type EntryKind = "song" | "album" | "event" | "memory" | "feeling" | "environment" | "date"; +export type KnownFeeling = + | 'relaxed' + | 'afraid' + | 'angry' + | 'bad' + | 'bored' + | 'confused' + | 'excited' + | 'fine' + | 'happy' + | 'hurt' + | 'in love' + | 'mad' + | 'nervous' + | 'okay' + | 'sad' + | 'scared' + | 'shy' + | 'sleepy' + | 'active' + | 'surprised' + | 'tired' + | 'upset' + | 'worried'; +export type EntryKind = 'song' | 'album' | 'event' | 'memory' | 'feeling' | 'environment' | 'date'; export type IdlessEntry = { - base: SongEntry | AlbumEntry | EventEntry | MemoryEntry | FeelingEntry | EnvironmentEntry | DateEntry, - creationDate: string, - feelings: (KnownFeeling | { - identifier: string, - description: string, - backgroundColor: string, - textColor: string, - })[], - assets: string[], - title?: string, - description?: string, + base: + | SongEntry + | AlbumEntry + | EventEntry + | MemoryEntry + | FeelingEntry + | EnvironmentEntry + | DateEntry; + creationDate: string; + feelings: ( + | KnownFeeling + | { + identifier: string; + description: string; + backgroundColor: string; + textColor: string; + } + )[]; + assets: string[]; + title?: string; + description?: string; }; export type Entry = { - id: string, - base: SongEntry | AlbumEntry | EventEntry | MemoryEntry | FeelingEntry | EnvironmentEntry | DateEntry, - creationDate: string, - feelings: (KnownFeeling | { - identifier: string, - description: string, - backgroundColor: string, - textColor: string, - })[], - assets: string[], - title?: string, - description?: string, + id: string; + base: + | SongEntry + | AlbumEntry + | EventEntry + | MemoryEntry + | FeelingEntry + | EnvironmentEntry + | DateEntry; + creationDate: string; + feelings: ( + | KnownFeeling + | { + identifier: string; + description: string; + backgroundColor: string; + textColor: string; + } + )[]; + assets: string[]; + title?: string; + description?: string; }; export type UniversalID = { - provider: string, - id: string, -} + provider: string; + id: string; +}; export type SongEntry = { - kind: "song", - artist: string, - title: string, - link: string[], - id: UniversalID[], -} + kind: 'song'; + artist: string; + title: string; + link: string[]; + id: UniversalID[]; +}; export type AlbumEntry = { - kind: "album", - artist: string, - title: string, - link: string[], - id: UniversalID[], -} + kind: 'album'; + artist: string; + title: string; + link: string[]; + id: UniversalID[]; +}; export type EventEntry = { - kind: "event", -} + kind: 'event'; +}; export type MemoryEntry = { - kind: "memory", -} + kind: 'memory'; +}; export type FeelingEntry = { - kind: "feeling", -} + kind: 'feeling'; +}; export type EnvironmentEntry = { - kind: "environment", - location?: string | { - latitude: number, - longitude: number, - }, -} + kind: 'environment'; + location?: + | string + | { + latitude: number; + longitude: number; + }; +}; export type DateEntry = { - kind: "date", - referencedDate: string, -} \ No newline at end of file + kind: 'date'; + referencedDate: string; +}; diff --git a/identity-web/src/lib/stores.ts b/identity-web/src/lib/stores.ts index c8c23d2..8cddc30 100644 --- a/identity-web/src/lib/stores.ts +++ b/identity-web/src/lib/stores.ts @@ -1,66 +1,67 @@ -import { writable } from "svelte/store"; -import { accountData, assetEndpoint, genSessionKey, type Account, type Credentials } from "./api"; +import { writable } from 'svelte/store'; +import { accountData, assetEndpoint, genSessionKey, type Account, type Credentials } from './api'; -const CREDENTIALS_KEY = 'v0:credentials' +const CREDENTIALS_KEY = 'v0:credentials'; -let _credentials: Credentials | null = null -export const credentials = writable() +let _credentials: Credentials | null = null; +export const credentials = writable(); credentials.subscribe((value) => { - if (value != null) { - _credentials = value; - localStorage.setItem( CREDENTIALS_KEY, JSON.stringify(value)) - } else { - _credentials = null; - } -}) + if (value != null) { + _credentials = value; + localStorage.setItem(CREDENTIALS_KEY, JSON.stringify(value)); + } else { + _credentials = null; + } +}); -export const account = writable() -export const session_key = writable() -export const asset_endpoint = writable() +export const account = writable(); +export const session_key = writable(); +export const asset_endpoint = writable(); export async function initializeStores() { - let rawCredentials = localStorage.getItem(CREDENTIALS_KEY) - let parsedCredentials - if (rawCredentials != null && rawCredentials.length > 0) { - try { - parsedCredentials = JSON.parse(rawCredentials) - credentials.set(parsedCredentials) - } - catch (e) { localStorage.removeItem(CREDENTIALS_KEY) } - } + let rawCredentials = localStorage.getItem(CREDENTIALS_KEY); + let parsedCredentials; + if (rawCredentials != null && rawCredentials.length > 0) { + try { + parsedCredentials = JSON.parse(rawCredentials); + credentials.set(parsedCredentials); + } catch (e) { + localStorage.removeItem(CREDENTIALS_KEY); + } + } - if (parsedCredentials != null) { - let data = await accountData(parsedCredentials) - if ('error' in data) { - credentials.set(null) - localStorage.removeItem(CREDENTIALS_KEY) - } else { - account.set(data) - } + if (parsedCredentials != null) { + let data = await accountData(parsedCredentials); + if ('error' in data) { + credentials.set(null); + localStorage.removeItem(CREDENTIALS_KEY); + } else { + account.set(data); + } - let key_result = await genSessionKey(parsedCredentials) - if ('error' in key_result) { - console.warn('Couldn\'t generate a session key!') - } else { - session_key.set(key_result.session_key) - } + let key_result = await genSessionKey(parsedCredentials); + if ('error' in key_result) { + console.warn("Couldn't generate a session key!"); + } else { + session_key.set(key_result.session_key); + } - let asset_result = await assetEndpoint() - asset_endpoint.set(asset_result) - } + let asset_result = await assetEndpoint(); + asset_endpoint.set(asset_result); + } } export async function refreshAccount() { - if (_credentials == null) { - console.warn("Requested to refresh the user account but credentials are null.") - return; - } + if (_credentials == null) { + console.warn('Requested to refresh the user account but credentials are null.'); + return; + } - let refreshedAccount = await accountData(_credentials) - if ('error' in refreshedAccount) { - console.warn("Failed to refresh the user account.") - return; - } + let refreshedAccount = await accountData(_credentials); + if ('error' in refreshedAccount) { + console.warn('Failed to refresh the user account.'); + return; + } - account.set(refreshedAccount) -} \ No newline at end of file + account.set(refreshedAccount); +} diff --git a/identity-web/src/lib/variables.ts b/identity-web/src/lib/variables.ts new file mode 100644 index 0000000..f5bc999 --- /dev/null +++ b/identity-web/src/lib/variables.ts @@ -0,0 +1,5 @@ +export const ENV_VARIABLES = { + IDENTITY_API_ENDPOINT: import.meta.env.VITE_IDENTITY_API_ENDPOINT, + ASSET_API_ENDPOINT: import.meta.env.VITE_ASSET_API_ENDPOINT, + SUPPORT_PAGE: import.meta.env.VITE_SUPPORT_PAGE +}; diff --git a/identity-web/src/routes/+layout.svelte b/identity-web/src/routes/+layout.svelte index 3374ae3..9f71554 100644 --- a/identity-web/src/routes/+layout.svelte +++ b/identity-web/src/routes/+layout.svelte @@ -1,34 +1,39 @@ - -
- + +
+
- +
diff --git a/identity-web/src/routes/+layout.ts b/identity-web/src/routes/+layout.ts index 690c1cb..910e361 100644 --- a/identity-web/src/routes/+layout.ts +++ b/identity-web/src/routes/+layout.ts @@ -1,2 +1,3 @@ // FIXME: Update code to support SSR -export const ssr = false; \ No newline at end of file +export const ssr = false; +export const prerender = false; diff --git a/identity-web/src/routes/+page.svelte b/identity-web/src/routes/+page.svelte index 8e7640d..42d911c 100644 --- a/identity-web/src/routes/+page.svelte +++ b/identity-web/src/routes/+page.svelte @@ -1 +1,75 @@ -

Landing

\ No newline at end of file + + +
+
+
+
+

+ Store your memories for your future you. +

+

+ Identity + helps you store your memories and experiences. Our memories are our most precious belonging, + we should store them in a safe place. +

+

+ Identity + is an open-source software you can self-host to have full control over the storage of your + memories. +

+

+ This instance is maintained by volunnteers and financed by our community and sponsors. You + can export your data at any time. +

+ + Join now + +
+
+ Collage of Polaroid-like pictures + Photo by Lisa Fotios +
+
+
+
+ Collage of Polaroid-like pictures + Photo by cottonbro studio +
+
+

+ Remember your younger self. +

+

+ Reminiscence and life story work can help dementia patients ease their symptoms. + + + [1] + + +

+

+ Both your future you and your descendants may find your legacy useful: +
+ environments, music, memories… +

+
+
+
+
diff --git a/identity-web/src/routes/auth/account/+page.svelte b/identity-web/src/routes/auth/account/+page.svelte index df40240..240cd87 100644 --- a/identity-web/src/routes/auth/account/+page.svelte +++ b/identity-web/src/routes/auth/account/+page.svelte @@ -1,119 +1,177 @@ -
-
-

Welcome back, {$account?.name}.

-
-
-

Heirs

- {#if $account?.heirs.length > 0} - - {/if} -
- {#if !heirWizard && $account?.heirs.length === 0} -
- -
- {/if} - {#if heirWizard} -
-
-
- - - {#if $errors.contactMethod != null} -

{$errors.contactMethod[0]}

- {/if} -
-
- - - {#if $errors.name != null} -

{$errors.name[0]}

- {/if} -
-
- - - {#if $errors.contactDetails != null} -

{$errors.contactDetails[0]}

- {/if} -
- -
-
- {/if} - {#each $account?.heirs || [] as heir (heir.value)} -
-
- Contact method: {heir.contactMethod} - -
-
- {heir.name} · {heir.value} -
-
- {/each} -
-
-
\ No newline at end of file +
+
+

+ Welcome back, {$account?.name} + . +

+
+
+

Heirs

+ {#if $account?.heirs.length > 0} + + {/if} +
+ {#if !heirWizard && $account?.heirs.length === 0} +
+ +
+ {/if} + {#if heirWizard} +
+
+
+ + + {#if $errors.contactMethod != null} +

+ {$errors.contactMethod[0]} +

+ {/if} +
+
+ + + {#if $errors.name != null} +

+ {$errors.name[0]} +

+ {/if} +
+
+ + + {#if $errors.contactDetails != null} +

+ {$errors.contactDetails[0]} +

+ {/if} +
+ +
+
+ {/if} + {#each $account?.heirs || [] as heir (heir.value)} +
+
+ + Contact method: {heir.contactMethod} + + +
+
+ {heir.name} + · + {heir.value} +
+
+ {/each} +
+
+
diff --git a/identity-web/src/routes/auth/login/+page.svelte b/identity-web/src/routes/auth/login/+page.svelte index be1419b..afaf76e 100644 --- a/identity-web/src/routes/auth/login/+page.svelte +++ b/identity-web/src/routes/auth/login/+page.svelte @@ -1,73 +1,108 @@ -
-
-

Log in

-
-
- - - {#if $errors.email != null} -

{$errors.email[0]}

- {/if} -
-
- - - {#if $errors.password != null} -

{$errors.password[0]}

- {/if} -
- - {#if submitError != null && submitError.length > 0} -

{submitError}

- {/if} - -
-
-
\ No newline at end of file +
+
+

Log in

+
+
+ + + {#if $errors.email != null} +

+ {$errors.email[0]} +

+ {/if} +
+
+ + + {#if $errors.password != null} +

+ {$errors.password[0]} +

+ {/if} +
+ + {#if submitError != null && submitError.length > 0} +

{submitError}

+ {/if} + +
+
+
diff --git a/identity-web/src/routes/auth/register/+page.svelte b/identity-web/src/routes/auth/register/+page.svelte index 362ae49..f25c507 100644 --- a/identity-web/src/routes/auth/register/+page.svelte +++ b/identity-web/src/routes/auth/register/+page.svelte @@ -1,83 +1,128 @@ -
-
-

Register

-
-
- - - {#if $errors.name != null} -

{$errors.name[0]}

- {/if} -
-
- - - {#if $errors.email != null} -

{$errors.email[0]}

- {/if} -
-
- - - {#if $errors.password != null} -

{$errors.password[0]}

- {/if} -
- - {#if submitError != null && submitError.length > 0} -

{submitError}

- {/if} - Already have an account? -
-
-
\ No newline at end of file +
+
+

Register

+
+
+ + + {#if $errors.name != null} +

+ {$errors.name[0]} +

+ {/if} +
+
+ + + {#if $errors.email != null} +

+ {$errors.email[0]} +

+ {/if} +
+
+ + + {#if $errors.password != null} +

+ {$errors.password[0]} +

+ {/if} +
+ + {#if submitError != null && submitError.length > 0} +

{submitError}

+ {/if} + + Already have an account? + +
+
+
diff --git a/identity-web/src/routes/dashboard/+page.svelte b/identity-web/src/routes/dashboard/+page.svelte index cb3a519..21884b0 100644 --- a/identity-web/src/routes/dashboard/+page.svelte +++ b/identity-web/src/routes/dashboard/+page.svelte @@ -1,154 +1,213 @@ {#if $account != null} - {#await entries} -
-
- Loading entries... - -
-
- {:then entries} -
-
- {#if entries.length === 0} - - + -

Add an entry

-
- {:else} -

Welcome back, {$account?.name}.

-
- {#await overview} - Loading... - {:then overview} - !["feeling"].includes(v.base.kind))} past={overview[1].value}/> - {/await} -
+ {#await entries} +
+
+ Loading entries... + +
+
+ {:then entries} +
+
+ {#if entries.length === 0} + + + +

Add an entry

+
+ {:else} +

+ Welcome back, {$account?.name} + . +

+
+ {#await overview} + Loading... + {:then overview} + !['feeling'].includes(v.base.kind))} + past={overview[1].value} + /> + {/await} +
-

Entries

-
- + Add an entry - -
+

Entries

+
+ + + Add an entry + + +
- {#if showFilterSelector} - chosenFilterFeelings = e.detail} on:updatedFilter={(e) => filters = e.detail} chosenFeelings={chosenFilterFeelings} filters={filters}/> - {/if} + {#if showFilterSelector} + (chosenFilterFeelings = e.detail)} + on:updatedFilter={(e) => (filters = e.detail)} + chosenFeelings={chosenFilterFeelings} + {filters} + /> + {/if} -
- filterStatus = e.detail} on:deleted={() => refreshEntries()} entries={entries} filters={filters}/> -
- {#if loadingPage && !reachedEnd} -
-
- Loading entries... - -
-
- {/if} +
+ (filterStatus = e.detail)} + on:deleted={() => refreshEntries()} + {entries} + {filters} + /> +
+ {#if loadingPage && !reachedEnd} +
+
+ Loading entries... + +
+
+ {/if} - {#if reachedEnd} -
-
- You've reached the end -
-
- {/if} - {/if} -
-
- {/await} -{/if} \ No newline at end of file + {#if reachedEnd} +
+
+ You've reached the end +
+
+ {/if} + {/if} +
+
+ {/await} +{/if} diff --git a/identity-web/src/routes/dashboard/Entries.svelte b/identity-web/src/routes/dashboard/Entries.svelte index d2bc280..551563f 100644 --- a/identity-web/src/routes/dashboard/Entries.svelte +++ b/identity-web/src/routes/dashboard/Entries.svelte @@ -1,156 +1,171 @@ {#if entries.length != filteredEntries.length && filteredEntries.length === 0} -
-
- No results found -
-
+
+
+ No results found +
+
{/if} {#each filteredEntries as entry (entry.id)} - extended = [e.detail.id, ...extended]} - on:contracted={(e) => extended = extended.filter(v => v !== e.detail.id)} - on:deleted={(e) => { dispatch('deleted', e.detail) }} + (extended = [e.detail.id, ...extended])} + on:contracted={(e) => (extended = extended.filter((v) => v !== e.detail.id))} + on:deleted={(e) => { + dispatch('deleted', e.detail); + }} + id={entry.id} + kind={entry.base.kind} + creationDate={new Date(entry.creationDate)} + title={entry.base.kind === 'date' + ? new Date(entry.base.referencedDate).toLocaleDateString() + : entry.title} + isExtended={extended.includes(entry.id)} + > +
+ {#if entry.base.kind === 'song' || entry.base.kind === 'album'} + + {entry.base.artist} ‐ {entry.base.title} + + {/if} - id={entry.id} - kind={entry.base.kind} - creationDate={new Date(entry.creationDate)} - title={entry.base.kind === "date" ? new Date(entry.base.referencedDate).toLocaleDateString() : entry.title} - isExtended={extended.includes(entry.id)} - > -
- {#if entry.base.kind === "song" || entry.base.kind === "album"} - {entry.base.artist} ‐ {entry.base.title} - {/if} + {#if entry.base.kind === 'feeling'} +
+ {#each entry.feelings as feeling} + {#if typeof feeling === 'string'} + + {:else} + + {/if} + {/each} +
+ {/if} +
- {#if entry.base.kind === "feeling"} -
- {#each entry.feelings as feeling} - {#if typeof feeling === "string"} - - {:else} - - {/if} - {/each} -
- {/if} -
+
+
+ {#each entry.feelings as feeling} + {#if typeof feeling === 'string'} + + {:else} + + {/if} + {/each} +
-
-
- {#each entry.feelings as feeling} - {#if typeof feeling === "string"} - - {:else} - - {/if} - {/each} -
+ {#if entry.base.kind === 'song' || entry.base.kind === 'album'} + + {entry.base.artist} ‐ {entry.base.title} + + {/if} - {#if entry.base.kind === "song" || entry.base.kind === "album"} - {entry.base.artist} ‐ {entry.base.title} - {/if} + {#if entry.description != null} + {entry.description} + {/if} - {#if entry.description != null} - {entry.description} - {/if} - -
- {#each entry.assets as asset} - - {/each} -
-
- -{/each} \ No newline at end of file +
+ {#each entry.assets as asset} + + {/each} +
+
+
+{/each} diff --git a/identity-web/src/routes/dashboard/Overview.svelte b/identity-web/src/routes/dashboard/Overview.svelte index 980743f..5813835 100644 --- a/identity-web/src/routes/dashboard/Overview.svelte +++ b/identity-web/src/routes/dashboard/Overview.svelte @@ -1,26 +1,26 @@ -
-

Latest activity

-
- {#each latest as entry (entry.id)} - - {/each} -
+
+

Latest activity

+
+ {#each latest as entry (entry.id)} + + {/each} +
{#if past.length > 0} -
-

Memories from the past

-
- {#each past as entry (entry.id)} - - {/each} -
-
-{/if} \ No newline at end of file +
+

Memories from the past

+
+ {#each past as entry (entry.id)} + + {/each} +
+
+{/if} diff --git a/identity-web/src/routes/dashboard/utils/AssetPreview.svelte b/identity-web/src/routes/dashboard/utils/AssetPreview.svelte index 07aa22f..28d7efb 100644 --- a/identity-web/src/routes/dashboard/utils/AssetPreview.svelte +++ b/identity-web/src/routes/dashboard/utils/AssetPreview.svelte @@ -1,32 +1,44 @@ - - {#if kind == null} - - {:else if kind === "image"} - - {:else if kind === "audio"} - - {:else if kind === "video"} - - {:else} - - {/if} + + {#if kind == null} + + {:else if kind === 'image'} + + {:else if kind === 'audio'} + + {:else if kind === 'video'} + + {:else} + + {/if} - {#if kind != null && kind !== "application"} - {kind} - {:else} - Asset - {/if} - \ No newline at end of file + {#if kind != null && kind !== 'application'} + {kind} + {:else} + Asset + {/if} + diff --git a/identity-web/src/routes/dashboard/utils/Entry.svelte b/identity-web/src/routes/dashboard/utils/Entry.svelte index 837c904..a66889e 100644 --- a/identity-web/src/routes/dashboard/utils/Entry.svelte +++ b/identity-web/src/routes/dashboard/utils/Entry.svelte @@ -1,78 +1,96 @@
- - {/if} -
- - {#if title != null && isExtended} -

{title}

- {/if} - + + {/if} +
- + {#if title != null && isExtended} +

{title}

+ {/if} + - {#if !isExtended} - - {/if} + - {#if isExtended} - - {/if} -
\ No newline at end of file + {#if !isExtended} + + {/if} + + {#if isExtended} + + {/if} + diff --git a/identity-web/src/routes/dashboard/utils/EntryDescription.svelte b/identity-web/src/routes/dashboard/utils/EntryDescription.svelte index 4d4743e..4da98df 100644 --- a/identity-web/src/routes/dashboard/utils/EntryDescription.svelte +++ b/identity-web/src/routes/dashboard/utils/EntryDescription.svelte @@ -1,3 +1,3 @@

- -

\ No newline at end of file + +

diff --git a/identity-web/src/routes/dashboard/utils/EntryKind.svelte b/identity-web/src/routes/dashboard/utils/EntryKind.svelte index 5c53be9..f31cec7 100644 --- a/identity-web/src/routes/dashboard/utils/EntryKind.svelte +++ b/identity-web/src/routes/dashboard/utils/EntryKind.svelte @@ -1,24 +1,45 @@ -{#if kind === "song"} - Song -{:else if kind === "album"} - Album -{:else if kind === "event"} - Event -{:else if kind === "memory"} - Memory -{:else if kind === "feeling"} - Feeling -{:else if kind === "environment"} - Environment -{:else if kind === "date"} - Date +{#if kind === 'song'} + Song +{:else if kind === 'album'} + + Album + +{:else if kind === 'event'} + + Event + +{:else if kind === 'memory'} + + Memory + +{:else if kind === 'feeling'} + + Feeling + +{:else if kind === 'environment'} + + Environment + +{:else if kind === 'date'} + + Date + {:else} - Unknown value. Try loading the page again. -{/if} \ No newline at end of file + Unknown value. Try loading the page again. +{/if} diff --git a/identity-web/src/routes/dashboard/utils/ExternalLink.svelte b/identity-web/src/routes/dashboard/utils/ExternalLink.svelte index 92b27b5..3ae1a5e 100644 --- a/identity-web/src/routes/dashboard/utils/ExternalLink.svelte +++ b/identity-web/src/routes/dashboard/utils/ExternalLink.svelte @@ -1,11 +1,15 @@ - - - - \ No newline at end of file + + + + diff --git a/identity-web/src/routes/dashboard/utils/FeelingPill.svelte b/identity-web/src/routes/dashboard/utils/FeelingPill.svelte index 584ee66..0ceb530 100644 --- a/identity-web/src/routes/dashboard/utils/FeelingPill.svelte +++ b/identity-web/src/routes/dashboard/utils/FeelingPill.svelte @@ -1,40 +1,67 @@ -
- +
+ - {feeling.charAt(0).toUpperCase() + feeling.slice(1)} -
\ No newline at end of file + {feeling.charAt(0).toUpperCase() + feeling.slice(1)} +
diff --git a/identity-web/src/routes/dashboard/utils/FilterSelector.svelte b/identity-web/src/routes/dashboard/utils/FilterSelector.svelte index db80496..77f7b90 100644 --- a/identity-web/src/routes/dashboard/utils/FilterSelector.svelte +++ b/identity-web/src/routes/dashboard/utils/FilterSelector.svelte @@ -1,105 +1,143 @@ -
-
-
- - handleDate(e.target.valueAsDate, "fromDate")} type="date" name="date" class="bg-gray-50 border border-gray-300 text-greay-900 text-sm rounded-lg focus:ring-violet-500 focus:border-violet-500 block w-full p-1.5"> -
-
- - handleDate(e.target.valueAsDate, "toDate")} type="date" name="date" class="bg-gray-50 border border-gray-300 text-greay-900 text-sm rounded-lg focus:ring-violet-500 focus:border-violet-500 block w-full p-1.5"> -
-
- - -
-
-
- handleFeelings(e.detail)} displayText={false} slim={true} /> -
-
- handleSearchQuery(e.target.value)} type="text" placeholder="Search query" class="bg-gray-50 border border-gray-300 text-greay-900 text-sm rounded-lg focus:ring-violet-500 focus:border-violet-500 block w-full p-2.5"> -
-
\ No newline at end of file +
+
+
+ + handleDate(e.target.valueAsDate, 'fromDate')} + type="date" + name="date" + class="text-greay-900 block w-full rounded-lg border border-gray-300 bg-gray-50 p-1.5 text-sm focus:border-violet-500 focus:ring-violet-500" + /> +
+
+ + handleDate(e.target.valueAsDate, 'toDate')} + type="date" + name="date" + class="text-greay-900 block w-full rounded-lg border border-gray-300 bg-gray-50 p-1.5 text-sm focus:border-violet-500 focus:ring-violet-500" + /> +
+
+ + +
+
+
+ handleFeelings(e.detail)} + displayText={false} + slim={true} + /> +
+
+ handleSearchQuery(e.target.value)} + type="text" + placeholder="Search query" + class="text-greay-900 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm focus:border-violet-500 focus:ring-violet-500" + /> +
+
diff --git a/identity-web/src/routes/dashboard/utils/OverviewEntry.svelte b/identity-web/src/routes/dashboard/utils/OverviewEntry.svelte index d7bd8fc..1a0b075 100644 --- a/identity-web/src/routes/dashboard/utils/OverviewEntry.svelte +++ b/identity-web/src/routes/dashboard/utils/OverviewEntry.svelte @@ -1,22 +1,35 @@

- {#if showDate} - - {/if} + {#if showDate} + + {/if} - {#if TITLED_ENTRIES.includes(entry.base.kind)} - New {entry.base.kind}: § {entry.title} - {:else if ["song", "album"].includes(entry.base.kind)} - New {entry.base.kind}: {entry.base.artist} ‐ {entry.base.title} - {:else if entry.base.kind === "date"} - New {entry.base.kind}: {new Date(entry.base.referencedDate).toLocaleDateString()} - {:else} - New {entry.base.kind} - {/if} -

\ No newline at end of file + {#if TITLED_ENTRIES.includes(entry.base.kind)} + New {entry.base.kind}: + + § {entry.title} + + {:else if ['song', 'album'].includes(entry.base.kind)} + New {entry.base.kind}: + + {entry.base.artist} ‐ {entry.base.title} + + {:else if entry.base.kind === 'date'} + New {entry.base.kind}: + + {new Date(entry.base.referencedDate).toLocaleDateString()} + + {:else} + + New {entry.base.kind} + + {/if} +

diff --git a/identity-web/src/routes/entry/new/+page.svelte b/identity-web/src/routes/entry/new/+page.svelte index cf0eaa4..92013cc 100644 --- a/identity-web/src/routes/entry/new/+page.svelte +++ b/identity-web/src/routes/entry/new/+page.svelte @@ -1,219 +1,351 @@ -
-
-

Add an entry

-
-
- - - {#if $errors.kind != null} -

{$errors.kind[0]}

- {/if} -
- {#if TITLED_ENTRIES.includes(kind)} -
- - - {#if $errors.title != null} -

{$errors.title[0]}

- {/if} -
- {/if} +
+
+

Add an entry

+ +
+ + + {#if $errors.kind != null} +

+ {$errors.kind[0]} +

+ {/if} +
+ {#if TITLED_ENTRIES.includes(kind)} +
+ + + {#if $errors.title != null} +

+ {$errors.title[0]} +

+ {/if} +
+ {/if} - {#if ["song", "album"].includes(kind)} -
-
- - - {#if $errors.artist != null} -

{$errors.artist[0]}

- {/if} -
-
- - - {#if $errors.musicTitle != null} -

{$errors.musicTitle[0]}

- {/if} -
-
-
- -
- - - - -
-
-
- -
- - - - -
-
-
- -
- - - - -
-
- {#if $errors.links != null} -

{$errors.links[0]}

- {/if} - {:else if kind === "environment"} -
- - -
- {:else if kind === "date"} -
- - - {#if $errors.date != null} -

{$errors.date[0]}

- {/if} -
- {/if} + {#if ['song', 'album'].includes(kind)} +
+
+ + + {#if $errors.artist != null} +

+ {$errors.artist[0]} +

+ {/if} +
+
+ + + {#if $errors.musicTitle != null} +

+ {$errors.musicTitle[0]} +

+ {/if} +
+
+
+ +
+ + + + +
+
+
+ +
+ + + + +
+
+
+ +
+ + + + +
+
+ {#if $errors.links != null} +

+ {$errors.links[0]} +

+ {/if} + {:else if kind === 'environment'} +
+ + +
+ {:else if kind === 'date'} +
+ + + {#if $errors.date != null} +

+ {$errors.date[0]} +

+ {/if} +
+ {/if} - {#if kind != null && kind.length > 0} -
- - -
+ {#if kind != null && kind.length > 0} +
+ + +
-
- - -
+
+ + +
-
- - {#if $errors.feelings != null} -

{$errors.feelings[0]}

- {/if} -
+
+ + {#if $errors.feelings != null} +

+ {$errors.feelings[0]} +

+ {/if} +
- - {/if} - -
-
\ No newline at end of file + + {/if} + +
+
diff --git a/identity-web/svelte.config.js b/identity-web/svelte.config.js index 4a82086..c94ff7b 100644 --- a/identity-web/svelte.config.js +++ b/identity-web/svelte.config.js @@ -1,4 +1,5 @@ -import adapter from '@sveltejs/adapter-auto'; +import adapterStatic from '@sveltejs/adapter-static'; +import adapterAuto from '@sveltejs/adapter-auto'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ @@ -8,10 +9,16 @@ const config = { preprocess: vitePreprocess(), kit: { + adapter: adapterStatic({ + fallback: 'index.html' + }) + // 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() + // + // To use adapter-auto, uncomment the line below: + // adapter: adapterAuto(), } }; diff --git a/identity-web/tailwind.config.js b/identity-web/tailwind.config.js index 5d57bfd..66b98eb 100644 --- a/identity-web/tailwind.config.js +++ b/identity-web/tailwind.config.js @@ -1,11 +1,8 @@ /** @type {import('tailwindcss').Config} */ export default { - content: ['./src/**/*.{html,css,js,svelte,ts}'], - theme: { - extend: {}, - }, - plugins: [ - require("@tailwindcss/forms") - ], -} - + content: ['./src/**/*.{html,css,js,svelte,ts}'], + theme: { + extend: {} + }, + plugins: [require('@tailwindcss/forms')] +}; diff --git a/identity-web/yarn.lock b/identity-web/yarn.lock index 3de6bc6..7540bd5 100644 --- a/identity-web/yarn.lock +++ b/identity-web/yarn.lock @@ -559,6 +559,15 @@ __metadata: languageName: node linkType: hard +"@sveltejs/adapter-static@npm:^3.0.2": + version: 3.0.2 + resolution: "@sveltejs/adapter-static@npm:3.0.2" + peerDependencies: + "@sveltejs/kit": ^2.0.0 + checksum: 10c0/db3c287f0ed52b9c3c42e27cb54c18977627dd7596ac2f778b6a70500b6e07cc48f3fa305a39171d0279311cfe11ecf8afcd367abbb13e582ad10d96223721fe + languageName: node + linkType: hard + "@sveltejs/kit@npm:^2.0.0": version: 2.5.10 resolution: "@sveltejs/kit@npm:2.5.10" @@ -1930,6 +1939,7 @@ __metadata: "@fortawesome/free-solid-svg-icons": "npm:^6.5.2" "@fortawesome/svelte-fontawesome": "npm:^0.2.2" "@sveltejs/adapter-auto": "npm:^3.0.0" + "@sveltejs/adapter-static": "npm:^3.0.2" "@sveltejs/kit": "npm:^2.0.0" "@sveltejs/vite-plugin-svelte": "npm:^3.0.0" "@tailwindcss/forms": "npm:^0.5.7" @@ -1945,6 +1955,7 @@ __metadata: postcss: "npm:^8.4.38" prettier: "npm:^3.1.1" prettier-plugin-svelte: "npm:^3.1.2" + prettier-plugin-tailwindcss: "npm:^0.6.5" svelte: "npm:^4.2.7" svelte-check: "npm:^3.6.0" tailwindcss: "npm:^3.4.4" @@ -2855,6 +2866,61 @@ __metadata: languageName: node linkType: hard +"prettier-plugin-tailwindcss@npm:^0.6.5": + version: 0.6.5 + resolution: "prettier-plugin-tailwindcss@npm:0.6.5" + peerDependencies: + "@ianvs/prettier-plugin-sort-imports": "*" + "@prettier/plugin-pug": "*" + "@shopify/prettier-plugin-liquid": "*" + "@trivago/prettier-plugin-sort-imports": "*" + "@zackad/prettier-plugin-twig-melody": "*" + prettier: ^3.0 + prettier-plugin-astro: "*" + prettier-plugin-css-order: "*" + prettier-plugin-import-sort: "*" + prettier-plugin-jsdoc: "*" + prettier-plugin-marko: "*" + prettier-plugin-organize-attributes: "*" + prettier-plugin-organize-imports: "*" + prettier-plugin-sort-imports: "*" + prettier-plugin-style-order: "*" + prettier-plugin-svelte: "*" + peerDependenciesMeta: + "@ianvs/prettier-plugin-sort-imports": + optional: true + "@prettier/plugin-pug": + optional: true + "@shopify/prettier-plugin-liquid": + optional: true + "@trivago/prettier-plugin-sort-imports": + optional: true + "@zackad/prettier-plugin-twig-melody": + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + checksum: 10c0/30d62928592b48cab03c46ff63edd35d4a33c4e7c40e583f12bff7223eba8b6f780fd394965b0250160bcf39688f6fb602420374b2055bcbb6a69560b818ca4e + languageName: node + linkType: hard + "prettier@npm:^3.1.1": version: 3.3.2 resolution: "prettier@npm:3.3.2"