mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-30 16:50:47 +08:00
Implement project browsing
This commit is contained in:
@@ -19,10 +19,12 @@
|
||||
--color-secondary-2: #2c326d;
|
||||
--color-secondary-6: #e3e3ff;
|
||||
--color-tertiary: #55ffff;
|
||||
--color-tertiary-faded: #ade4e4;
|
||||
--color-tertiary-background: #55ffff11;
|
||||
--color-tertiary-1: #214047;
|
||||
--color-tertiary-2: #2c326d;
|
||||
--color-tertiary-6: #e3e3ff;
|
||||
--color-light: #add0e4;
|
||||
--color-yellow: #ffff99;
|
||||
--color-yellow-background: #ffff9911;
|
||||
--color-positive: #53db53;
|
||||
@@ -39,7 +41,9 @@
|
||||
--color-negative-2: #623237;
|
||||
--color-negative-6: #ffd4d4;
|
||||
--color-foreground: #ffffff;
|
||||
--color-foreground-90: #dddddd;
|
||||
--color-foreground-faded: #777788;
|
||||
--color-foreground-subtle: #444455;
|
||||
--color-foreground-background: #77778811;
|
||||
--color-foreground-1: #242e38;
|
||||
--color-foreground-2: #29343d;
|
||||
@@ -52,6 +56,8 @@
|
||||
--color-glow-error: #ff555522;
|
||||
|
||||
--font-family-sans-serif: Inter, sans-serif;
|
||||
--font-weight-medium: 600;
|
||||
--font-weight-bold: 700;
|
||||
--font-family-monospace: monospace;
|
||||
--border-radius: 50px;
|
||||
--box-shadow-color: var(--color-secondary-2);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: bold;
|
||||
font-style: bolder;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url("fonts/Inter-Bold.otf");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
RADICLE_ALCHEMY_API_KEY: process.env.RADICLE_ALCHEMY_API_KEY,
|
||||
RADICLE_HTTP_API: process.env.RADICLE_HTTP_API,
|
||||
},
|
||||
mount: {
|
||||
public: '/',
|
||||
@@ -17,6 +18,7 @@ module.exports = {
|
||||
routes: [
|
||||
/* Enable an SPA Fallback in development: */
|
||||
{"match": "routes", "src": ".*", "dest": "/index.html"},
|
||||
{"match": "all", "src": "/projects/.*", "dest": "/index.html"},
|
||||
],
|
||||
optimize: {
|
||||
/* Setting `bundle: true` breaks .json imports in snowpack 3.5.0 */
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import Vesting from '@app/base/vesting/Index.svelte';
|
||||
import Registrations from '@app/base/registrations/Routes.svelte';
|
||||
import Orgs from '@app/base/orgs/Routes.svelte';
|
||||
import Projects from '@app/base/projects/Routes.svelte';
|
||||
import Resolver from '@app/base/resolver/Routes.svelte';
|
||||
import Header from '@app/Header.svelte';
|
||||
import Loading from '@app/Loading.svelte';
|
||||
@@ -55,6 +56,7 @@
|
||||
</Route>
|
||||
<Registrations {config} session={$session} />
|
||||
<Orgs {config} />
|
||||
<Projects {config} />
|
||||
<Resolver {config} />
|
||||
</Router>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@
|
||||
<span class="network unavailable">No Network</span>
|
||||
{/if}
|
||||
|
||||
{#if address}
|
||||
{#if address && tokenBalance}
|
||||
<span class="balance">
|
||||
{formatBalance(tokenBalance)} <strong>RAD</strong>
|
||||
</span>
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { ContractReceipt } from '@ethersproject/contracts';
|
||||
import { assert } from '@app/error';
|
||||
import * as utils from '@app/utils';
|
||||
import type { Config } from '@app/config';
|
||||
import type { Project } from '@app/base/projects/Project';
|
||||
import type { Project } from '@app/project';
|
||||
|
||||
const GetProjects = `
|
||||
query GetProjects($org: ID!) {
|
||||
@@ -13,6 +13,7 @@ const GetProjects = `
|
||||
anchor {
|
||||
stateHash
|
||||
stateHashFormat
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<script lang="typescript">
|
||||
import type { Blob } from "@app/project";
|
||||
|
||||
export let blob: Blob;
|
||||
|
||||
const lines = (blob.content.match(/\n/g) || []).length;
|
||||
const lineNumbers = Array(lines).fill(0).map((_, index) => (index + 1).toString());
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.file-source {
|
||||
border: 1px solid var(--color-foreground-level-3);
|
||||
border-radius: 0.5rem;
|
||||
min-width: var(--content-min-width);
|
||||
}
|
||||
|
||||
header .file-header {
|
||||
display: flex;
|
||||
height: 3rem;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
color: var(--color-foreground);
|
||||
border-width: 1px 1px 0 1px;
|
||||
border-color: var(--color-foreground-subtle);
|
||||
border-style: solid;
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0.25rem;
|
||||
}
|
||||
|
||||
header .file-name {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.line-numbers {
|
||||
color: var(--color-foreground-subtle);
|
||||
font-family: var(--font-family-sans-serif);
|
||||
text-align: right;
|
||||
user-select: none;
|
||||
padding: 0 1rem 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.code {
|
||||
padding-bottom: 0.5rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
border: 1px solid var(--color-foreground-subtle);
|
||||
border-top-style: dashed;
|
||||
}
|
||||
|
||||
.no-scrollbar {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<div class="file-source">
|
||||
<header>
|
||||
<div class="file-header" data-cy="file-header">
|
||||
<span class="file-name">
|
||||
<span>{blob.path.split("/").join(" / ")}</span>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container">
|
||||
{#if blob.binary}
|
||||
👀 Binary content
|
||||
{:else}
|
||||
<pre class="line-numbers">
|
||||
{@html lineNumbers.join("\n")}
|
||||
</pre>
|
||||
<pre
|
||||
class="code no-scrollbar">
|
||||
{#if blob.html}
|
||||
{@html blob.content}
|
||||
{:else}
|
||||
{blob.content}
|
||||
{/if}
|
||||
</pre>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { Config } from '@app/config';
|
||||
|
||||
export interface Project {
|
||||
id: string
|
||||
anchor: {
|
||||
stateHash: string
|
||||
stateHashFormat: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface User {
|
||||
urn: string
|
||||
avatar: { emoji: string, background: { r: number, g: number, b: number } }
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
name: string
|
||||
description: string
|
||||
maintainers: User[]
|
||||
}
|
||||
|
||||
export async function getMetadata(urn: string, config: Config): Promise<Meta | null> {
|
||||
const response = await fetch(`${config.seed.url}/projects/${urn}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
if (! response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="typescript">
|
||||
import { Route } from "svelte-routing";
|
||||
import View from '@app/base/projects/View.svelte';
|
||||
import type { Config } from '@app/config';
|
||||
|
||||
export let config: Config;
|
||||
|
||||
const joinPaths = (path: string, glob: string): string => {
|
||||
return glob.length > 0 ? [path, glob].join("/") : path;
|
||||
};
|
||||
</script>
|
||||
|
||||
<Route path="/projects/:urn/:commit/:path/*" let:params>
|
||||
<View {config} urn={params.urn} commit={params.commit} path={joinPaths(params.path, params['*'])} />
|
||||
</Route>
|
||||
|
||||
<Route path="/projects/:urn/:commit" let:params>
|
||||
<View {config} urn={params.urn} commit={params.commit} path="/" />
|
||||
</Route>
|
||||
@@ -0,0 +1,37 @@
|
||||
<script lang="typescript">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import type { 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 path: string;
|
||||
export let tree: Tree;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const onSelect = ({ detail: path }: { detail: string }): void => {
|
||||
dispatch("select", path);
|
||||
};
|
||||
</script>
|
||||
|
||||
{#each tree.entries as entry (entry.path)}
|
||||
{#if entry.info.objectType === ObjectType.Tree}
|
||||
<Folder
|
||||
{fetchTree}
|
||||
name={entry.info.name}
|
||||
prefix={`${entry.path}/`}
|
||||
currentPath={path}
|
||||
on:select={onSelect}
|
||||
/>
|
||||
{:else}
|
||||
<File
|
||||
active={entry.path === path}
|
||||
loading={false}
|
||||
name={entry.info.name}
|
||||
on:click={() => onSelect({ detail: entry.path })}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -0,0 +1,55 @@
|
||||
<script lang="typescript">
|
||||
import Loading from '@app/Loading.svelte';
|
||||
|
||||
export let active: boolean;
|
||||
export let loading: boolean;
|
||||
export let name: string;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.file {
|
||||
color: var(--color-foreground-90);
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
line-height: 1.5em;
|
||||
margin: 0.125rem 0;
|
||||
padding: 0.25rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file:hover {
|
||||
background-color: var(--color-foreground-background);
|
||||
}
|
||||
|
||||
.file.active {
|
||||
color: var(--color-foreground) !important;
|
||||
background-color: var(--color-foreground-background);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-left: 0.25rem;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="file" class:active on:click>
|
||||
{#if loading}
|
||||
<div class="spinner">
|
||||
<Loading small />
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Nothing -->
|
||||
{/if}
|
||||
<span class="name">{name}</span>
|
||||
</div>
|
||||
@@ -0,0 +1,89 @@
|
||||
<script lang="typescript">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import Loading from '@app/Loading.svelte';
|
||||
import type { Tree } from "@app/project";
|
||||
import { ObjectType } from "@app/project";
|
||||
|
||||
import File from "./File.svelte";
|
||||
|
||||
export let fetchTree: (path: string) => Promise<Tree>;
|
||||
export let name: string;
|
||||
export let prefix: string;
|
||||
export let currentPath: string;
|
||||
|
||||
let expanded = currentPath.indexOf(prefix) === 0;
|
||||
let tree: Promise<Tree> | null = expanded ? fetchTree(prefix) : null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const onSelectFile = ({ detail: path }: { detail: string }) =>
|
||||
dispatch("select", path);
|
||||
|
||||
const onClick = () => {
|
||||
expanded = !expanded;
|
||||
|
||||
if (expanded) {
|
||||
tree = fetchTree(prefix);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.folder {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
margin: 0.125rem 0;
|
||||
color: var(--color-foreground-level-6);
|
||||
user-select: none;
|
||||
line-height: 1.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.folder:hover {
|
||||
background-color: var(--color-foreground-background);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.folder-name {
|
||||
margin-left: 0.25rem;
|
||||
color: var(--color-light);
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-left: 0.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="folder" on:click={onClick}>
|
||||
<span class="folder-name">{name}/</span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
{#if expanded}
|
||||
{#await tree}
|
||||
<Loading small center />
|
||||
{:then tree}
|
||||
{#if tree}
|
||||
{#each tree.entries as entry (entry.path)}
|
||||
{#if entry.info.objectType === ObjectType.Tree}
|
||||
<svelte:self
|
||||
{fetchTree}
|
||||
name={entry.info.name}
|
||||
on:select={onSelectFile}
|
||||
prefix={`${entry.path}/`}
|
||||
{currentPath} />
|
||||
{:else}
|
||||
<File
|
||||
active={entry.path === currentPath}
|
||||
loading={false}
|
||||
name={entry.info.name}
|
||||
on:click={() => {
|
||||
onSelectFile({ detail: entry.path });
|
||||
}} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,143 @@
|
||||
<script lang="typescript">
|
||||
import { onMount } from 'svelte';
|
||||
import { navigate } from 'svelte-routing';
|
||||
import type { Config } from '@app/config';
|
||||
import * as proj from '@app/project';
|
||||
import Loading from '@app/Loading.svelte';
|
||||
|
||||
import Tree from './Tree.svelte';
|
||||
import Blob from './Blob.svelte';
|
||||
|
||||
export let urn: string;
|
||||
export let commit: string;
|
||||
export let config: Config;
|
||||
export let path: string;
|
||||
|
||||
let meta: "loading" | proj.Meta | null = null;
|
||||
let blob: Promise<proj.Blob | null> | null = null;
|
||||
|
||||
const fetchTree = async (path: string) => {
|
||||
return proj.getTree(urn, commit, path, config);
|
||||
};
|
||||
|
||||
const onSelect = async ({ detail: path }: { detail: string }) => {
|
||||
navigate(`/projects/${urn}/${commit}/${path}`);
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
meta = "loading";
|
||||
meta = await proj.getMetadata(urn, config);
|
||||
});
|
||||
|
||||
$: if (path === "/") {
|
||||
blob = proj.getReadme(urn, commit, config);
|
||||
} else {
|
||||
blob = proj.getBlob(urn, commit, path, config);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
main {
|
||||
width: 100%;
|
||||
padding: 4rem 0;
|
||||
}
|
||||
main > header {
|
||||
margin-bottom: 4rem;
|
||||
padding: 0 8rem;
|
||||
}
|
||||
.title {
|
||||
font-size: 2.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.anchor {
|
||||
display: inline-block;
|
||||
font-size: 0.75rem;
|
||||
font-family: var(--font-family-monospace);
|
||||
color: var(--color-secondary);
|
||||
background-color: var(--color-secondary-background);
|
||||
padding: 0.75rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.urn {
|
||||
font-family: var(--font-family-monospace);
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-foreground-faded);
|
||||
}
|
||||
.description {
|
||||
margin: 1rem 0 1.5rem 0;
|
||||
}
|
||||
|
||||
.center-content {
|
||||
margin: 0 auto;
|
||||
max-width: var(--content-max-width);
|
||||
min-width: var(--content-min-width);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
width: inherit;
|
||||
margin-bottom: 4rem;
|
||||
padding: 0 2rem 0 8rem;
|
||||
}
|
||||
|
||||
.column-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.column-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 1rem;
|
||||
min-width: var(--content-min-width);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.source-tree {
|
||||
overflow-x: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<main>
|
||||
<header>
|
||||
{#if meta === "loading"}
|
||||
<Loading small />
|
||||
{:else if meta}
|
||||
<div class="title bold">{meta.name}</div>
|
||||
<div class="urn">{urn}</div>
|
||||
<div class="description">{meta.description}</div>
|
||||
{/if}
|
||||
<div class="anchor">
|
||||
commit {commit}
|
||||
</div>
|
||||
</header>
|
||||
<div class="container center-content">
|
||||
<div class="column-left">
|
||||
<div class="source-tree">
|
||||
{#await proj.getTree(urn, commit, "/", config)}
|
||||
Loading..
|
||||
{:then tree}
|
||||
<Tree {tree} {path} {fetchTree} on:select={onSelect} />
|
||||
{:catch err}
|
||||
{err}
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column-right">
|
||||
{#await blob}
|
||||
<Loading small center />
|
||||
{:then blob}
|
||||
{#if blob}
|
||||
<Blob {blob} />
|
||||
{:else}
|
||||
<!-- Project has no README -->
|
||||
{/if}
|
||||
{:catch}
|
||||
<!-- TODO: Handle error -->
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -1,12 +1,10 @@
|
||||
<script type="typescript">
|
||||
import { onMount } from 'svelte';
|
||||
import type { Config } from '@app/config';
|
||||
import * as proj from '@app/project';
|
||||
import Loading from '@app/Loading.svelte';
|
||||
import Blockies from '@app/Blockies.svelte';
|
||||
|
||||
import { getMetadata } from './Project';
|
||||
import type { Project, Meta } from './Project';
|
||||
|
||||
enum Status { Loading, Loaded, Error }
|
||||
|
||||
type State =
|
||||
@@ -14,15 +12,15 @@
|
||||
| { status: Status.Loaded }
|
||||
| { status: Status.Error, error: string };
|
||||
|
||||
export let project: Project;
|
||||
export let project: proj.Project;
|
||||
export let config: Config;
|
||||
|
||||
let state: State = { status: Status.Loading };
|
||||
let meta: Meta | null = null;
|
||||
let meta: proj.Meta | null = null;
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const result = await getMetadata(project.id, config);
|
||||
const result = await proj.getMetadata(project.id, config);
|
||||
state = { status: Status.Loaded };
|
||||
meta = result;
|
||||
} catch (err) {
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
},
|
||||
"tokens": []
|
||||
},
|
||||
"seed": {
|
||||
"url": "https://sprout.radicle.xyz"
|
||||
},
|
||||
"abi": {
|
||||
"registrar": [
|
||||
"function rad() view returns (address)",
|
||||
|
||||
+6
-4
@@ -11,6 +11,7 @@ declare global {
|
||||
interface ImportMeta {
|
||||
env: {
|
||||
RADICLE_ALCHEMY_API_KEY: string | null,
|
||||
RADICLE_HTTP_API: string | null,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,9 +25,9 @@ export class Config {
|
||||
gasLimits: { createOrg: number };
|
||||
provider: ethers.providers.JsonRpcProvider;
|
||||
signer: ethers.Signer & TypedDataSigner | null;
|
||||
seed: { url: string };
|
||||
safe: { api: string | null, viewer: string | null };
|
||||
abi: { [contract: string]: string[] }
|
||||
seed: { api: string | null };
|
||||
tokens: string[];
|
||||
|
||||
constructor(
|
||||
@@ -34,13 +35,15 @@ export class Config {
|
||||
provider: ethers.providers.JsonRpcProvider,
|
||||
signer: ethers.Signer & TypedDataSigner | null,
|
||||
) {
|
||||
let cfg = (<Record<string, any>> config)[network.name];
|
||||
|
||||
const cfg = (<Record<string, any>> config)[network.name];
|
||||
const api = import.meta.env.RADICLE_HTTP_API;
|
||||
|
||||
if (!cfg) {
|
||||
throw `Network ${network.name} is not supported`;
|
||||
}
|
||||
|
||||
this.network = network;
|
||||
this.seed = { api };
|
||||
this.registrar = cfg.registrar;
|
||||
this.radToken = cfg.radToken;
|
||||
this.orgFactory = cfg.orgFactory;
|
||||
@@ -49,7 +52,6 @@ export class Config {
|
||||
this.provider = provider;
|
||||
this.signer = signer;
|
||||
this.gasLimits = gasLimits;
|
||||
this.seed = config.seed;
|
||||
this.abi = config.abi;
|
||||
this.tokens = cfg.tokens;
|
||||
}
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
import type { Config } from '@app/config';
|
||||
|
||||
export interface Project {
|
||||
id: string
|
||||
anchor: {
|
||||
stateHash: string
|
||||
stateHashFormat: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface Person {
|
||||
urn: string
|
||||
avatar: { emoji: string, background: { r: number, g: number, b: number } }
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
name: string
|
||||
description: string
|
||||
maintainers: Person[]
|
||||
}
|
||||
|
||||
export interface Author {
|
||||
avatar: string;
|
||||
email: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export enum ObjectType {
|
||||
Blob = "BLOB",
|
||||
Tree = "TREE",
|
||||
}
|
||||
|
||||
export interface CommitHeader {
|
||||
author: Author;
|
||||
committer: Author;
|
||||
committerTime: number;
|
||||
description: string;
|
||||
sha1: string;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
export interface Info {
|
||||
name: string;
|
||||
objectType: ObjectType;
|
||||
lastCommit: CommitHeader;
|
||||
}
|
||||
|
||||
export interface Entry {
|
||||
path: string;
|
||||
info: Info;
|
||||
}
|
||||
|
||||
export interface Blob {
|
||||
binary?: boolean;
|
||||
html?: boolean;
|
||||
content: string;
|
||||
path: string;
|
||||
info: Info;
|
||||
}
|
||||
|
||||
export interface Tree {
|
||||
entries: Entry[];
|
||||
info: Info;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export async function getMetadata(urn: string, config: Config): Promise<Meta | null> {
|
||||
const api = import.meta.env.RADICLE_HTTP_API;
|
||||
const response = await fetch(`${api}/v1/projects/${urn}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
if (! response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export async function getTree(
|
||||
urn: string,
|
||||
commit: string,
|
||||
path: string,
|
||||
config: Config
|
||||
): Promise<any | null> {
|
||||
if (! config.seed.api) return null;
|
||||
|
||||
const response = await fetch(`${config.seed.api}/v1/projects/${urn}/tree/${commit}/${path}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
if (! response.ok) {
|
||||
return null;
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function getBlob(
|
||||
urn: string,
|
||||
commit: string,
|
||||
path: string,
|
||||
config: Config
|
||||
): Promise<Blob | null> {
|
||||
if (! config.seed.api) return null;
|
||||
|
||||
const response = await fetch(`${config.seed.api}/v1/projects/${urn}/blob/${commit}/${path}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
if (! response.ok) {
|
||||
return null;
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function getReadme(
|
||||
urn: string,
|
||||
commit: string,
|
||||
config: Config
|
||||
): Promise<Blob | null> {
|
||||
if (! config.seed.api) return null;
|
||||
|
||||
const response = await fetch(`${config.seed.api}/v1/projects/${urn}/readme/${commit}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
if (! response.ok) {
|
||||
return null;
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
Reference in New Issue
Block a user