Add local switch and contextual help to sidebar

This commit is contained in:
Sebastian Martinez
2024-01-19 17:01:01 +00:00
committed by Rūdolfs Ošiņš
parent ff593e6567
commit 10170d79e5
5 changed files with 207 additions and 37 deletions
+6 -1
View File
@@ -121,6 +121,11 @@ async function checkState() {
.runExclusive(async () => {
try {
const { state: node } = await api.getNode();
if (httpdState && httpdState.state !== "stopped") {
httpdState.node = node;
}
if (httpdState && httpdState.state === "authenticated") {
const sess = await api.session.getById(httpdState.session.id);
const unixTimeInSeconds = Math.floor(Date.now() / 1000);
@@ -130,7 +135,7 @@ async function checkState() {
) {
update({ state: "running", node });
} else {
update({ ...httpdState, node });
update(httpdState);
}
} else {
update({ state: "running", node });
+2 -5
View File
@@ -1,16 +1,13 @@
<script lang="ts">
import type { ProjectBaseUrlActivity } from "./router";
import { api } from "@app/lib/httpd";
import { twemoji } from "@app/lib/utils";
import { isLocal, twemoji } from "@app/lib/utils";
import AppLayout from "@app/App/AppLayout.svelte";
import Link from "@app/components/Link.svelte";
import ProjectCard from "@app/components/ProjectCard.svelte";
export let projects: ProjectBaseUrlActivity[];
$: localProjects = projects[0]?.baseUrl === api.baseUrl;
</script>
<style>
@@ -69,7 +66,7 @@
{#if projects.length > 0}
<div class="heading">
{#if localProjects}
{#if isLocal(projects[0].baseUrl.hostname)}
<!-- prettier-ignore -->
<span>Explore projects on your <span class="txt-bold">local node</span>.</span>
{:else}
+1 -1
View File
@@ -8,7 +8,7 @@
import IconSmall from "@app/components/IconSmall.svelte";
import Link from "@app/components/Link.svelte";
import MobileFooter from "@app/App/MobileFooter.svelte";
import Sidebar from "./Sidebar.svelte";
import Sidebar from "@app/views/projects/Sidebar.svelte";
export let activeTab: ActiveTab | undefined = undefined;
export let baseUrl: BaseUrl;
+128 -30
View File
@@ -2,11 +2,17 @@
import type { ActiveTab } from "./Header.svelte";
import type { BaseUrl, Project } from "@httpd-client";
import { ResponseError } from "@httpd-client/lib/fetcher";
import { api, httpdStore } from "@app/lib/httpd";
import { isLocal } from "@app/lib/utils";
import { onMount } from "svelte";
import Button from "@app/components/Button.svelte";
import ContextHelp from "@app/views/projects/Sidebar/ContextHelp.svelte";
import IconSmall from "@app/components/IconSmall.svelte";
import Link from "@app/components/Link.svelte";
import Loading from "@app/components/Loading.svelte";
import Popover from "@app/components/Popover.svelte";
import Help from "@app/App/Help.svelte";
@@ -27,6 +33,20 @@
);
}
// To avoid concurrent request.
let queryingLocalProject: boolean = true;
let localProject: "notFound" | "found" | undefined = undefined;
$: hideContextHelp =
isLocal(baseUrl.hostname) && $httpdStore.state === "authenticated";
$: loadingContextHelp =
$httpdStore.state !== "stopped" && localProject === undefined;
httpdStore.subscribe(async () => {
if ($httpdStore.state !== "stopped" && !queryingLocalProject) {
await detectLocalProject();
}
});
function loadSidebarState(): boolean {
const storedSidebarState = window.localStorage.getItem(SIDEBAR_STATE_KEY);
@@ -42,8 +62,22 @@
storeSidebarState(expanded);
}
onMount(() => {
async function detectLocalProject(): Promise<void> {
queryingLocalProject = true;
localProject = await api.project
.getById(project.id)
.then<"found">(() => "found")
.catch((error: unknown) =>
error instanceof ResponseError && error.status === 404
? "notFound"
: undefined,
);
queryingLocalProject = false;
}
onMount(async () => {
expanded = loadSidebarState();
await detectLocalProject();
});
</script>
@@ -55,6 +89,9 @@
flex-direction: column;
justify-content: space-between;
}
.expanded {
width: 22.5rem;
}
.project-navigation {
display: flex;
flex-direction: column;
@@ -85,9 +122,16 @@
justify-content: space-between;
width: 100%;
}
.help-box {
padding: 1rem;
background-color: var(--color-background-float);
border: 1px solid var(--color-border-hint);
font-size: var(--font-size-small);
border-radius: var(--border-radius-small);
}
</style>
<div class="sidebar">
<div class="sidebar" class:expanded>
{#if expanded}
<div style="display: flex; flex-direction: column; gap: 1rem;">
<div class="project-navigation">
@@ -164,39 +208,62 @@
</Link>
</div>
</div>
<div style="display: flex; flex-direction:column; gap: 1rem;">
{#if !hideContextHelp}
{#if loadingContextHelp}
<div
style="display: flex; justify-content: center; align-items: center; height: 2rem;">
<Loading small />
</div>
{:else}
<div class="help-box">
<ContextHelp
{localProject}
{baseUrl}
projectId={project.id}
hideLocalButton={isLocal(baseUrl.hostname)}
disableLocalButton={$httpdStore.state !== "authenticated" ||
localProject !== "found"} />
</div>
{/if}
{/if}
<div class="sidebar-footer" style:flex-direction="row">
<Button title={"Collapse"} on:click={toggleSidebar} variant="background">
<IconSmall name="chevron-left" />
</Button>
<div style:width="1.5rem" />
<Popover popoverPositionBottom="2.5rem" popoverPositionLeft="0">
<div class="sidebar-footer" style:flex-direction="row">
<Button
variant="background"
title="Settings"
slot="toggle"
let:toggle
on:click={toggle}>
<IconSmall name="settings" />
Settings
title={"Collapse"}
on:click={toggleSidebar}
variant="background">
<IconSmall name="chevron-left" />
</Button>
<div style:width="1.5rem" />
<Settings slot="popover" />
</Popover>
<Popover popoverPositionBottom="2.5rem" popoverPositionLeft="0">
<Button
variant="background"
title="Help"
slot="toggle"
let:toggle
on:click={toggle}>
<IconSmall name="help" />
Help
</Button>
<Popover popoverPositionBottom="2.5rem" popoverPositionLeft="0">
<Button
variant="background"
title="Settings"
slot="toggle"
let:toggle
on:click={toggle}>
<IconSmall name="settings" />
Settings
</Button>
<Help slot="popover" />
</Popover>
<Settings slot="popover" />
</Popover>
<Popover popoverPositionBottom="2.5rem" popoverPositionLeft="0">
<Button
variant="background"
title="Help"
slot="toggle"
let:toggle
on:click={toggle}>
<IconSmall name="help" />
Help
</Button>
<Help slot="popover" />
</Popover>
</div>
</div>
{:else}
<div style="display: flex; flex-direction: column; gap: 1rem;">
@@ -287,6 +354,37 @@
<Help slot="popover" />
</Popover>
{#if !hideContextHelp}
{#if loadingContextHelp}
<div
style="display: flex; justify-content: center; align-items: center; height: 2rem;">
<Loading small condensed />
</div>
{:else}
<Popover popoverPositionBottom="0" popoverPositionLeft="3rem">
<Button
stylePadding="0 0.75rem"
variant="background"
title="Help"
slot="toggle"
let:toggle
on:click={toggle}>
<IconSmall name="globe" />
</Button>
<ContextHelp
{localProject}
{baseUrl}
popover
projectId={project.id}
hideLocalButton={isLocal(baseUrl.hostname)}
disableLocalButton={$httpdStore.state !== "authenticated" ||
localProject !== "found"}
slot="popover" />
</Popover>
{/if}
{/if}
</div>
{/if}
</div>
@@ -0,0 +1,70 @@
<script lang="ts">
import type { BaseUrl } from "@httpd-client";
import { api, httpdStore } from "@app/lib/httpd";
import { isLocal } from "@app/lib/utils";
import Button from "@app/components/Button.svelte";
import ExternalLink from "@app/components/ExternalLink.svelte";
import IconSmall from "@app/components/IconSmall.svelte";
import Link from "@app/components/Link.svelte";
export let baseUrl: BaseUrl;
export let disableLocalButton: boolean;
export let hideLocalButton: boolean;
export let localProject: "notFound" | "found" | undefined;
export let projectId: string;
export let popover: boolean = false;
</script>
<style>
.help {
font-size: var(--font-size-small);
}
.popover {
width: 18.5rem;
}
.title {
padding-bottom: 0.75rem;
}
</style>
<div class="help" class:popover>
{#if $httpdStore.state === "stopped"}
<div class="title txt-bold">Device not connected</div>
<div>Click the Connect button in the top right corner to get started.</div>
{:else if localProject === "notFound"}
<div class="title txt-bold">Project not available locally</div>
<div>
This project hasn't been found on your local node. Click the Clone button
in the top right corner to get a local copy.
</div>
{:else if $httpdStore.state === "running" && localProject === "found"}
<div class="title txt-bold">Not authenticated</div>
<div>To make changes you need to authenticate yourself.</div>
<div>
Click the Authenticate button in the top right corner to get
authenticated.
</div>
{:else if !isLocal(baseUrl.hostname) && localProject === "found"}
<div class="title txt-bold">Read Only</div>
<div>This is a read only preview hosted on</div>
<ExternalLink href={baseUrl.hostname} />
{/if}
{#if !hideLocalButton}
<div style:padding-top="1rem">
<Link
disabled={disableLocalButton}
route={{
resource: "project.source",
node: api.baseUrl,
project: projectId,
}}>
<Button size="large" styleWidth="100%" disabled={disableLocalButton}>
<IconSmall name="device" />Make changes on your local node
</Button>
</Link>
</div>
{/if}
</div>