Handle errors in Browser.svelte

Updates also the seed fixture to fix some minor issues and add a
relative image markdown example

Signed-off-by: Sebastian Martinez <me@sebastinez.dev>
This commit is contained in:
Sebastian Martinez
2022-12-21 17:10:20 +01:00
parent 5f2cd259a0
commit e86accafae
22 changed files with 251 additions and 144 deletions
+2 -2
View File
@@ -19,7 +19,7 @@
export let content: string;
export let doc = matter(content);
export let getImage: (path: string) => Promise<proj.Blob>;
export let getImage: (path: string) => Promise<proj.MaybeBlob>;
export let hash: string | null = null;
const frontMatter = Object.entries(doc.data).filter(
@@ -54,7 +54,7 @@
// Make sure the source isn't a URL before trying to fetch it from the repo
if (path && !isUrl(path) && !path.startsWith(`${base}twemoji`)) {
getImage(path).then(blob => {
if (blob.content) {
if (blob?.content) {
const mime = getImageMime(path);
if (mime) {
i.setAttribute("src", `data:${mime};base64,${blob.content}`);
+2 -2
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import type { Blob } from "@app/project";
import type { MaybeBlob, Blob } from "@app/project";
import type { MaybeHighlighted } from "@app/syntax";
import type { ProjectRoute } from "@app/router/definitions";
@@ -14,7 +14,7 @@
export let activeRoute: ProjectRoute;
export let blob: Blob;
export let getImage: (path: string) => Promise<Blob>;
export let getImage: (path: string) => Promise<MaybeBlob>;
export let line: string | undefined = undefined;
const fileExtension = blob.path.split(".").pop() ?? "";
+73 -49
View File
@@ -1,14 +1,21 @@
<script lang="ts">
import type { Theme } from "@app/ThemeToggle.svelte";
import type { ProjectRoute } from "@app/router/definitions";
import type * as proj from "@app/project";
<script lang="ts" context="module">
import { writable } from "svelte/store";
import Loading from "@app/Loading.svelte";
import Placeholder from "@app/Placeholder.svelte";
export const browserErrorStore = writable<
{ message: string; path: string } | undefined
>();
</script>
<script lang="ts">
import type * as proj from "@app/project";
import type { ProjectRoute } from "@app/router/definitions";
import * as router from "@app/router";
import * as utils from "@app/utils";
import Button from "@app/Button.svelte";
import { theme } from "@app/ThemeToggle.svelte";
import * as router from "@app/router";
import Loading from "@app/Loading.svelte";
import Placeholder from "@app/Placeholder.svelte";
import { onMount } from "svelte";
import Tree from "./Tree.svelte";
import Blob from "./Blob.svelte";
@@ -20,7 +27,7 @@
type State =
| { status: Status.Loading; path: string }
| { status: Status.Loaded; path: string; blob: proj.Blob; theme: Theme };
| { status: Status.Loaded; path: string; blob: proj.Blob };
export let project: proj.Project;
export let tree: proj.Tree;
@@ -35,15 +42,8 @@
// Whether the mobile file tree is visible.
let mobileFileTree = false;
const loadBlob = async (
path: string,
theme: Theme,
): Promise<proj.Blob | undefined> => {
if (
state.status === Status.Loaded &&
state.path === path &&
state.theme === theme
) {
const loadBlob = async (path: string) => {
if (state.status === Status.Loaded && state.path === path) {
return state.blob;
}
@@ -51,26 +51,38 @@
path === "/" ? project.getReadme(commit) : project.getBlob(commit, path);
state = { status: Status.Loading, path };
try {
state = { status: Status.Loaded, path, blob: await promise, theme };
return state.blob;
} catch (err) {
console.warn("Could not load blob.");
}
state = { status: Status.Loaded, path, blob: await promise };
return state.blob;
};
onMount(() => {
browserErrorStore.set(undefined);
});
// Get an image blob based on a relative path.
const getImage = async (imagePath: string): Promise<proj.Blob> => {
const getImage = async (imagePath: string) => {
const finalPath = utils.canonicalize(imagePath, path);
return project.getBlob(commit, finalPath);
return project.getBlob(commit, finalPath).catch(() => {
console.warn("Not able to load image blob:", finalPath);
return undefined;
});
};
const onSelect = async (newPath: string, theme: Theme) => {
const onSelect = async (newPath: string) => {
browserErrorStore.set(undefined);
// Ensure we don't spend any time in a "loading" state. This means
// the loading spinner won't be shown, and instead the blob will be
// displayed once loaded.
const blob = await loadBlob(newPath, theme);
getBlob = new Promise(resolve => resolve(blob));
const blob = await loadBlob(newPath).catch(() => {
browserErrorStore.set({
message: "Not able to load selected file",
path: newPath,
});
return undefined;
});
if (blob) {
getBlob = new Promise(resolve => resolve(blob));
}
// Close mobile tree if user navigates to other file
mobileFileTree = false;
@@ -82,15 +94,25 @@
};
const fetchTree = async (path: string) => {
return project.getTree(commit, path);
return project.getTree(commit, path).catch(() => {
browserErrorStore.set({
message: "Not able to expand directory",
path,
});
return undefined;
});
};
const toggleMobileFileTree = () => {
mobileFileTree = !mobileFileTree;
};
$: getBlob = loadBlob(path, $theme);
$: loadingPath = state.status === Status.Loading ? state.path : null;
$: getBlob = loadBlob(path).catch(() => {
browserErrorStore.set({ message: "Not able to load file", path });
return undefined;
});
$: loadingPath =
!$browserErrorStore && state.status === Status.Loading ? state.path : null;
</script>
<style>
@@ -195,33 +217,35 @@
{fetchTree}
{loadingPath}
on:select={e => {
onSelect(e.detail, $theme);
onSelect(e.detail);
}} />
</div>
</div>
<div class="column-right">
{#await getBlob}
<Loading small center />
{:then blob}
{#if blob}
<Blob {line} {blob} {getImage} {activeRoute} />
{/if}
{:catch}
{#if $browserErrorStore}
<Placeholder emoji="🍂">
<span slot="title">
{#if path !== "/"}
<div class="txt-monospace">{path}</div>
{/if}
<div class="txt-monospace">{$browserErrorStore.path}</div>
</span>
<span slot="body">
{#if path === "/"}
The README could not be loaded.
{:else}
This path could not be loaded.
{/if}
<span>
{#if $browserErrorStore.path === "/"}
The README could not be loaded.
{:else}
{$browserErrorStore.message}
{/if}
</span>
</span>
</Placeholder>
{/await}
{:else}
{#await getBlob}
<Loading small center />
{:then blob}
{#if blob}
<Blob {line} {blob} {getImage} {activeRoute} />
{/if}
{/await}
{/if}
</div>
{:else}
<div class="placeholder">
+1 -1
View File
@@ -5,7 +5,7 @@
import Markdown from "@app/Markdown.svelte";
export let content: string;
export let getImage: (path: string) => Promise<proj.Blob>;
export let getImage: (path: string) => Promise<proj.MaybeBlob>;
export let activeRoute: ProjectRoute;
$: hash = activeRoute.params.hash || null;
+2 -2
View File
@@ -1,13 +1,13 @@
<script lang="ts" strictEvents>
import { createEventDispatcher } from "svelte";
import type { Tree } from "@app/project";
import type { MaybeTree, Tree } from "@app/project";
import { ObjectType } from "@app/project";
import File from "./Tree/File.svelte";
import Folder from "./Tree/Folder.svelte";
export let fetchTree: (path: string) => Promise<Tree>;
export let fetchTree: (path: string) => Promise<MaybeTree>;
export let path: string;
export let tree: Tree;
export let loadingPath: string | null = null;
+9 -7
View File
@@ -1,20 +1,22 @@
<script lang="ts" strictEvents>
import { createEventDispatcher } from "svelte";
import type { MaybeTree } from "@app/project";
import Loading from "@app/Loading.svelte";
import type { Tree } from "@app/project";
import { ObjectType } from "@app/project";
import { createEventDispatcher } from "svelte";
import File from "./File.svelte";
export let fetchTree: (path: string) => Promise<Tree>;
export let fetchTree: (path: string) => Promise<MaybeTree>;
export let name: string;
export let prefix: string;
export let currentPath: string;
export let loadingPath: string | null = null;
let expanded = currentPath.indexOf(prefix) === 0;
let tree: Promise<Tree> | null = expanded ? fetchTree(prefix) : null;
let tree: Promise<MaybeTree> = fetchTree(prefix).then(tree => {
if (expanded) return tree;
});
const dispatch = createEventDispatcher<{ select: string }>();
const onSelectFile = ({ detail: path }: { detail: string }) =>
@@ -23,9 +25,9 @@
const onClick = () => {
expanded = !expanded;
if (expanded) {
tree = fetchTree(prefix);
}
tree = fetchTree(prefix).then(tree => {
if (expanded) return tree;
});
};
</script>
+2
View File
@@ -8,6 +8,8 @@ import type { Wallet } from "@app/wallet";
export type Urn = string;
export type PeerId = string;
export type Branches = { [key: string]: string };
export type MaybeBlob = Blob | undefined;
export type MaybeTree = Tree | undefined;
export type Delegate =
| {