mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-31 09:10:44 +08:00
265 lines
7.7 KiB
Svelte
265 lines
7.7 KiB
Svelte
<script lang="ts" module>
|
|
export const addRepoPopoverToggleId = "add-repo-popover-toggle";
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import type { RepoSummary } from "@bindings/repo/RepoSummary";
|
|
|
|
import { z } from "zod";
|
|
|
|
import { nodeRunning } from "@app/lib/events";
|
|
import { invoke } from "@app/lib/invoke";
|
|
import useLocalStorage from "@app/lib/useLocalStorage.svelte";
|
|
import { parseRepositoryId, twemoji } from "@app/lib/utils";
|
|
|
|
import { announce } from "@app/components/AnnounceSwitch.svelte";
|
|
import Button from "@app/components/Button.svelte";
|
|
import Command from "@app/components/Command.svelte";
|
|
import ExternalLink from "@app/components/ExternalLink.svelte";
|
|
import Icon from "@app/components/Icon.svelte";
|
|
import { closeFocused } from "@app/components/Popover.svelte";
|
|
import Popover from "@app/components/Popover.svelte";
|
|
import TextInput from "@app/components/TextInput.svelte";
|
|
|
|
interface Props {
|
|
onOpen: () => void;
|
|
reload: () => Promise<void>;
|
|
repos: RepoSummary[];
|
|
seededNotReplicated: string[];
|
|
}
|
|
|
|
const { onOpen, reload, repos, seededNotReplicated }: Props = $props();
|
|
|
|
let popoverExpanded: boolean = $state(false);
|
|
let rid = $state("");
|
|
let validationMessage: string | undefined = $state(undefined);
|
|
|
|
// Clear validation message when changing the input.
|
|
$effect(() => {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
rid;
|
|
|
|
validationMessage = undefined;
|
|
});
|
|
|
|
// Clear input when the popover is closed.
|
|
$effect(() => {
|
|
if (!popoverExpanded) {
|
|
rid = "";
|
|
}
|
|
});
|
|
|
|
const tab = useLocalStorage(
|
|
"addRepoPopoverSelectedTab",
|
|
z.union([z.literal("seed"), z.literal("publish")]),
|
|
"seed",
|
|
!window.localStorage,
|
|
);
|
|
|
|
async function submit() {
|
|
const trimmedRid = rid.trim();
|
|
|
|
if (trimmedRid === "") {
|
|
return;
|
|
}
|
|
|
|
validationMessage = validate(trimmedRid);
|
|
|
|
if (validationMessage === undefined) {
|
|
await seed(trimmedRid);
|
|
await reload();
|
|
rid = "";
|
|
closeFocused();
|
|
}
|
|
}
|
|
|
|
async function seed(rid: string) {
|
|
try {
|
|
await invoke<null>("seed", {
|
|
rid: rid,
|
|
opts: { announce: $nodeRunning && $announce },
|
|
});
|
|
} catch (error) {
|
|
console.error("Seeding failed", error);
|
|
}
|
|
}
|
|
|
|
function validate(rid: string): string | undefined {
|
|
const parsedRid = parseRepositoryId(rid);
|
|
if (parsedRid === undefined) {
|
|
return "RID is not valid";
|
|
}
|
|
|
|
if (seededNotReplicated.includes(rid)) {
|
|
return "This repo is already queued for fetching";
|
|
}
|
|
if (repos.map(r => r.rid).includes(rid)) {
|
|
return "This repo is already seeded";
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
li {
|
|
padding: 0;
|
|
}
|
|
</style>
|
|
|
|
<Popover placement="bottom-start" bind:expanded={popoverExpanded}>
|
|
{#snippet toggle(onclick)}
|
|
<Button
|
|
variant="naked"
|
|
id={addRepoPopoverToggleId}
|
|
onclick={() => {
|
|
onOpen();
|
|
onclick();
|
|
}}
|
|
active={popoverExpanded}>
|
|
<Icon name="plus" />
|
|
</Button>
|
|
{/snippet}
|
|
|
|
{#snippet popover()}
|
|
<div
|
|
class="txt-body-m-regular"
|
|
style:line-height="1.625rem"
|
|
style:padding="1rem"
|
|
style:border-radius="var(--border-radius-md)"
|
|
style:border="1px solid var(--color-border-subtle)"
|
|
style:background-color="var(--color-surface-canvas)"
|
|
style:width="32rem">
|
|
<div class="global-flex" style:margin-bottom="1rem">
|
|
<Button
|
|
variant="naked"
|
|
active={tab.value === "seed"}
|
|
onclick={() => {
|
|
tab.value = "seed";
|
|
}}>
|
|
Seed a repo
|
|
</Button>
|
|
<Button
|
|
variant="naked"
|
|
active={tab.value === "publish"}
|
|
onclick={() => {
|
|
tab.value = "publish";
|
|
}}>
|
|
Publish existing
|
|
</Button>
|
|
</div>
|
|
|
|
{#if tab.value === "seed"}
|
|
<!-- prettier-ignore -->
|
|
<div style:margin-bottom="1rem" style:color="var(--color-text-primary)">
|
|
You can look for Radicle repos on
|
|
<ExternalLink href="https://radicle.network">
|
|
radicle.network
|
|
</ExternalLink>.
|
|
</div>
|
|
<div style:width="100%">
|
|
<div class="txt-body-l-semibold" style:margin-bottom="0.5rem"></div>
|
|
<div
|
|
class="global-flex"
|
|
style:flex-direction="column"
|
|
style:align-items="flex-start"
|
|
style:gap="1rem">
|
|
<div style:width="100%">
|
|
<div class="global-flex" style:width="100%">
|
|
<TextInput
|
|
autofocus
|
|
valid={validationMessage === undefined}
|
|
bind:value={rid}
|
|
onSubmit={submit}
|
|
placeholder="RID, e.g. rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5" />
|
|
<Button
|
|
variant="secondary"
|
|
onclick={submit}
|
|
disabled={rid.trim() === ""}>
|
|
<Icon name="seed" />
|
|
Seed
|
|
</Button>
|
|
</div>
|
|
{#if validationMessage}
|
|
<div
|
|
class="txt-body-m-regular global-flex"
|
|
style:color="var(--color-feedback-error-text)"
|
|
style:padding="0.25rem 0 0 0.25rem"
|
|
style:gap="0.25rem">
|
|
<Icon name="warning" />
|
|
{validationMessage}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="global-flex txt-missing"
|
|
style:align-items="flex-start"
|
|
style:margin-top="2rem">
|
|
By seeding a repository, your node fetches it from the network,
|
|
allowing you to interact with it locally while also making it
|
|
available to others.
|
|
</div>
|
|
{#if !$nodeRunning}
|
|
<div
|
|
class="global-flex txt-missing"
|
|
style:align-items="flex-start"
|
|
style:margin-top="1rem">
|
|
<div>
|
|
Your node is Offline. You can still add repos, but they will only
|
|
be fetched once your node is back online.
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
{:else if tab.value === "publish"}
|
|
<p style="margin: 0 0 1rem 0" style:color="var(--color-text-primary)">
|
|
Navigate to an existing Git repo in your terminal
|
|
<code
|
|
style:white-space="nowrap"
|
|
style:padding="0.125rem 0.25rem"
|
|
style:background-color="var(--color-surface-subtle)">
|
|
cd path/to/your/repo
|
|
</code>
|
|
and run the following command:
|
|
</p>
|
|
|
|
<Command styleWidth="fit-content" command="rad init" />
|
|
|
|
<p style="margin: 1rem 0 0 0" style:color="var(--color-text-primary)">
|
|
Follow the setup prompts to initialize the repo and publish it on the
|
|
Radicle network:
|
|
</p>
|
|
|
|
<ul style:padding="0 1rem">
|
|
<li>
|
|
<strong>Repository Name:</strong>
|
|
The name of your repo.
|
|
</li>
|
|
<li>
|
|
<strong>Description:</strong>
|
|
A brief summary of what your repo does.
|
|
</li>
|
|
<!-- prettier-ignore -->
|
|
<li>
|
|
<strong>Default Branch:</strong>
|
|
Typically
|
|
<strong>main</strong>
|
|
or
|
|
<strong>master</strong>.
|
|
</li>
|
|
<li>
|
|
<strong>Visibility:</strong>
|
|
Choose
|
|
<strong>public</strong>
|
|
to share with others or
|
|
<strong>private</strong>
|
|
to not publish it to the network yet.
|
|
</li>
|
|
</ul>
|
|
<p use:twemoji style:margin="2rem 0 0 0">
|
|
That's it! Your repo is now on the Radicle network.
|
|
</p>
|
|
{/if}
|
|
</div>
|
|
{/snippet}
|
|
</Popover>
|