Simplify project view by splitting components

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier
2022-02-11 16:54:30 +01:00
parent b9c4cda4cf
commit 27895b7d0c
2 changed files with 144 additions and 130 deletions
+129
View File
@@ -0,0 +1,129 @@
<script lang="ts">
import { Link } from 'svelte-routing';
import type { Config } from '@app/config';
import * as proj from '@app/project';
import Avatar from '@app/Avatar.svelte';
import { formatProfile, formatSeedId, setOpenGraphMetaTag } from '@app/utils';
import { browserStore } from '@app/project';
import Header from '@app/base/projects/Header.svelte';
import ProjectContentRoutes from '@app/base/projects/ProjectContentRoutes.svelte';
export let peer: string | null = null;
export let config: Config;
export let source: proj.Source;
export let peerSelector = false;
const project = source.project;
let parentName = source.profile ? formatProfile(source.profile.nameOrAddress, config) : null;
let pageTitle = parentName ? `${parentName}/${project.name}` : project.name;
function rootPath(source: proj.Source): string {
return proj.pathTo({
content: proj.ProjectContent.Tree,
peer: null,
path: "/",
revision: null,
}, source);
}
const baseName = parentName
? `${parentName}/${project.name}`
: project.name;
if (project.description) {
pageTitle = `${baseName}: ${project.description}`;
} else {
pageTitle = baseName;
}
setOpenGraphMetaTag([
{ prop: "og:title", content: project.name },
{ prop: "og:description", content: project.description },
{ prop: "og:url", content: window.location.href }
]);
</script>
<style>
header {
padding: 0 2rem 0 8rem;
}
.title {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 2rem;
margin-bottom: 0.5rem;
}
.title .divider {
color: var(--color-foreground-subtle);
margin: 0 0.5rem;
font-weight: normal;
}
.org-avatar {
display: inline-block;
width: 2rem;
height: 2rem;
}
.urn {
font-family: var(--font-family-monospace);
font-size: 0.75rem;
color: var(--color-foreground-faded);
overflow-wrap: anywhere;
}
.description {
margin: 1rem 0 1.5rem 0;
}
@media (max-width: 960px) {
.title {
font-size: 1.5rem;
overflow: hidden;
text-overflow: ellipsis;
}
.description {
font-size: 0.875rem;
}
.org-avatar {
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
}
</style>
<svelte:head>
<title>{pageTitle}</title>
</svelte:head>
<header>
<div class="title bold">
{#if source.profile}
<a class="org-avatar" title={source.profile.nameOrAddress} href="/{source.profile.nameOrAddress}">
<Avatar source={source.profile.avatar || source.profile.address} address={source.profile.address}/>
</a>
<span class="divider">/</span>
{/if}
<Link to={rootPath(source)}>{source.project.name}</Link>
{#if peer}
<span class="divider" title={peer}>/ {formatSeedId(peer)}</span>
{/if}
</div>
<div class="urn">{source.urn}</div>
<div class="description">{source.project.description}</div>
</header>
<!-- TODO: Should reivision be null? -->
{#await proj.getRoot(source.project, null, peer, source.seed.api) then { tree, branches, commit }}
<Header {tree} {branches} {commit} {browserStore} {source} {peerSelector} />
<ProjectContentRoutes {tree} {peer} {branches} {browserStore} {source} />
{:catch err}
<div class="container center-content">
<div class="error error-message text-xsmall">
<!-- TODO: Differentiate between (1) commit doesn't exist and (2) failed
to fetch - this needs a change to the backend. -->
API request to <code class="text-xsmall">{err.url}</code> failed
</div>
</div>
{/await}
+15 -130
View File
@@ -1,49 +1,35 @@
<script lang="ts">
import { Link } from 'svelte-routing';
import type { Config } from '@app/config';
import * as proj from '@app/project';
import Loading from '@app/Loading.svelte';
import Avatar from '@app/Avatar.svelte';
import { Profile, ProfileType } from '@app/profile';
import type { ProjectInfo } from '@app/project';
import { formatProfile, formatSeedId, isRadicleId, setOpenGraphMetaTag } from '@app/utils';
import { browserStore } from '@app/project';
import { isRadicleId } from '@app/utils';
import { Seed } from '@app/base/seeds/Seed';
import Header from '@app/base/projects/Header.svelte';
import ProjectContentRoutes from '@app/base/projects/ProjectContentRoutes.svelte';
import NotFound from '@app/NotFound.svelte';
import Project from './Project.svelte';
export let id: string; // Project name or URN.
export let seedHost: string | null = null;
export let profileName: string | null = null; // Address or name of parent profile.
export let peer: string | null = null;
export let config: Config;
// Show peer selector only if we're browsing via a seed.
const peerSelector = Boolean(seedHost);
// Nb. Once we move the content routing above this component, this should
// no longer be necessary, but right now, we have the project header that
// is rendered before the routes are parsed, so we have to set this here.
proj.browse({ peer });
let parentName = profileName ? formatProfile(profileName, config) : null;
let pageTitle = parentName ? `${parentName}/${id}` : id;
let projectInfo: ProjectInfo | null = null;
let getProject = new Promise<{ profile?: Profile | null; seed?: Seed } | null>((resolve, reject) => {
if (profileName) {
Profile.get(profileName, ProfileType.Project, config).then(p => resolve({ profile: p })).catch(err => reject(err.message));
} else if (seedHost) {
Seed.lookup(seedHost, config).then(s => resolve({ seed: s })).catch(err => reject(err.message));
} else {
resolve(null);
}
}).then(async (result): Promise<proj.Source> => {
if (! result) {
const getProject = async (): Promise<proj.Source> => {
const profile = profileName ? await Profile.get(profileName, ProfileType.Project, config) : null;
const seed = profile ? profile.seed : seedHost ? await Seed.lookup(seedHost, config) : null;
if (!profile && !seed) {
throw new Error("Couldn't load project");
}
const profile = result.profile;
const seed = result.seed || profile?.seed;
if (! seed?.valid) {
throw new Error("Couldn't load project: invalid seed");
}
@@ -55,41 +41,12 @@
// Older versions of http-api don't include the URN.
if (! info.urn) info.urn = urn;
projectInfo = info;
const peers: proj.PeerId[] = info.delegates
? await proj.getRemotes(urn, seed.api)
: [];
return { urn, seed, project: info, peers, profile, anchors };
});
function rootPath(source: proj.Source): string {
return proj.pathTo({
content: proj.ProjectContent.Tree,
peer: null,
path: "/",
revision: null,
}, source);
}
$: if (projectInfo) {
const baseName = parentName
? `${parentName}/${projectInfo.name}`
: projectInfo.name;
if (projectInfo.description) {
pageTitle = `${baseName}: ${projectInfo.description}`;
} else {
pageTitle = baseName;
}
setOpenGraphMetaTag([
{ prop: "og:title", content: projectInfo.name },
{ prop: "og:description", content: projectInfo.description },
{ prop: "og:url", content: window.location.href }
]);
}
};
</script>
<style>
@@ -102,32 +59,6 @@
main > header {
padding: 0 2rem 0 8rem;
}
.title {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 2rem;
margin-bottom: 0.5rem;
}
.title .divider {
color: var(--color-foreground-subtle);
margin: 0 0.5rem;
font-weight: normal;
}
.org-avatar {
display: inline-block;
width: 2rem;
height: 2rem;
}
.urn {
font-family: var(--font-family-monospace);
font-size: 0.75rem;
color: var(--color-foreground-faded);
overflow-wrap: anywhere;
}
.description {
margin: 1rem 0 1.5rem 0;
}
@media (max-width: 960px) {
main > header {
@@ -137,62 +68,16 @@
padding-top: 2rem;
min-width: 0;
}
.title {
font-size: 1.5rem;
overflow: hidden;
text-overflow: ellipsis;
}
.description {
font-size: 0.875rem;
}
.org-avatar {
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
}
</style>
<svelte:head>
<title>{pageTitle}</title>
</svelte:head>
<main>
{#await getProject}
{#await getProject()}
<header>
<Loading center />
</header>
{:then result}
<header>
<div class="title bold">
{#if result.profile}
<a class="org-avatar" title={result.profile.nameOrAddress} href="/{result.profile.nameOrAddress}">
<Avatar source={result.profile.avatar || result.profile.address} address={result.profile.address}/>
</a>
<span class="divider">/</span>
{/if}
<Link to={rootPath(result)}>{result.project.name}</Link>
{#if peer}
<span class="divider" title={peer}>/ {formatSeedId(peer)}</span>
{/if}
</div>
<div class="urn">{result.urn}</div>
<div class="description">{result.project.description}</div>
</header>
<!-- TODO: Should reivision be null? -->
{#await proj.getRoot(result.project, null, peer, result.seed.api) then { tree, branches, commit }}
<Header {tree} {branches} {commit} {browserStore} source={result} peerSelector={!!seedHost} />
<ProjectContentRoutes {tree} {peer} {branches} {browserStore} source={result} />
{:catch err}
<div class="container center-content">
<div class="error error-message text-xsmall">
<!-- TODO: Differentiate between (1) commit doesn't exist and (2) failed
to fetch - this needs a change to the backend. -->
API request to <code class="text-xsmall">{err.url}</code> failed
</div>
</div>
{/await}
{:then source}
<Project {source} {peer} {config} {peerSelector} />
{:catch}
<NotFound title={id} subtitle="This project was not found." />
{/await}