mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-09 05:30:28 +08:00
Navigate seed nodes from explore section
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let popoverContainerMinWidth: string | undefined = undefined;
|
||||
export let popoverBorderRadius: string | undefined = undefined;
|
||||
export let popoverPadding: string | undefined = undefined;
|
||||
export let popoverPositionBottom: string | undefined = undefined;
|
||||
@@ -53,7 +54,10 @@
|
||||
|
||||
<svelte:window on:click={clickOutside} on:touchstart={clickOutside} />
|
||||
|
||||
<div bind:this={thisComponent} class="container">
|
||||
<div
|
||||
bind:this={thisComponent}
|
||||
class="container"
|
||||
style:min-width={popoverContainerMinWidth}>
|
||||
<slot name="toggle" {expanded} {toggle} />
|
||||
|
||||
{#if expanded}
|
||||
|
||||
@@ -154,7 +154,9 @@
|
||||
|
||||
<div class="right-container" bind:clientWidth={rightContainerWidth}>
|
||||
{#if loading}
|
||||
<Loading small noDelay />
|
||||
<div style:padding-right="0.5rem">
|
||||
<Loading small noDelay />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if valid && !loading && isFocused && showKeyHint}
|
||||
|
||||
+19
-65
@@ -1,73 +1,48 @@
|
||||
import type { BaseUrl } from "@httpd-client";
|
||||
|
||||
import storedWritable from "@efstajas/svelte-stored-writable";
|
||||
import { writable, derived, get } from "svelte/store";
|
||||
import { number, string, object } from "zod";
|
||||
import unionBy from "lodash/unionBy";
|
||||
import { array, number, string, object } from "zod";
|
||||
import { derived } from "svelte/store";
|
||||
|
||||
import { api, httpdStore, type HttpdState } from "./httpd";
|
||||
import config from "virtual:config";
|
||||
|
||||
const preferredSeedSchema = object({
|
||||
hostname: string(),
|
||||
port: number(),
|
||||
scheme: string(),
|
||||
}).optional();
|
||||
});
|
||||
|
||||
const configuredPreferredSeeds = writable<BaseUrl[] | undefined>(undefined);
|
||||
export const configuredPreferredSeeds = storedWritable<BaseUrl[]>(
|
||||
"configuredPreferredSeeds",
|
||||
array(preferredSeedSchema),
|
||||
[],
|
||||
);
|
||||
const storedPreferredSeed = storedWritable<BaseUrl | undefined>(
|
||||
"preferredSeed",
|
||||
preferredSeedSchema,
|
||||
undefined,
|
||||
);
|
||||
|
||||
async function loadConfiguredPreferredSeeds() {
|
||||
if (get(httpdStore).state === "stopped") {
|
||||
configuredPreferredSeeds.set([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const profile = await api.profile.getProfile();
|
||||
|
||||
let newValue = profile.config.preferredSeeds.map(seed => {
|
||||
const preferredSeedValue = seed?.split("@")[1];
|
||||
const preferredSeedOrigin = preferredSeedValue?.split(":")[0];
|
||||
|
||||
return {
|
||||
hostname: preferredSeedOrigin,
|
||||
port: 443,
|
||||
scheme: "https",
|
||||
};
|
||||
});
|
||||
|
||||
if (newValue.length === 0) {
|
||||
newValue = [config.fallbackPreferredSeed];
|
||||
}
|
||||
|
||||
configuredPreferredSeeds.set(newValue);
|
||||
}
|
||||
|
||||
export function initialize() {
|
||||
let previousHttpdState: HttpdState["state"] | undefined;
|
||||
|
||||
httpdStore.subscribe(async v => {
|
||||
if (previousHttpdState === v.state) return;
|
||||
|
||||
await loadConfiguredPreferredSeeds();
|
||||
|
||||
previousHttpdState = v.state;
|
||||
});
|
||||
export function addSeedsToConfiguredSeeds(newSeeds: BaseUrl[]) {
|
||||
configuredPreferredSeeds.update(seeds =>
|
||||
unionBy(seeds, newSeeds, "hostname"),
|
||||
);
|
||||
}
|
||||
|
||||
export function selectPreferredSeed(seed: BaseUrl) {
|
||||
storedPreferredSeed.set(seed);
|
||||
}
|
||||
|
||||
export function removeSeedFromConfiguredSeeds(seedHostname: string) {
|
||||
configuredPreferredSeeds.update(
|
||||
seeds => seeds.filter(s => s.hostname !== seedHostname) ?? seeds,
|
||||
);
|
||||
}
|
||||
|
||||
export const preferredSeeds = derived(
|
||||
[configuredPreferredSeeds, storedPreferredSeed],
|
||||
([configuredPreferredSeeds, storedPreferredSeed]) => {
|
||||
// Not loaded yet
|
||||
if (!configuredPreferredSeeds) return undefined;
|
||||
|
||||
// No configured preferred seeds
|
||||
if (configuredPreferredSeeds.length === 0)
|
||||
return {
|
||||
@@ -99,24 +74,3 @@ export const preferredSeeds = derived(
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export async function waitForLoad(): Promise<{
|
||||
selected: BaseUrl;
|
||||
seeds: BaseUrl[];
|
||||
}> {
|
||||
if (!get(configuredPreferredSeeds)) {
|
||||
await new Promise<void>(resolve => {
|
||||
const unsubscribe = preferredSeeds.subscribe(v => {
|
||||
if (v) {
|
||||
unsubscribe();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const seeds = get(preferredSeeds);
|
||||
if (!seeds) throw new Error("Preferred seed undefined after loading");
|
||||
|
||||
return seeds;
|
||||
}
|
||||
|
||||
+18
-38
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { ComponentProps } from "svelte";
|
||||
import type { ProjectInfo } from "@app/components/ProjectCard";
|
||||
import type { ProjectListQuery } from "@httpd-client";
|
||||
import type { BaseUrl, ProjectListQuery } from "@httpd-client";
|
||||
|
||||
import storedWritable from "@efstajas/svelte-stored-writable";
|
||||
import { derived } from "svelte/store";
|
||||
@@ -23,11 +23,13 @@
|
||||
import ErrorMessage from "@app/components/ErrorMessage.svelte";
|
||||
import FilterButton from "./components/FilterButton.svelte";
|
||||
import HomepageSection from "./components/HomepageSection.svelte";
|
||||
import IconButton from "@app/components/IconButton.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import NewProjectButton from "./components/NewProjectButton.svelte";
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
import PreferredSeedDropdown from "./components/PreferredSeedDropdown.svelte";
|
||||
import IconButton from "@app/components/IconButton.svelte";
|
||||
|
||||
export let configPreferredSeeds: BaseUrl[];
|
||||
|
||||
const selectedSeed = deduplicateStore(
|
||||
derived(preferredSeeds, $ => $?.selected),
|
||||
@@ -120,33 +122,22 @@
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: var(--font-weight-regular);
|
||||
}
|
||||
.flex-icon-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.project-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
.seed {
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.125rem;
|
||||
color: var(--color-foreground-contrast);
|
||||
}
|
||||
.seed-name {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 719.98px) {
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.seed-dropdown {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -216,18 +207,16 @@
|
||||
empty={preferredSeedProjects instanceof Error ||
|
||||
preferredSeedProjects?.length === 0}
|
||||
title="Explore">
|
||||
<svelte:fragment slot="title">
|
||||
<div class="flex-icon-item" style:min-width="0">
|
||||
<span class="txt-large">Explore</span>
|
||||
<PreferredSeedDropdown
|
||||
initialPreferredSeeds={configPreferredSeeds}
|
||||
selectedSeed={$preferredSeeds.selected} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
{#if nodeId && $preferredSeeds}
|
||||
Pinned repositories on your selected seed node
|
||||
{:else}
|
||||
Pinned repositories on
|
||||
<div class="seed">
|
||||
<IconSmall name="seedling" />
|
||||
<span class="seed-name">
|
||||
{$selectedSeed?.hostname}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
Pinned repositories on your selected seed node
|
||||
{#if !nodeId}
|
||||
<div class="global-hide-on-mobile-down">
|
||||
<Popover popoverPositionTop="2.5rem" popoverPositionLeft="0">
|
||||
@@ -247,15 +236,6 @@
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<div class="seed-dropdown">
|
||||
{#if nodeId && $preferredSeeds}
|
||||
<PreferredSeedDropdown
|
||||
disabled={!nodeId || preferredSeedProjects === undefined}
|
||||
preferredSeed={$preferredSeeds?.selected} />
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="empty">
|
||||
<div class="empty-state">
|
||||
{#if preferredSeedProjects instanceof Error}
|
||||
|
||||
@@ -3,16 +3,11 @@
|
||||
|
||||
export let title: string;
|
||||
export let loading = false;
|
||||
|
||||
export let empty: boolean = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.section-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.title {
|
||||
@@ -62,7 +57,9 @@
|
||||
<section>
|
||||
<div class="section-header">
|
||||
<div class="title">
|
||||
<h2>{title}</h2>
|
||||
<slot name="title">
|
||||
<h2>{title}</h2>
|
||||
</slot>
|
||||
<div class="actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
|
||||
@@ -1,42 +1,78 @@
|
||||
<script lang="ts">
|
||||
import type { BaseUrl } from "@httpd-client";
|
||||
import { HttpdClient, type BaseUrl } from "@httpd-client";
|
||||
|
||||
import config from "virtual:config";
|
||||
import {
|
||||
addSeedsToConfiguredSeeds,
|
||||
configuredPreferredSeeds,
|
||||
preferredSeeds as preferredSeedsStore,
|
||||
removeSeedFromConfiguredSeeds,
|
||||
selectPreferredSeed,
|
||||
} from "@app/lib/seeds";
|
||||
import { closeFocused } from "@app/components/Popover.svelte";
|
||||
import { httpdStore } from "@app/lib/httpd";
|
||||
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
import Button from "@app/components/Button.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Command from "@app/components/Command.svelte";
|
||||
import DropdownList from "@app/components/DropdownList.svelte";
|
||||
import DropdownListItem from "@app/components/DropdownList/DropdownListItem.svelte";
|
||||
import Command from "@app/components/Command.svelte";
|
||||
import IconButton from "@app/components/IconButton.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
import TextInput from "@app/components/TextInput.svelte";
|
||||
|
||||
export let preferredSeed: BaseUrl;
|
||||
export let disabled = false;
|
||||
export let initialPreferredSeeds: BaseUrl[];
|
||||
export let selectedSeed: BaseUrl;
|
||||
|
||||
$: stateOptions = $preferredSeedsStore?.seeds;
|
||||
const validateInput = async (seed: BaseUrl) => {
|
||||
if (stateOptions.find(s => s.hostname === seed.hostname)) {
|
||||
validationMessage = "Seed node already added.";
|
||||
return false;
|
||||
}
|
||||
const api = new HttpdClient(seed);
|
||||
try {
|
||||
await api.getNode();
|
||||
return true;
|
||||
} catch (e) {
|
||||
validationMessage = "Seed node isn't reachable";
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Reset state if inputValue changes
|
||||
$: {
|
||||
customSeed;
|
||||
submittingInput = false;
|
||||
validationMessage = undefined;
|
||||
valid = true;
|
||||
}
|
||||
$: stateOptions = $preferredSeedsStore.seeds;
|
||||
let valid = true;
|
||||
let submittingInput = false;
|
||||
let validationMessage: undefined | string = undefined;
|
||||
let customSeed: string = "";
|
||||
let expanded = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.popover {
|
||||
width: 16rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
.validation-message {
|
||||
color: var(--color-foreground-red);
|
||||
margin-top: 0.5rem;
|
||||
margin-left: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.divider {
|
||||
@@ -53,53 +89,127 @@
|
||||
padding: 0.5rem;
|
||||
color: var(--color-foreground-dim);
|
||||
}
|
||||
.icon-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<Popover
|
||||
bind:expanded
|
||||
popoverContainerMinWidth="0"
|
||||
popoverPositionTop="2.5rem"
|
||||
popoverPositionLeft="-0.25rem"
|
||||
popoverPadding="0.25rem"
|
||||
popoverBorderRadius="var(--border-radius-small)">
|
||||
<Button
|
||||
variant="outline"
|
||||
<div
|
||||
class="icon-item"
|
||||
slot="toggle"
|
||||
let:toggle
|
||||
on:click={toggle}
|
||||
title="Change peer"
|
||||
{disabled}>
|
||||
<IconSmall name="seedling" />
|
||||
{preferredSeed.hostname}
|
||||
<IconSmall name={expanded ? "chevron-up" : "chevron-down"} />
|
||||
</Button>
|
||||
title="Switch preferred seeds"
|
||||
let:toggle>
|
||||
<div class="txt-large txt-bold txt-overflow">{selectedSeed.hostname}</div>
|
||||
<IconButton on:click={toggle}>
|
||||
<IconSmall name={expanded ? "chevron-up" : "chevron-down"} />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="popover">
|
||||
<div class="popover">
|
||||
{#if stateOptions}
|
||||
<DropdownList items={stateOptions}>
|
||||
<DropdownListItem
|
||||
let:item
|
||||
on:click={() => {
|
||||
selectPreferredSeed(item);
|
||||
closeFocused();
|
||||
}}
|
||||
slot="item"
|
||||
selected={item.hostname === preferredSeed.hostname}>
|
||||
<div class="label">
|
||||
<IconSmall name="seedling" />
|
||||
{item.hostname}
|
||||
</div>
|
||||
</DropdownListItem>
|
||||
</DropdownList>
|
||||
<div style:width="16rem">
|
||||
<TextInput
|
||||
{valid}
|
||||
name="seed"
|
||||
bind:value={customSeed}
|
||||
loading={submittingInput}
|
||||
placeholder="Navigate to seed URL"
|
||||
on:submit={async () => {
|
||||
submittingInput = true;
|
||||
const customSeedBaseUrl = {
|
||||
hostname: customSeed,
|
||||
port: config.nodes.defaultHttpdPort,
|
||||
scheme: config.nodes.defaultHttpdScheme,
|
||||
};
|
||||
valid = await validateInput(customSeedBaseUrl);
|
||||
if (valid) {
|
||||
addSeedsToConfiguredSeeds(
|
||||
$configuredPreferredSeeds.length === 0
|
||||
? [customSeedBaseUrl, config.fallbackPreferredSeed]
|
||||
: [customSeedBaseUrl],
|
||||
);
|
||||
selectPreferredSeed(customSeedBaseUrl);
|
||||
customSeed = "";
|
||||
closeFocused();
|
||||
} else {
|
||||
submittingInput = false;
|
||||
}
|
||||
}} />
|
||||
{#if validationMessage}
|
||||
<span class="validation-message txt-small">{validationMessage}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="divider" />
|
||||
<div class="add-seed-node-instructions txt-small">
|
||||
<div class="" style:font-weight="bold">Add a different seed node</div>
|
||||
<div class="">
|
||||
Update preferred seeds in your Radicle config and restart httpd.
|
||||
<div class="divider" />
|
||||
<div class="popover">
|
||||
{#if stateOptions}
|
||||
<DropdownList items={stateOptions}>
|
||||
<DropdownListItem
|
||||
let:item
|
||||
on:click={() => {
|
||||
selectPreferredSeed(item);
|
||||
closeFocused();
|
||||
}}
|
||||
slot="item"
|
||||
selected={item.hostname === selectedSeed.hostname}>
|
||||
<div class="dropdown-item">
|
||||
<div class="icon-item" style:min-width="0">
|
||||
<IconSmall name="seedling" />
|
||||
<div class="txt-overflow">
|
||||
{item.hostname}
|
||||
</div>
|
||||
</div>
|
||||
{#if stateOptions && stateOptions.length > 1}
|
||||
<IconButton
|
||||
on:click={() => {
|
||||
removeSeedFromConfiguredSeeds(item.hostname);
|
||||
selectPreferredSeed(config.fallbackPreferredSeed);
|
||||
}}>
|
||||
<IconSmall name="cross" />
|
||||
</IconButton>
|
||||
{/if}
|
||||
</div>
|
||||
</DropdownListItem>
|
||||
<DropdownListItem
|
||||
on:click={() => {
|
||||
selectPreferredSeed(config.fallbackPreferredSeed);
|
||||
closeFocused();
|
||||
}}
|
||||
slot="empty"
|
||||
selected>
|
||||
<div class="dropdown-item">
|
||||
<div class="icon-item" style:min-width="0">
|
||||
<IconSmall name="seedling" />
|
||||
<div class="txt-overflow">
|
||||
{config.fallbackPreferredSeed.hostname}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownListItem>
|
||||
</DropdownList>
|
||||
{/if}
|
||||
</div>
|
||||
<Command fullWidth command="rad config edit" />
|
||||
{#if $httpdStore.state !== "stopped" && !initialPreferredSeeds.find(s => s.hostname === selectedSeed.hostname)}
|
||||
<div class="divider" />
|
||||
<div class="add-seed-node-instructions txt-small">
|
||||
<div class="txt-bold">Store in config</div>
|
||||
<div>
|
||||
Add <code style:word-break="break-all">
|
||||
{selectedSeed.hostname}
|
||||
</code>
|
||||
to your
|
||||
<code>preferredSeeds</code>
|
||||
in your Radicle config and restart httpd.
|
||||
</div>
|
||||
<Command fullWidth command="rad config edit" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { BaseUrl } from "@httpd-client";
|
||||
import type { ErrorRoute } from "@app/lib/router/definitions";
|
||||
|
||||
import * as seeds from "@app/lib/seeds";
|
||||
import config from "virtual:config";
|
||||
import { api, httpdStore } from "@app/lib/httpd";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
export interface HomeRoute {
|
||||
resource: "home";
|
||||
@@ -8,15 +12,34 @@ export interface HomeRoute {
|
||||
|
||||
export interface HomeLoadedRoute {
|
||||
resource: "home";
|
||||
params: Record<string, never>;
|
||||
params: { configPreferredSeeds: BaseUrl[] };
|
||||
}
|
||||
|
||||
export async function loadHomeRoute(): Promise<HomeLoadedRoute | ErrorRoute> {
|
||||
seeds.initialize();
|
||||
await seeds.waitForLoad();
|
||||
if (get(httpdStore).state !== "stopped") {
|
||||
const profile = await api.profile.getProfile();
|
||||
const newValue = profile.config.preferredSeeds.map(seed => {
|
||||
const preferredSeedValue = seed?.split("@")[1];
|
||||
const preferredSeedOrigin = preferredSeedValue?.split(":")[0];
|
||||
|
||||
return {
|
||||
hostname: preferredSeedOrigin,
|
||||
port: config.nodes.defaultHttpdPort,
|
||||
scheme: config.nodes.defaultHttpdScheme,
|
||||
};
|
||||
});
|
||||
if (get(seeds.configuredPreferredSeeds).length === 0) {
|
||||
seeds.addSeedsToConfiguredSeeds(newValue);
|
||||
}
|
||||
|
||||
return {
|
||||
resource: "home",
|
||||
params: { configPreferredSeeds: newValue },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
resource: "home",
|
||||
params: {},
|
||||
params: { configPreferredSeeds: [] },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,3 +13,127 @@ test("show pinned projects", async ({ page }) => {
|
||||
page.getByText("Git repository for source browsing tests"),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("no duplicate entry for preferred seeds", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page.getByText("seed.radicle.garden")).toBeVisible();
|
||||
|
||||
await page.getByTitle("Switch preferred seeds").getByRole("button").click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "seed.radicle.garden" }),
|
||||
).toBeVisible();
|
||||
|
||||
await page
|
||||
.getByPlaceholder("Navigate to seed URL")
|
||||
.fill("seed.radicle.garden");
|
||||
await page.getByPlaceholder("Navigate to seed URL").press("Enter");
|
||||
await expect(page.getByText("Seed node already added.")).toBeVisible();
|
||||
|
||||
await page.getByPlaceholder("Navigate to seed URL").fill("");
|
||||
await expect(page.getByText("Seed node already added.")).toBeHidden();
|
||||
});
|
||||
|
||||
test("adding and removing a new preferred seed", async ({ page }) => {
|
||||
await page.route(
|
||||
({ hostname }) => hostname === "seed.rhizoma.dev",
|
||||
route => route.fulfill({ json: nodeInfo }),
|
||||
);
|
||||
|
||||
await page.goto("/");
|
||||
await expect(page.getByText("seed.radicle.garden")).toBeVisible();
|
||||
|
||||
await page.getByTitle("Switch preferred seeds").getByRole("button").click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "seed.radicle.garden" }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByPlaceholder("Navigate to seed URL").fill("seed.rhizoma.dev");
|
||||
await page.getByPlaceholder("Navigate to seed URL").press("Enter");
|
||||
await expect(page.getByText("seed.rhizoma.dev")).toBeVisible();
|
||||
|
||||
await page.getByTitle("Switch preferred seeds").getByRole("button").click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "seed.rhizoma.dev" }),
|
||||
).toBeVisible();
|
||||
|
||||
// Test that removing the selected seed doesn't end in an undefined state.
|
||||
await page
|
||||
.getByRole("button", { name: "seed.rhizoma.dev" })
|
||||
.getByRole("button")
|
||||
.click();
|
||||
await expect(page.getByText("seed.radicle.garden")).toBeVisible();
|
||||
|
||||
await page.getByTitle("Switch preferred seeds").getByRole("button").click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "seed.rhizoma.dev" }),
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
test("stored custom preferred seeds in local storage", async ({ page }) => {
|
||||
await page.addInitScript(() =>
|
||||
localStorage.setItem(
|
||||
"configuredPreferredSeeds",
|
||||
'[{"hostname":"seed.radicle.xyz","port":443,"scheme":"https"},{"hostname":"seed.rhizoma.dev","port":443,"scheme":"https"}]',
|
||||
),
|
||||
);
|
||||
await page.goto("/");
|
||||
await expect(page.getByText("seed.radicle.xyz")).toBeVisible();
|
||||
|
||||
await page.getByTitle("Switch preferred seeds").getByRole("button").click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "seed.radicle.xyz" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "seed.rhizoma.dev" }),
|
||||
).toBeVisible();
|
||||
// Check that the fallback node hasn't been added on load.
|
||||
await expect(
|
||||
page.getByRole("button", { name: "seed.radicle.garden" }),
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
const nodeInfo = {
|
||||
id: "z6MkkGfMNQmjrp66Po2n4snzcSyTFRFw1m1fbYhCURxLxZpD",
|
||||
version: "1.0.0-rc.9-d56d619f",
|
||||
config: {
|
||||
alias: "rhizoma",
|
||||
listen: [],
|
||||
peers: {
|
||||
type: "dynamic",
|
||||
target: 0,
|
||||
},
|
||||
connect: [],
|
||||
externalAddresses: ["seed.rhizoma.dev:8776"],
|
||||
db: {
|
||||
journalMode: "wal",
|
||||
},
|
||||
network: "main",
|
||||
log: "INFO",
|
||||
relay: "auto",
|
||||
limits: {
|
||||
routingMaxSize: 1000,
|
||||
routingMaxAge: 604800,
|
||||
gossipMaxAge: 1209600,
|
||||
fetchConcurrency: 1,
|
||||
maxOpenFiles: 4096,
|
||||
rate: {
|
||||
inbound: {
|
||||
fillRate: 2,
|
||||
capacity: 128,
|
||||
},
|
||||
outbound: {
|
||||
fillRate: 5,
|
||||
capacity: 256,
|
||||
},
|
||||
},
|
||||
connection: {
|
||||
inbound: 128,
|
||||
outbound: 16,
|
||||
},
|
||||
},
|
||||
workers: 32,
|
||||
policy: "block",
|
||||
scope: "all",
|
||||
},
|
||||
state: "running",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user