Merge remote-tracking branch 'origin/main'

This commit is contained in:
Kay Faraday 2023-09-19 19:50:17 +00:00
commit 8eaa96f018
17 changed files with 2108 additions and 138 deletions

View File

@ -6,7 +6,7 @@ tmp_dir = "tmp"
bin = "./tmp/main" bin = "./tmp/main"
cmd = "go build -o ./tmp/main ." cmd = "go build -o ./tmp/main ."
delay = 1000 delay = 1000
exclude_dir = ["docs", "frontend", "prns", "pronounslib", "tmp", "target"] exclude_dir = ["docs", "frontend", "prns", "pronounslib", "tmp", "target", "node_modules"]
exclude_file = [] exclude_file = []
exclude_regex = ["_test.go"] exclude_regex = ["_test.go"]
exclude_unchanged = false exclude_unchanged = false

View File

@ -33,8 +33,9 @@ Requirements:
1. Create a PostgreSQL user and database (the user should own the database). 1. Create a PostgreSQL user and database (the user should own the database).
For example: `create user pronouns with password 'password'; create database pronouns with owner pronouns;` For example: `create user pronouns with password 'password'; create database pronouns with owner pronouns;`
2. Copy `.env.example` in the repository root to a new file named `.env` and fill out the required options. 2. Copy `.env.example` in the repository root to a new file named `.env` and fill out the required options.
3. Run `go run -v . database migrate` to initialize the database, then optionally `go run -v . database seed` to insert a test user. 3. Copy `frontend/.env.example` to `frontend/env` and fill out the required options.
4. Run `pnpm dev`. Alternatively, if you don't want the backend to live reload, run `go run -v . web`, 4. Run `go run -v . database migrate` to initialize the database, then optionally `go run -v . database seed` to insert a test user.
5. Run `pnpm dev`. Alternatively, if you don't want the backend to live reload, run `go run -v . web`,
then change to the `frontend/` directory and run `pnpm dev`. then change to the `frontend/` directory and run `pnpm dev`.
See [`docs/production.md`](/docs/production.md#configuration) for more information about keys in the backend and frontend `.env` files. See [`docs/production.md`](/docs/production.md#configuration) for more information about keys in the backend and frontend `.env` files.

View File

@ -43,7 +43,10 @@ func (f Field) Validate(custom CustomPreferences) string {
} }
if !entry.Status.Valid(custom) { if !entry.Status.Valid(custom) {
return fmt.Sprintf("entries.%d: status is invalid", i) if entry.Status == "missing" {
return fmt.Sprintf("didn't select a status for entries.%d. make sure to select it to the right of the field", i)
}
return fmt.Sprintf("entries.%d status: '%s' is invalid", i, entry.Status)
} }
} }

View File

@ -65,6 +65,9 @@ func (s *Server) noAppFediverseURL(ctx context.Context, w http.ResponseWriter, r
} }
switch softwareName { switch softwareName {
case "iceshrimp":
softwareName = "firefish"
fallthrough
case "misskey", "foundkey", "calckey", "firefish": case "misskey", "foundkey", "calckey", "firefish":
return s.noAppMisskeyURL(ctx, w, r, softwareName, instance) return s.noAppMisskeyURL(ctx, w, r, softwareName, instance)
case "mastodon", "pleroma", "akkoma", "incestoma", "pixelfed", "gotosocial": case "mastodon", "pleroma", "akkoma", "incestoma", "pixelfed", "gotosocial":

View File

@ -49,7 +49,7 @@ func (s *Server) meta(w http.ResponseWriter, r *http.Request) error {
var notice *MetaNotice var notice *MetaNotice
if n, err := s.DB.CurrentNotice(ctx); err != nil { if n, err := s.DB.CurrentNotice(ctx); err != nil {
if err == db.ErrNoNotice { if err != db.ErrNoNotice {
log.Errorf("getting notice: %v", err) log.Errorf("getting notice: %v", err)
} }
} else { } else {

View File

@ -1,4 +1,4 @@
# Base of frontend URLs # Base of frontend URLs (required)
PUBLIC_BASE_URL=http://localhost:5173 PUBLIC_BASE_URL=http://localhost:5173
# Base of media URLs, required for avatars, pride flags, and data exports # Base of media URLs, required for avatars, pride flags, and data exports

View File

@ -1,20 +1,20 @@
module.exports = { module.exports = {
root: true, root: true,
parser: '@typescript-eslint/parser', parser: "@typescript-eslint/parser",
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
plugins: ['svelte3', '@typescript-eslint'], plugins: ["svelte3", "@typescript-eslint"],
ignorePatterns: ['*.cjs'], ignorePatterns: ["*.cjs"],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
settings: { settings: {
'svelte3/typescript': () => require('typescript') "svelte3/typescript": () => require("typescript"),
}, },
parserOptions: { parserOptions: {
sourceType: 'module', sourceType: "module",
ecmaVersion: 2020 ecmaVersion: 2020,
}, },
env: { env: {
browser: true, browser: true,
es2017: true, es2017: true,
node: true node: true,
} },
}; };

View File

@ -1,52 +1,52 @@
{ {
"name": "pronouns-fe", "name": "pronouns-fe",
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "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",
"lint": "prettier --plugin-search-dir . --check . && eslint .", "lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ." "format": "prettier --plugin-search-dir . --write ."
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-node": "^1.2.3", "@sveltejs/adapter-node": "^1.2.3",
"@sveltejs/kit": "^1.15.0", "@sveltejs/kit": "^1.15.0",
"@types/luxon": "^3.2.2", "@types/luxon": "^3.2.2",
"@types/markdown-it": "^12.2.3", "@types/markdown-it": "^12.2.3",
"@types/node": "^18.15.11", "@types/node": "^18.15.11",
"@types/sanitize-html": "^2.9.0", "@types/sanitize-html": "^2.9.0",
"@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1", "@typescript-eslint/parser": "^5.57.1",
"eslint": "^8.37.0", "eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-svelte3": "^4.0.0", "eslint-plugin-svelte3": "^4.0.0",
"prettier": "^2.8.7", "prettier": "^2.8.7",
"prettier-plugin-svelte": "^2.10.0", "prettier-plugin-svelte": "^2.10.0",
"svelte": "^3.58.0", "svelte": "^3.58.0",
"svelte-check": "^3.1.4", "svelte-check": "^3.1.4",
"svelte-hcaptcha": "^0.1.1", "svelte-hcaptcha": "^0.1.1",
"sveltestrap": "^5.10.0", "sveltestrap": "^5.10.0",
"tslib": "^2.5.0", "tslib": "^2.5.0",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"vite": "^4.2.1", "vite": "^4.2.1",
"vite-plugin-markdown": "^2.1.0" "vite-plugin-markdown": "^2.1.0"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@fontsource/firago": "^4.5.3", "@fontsource/firago": "^4.5.3",
"@popperjs/core": "^2.11.7", "@popperjs/core": "^2.11.7",
"@sentry/node": "^7.46.0", "@sentry/node": "^7.46.0",
"base64-arraybuffer": "^1.0.2", "base64-arraybuffer": "^1.0.2",
"bootstrap": "5.3.0-alpha1", "bootstrap": "5.3.0-alpha1",
"bootstrap-icons": "^1.10.4", "bootstrap-icons": "^1.10.4",
"jose": "^4.13.1", "jose": "^4.13.1",
"luxon": "^3.3.0", "luxon": "^3.3.0",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"pretty-bytes": "^6.1.0", "pretty-bytes": "^6.1.0",
"sanitize-html": "^2.10.0" "sanitize-html": "^2.10.0"
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -69,7 +69,7 @@ export interface Settings {
} }
export interface Field { export interface Field {
name: string; name: string | null;
entries: FieldEntry[]; entries: FieldEntry[];
} }

View File

@ -15,10 +15,11 @@
$: currentPreference = $: currentPreference =
status in mergedPreferences ? mergedPreferences[status] : defaultPreferences.missing; status in mergedPreferences ? mergedPreferences[status] : defaultPreferences.missing;
let iconElement: HTMLElement; let iconElement: HTMLSpanElement;
</script> </script>
<span bind:this={iconElement} tabindex={0} <span bind:this={iconElement} aria-hidden>
><Icon name={currentPreference.icon} class={className} /></span <Icon name={currentPreference.icon} class={className} />
> </span>
<Tooltip target={iconElement} placement="top">{currentPreference.tooltip}</Tooltip> <span class="visually-hidden">{currentPreference.tooltip}:</span>
<Tooltip aria-hidden target={iconElement} placement="top">{currentPreference.tooltip}</Tooltip>

View File

@ -36,7 +36,7 @@
</script> </script>
<div class="p-3"> <div class="p-3">
<h5>{field.name}</h5> <h5>{field.name ? field.name : "New field"}</h5>
<InputGroup class="m-1"> <InputGroup class="m-1">
<IconButton <IconButton
color="secondary" color="secondary"
@ -50,7 +50,7 @@
tooltip="Move field right" tooltip="Move field right"
click={() => moveField(false)} click={() => moveField(false)}
/> />
<Input bind:value={field.name} /> <Input placeholder="New field" bind:value={field.name} />
<Button color="danger" on:click={() => deleteField()}>Delete field</Button> <Button color="danger" on:click={() => deleteField()}>Delete field</Button>
</InputGroup> </InputGroup>
<hr /> <hr />
@ -65,7 +65,7 @@
/> />
{/each} {/each}
<form class="input-group m-1" on:submit={addEntry}> <form class="m-1 input-group" on:submit={addEntry}>
<input type="text" class="form-control" placeholder="New entry" bind:value={newEntry} /> <input type="text" class="form-control" placeholder="New entry" bind:value={newEntry} />
<IconButton color="success" icon="plus" tooltip="Add entry" /> <IconButton color="success" icon="plus" tooltip="Add entry" />
</form> </form>

View File

@ -46,7 +46,7 @@
</div> </div>
<div> <div>
<Button <Button
on:click={() => ($member.fields = [...$member.fields, { name: "New field", entries: [] }])} on:click={() => ($member.fields = [...$member.fields, { name: null, entries: [] }])}
> >
<Icon name="plus" aria-hidden /> Add new field <Icon name="plus" aria-hidden /> Add new field
</Button> </Button>

View File

@ -45,7 +45,7 @@
</div> </div>
</div> </div>
<div> <div>
<Button on:click={() => ($user.fields = [...$user.fields, { name: "New field", entries: [] }])}> <Button on:click={() => ($user.fields = [...$user.fields, { name: null, entries: [] }])}>
<Icon name="plus" aria-hidden /> Add new field <Icon name="plus" aria-hidden /> Add new field
</Button> </Button>
</div> </div>

View File

@ -34,7 +34,9 @@
} }
}); });
const fediLogin = async () => { const fediLogin = async (e: Event) => {
e.preventDefault();
fediDisabled = true; fediDisabled = true;
try { try {
const resp = await apiFetch<{ url: string }>( const resp = await apiFetch<{ url: string }>(
@ -71,26 +73,28 @@
{/if} {/if}
</ListGroup> </ListGroup>
<Modal header="Pick an instance" isOpen={modalOpen} toggle={toggleModal}> <Modal header="Pick an instance" isOpen={modalOpen} toggle={toggleModal}>
<ModalBody> <form on:submit={(e) => fediLogin(e)}>
<Input placeholder="Instance (e.g. mastodon.social)" bind:value={instance} /> <ModalBody>
<p class="text-muted mt-2"> <Input placeholder="Instance (e.g. mastodon.social)" bind:value={instance} />
<Icon name="info-circle-fill" aria-label="Info" /> This should be the domain you use to access <p class="text-muted mt-2">
posts. For example, if your username is <code>@timmie@mastodon.example</code>, but your <Icon name="info-circle-fill" aria-label="Info" /> This should be the domain you use to
user <em>page</em> is at access posts. For example, if your username is <code>@timmie@mastodon.example</code>,
<code>social.mastodon.example/timmie</code>, you should fill in but your user <em>page</em> is at
<code>social.mastodon.example</code>. <code>social.mastodon.example/timmie</code>, you should fill in
</p> <code>social.mastodon.example</code>.
{#if error} </p>
<div class="mt-2"> {#if error}
<ErrorAlert {error} /> <div class="mt-2">
</div> <ErrorAlert {error} />
{/if} </div>
</ModalBody> {/if}
<ModalFooter> </ModalBody>
<Button color="primary" disabled={fediDisabled || instance === ""} on:click={fediLogin} <ModalFooter>
>Log in</Button <Button type="submit" color="primary" disabled={fediDisabled || instance === ""}
> >Log in</Button
</ModalFooter> >
</ModalFooter>
</form>
</Modal> </Modal>
<p class="text-muted mt-2 mx-1"> <p class="text-muted mt-2 mx-1">
<Icon name="info-circle-fill" aria-hidden /> By signing in, you consent to pronouns.cc storing <Icon name="info-circle-fill" aria-hidden /> By signing in, you consent to pronouns.cc storing

View File

@ -44,7 +44,9 @@
let googleUnlinkModalOpen = false; let googleUnlinkModalOpen = false;
let toggleGoogleUnlinkModal = () => (googleUnlinkModalOpen = !googleUnlinkModalOpen); let toggleGoogleUnlinkModal = () => (googleUnlinkModalOpen = !googleUnlinkModalOpen);
const fediLogin = async () => { const fediLogin = async (e: Event) => {
e.preventDefault();
fediDisabled = true; fediDisabled = true;
try { try {
const resp = await apiFetch<{ url: string }>( const resp = await apiFetch<{ url: string }>(
@ -205,19 +207,21 @@
</div> </div>
{/if} {/if}
<Modal header="Pick an instance" isOpen={fediLinkModalOpen} toggle={toggleFediLinkModal}> <Modal header="Pick an instance" isOpen={fediLinkModalOpen} toggle={toggleFediLinkModal}>
<ModalBody> <form on:submit={(e) => fediLogin(e)}>
<Input placeholder="Instance (e.g. mastodon.social)" bind:value={instance} /> <ModalBody>
{#if error} <Input placeholder="Instance (e.g. mastodon.social)" bind:value={instance} />
<div class="mt-2"> {#if error}
<ErrorAlert {error} /> <div class="mt-2">
</div> <ErrorAlert {error} />
{/if} </div>
</ModalBody> {/if}
<ModalFooter> </ModalBody>
<Button color="primary" disabled={fediDisabled || instance === ""} on:click={fediLogin} <ModalFooter>
>Log in</Button <Button type="submit" color="primary" disabled={fediDisabled || instance === ""}
> >Log in</Button
</ModalFooter> >
</ModalFooter>
</form>
</Modal> </Modal>
<Modal <Modal

View File

@ -1,18 +1,18 @@
{ {
"extends": "./.svelte-kit/tsconfig.json", "extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"ignoreDeprecations": "5.0", "ignoreDeprecations": "5.0",
"allowJs": true, "allowJs": true,
"checkJs": true, "checkJs": true,
"esModuleInterop": true, "esModuleInterop": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"skipLibCheck": true, "skipLibCheck": true,
"sourceMap": true, "sourceMap": true,
"strict": true "strict": true
} }
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
// //
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in // from the referenced tsconfig.json - TypeScript does not merge them in
} }