upend/webext/src/App.svelte

223 lines
4.8 KiB
Svelte

<script lang="ts">
import browser from "webextension-polyfill";
import { UpEndApi } from "upend/api";
import { cleanInstanceUrl, instanceUrlStore } from "./common";
import { onMount } from "svelte";
import "./main.scss";
const api = new UpEndApi("http://localhost:8093");
let opening = false;
let openError: string | undefined;
let instanceUrl: string;
$: instanceUrl = $instanceUrlStore;
let instanceUrlModified = false;
$: instanceUrlModified = $instanceUrlStore !== instanceUrl;
let instanceVersion: string;
let instanceVersionError: string;
$: Boolean($instanceUrlStore) && updateVersion();
async function updateVersion() {
instanceVersion = undefined;
instanceVersionError = undefined;
try {
const vaultInfo = await api.fetchInfo();
instanceVersion = vaultInfo.version;
} catch (err: unknown) {
instanceVersionError = processError(err);
}
}
let currentUrl: string | undefined;
let contentType: string | undefined;
onMount(async () => {
const currentTab = (
await browser.tabs.query({
active: true,
currentWindow: true,
})
)[0];
currentUrl = currentTab.url;
contentType = (await browser.tabs.executeScript(currentTab.id, {
code: "document.contentType",
})) as unknown as string | undefined;
});
function visit(address: string) {
browser.tabs.create({ url: `${$cleanInstanceUrl}/#/browse/${address}` });
window.close();
}
async function openAsUrl() {
open({ url: currentUrl });
}
async function openContent() {
open({ urlContent: currentUrl });
}
async function open(input: { url?: string; urlContent?: string }) {
opening = true;
try {
const address = await api.getAddress(input);
// const obj = (await (
// await fetch(`${$cleanInstanceUrl}/api/obj/${address}`)
// ).json()) as EntityListing;
visit(address);
} catch (err) {
openError = processError(err);
}
}
let primaryAction: [string, () => void] | undefined;
$: primaryAction =
contentType &&
(contentType == "text/html"
? ["Open as URL", openAsUrl]
: ["Open Content", openContent]);
$: primaryActionLabel = primaryAction ? primaryAction[0] : "...";
function performPrimaryAction() {
primaryAction[1]();
}
function processError(err: unknown): string {
if (err instanceof Error) {
if (err.message.includes("NetworkError")) {
return "Network Error. Is UpEnd running?";
} else {
return err.message;
}
} else {
return String(err);
}
}
</script>
<main>
<div class="primary-controls">
<button
class="button"
disabled={!primaryAction}
on:click={performPrimaryAction}
>
{primaryActionLabel}
</button>
<div class="label">Content type: {contentType || "???"}</div>
</div>
<div class="controls row">
<button class="button" on:click={openAsUrl}>Open as URL</button>
<button class="button" on:click={openContent}>Open Content</button>
</div>
{#if opening && !openError}
<div class="status-label">Opening, please wait...</div>
{/if}
{#if openError}
<div class="status-label error">{openError}</div>
{/if}
<hr />
<div class="row">
<label>
Instance URL
<input
class="instance-input"
type="url"
bind:value={instanceUrl}
class:modified={instanceUrlModified}
/>
</label>
<button class="button" on:click={() => ($instanceUrlStore = instanceUrl)}>
Save
</button>
</div>
<div class="version">
Status: {#if !instanceVersionError}
{`OK, v.${instanceVersion}` || "???"}
{:else}
<div class="error">{instanceVersionError}</div>
{/if}
</div>
</main>
<style lang="scss">
@use "../../webui/src/styles/colors";
main {
padding: 1em;
}
input {
background: var(--background);
color: var(--foreground);
border: 1px solid var(--foreground);
border-radius: 2px;
}
input[type="url"] {
font-family: var(--monospace-font);
&:focus-visible {
outline: 1px solid var(--primary-lighter);
}
&:invalid {
color: colors.$red;
outline: 2px solid colors.$red;
}
}
.instance-input.modified {
color: colors.$yellow;
}
hr {
margin: 1rem 0;
}
.controls {
display: flex;
justify-content: space-evenly;
}
.primary-controls {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
margin-bottom: 1rem;
.button {
font-size: 1.5rem;
}
.label {
font-size: 0.75rem;
}
}
.row {
display: flex;
align-items: center;
gap: 1rem;
}
.status-label {
margin-top: 1rem;
text-align: center;
}
.error {
color: colors.$red;
}
.version .error {
display: inline;
}
</style>