mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-03 18:50:30 +08:00
Simplify project view by splitting components
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
@@ -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
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user