pronounscc/frontend/src/routes/settings/flags/+page.svelte

165 lines
4.8 KiB
Svelte
Raw Normal View History

2023-05-29 00:18:02 +02:00
<script lang="ts">
import type { APIError, PrideFlag } from "$lib/api/entities";
import { Button, Icon, Input, Modal, ModalBody, ModalFooter, ModalHeader } from "sveltestrap";
import type { PageData } from "./$types";
import Flag from "./Flag.svelte";
import prettyBytes from "pretty-bytes";
import { addToast } from "$lib/toast";
import { encode } from "base64-arraybuffer";
import unknownFlag from "./unknown_flag.png";
import { apiFetchClient } from "$lib/api/fetch";
import ErrorAlert from "$lib/components/ErrorAlert.svelte";
const MAX_FLAG_BYTES = 500_000;
export let data: PageData;
let search = "";
let error: APIError | null = null;
let filtered: PrideFlag[];
$: filtered = search
? data.flags.filter((flag) =>
flag.name.toLocaleLowerCase().includes(search.toLocaleLowerCase()),
)
: data.flags;
// NEW FLAG UPLOADING CODE
let modalOpen = false;
const toggleModal = () => (modalOpen = !modalOpen);
let canUpload: boolean;
$: canUpload = !!(newFlag && newName);
let newFlag: string | null;
let flagFiles: FileList | null;
$: getFlag(flagFiles).then((b64) => (newFlag = b64));
let newName = "";
let newDescription = "";
const getFlag = async (list: FileList | null) => {
if (!list || list.length === 0) return null;
if (list[0].size > MAX_FLAG_BYTES) {
addToast({
header: "Flag too large",
body: `This flag file is too large, please resize it (maximum is ${prettyBytes(
MAX_FLAG_BYTES,
)}, the file you tried to upload is ${prettyBytes(list[0].size)})`,
});
return null;
}
const buffer = await list[0].arrayBuffer();
const base64 = encode(buffer);
const uri = `data:${list[0].type};base64,${base64}`;
return uri;
};
const uploadFlag = async () => {
try {
const resp = await apiFetchClient<PrideFlag>("/users/@me/flags", "POST", {
flag: newFlag,
name: newName,
description: newDescription || null,
});
error = null;
data.flags.push(resp);
data.flags.sort((a, b) => a.name.localeCompare(b.name));
data.flags = [...data.flags];
// reset flag
newFlag = null;
newName = "";
newDescription = "";
addToast({ header: "Uploaded flag", body: "Successfully uploaded flag!" });
toggleModal();
} catch (e) {
error = e as APIError;
}
};
</script>
<h1>Pride flags ({data.flags.length})</h1>
<p>
You can upload pride flags to use on your profiles here. Flags you upload here will <em>not</em> automatically
show up on your profile.
</p>
<div class="input-group">
<Input placeholder="Filter flags" bind:value={search} disabled={data.flags.length === 0} />
<Button color="success" on:click={toggleModal}>
<Icon name="upload" aria-hidden /> Upload flag
</Button>
</div>
<div class="p-2">
{#each filtered as flag}
<Flag {flag} />
{:else}
{#if data.flags.length === 0}
You haven't uploaded any flags yet, press the button above to do so.
{:else}
There are no flags matching your search <strong>{search}</strong>
{/if}
{/each}
</div>
<Modal isOpen={modalOpen} toggle={toggleModal}>
<ModalHeader toggle={toggleModal}>Upload flag</ModalHeader>
<ModalBody>
{#if error}
<ErrorAlert {error} />
{/if}
<div class="d-flex align-items-center">
<img src={newFlag || unknownFlag} alt="New flag" class="flag m-1" />
<input
class="form-control"
id="flag-file"
type="file"
bind:files={flagFiles}
accept="image/png, image/jpeg, image/gif, image/webp, image/svg+xml"
/>
</div>
<p class="text-muted mt-2">
<Icon name="info-circle-fill" aria-hidden /> Only PNG, JPEG, GIF, and WebP images can be uploaded
as flags. The file cannot be larger than 512 kilobytes.
</p>
<p>
<label for="newName" class="form-label">Name</label>
<Input id="newName" bind:value={newName} />
</p>
<p class="text-muted">
<Icon name="info-circle-fill" aria-hidden /> This name will be shown beside the flag.
</p>
<p>
<label for="description" class="form-label">Description</label>
<textarea id="description" class="form-control" bind:value={newDescription} />
</p>
<p class="text-muted">
<Icon name="info-circle-fill" aria-hidden /> This text will be used as the alt text of the flag
image, and will also be shown on hover. Optional, but <strong>strongly recommended</strong> as
it improves accessibility.
</p>
</ModalBody>
<ModalFooter>
<Button disabled={!canUpload} color="success" on:click={() => uploadFlag()}>Upload flag</Button>
</ModalFooter>
</Modal>
<style>
.flag {
height: 2rem;
max-width: 200px;
border-radius: 3px;
}
textarea {
height: 100px;
}
</style>