johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<script lang="ts">
import type { ActorView } from '#lib/lexicons/dev/knotless/defs.ts';
import type * as repo from '#lib/lexicons/dev/knotless/repo.ts';
import ItemFacts from '#lib/organisms/ItemFacts.svelte';
/**
* What a pull or an issue will carry as it opens, in the facts line
* its page shows once open: the labels to put on it, of those
* `offered`, and with `people` the reviewers to ask, from them. Nothing
* is written until it opens: what is chosen is bound, `labels` by name
* and `reviewers` by DID, for the form to send.
*/
let {
offered,
labels = $bindable([]),
people = [],
reviewers = $bindable([])
}: {
offered: repo.Label[];
labels?: string[];
people?: ActorView[];
reviewers?: ActorView['did'][];
} = $props();
const chosen = $derived(offered.filter((l) => labels.includes(l.name)));
const asked = $derived(people.filter((p) => reviewers.includes(p.did)));
const flip = <T extends string>(list: T[], one: T, removed: boolean) =>
removed ? list.filter((x) => x !== one) : [...list, one];
/** Its scope: what comes before its last `/`. */
const scope = (name: string) => name.slice(0, Math.max(0, name.lastIndexOf('/')));
/** Labels with `name` put on or taken off: an exclusive one put on takes the others of its scope off, as labelling does. */
async function label(name: string, removed: boolean) {
const l = offered.find((o) => o.name === name);
const rivals = (o: string) =>
!!l?.exclusive &&
!removed &&
!!scope(name) &&
scope(o) === scope(name) &&
!!offered.find((x) => x.name === o)?.exclusive;
labels = flip(
labels.filter((o) => !rivals(o)),
name,
removed
);
}
/** Asks `did` to review it as it opens, or no longer. */
async function ask(did: ActorView['did'], removed: boolean) {
reviewers = flip(reviewers, did, removed);
}
</script>
<ItemFacts
requested={asked}
{people}
ask={people.length ? ask : undefined}
labels={chosen}
{offered}
label={offered.length ? label : undefined}
/>