johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<script lang="ts">
import type { ReactionCountView } from '#lib/lexicons/dev/knotless/defs.ts';
import type * as addReaction from '#lib/lexicons/dev/knotless/addReaction.ts';
import type * as removeReaction from '#lib/lexicons/dev/knotless/removeReaction.ts';
import type { AtUriString } from '@atproto/lex';
import { call } from '#lib/data.ts';
import { LOGIN } from '#lib/login.ts';
import { REACTIONS } from '#lib/reactions.ts';
import Icon from '#lib/atoms/Icon.svelte';
import ReactionPill from '#lib/atoms/ReactionPill.svelte';
/**
* How accounts reacted to `subject`, a pull, an issue or a comment on the
* repository `repo`, and reacting to it as whoever is `in`, from the
* eight a reaction may say: for anyone else, the way to log in first.
*/
let {
repo,
subject,
reactions = [],
in: logged
}: { repo: string; subject: string; reactions?: ReactionCountView[]; in: boolean } = $props();
let counts = $derived(reactions);
// The reaction being added or taken back.
let busy = $state('');
let open = $state(false);
let failed = $state('');
const mine = (content: string) => !!counts.find((c) => c.content === content)?.reaction;
const shown = $derived(
REACTIONS.flatMap((r) => {
const c = counts.find((c) => c.content === r.content);
return c && c.count > 0 ? [{ ...r, count: c.count, mine: !!c.reaction }] : [];
})
);
async function toggle(content: string) {
open = false;
busy = content;
const nsid = mine(content) ? 'dev.knotless.removeReaction' : 'dev.knotless.addReaction';
const input = { repo: repo as AtUriString, subject: subject as AtUriString, content };
try {
counts = (await call<typeof addReaction.main | typeof removeReaction.main>(nsid, input))
.reactions;
failed = '';
} catch (e) {
failed = (e as Error).message;
}
busy = '';
}
</script>
<div class="reactions">
{#each shown as r (r.content)}
<ReactionPill
emoji={r.emoji}
name={r.name}
count={r.count}
mine={r.mine}
disabled={!!busy}
busy={busy === r.content}
onclick={logged ? () => toggle(r.content) : undefined}
target={logged ? undefined : LOGIN}
/>
{/each}
{#if logged}
<details class="popover reaction-menu" bind:open>
<summary
class="reaction add"
aria-label="Add reaction"
title="Add reaction"
aria-busy={!!busy && !shown.some((r) => r.content === busy)}><Icon name="smiley" /></summary
>
<div class="menu reaction-picker">
{#each REACTIONS as r (r.content)}
<button
class="reaction-pick"
type="button"
aria-label={r.name}
title={r.name}
aria-pressed={mine(r.content)}
disabled={!!busy}
onclick={() => toggle(r.content)}>{r.emoji}</button
>
{/each}
</div>
</details>
{:else}
<button
class="reaction add"
type="button"
aria-label="Add reaction"
title="Add reaction"
popovertarget={LOGIN}><Icon name="smiley" /></button
>
{/if}
{#if failed}<span class="reaction-error">{failed}</span>{/if}
</div>