mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-03 18:50:30 +08:00
Add experimental switch in settings
Hide all read/write functionality behind this setting.
This commit is contained in:
+21
-18
@@ -11,6 +11,7 @@
|
||||
import NodeInfo from "@app/App/Header/NodeInfo.svelte";
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
import ConnectInstructions from "@app/components/ConnectInstructions.svelte";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
|
||||
const buttonTitle: Record<HttpdState["state"], string> = {
|
||||
stopped: "radicle-httpd is stopped",
|
||||
@@ -60,24 +61,26 @@
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
{#if $httpdStore.state === "stopped"}
|
||||
<Popover popoverPositionTop="3rem" popoverPositionRight="0">
|
||||
<Button
|
||||
slot="toggle"
|
||||
let:toggle
|
||||
on:click={toggle}
|
||||
title={buttonTitle[$httpdStore.state]}
|
||||
variant="naked-toggle">
|
||||
<IconSmall name="device" />
|
||||
Connect
|
||||
</Button>
|
||||
<div slot="popover" class="connect-popover">
|
||||
<ConnectInstructions />
|
||||
</div>
|
||||
</Popover>
|
||||
{:else}
|
||||
<NodeInfo node={$httpdStore.node} />
|
||||
<Authenticate />
|
||||
{#if $experimental}
|
||||
{#if $httpdStore.state === "stopped"}
|
||||
<Popover popoverPositionTop="3rem" popoverPositionRight="0">
|
||||
<Button
|
||||
slot="toggle"
|
||||
let:toggle
|
||||
on:click={toggle}
|
||||
title={buttonTitle[$httpdStore.state]}
|
||||
variant="naked-toggle">
|
||||
<IconSmall name="device" />
|
||||
Connect
|
||||
</Button>
|
||||
<div slot="popover" class="connect-popover">
|
||||
<ConnectInstructions />
|
||||
</div>
|
||||
</Popover>
|
||||
{:else}
|
||||
<NodeInfo node={$httpdStore.node} />
|
||||
<Authenticate />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
+28
-1
@@ -2,7 +2,9 @@
|
||||
import {
|
||||
codeFont,
|
||||
codeFonts,
|
||||
experimental,
|
||||
storeCodeFont,
|
||||
storeExperimental,
|
||||
storeTheme,
|
||||
theme,
|
||||
} from "@app/lib/appearance";
|
||||
@@ -14,7 +16,7 @@
|
||||
|
||||
<style>
|
||||
.settings {
|
||||
width: 18.5rem;
|
||||
width: 24rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -76,4 +78,29 @@
|
||||
</Radio>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div
|
||||
style="display: flex; flex-direction: row; align-items: center; gap: 0.5rem;">
|
||||
Make changes on the web (experimental)
|
||||
</div>
|
||||
<div class="right">
|
||||
<Radio>
|
||||
<Button
|
||||
styleBorderRadius="0"
|
||||
on:click={() => storeExperimental(true)}
|
||||
variant={$experimental ? "selected" : "not-selected"}>
|
||||
On
|
||||
</Button>
|
||||
<div class="global-spacer" />
|
||||
<Radio>
|
||||
<Button
|
||||
styleBorderRadius="0"
|
||||
on:click={() => storeExperimental(undefined)}
|
||||
variant={$experimental ? "not-selected" : "selected"}>
|
||||
Off
|
||||
</Button>
|
||||
</Radio>
|
||||
</Radio>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<script>
|
||||
import Command from "./Command.svelte";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import { api, httpdStore } from "@app/lib/httpd";
|
||||
import { routeToPath, activeUnloadedRouteStore } from "@app/lib/router";
|
||||
|
||||
import Command from "@app/components/Command.svelte";
|
||||
import ExternalLink from "./ExternalLink.svelte";
|
||||
|
||||
$: path = routeToPath($activeUnloadedRouteStore);
|
||||
$: pathParam = path === "/" ? "" : `--path "${path}"`;
|
||||
</script>
|
||||
@@ -30,25 +33,41 @@
|
||||
</style>
|
||||
|
||||
<div>
|
||||
{#if $httpdStore.state === "running"}
|
||||
<div class="label">Authenticate with your local node to make changes.</div>
|
||||
<Command
|
||||
fullWidth
|
||||
command={`rad web ${window.origin} --connect ${api.hostname}:${api.port} ${pathParam}`} />
|
||||
{#if $experimental}
|
||||
{#if $httpdStore.state === "running"}
|
||||
<div class="label">
|
||||
Authenticate with your local node to make changes.
|
||||
</div>
|
||||
<Command
|
||||
fullWidth
|
||||
command={`rad web ${window.origin} --connect ${api.hostname}:${api.port} ${pathParam}`} />
|
||||
{:else}
|
||||
<div class="heading">Connect & Authenticate</div>
|
||||
<div class="label">
|
||||
Connect to your local node to browse projects on your local machine,
|
||||
create issues, and participate in discussions.
|
||||
</div>
|
||||
<Command fullWidth command={`rad web ${window.origin} ${pathParam}`} />
|
||||
|
||||
<div class="divider" />
|
||||
<div class="heading">New to Radicle?</div>
|
||||
<div class="label">
|
||||
Visit <ExternalLink href="https://radicle.xyz/#try" /> to download Radicle
|
||||
and get started.
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="heading">Connect & Authenticate</div>
|
||||
<div class="heading">Browse your local projects</div>
|
||||
<div class="label">
|
||||
Connect to your local node to browse projects on your local machine,
|
||||
create issues, and participate in discussions.
|
||||
To browse projects on your local node, run the following command.
|
||||
</div>
|
||||
<Command fullWidth command={`rad web ${window.origin} ${pathParam}`} />
|
||||
<Command fullWidth command="radicle-httpd" />
|
||||
|
||||
<div class="divider" />
|
||||
<div class="heading">New to Radicle?</div>
|
||||
<div class="label">
|
||||
Run the following command and follow the instructions to install Radicle
|
||||
Visit <ExternalLink href="https://radicle.xyz/#try" /> to download Radicle
|
||||
and get started.
|
||||
</div>
|
||||
<Command fullWidth command="curl -sSf https://radicle.xyz/install | sh" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -25,16 +25,23 @@
|
||||
|
||||
<div class="reactions">
|
||||
{#each reactions as { emoji, authors }}
|
||||
<IconButton
|
||||
on:click={async () => {
|
||||
if (handleReaction) {
|
||||
await handleReaction(authors, emoji);
|
||||
}
|
||||
}}>
|
||||
<div class="reaction txt-tiny">
|
||||
{#if handleReaction}
|
||||
<IconButton
|
||||
on:click={async () => {
|
||||
if (handleReaction) {
|
||||
await handleReaction(authors, emoji);
|
||||
}
|
||||
}}>
|
||||
<div class="reaction txt-tiny">
|
||||
<span>{emoji}</span>
|
||||
<span title={authors.join("\n")}>{authors.length}</span>
|
||||
</div>
|
||||
</IconButton>
|
||||
{:else}
|
||||
<div class="reaction txt-tiny" style="padding: 2px 4px;">
|
||||
<span>{emoji}</span>
|
||||
<span title={authors.join("\n")}>{authors.length}</span>
|
||||
</div>
|
||||
</IconButton>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -48,3 +48,25 @@ export function storeCodeFont(newCodeFont: CodeFont): void {
|
||||
codeFont.set(newCodeFont);
|
||||
window.localStorage.setItem("codefont", newCodeFont);
|
||||
}
|
||||
|
||||
export const experimental = writable<true | undefined>(
|
||||
loadExperimentalSetting(),
|
||||
);
|
||||
|
||||
function loadExperimentalSetting(): true | undefined {
|
||||
const storedExperimental = window.localStorage.getItem("experimental");
|
||||
|
||||
if (storedExperimental === null) {
|
||||
return undefined;
|
||||
} else {
|
||||
return storedExperimental === "true" ? true : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function storeExperimental(newSetting: true | undefined): void {
|
||||
experimental.set(newSetting);
|
||||
window.localStorage.setItem(
|
||||
"experimental",
|
||||
newSetting === true ? "true" : "undefined",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { withTimeout, Mutex, E_CANCELED, E_TIMEOUT } from "async-mutex";
|
||||
import { HttpdClient } from "@httpd-client";
|
||||
import { config } from "@app/lib/config";
|
||||
import { deduplicateStore } from "@app/lib/deduplicateStore";
|
||||
import { experimental } from "./appearance";
|
||||
|
||||
export interface Session {
|
||||
id: string;
|
||||
@@ -128,6 +129,15 @@ async function checkState() {
|
||||
try {
|
||||
const node = await api.getNode();
|
||||
|
||||
// Return quickly and avoid additional fetches
|
||||
// if experimental settings aren't updated
|
||||
if (!get(experimental)) {
|
||||
update({
|
||||
state: "running",
|
||||
node,
|
||||
});
|
||||
}
|
||||
|
||||
if (httpdState && httpdState.state !== "stopped") {
|
||||
httpdState.node = node;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { parseNodeId } from "@app/lib/utils";
|
||||
import { get } from "svelte/store";
|
||||
import { experimental } from "./appearance";
|
||||
|
||||
export function isDelegate(
|
||||
publicKey: string | undefined,
|
||||
@@ -27,10 +29,13 @@ function matchAuthor(
|
||||
// All restricted actions are a combination of either:
|
||||
// - the user is a delegate
|
||||
// - the user is an author of the comment, issue, patch, etc.
|
||||
//
|
||||
// If the experimental setting isn't turned on, we return undefined early.
|
||||
export function isDelegateOrAuthor(
|
||||
publicKey: string | undefined,
|
||||
delegates: string[],
|
||||
author: string,
|
||||
) {
|
||||
if (get(experimental) === undefined) return undefined;
|
||||
return isDelegate(publicKey, delegates) || matchAuthor(publicKey, author);
|
||||
}
|
||||
|
||||
+59
-47
@@ -6,8 +6,9 @@
|
||||
import { literal, union } from "zod";
|
||||
|
||||
import { api, httpdStore } from "@app/lib/httpd";
|
||||
import { deduplicateStore } from "@app/lib/deduplicateStore";
|
||||
import { baseUrlToString } from "@app/lib/utils";
|
||||
import { deduplicateStore } from "@app/lib/deduplicateStore";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import { handleError } from "@app/views/home/error";
|
||||
import { isDelegate } from "@app/lib/roles";
|
||||
import { preferredSeeds } from "@app/lib/seeds";
|
||||
@@ -127,54 +128,65 @@
|
||||
|
||||
<AppLayout>
|
||||
<div class="wrapper">
|
||||
<div class="global-hide-on-mobile">
|
||||
<HomepageSection
|
||||
loading={$httpdStore.state !== "stopped" && localProjects === undefined}
|
||||
empty={$httpdStore.state === "stopped" ||
|
||||
(filteredLocalProjects instanceof Array &&
|
||||
!filteredLocalProjects.length) ||
|
||||
localProjects instanceof Error}
|
||||
title="Local projects"
|
||||
subtitle="Projects you're seeding with your local node">
|
||||
<svelte:fragment slot="actions">
|
||||
<FilterButton disabled={!nodeId} bind:value={$localProjectsFilter} />
|
||||
<NewProjectButton disabled={!nodeId} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="empty">
|
||||
<div class="empty-state">
|
||||
{#if !nodeId}
|
||||
<div style="text-align: left; width: 100%;">
|
||||
<ConnectInstructions />
|
||||
</div>
|
||||
{:else if localProjects instanceof Error}
|
||||
<ErrorMessage
|
||||
{...handleError(localProjects, baseUrlToString(api.baseUrl))} />
|
||||
{:else if !localProjects?.length}
|
||||
<div class="heading">No local projects</div>
|
||||
<div class="label">
|
||||
Seed or check out a project to work with it on your local node.
|
||||
</div>
|
||||
{:else}
|
||||
<div class="heading">Nothing to see here</div>
|
||||
<div class="label">
|
||||
No local projects matched your filter settings.
|
||||
</div>
|
||||
{#if $experimental}
|
||||
<div class="global-hide-on-mobile">
|
||||
<HomepageSection
|
||||
loading={$httpdStore.state !== "stopped" &&
|
||||
localProjects === undefined}
|
||||
empty={$httpdStore.state === "stopped" ||
|
||||
(filteredLocalProjects instanceof Array &&
|
||||
!filteredLocalProjects.length) ||
|
||||
localProjects instanceof Error}
|
||||
title="Local projects"
|
||||
subtitle="Projects you're seeding with your local node">
|
||||
<svelte:fragment slot="actions">
|
||||
<FilterButton
|
||||
disabled={!nodeId}
|
||||
bind:value={$localProjectsFilter} />
|
||||
<NewProjectButton disabled={!nodeId} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="empty">
|
||||
<div class="empty-state">
|
||||
{#if !nodeId}
|
||||
<div style="text-align: left; width: 100%;">
|
||||
<ConnectInstructions />
|
||||
</div>
|
||||
{:else if localProjects instanceof Error}
|
||||
<ErrorMessage
|
||||
{...handleError(
|
||||
localProjects,
|
||||
baseUrlToString(api.baseUrl),
|
||||
)} />
|
||||
{:else if !localProjects?.length}
|
||||
<div class="heading">No local projects</div>
|
||||
<div class="label">
|
||||
Seed or check out a project to work with it on your local
|
||||
node.
|
||||
</div>
|
||||
{:else}
|
||||
<div class="heading">Nothing to see here</div>
|
||||
<div class="label">
|
||||
No local projects matched your filter settings.
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<div class="project-grid">
|
||||
{#if filteredLocalProjects && !(filteredLocalProjects instanceof Error)}
|
||||
{#each filteredLocalProjects as projectInfo}
|
||||
<ProjectCard
|
||||
{projectInfo}
|
||||
isSeeding={true}
|
||||
isDelegate={isDelegate(
|
||||
nodeId,
|
||||
projectInfo.project.delegates,
|
||||
) ?? false} />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<div class="project-grid">
|
||||
{#if filteredLocalProjects && !(filteredLocalProjects instanceof Error)}
|
||||
{#each filteredLocalProjects as projectInfo}
|
||||
<ProjectCard
|
||||
{projectInfo}
|
||||
isSeeding={true}
|
||||
isDelegate={isDelegate(nodeId, projectInfo.project.delegates) ??
|
||||
false} />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</HomepageSection>
|
||||
</div>
|
||||
</HomepageSection>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<HomepageSection
|
||||
loading={preferredSeedProjects === undefined}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script lang="ts">
|
||||
import * as modal from "@app/lib/modal";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import { httpdStore, api } from "@app/lib/httpd";
|
||||
|
||||
import Button from "@app/components/Button.svelte";
|
||||
import Command from "@app/components/Command.svelte";
|
||||
import ErrorModal from "@app/modals/ErrorModal.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
@@ -70,63 +72,80 @@
|
||||
.counter {
|
||||
font-weight: var(--font-weight-regular);
|
||||
border-radius: var(--border-radius-tiny);
|
||||
background-color: var(--color-fill-secondary-counter);
|
||||
background-color: var(--color-fill-ghost-hover);
|
||||
border: 1px solid var(--color-border-secondary-counter);
|
||||
color: var(--color-foreground-match-background);
|
||||
color: var(--color-foreground-contrast);
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
.seeding {
|
||||
background-color: var(--color-fill-counter-emphasized);
|
||||
color: var(--color-foreground-emphasized);
|
||||
}
|
||||
.not-seeding {
|
||||
background-color: var(--color-fill-secondary-counter);
|
||||
color: var(--color-foreground-match-background);
|
||||
}
|
||||
.disabled {
|
||||
background-color: var(--color-fill-ghost-hover);
|
||||
background-color: var(--color-fill-float-hover);
|
||||
color: var(--color-foreground-disabled);
|
||||
}
|
||||
</style>
|
||||
|
||||
<Popover popoverPositionTop="2.5rem" popoverPositionRight="0">
|
||||
<Button
|
||||
disabled={!canEditSeeding}
|
||||
slot="toggle"
|
||||
disabled={$experimental ? !canEditSeeding : false}
|
||||
let:toggle
|
||||
on:click={async () => {
|
||||
if (!seeding && canEditSeeding) {
|
||||
if ($experimental && !seeding && canEditSeeding) {
|
||||
await editSeeding();
|
||||
} else {
|
||||
toggle();
|
||||
}
|
||||
}}
|
||||
variant={seeding ? "secondary-toggle-on" : "secondary-toggle-off"}>
|
||||
variant={!$experimental
|
||||
? "outline"
|
||||
: seeding
|
||||
? "secondary-toggle-on"
|
||||
: "secondary-toggle-off"}>
|
||||
<IconSmall name="network" />
|
||||
<span class="title-counter">
|
||||
{seeding ? "Seeding" : "Seed"}
|
||||
<span
|
||||
class="counter"
|
||||
class:seeding
|
||||
class:disabled={!canEditSeeding}
|
||||
class:seeding={$experimental ? seeding : false}
|
||||
class:not-seeding={$experimental ? !seeding : false}
|
||||
class:disabled={$experimental ? !canEditSeeding : false}
|
||||
style:font-weight="var(--font-weight-regular)">
|
||||
{seedCount}
|
||||
</span>
|
||||
</span>
|
||||
</Button>
|
||||
|
||||
<div slot="popover" let:toggle style:width={seeding ? "19.5rem" : "30.5rem"}>
|
||||
<div class="seed-label txt-bold">Stop seeding</div>
|
||||
<div class="seed-label">
|
||||
Are you sure you want to stop seeding this project? If you don't seed a
|
||||
project it won't appear in the local projects section anymore and any
|
||||
changes you make to it won't propagate to the network.
|
||||
</div>
|
||||
<Button
|
||||
styleWidth="100%"
|
||||
disabled={editSeedingInProgress}
|
||||
on:click={async () => {
|
||||
await editSeeding();
|
||||
toggle();
|
||||
}}>
|
||||
<IconSmall name="network" />
|
||||
Stop seeding
|
||||
</Button>
|
||||
<div
|
||||
slot="popover"
|
||||
style:width={$experimental ? (seeding ? "19.5rem" : "30.5rem") : "auto"}>
|
||||
{#if $experimental && canEditSeeding && seeding}
|
||||
<div class="seed-label txt-bold">Stop seeding</div>
|
||||
<div class="seed-label">
|
||||
Are you sure you want to stop seeding this project? If you don't seed a
|
||||
project it won't appear in the local projects section anymore and any
|
||||
changes you make to it won't propagate to the network.
|
||||
</div>
|
||||
<Button
|
||||
styleWidth="100%"
|
||||
disabled={editSeedingInProgress}
|
||||
on:click={async () => {
|
||||
await editSeeding();
|
||||
}}>
|
||||
<IconSmall name="network" />
|
||||
Stop seeding
|
||||
</Button>
|
||||
{:else}
|
||||
<span class="seed-label">
|
||||
Use the Radicle CLI to {seeding ? "stop" : "start"} seeding this project.
|
||||
</span>
|
||||
<Command command={`rad ${seeding ? "unseed" : "seed"} ${projectId}`} />
|
||||
{/if}
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import * as role from "@app/lib/roles";
|
||||
import * as router from "@app/lib/router";
|
||||
import * as utils from "@app/lib/utils";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import { HttpdClient } from "@httpd-client";
|
||||
import { closeFocused } from "@app/components/Popover.svelte";
|
||||
import { httpdStore } from "@app/lib/httpd";
|
||||
@@ -537,7 +538,7 @@
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<div slot="description">
|
||||
{#if issueState !== "read"}
|
||||
{#if $experimental && issueState !== "read"}
|
||||
<ExtendedTextarea
|
||||
isValid={() => newTitle.length > 0}
|
||||
disallowEmptyBody
|
||||
@@ -578,7 +579,7 @@
|
||||
<span class="txt-missing">No description</span>
|
||||
{/if}
|
||||
<div class="reactions">
|
||||
{#if session}
|
||||
{#if $experimental && session}
|
||||
<ReactionSelector
|
||||
reactions={issue.discussion[0].reactions}
|
||||
on:select={async ({ detail: { authors, emoji } }) => {
|
||||
@@ -635,14 +636,20 @@
|
||||
session?.publicKey,
|
||||
project.delegates,
|
||||
)}
|
||||
editComment={session && partial(editComment, session.id)}
|
||||
createReply={session && partial(createReply, session.id)}
|
||||
reactOnComment={session && partial(reactOnComment, session)} />
|
||||
editComment={$experimental &&
|
||||
session &&
|
||||
partial(editComment, session.id)}
|
||||
createReply={$experimental &&
|
||||
session &&
|
||||
partial(createReply, session.id)}
|
||||
reactOnComment={$experimental &&
|
||||
session &&
|
||||
partial(reactOnComment, session)} />
|
||||
<div class="connector" />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if session}
|
||||
{#if $experimental && session}
|
||||
{#if threads.length === 0}
|
||||
<div class="connector" />{/if}
|
||||
<CommentToggleInput
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { BaseUrl, Issue, IssueState, Project } from "@httpd-client";
|
||||
|
||||
import capitalize from "lodash/capitalize";
|
||||
import { HttpdClient } from "@httpd-client";
|
||||
import { ISSUES_PER_PAGE } from "./router";
|
||||
import { closeFocused } from "@app/components/Popover.svelte";
|
||||
import { httpdStore } from "@app/lib/httpd";
|
||||
import { baseUrlToString, isLocal } from "@app/lib/utils";
|
||||
import capitalize from "lodash/capitalize";
|
||||
import { closeFocused } from "@app/components/Popover.svelte";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import { httpdStore } from "@app/lib/httpd";
|
||||
|
||||
import Button from "@app/components/Button.svelte";
|
||||
import DropdownList from "@app/components/DropdownList.svelte";
|
||||
@@ -146,7 +147,7 @@
|
||||
|
||||
<div style="margin-left: auto; display: flex; gap: 1rem;">
|
||||
<Share {baseUrl} />
|
||||
{#if $httpdStore.state === "authenticated" && isLocal(baseUrl.hostname)}
|
||||
{#if $experimental && $httpdStore.state === "authenticated" && isLocal(baseUrl.hostname)}
|
||||
<Link
|
||||
route={{
|
||||
resource: "project.newIssue",
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
import * as role from "@app/lib/roles";
|
||||
import * as router from "@app/lib/router";
|
||||
import * as utils from "@app/lib/utils";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import capitalize from "lodash/capitalize";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import partial from "lodash/partial";
|
||||
@@ -767,7 +768,7 @@
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="description">
|
||||
<div class="revision-description">
|
||||
{#if session && patchState !== "read" && lastEdit}
|
||||
{#if $experimental && session && patchState !== "read" && lastEdit}
|
||||
<ExtendedTextarea
|
||||
isValid={() => patch.title.length > 0}
|
||||
enableAttachments
|
||||
@@ -802,7 +803,7 @@
|
||||
{:else}
|
||||
<span class="txt-missing">No description available</span>
|
||||
{/if}
|
||||
{#if session || (firstRevision.revisionReactions && firstRevision.revisionReactions.length > 0)}
|
||||
{#if ($experimental && session) || (firstRevision.revisionReactions && firstRevision.revisionReactions.length > 0)}
|
||||
<div class="actions">
|
||||
{#if session}
|
||||
<ReactionSelector
|
||||
@@ -979,15 +980,20 @@
|
||||
session?.publicKey,
|
||||
project.delegates,
|
||||
)}
|
||||
editRevision={session &&
|
||||
editRevision={$experimental &&
|
||||
session &&
|
||||
partial(editRevision, session.id, revision.revisionId)}
|
||||
editComment={session &&
|
||||
editComment={$experimental &&
|
||||
session &&
|
||||
partial(editComment, session.id, revision.revisionId)}
|
||||
reactOnComment={session &&
|
||||
reactOnComment={$experimental &&
|
||||
session &&
|
||||
partial(reactOnComment, session, revision.revisionId)}
|
||||
reactOnRevision={session &&
|
||||
reactOnRevision={$experimental &&
|
||||
session &&
|
||||
partial(reactOnRevision, session, revision.revisionId)}
|
||||
createReply={session &&
|
||||
createReply={$experimental &&
|
||||
session &&
|
||||
partial(createReply, session.id, revision.revisionId)}
|
||||
patchId={patch.id}
|
||||
patchState={patch.state}
|
||||
@@ -995,7 +1001,7 @@
|
||||
previousRevId={previousRevision?.id}
|
||||
previousRevOid={previousRevision?.oid}>
|
||||
{#if index === patch.revisions.length - 1}
|
||||
{#if session && view.name === "activity"}
|
||||
{#if $experimental && session && view.name === "activity"}
|
||||
<div class="connector" />
|
||||
<CommentToggleInput
|
||||
rawPath={rawPath(patch.revisions[0].id)}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import capitalize from "lodash/capitalize";
|
||||
|
||||
import { PATCHES_PER_PAGE } from "./router";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import { httpdStore } from "@app/lib/httpd";
|
||||
import { baseUrlToString, isLocal } from "@app/lib/utils";
|
||||
|
||||
@@ -160,7 +161,7 @@
|
||||
|
||||
<div style="margin-left: auto; display: flex; gap: 1rem;">
|
||||
<Share {baseUrl} />
|
||||
{#if $httpdStore.state === "authenticated" && isLocal(baseUrl.hostname)}
|
||||
{#if $experimental && $httpdStore.state === "authenticated" && isLocal(baseUrl.hostname)}
|
||||
<Popover popoverPositionTop="2.5rem" popoverPositionRight="0">
|
||||
<Button
|
||||
slot="toggle"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type { ActiveTab } from "./Header.svelte";
|
||||
import type { BaseUrl, Project } from "@httpd-client";
|
||||
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import { queryProject } from "@app/lib/projects";
|
||||
import { httpdStore, api } from "@app/lib/httpd";
|
||||
import { isLocal } from "@app/lib/utils";
|
||||
@@ -36,7 +37,9 @@
|
||||
let queryingLocalProject: boolean = true;
|
||||
let localProject: "notFound" | "found" | undefined = undefined;
|
||||
$: hideContextHelp =
|
||||
isLocal(baseUrl.hostname) && $httpdStore.state === "authenticated";
|
||||
$experimental &&
|
||||
isLocal(baseUrl.hostname) &&
|
||||
$httpdStore.state === "authenticated";
|
||||
|
||||
httpdStore.subscribe(async () => {
|
||||
if ($httpdStore.state !== "stopped" && !queryingLocalProject) {
|
||||
@@ -65,7 +68,13 @@
|
||||
queryingLocalProject = false;
|
||||
}
|
||||
|
||||
onMount(async () => await detectLocalProject());
|
||||
onMount(async () => {
|
||||
if ($httpdStore.state !== "stopped") {
|
||||
await detectLocalProject();
|
||||
} else {
|
||||
localProject = "notFound";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -250,7 +259,7 @@
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="help" class:expanded>
|
||||
{#if !hideContextHelp && expanded}
|
||||
{#if !hideContextHelp && expanded && $experimental}
|
||||
{#if !localProject}
|
||||
<div
|
||||
style="display: flex; justify-content: center; align-items: center; height: 2rem;">
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
import { twemoji } from "@app/lib/utils";
|
||||
|
||||
import Badge from "@app/components/Badge.svelte";
|
||||
import CloneButton from "../Header/CloneButton.svelte";
|
||||
import CloneButton from "@app/views/projects/Header/CloneButton.svelte";
|
||||
import CopyableId from "@app/components/CopyableId.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import SeedButton from "../Header/SeedButton.svelte";
|
||||
import SeedButton from "@app/views/projects/Header/SeedButton.svelte";
|
||||
import Share from "@app/views/projects/Share.svelte";
|
||||
|
||||
export let project: Project;
|
||||
|
||||
@@ -27,6 +27,8 @@ import { ResponseError } from "@httpd-client/lib/fetcher";
|
||||
import { handleError } from "@app/views/projects/error";
|
||||
import { nodePath } from "@app/views/nodes/router";
|
||||
import { unreachable } from "@app/lib/utils";
|
||||
import { get } from "svelte/store";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
|
||||
export const COMMITS_PER_PAGE = 30;
|
||||
export const PATCHES_PER_PAGE = 10;
|
||||
@@ -242,6 +244,9 @@ function parseRevisionToOid(
|
||||
}
|
||||
|
||||
async function isLocalNodeSeeding(route: ProjectRoute): Promise<boolean> {
|
||||
if (!get(experimental) && get(httpd.httpdStore).state === "stopped") {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const tracking = await httpd.api.getTracking();
|
||||
return tracking.some(({ id }) => id === route.project);
|
||||
|
||||
@@ -5,5 +5,5 @@ test("exceptions in production build", async ({ page }) => {
|
||||
// Wait for scripts to finish executing, there might be exceptions that
|
||||
// happen after the page has been painted.
|
||||
await page.waitForTimeout(2000);
|
||||
await expect(page.getByText("Local projects")).toBeVisible();
|
||||
await expect(page.getByText("Explore", { exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ test.use({
|
||||
});
|
||||
|
||||
test("show pinned projects", async ({ page }) => {
|
||||
await page.addInitScript(() => localStorage.setItem("experimental", "true"));
|
||||
await page.addInitScript(appConfigWithFixture);
|
||||
await page.goto("/");
|
||||
await expect(page.getByText("Local projects")).toBeVisible();
|
||||
|
||||
@@ -151,6 +151,9 @@ export const test = base.extend<{
|
||||
},
|
||||
|
||||
authenticatedPeer: async ({ page, peerManager }, use) => {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem("experimental", "true");
|
||||
});
|
||||
const peer = await peerManager.createPeer({
|
||||
name: "httpd",
|
||||
gitOptions: gitOptions["bob"],
|
||||
@@ -161,6 +164,8 @@ export const test = base.extend<{
|
||||
const { stdout } = await peer.spawn("rad-web", [
|
||||
"http://localhost:3001",
|
||||
"--no-open",
|
||||
"--path",
|
||||
"/",
|
||||
"--connect",
|
||||
`${peer.httpdBaseUrl.hostname}:${peer.httpdBaseUrl.port}`,
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user