mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-28 16:00:45 +08:00
Only have projects on home page
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
@@ -2,28 +2,22 @@
|
||||
import { MockExtensionProvider } from "../support/e2e";
|
||||
|
||||
describe("Landing page", () => {
|
||||
it("Loads user, orgs and seeds", () => {
|
||||
it("Loads projects", () => {
|
||||
cy.intercept("https://willow.radicle.garden:8777/", { fixture: "projectHome.json" });
|
||||
cy.intercept("https://willow.radicle.garden:8777/v1/peer", { fixture: "projectPeer.json" });
|
||||
cy.intercept("https://pine.radicle.garden:8777/", { fixture: "projectHome.json" });
|
||||
cy.intercept("https://pine.radicle.garden:8777/v1/peer", { fixture: "projectPeer.json" });
|
||||
cy.intercept("https://maple.radicle.garden:8777/", { fixture: "projectHome.json" });
|
||||
cy.intercept("https://maple.radicle.garden:8777/v1/peer", { fixture: "projectPeer.json" });
|
||||
cy.intercept("https://gateway.thegraph.com/api/1758a78ae257ad4906f9c638e4a68c19/subgraphs/id/0x2f0963e77ca6ac0c2dad1bf4147b6b40e0dd8728-0", {
|
||||
"data": { "safe": null }
|
||||
});
|
||||
cy.intercept("https://maple.radicle.garden:8777/v1/projects", { fixture: "projectList.json" });
|
||||
cy.intercept("https://pine.radicle.garden:8777/v1/projects", { fixture: "projectList.json" });
|
||||
cy.intercept("https://willow.radicle.garden:8777/v1/projects", { fixture: "projectList.json" });
|
||||
cy.intercept("https://maple.radicle.garden:8777/v1/projects/*", { fixture: "projectInfo.json" });
|
||||
cy.intercept({ pathname: "/v1/projects/*" }, { fixture: "projectInfo.json" });
|
||||
cy.visit("/", {
|
||||
onBeforeLoad(win) {
|
||||
win.ethereum = new MockExtensionProvider("homestead", "0x3256a804085C24f3451cAb2C98a37e16DEEc5721");
|
||||
},
|
||||
});
|
||||
cy.get("div.card-label")
|
||||
cy.get(".project .name")
|
||||
.first()
|
||||
.should("have.text", "willow.radicle.garden")
|
||||
.next()
|
||||
.should("have.text", "1 project(s)");
|
||||
.should("have.text", "bright-forest-protocol");
|
||||
});
|
||||
});
|
||||
|
||||
+39
-32
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { navigate } from 'svelte-routing';
|
||||
import type { Config } from '@app/config';
|
||||
import { Profile } from '@app/profile';
|
||||
import { Org } from '@app/base/orgs/Org';
|
||||
import Loading from '@app/Loading.svelte';
|
||||
import Widget from '@app/base/projects/Widget.svelte';
|
||||
import { Project, ProjectInfo } from '@app/project';
|
||||
import type { Host } from '@app/api';
|
||||
import * as proj from "@app/project";
|
||||
import Message from '@app/Message.svelte';
|
||||
import Cards from '@app/Cards.svelte';
|
||||
import { setOpenGraphMetaTag } from '@app/utils';
|
||||
import { Seed } from '@app/base/seeds/Seed';
|
||||
|
||||
export let config: Config;
|
||||
|
||||
@@ -16,21 +17,18 @@
|
||||
{ prop: "og:url", content: window.location.href }
|
||||
]);
|
||||
|
||||
const getOrgs = config.orgs.pinned.length > 0
|
||||
? Org.getMulti(config.orgs.pinned, config)
|
||||
: Org.getAll(config);
|
||||
|
||||
const getUsers = config.users.pinned.length > 0
|
||||
? Profile.getMulti(config.users.pinned, config)
|
||||
const getProjects = config.projects.pinned.length > 0
|
||||
? Project.getMulti(config.projects.pinned)
|
||||
: Promise.resolve([]);
|
||||
|
||||
const getSeeds = Object.keys(config.seeds.pinned).length > 0
|
||||
? Seed.lookupMulti(Object.keys(config.seeds.pinned), config)
|
||||
: Promise.resolve([]);
|
||||
|
||||
const getEntities = Promise.all([getUsers, getOrgs, getSeeds]).then(([users, orgs, seeds]) => {
|
||||
return { users, orgs, seeds };
|
||||
});
|
||||
const onClick = (project: ProjectInfo, seed: Host) => {
|
||||
navigate(proj.path({
|
||||
urn: project.urn,
|
||||
seed: seed.host,
|
||||
profile: null,
|
||||
revision: project.head,
|
||||
}));
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -48,10 +46,21 @@
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.projects {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
.project {
|
||||
width: 16rem;
|
||||
}
|
||||
.heading {
|
||||
color: var(--color-secondary);
|
||||
padding: 1rem 0rem;
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.loading {
|
||||
padding-top: 2rem;
|
||||
@@ -77,31 +86,29 @@
|
||||
peer-to-peer network 🌐 built on Git.</p>
|
||||
</div>
|
||||
|
||||
{#await getEntities}
|
||||
{#await getProjects}
|
||||
<div class="loading">
|
||||
<Loading center />
|
||||
</div>
|
||||
{:then entities}
|
||||
{#if entities.seeds.length}
|
||||
{:then results}
|
||||
{#if results.length}
|
||||
<div class="heading">
|
||||
Explore <strong>seed nodes</strong> on the Radicle network.
|
||||
Explore <strong>projects</strong> on the Radicle network.
|
||||
</div>
|
||||
<Cards {config} seeds={entities.seeds}>
|
||||
<div class="empty">There are no seed nodes.</div>
|
||||
</Cards>
|
||||
{/if}
|
||||
{#if entities.orgs.length || entities.users.length}
|
||||
<div class="heading">
|
||||
Explore <strong>orgs</strong> and <strong>users</strong> on the Radicle network.
|
||||
|
||||
<div class="projects">
|
||||
{#each results as result}
|
||||
<div class="project">
|
||||
<Widget compact project={result.info} seed={{ api: result.seed }}
|
||||
on:click={() => onClick(result.info, result.seed)} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<Cards {config} profiles={entities.users} orgs={entities.orgs}>
|
||||
<div class="empty">There are no orgs or users.</div>
|
||||
</Cards>
|
||||
{/if}
|
||||
{:catch}
|
||||
<div class="padding">
|
||||
<Message error>
|
||||
<strong>Error: </strong> failed to load orgs and users.
|
||||
<strong>Error: </strong> failed to load projects.
|
||||
</Message>
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
import AnchorBadge from '@app/base/profiles/AnchorBadge.svelte';
|
||||
import Diagram from '@app/Diagram.svelte';
|
||||
import { groupCommitsByWeek } from '@app/commit';
|
||||
import type { Host } from '@app/api';
|
||||
import { Project } from '@app/project';
|
||||
import type { Seed } from '@app/base/seeds/Seed';
|
||||
import { formatCommit } from '@app/utils';
|
||||
|
||||
export let project: proj.ProjectInfo;
|
||||
export let seed: Seed;
|
||||
export let seed: { api: Host };
|
||||
export let faded = false;
|
||||
export let anchor: proj.Anchor | null = null;
|
||||
export let compact = false;
|
||||
|
||||
const getTimestampOneYearAgo = () => {
|
||||
const now = new Date();
|
||||
@@ -48,8 +50,22 @@
|
||||
}
|
||||
div .description {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
article.compact {
|
||||
min-width: 16rem;
|
||||
height: 8rem;
|
||||
}
|
||||
article.compact .left {
|
||||
width: 100%;
|
||||
}
|
||||
article.compact .right {
|
||||
display: none;
|
||||
}
|
||||
article.compact .description {
|
||||
white-space: nowrap;
|
||||
}
|
||||
article.project-faded {
|
||||
border: 1px dashed var(--color-foreground-subtle);
|
||||
cursor: not-allowed;
|
||||
@@ -126,7 +142,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<article on:click class:project-faded={faded}>
|
||||
<article on:click class:project-faded={faded} class:compact>
|
||||
<div class="left">
|
||||
<div class="id">
|
||||
<span class="name">{project.name}</span>
|
||||
@@ -136,7 +152,11 @@
|
||||
<span class="commit">
|
||||
<slot name="stateHash">
|
||||
{#if project.head}
|
||||
{project.head}
|
||||
{#if compact}
|
||||
{formatCommit(project.head)}
|
||||
{:else}
|
||||
{project.head}
|
||||
{/if}
|
||||
{:else}
|
||||
<span class="subtle">✗ No head</span>
|
||||
{/if}
|
||||
@@ -144,36 +164,39 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="id">
|
||||
<span class="urn desktop">{project.urn}</span>
|
||||
</div>
|
||||
<div class="anchor">
|
||||
<span class="anchor-info">
|
||||
<span class="actions">
|
||||
<slot name="actions">
|
||||
</slot>
|
||||
</span>
|
||||
<span class="anchor-badge">
|
||||
<slot name="anchor">
|
||||
{#if anchor && project.head}
|
||||
<AnchorBadge
|
||||
commit={project.head}
|
||||
head={project.head} noText noBg
|
||||
anchors={[anchor.anchor.stateHash]} />
|
||||
{/if}
|
||||
</slot>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{#await loadCommits() then points}
|
||||
<div class="desktop activity">
|
||||
<Diagram {points}
|
||||
strokeWidth={3}
|
||||
viewBoxHeight={100}
|
||||
viewBoxWidth={600}
|
||||
/>
|
||||
|
||||
{#if !compact}
|
||||
<div class="right">
|
||||
<div class="id">
|
||||
<span class="urn desktop">{project.urn}</span>
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
||||
<div class="anchor">
|
||||
<span class="anchor-info">
|
||||
<span class="actions">
|
||||
<slot name="actions">
|
||||
</slot>
|
||||
</span>
|
||||
<span class="anchor-badge">
|
||||
<slot name="anchor">
|
||||
{#if anchor && project.head}
|
||||
<AnchorBadge
|
||||
commit={project.head}
|
||||
head={project.head} noText noBg
|
||||
anchors={[anchor.anchor.stateHash]} />
|
||||
{/if}
|
||||
</slot>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{#await loadCommits() then points}
|
||||
<div class="desktop activity">
|
||||
<Diagram {points}
|
||||
strokeWidth={3}
|
||||
viewBoxHeight={100}
|
||||
viewBoxWidth={600}
|
||||
/>
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
||||
{/if}
|
||||
</article>
|
||||
|
||||
@@ -99,6 +99,50 @@
|
||||
"git": { "port": 443 }
|
||||
}
|
||||
},
|
||||
"projects": {
|
||||
"pinned": [
|
||||
{
|
||||
"name": "radicle-cli",
|
||||
"urn": "rad:git:hnrkmg77m8tfzj4gi4pa4mbhgysfgzwntjpao",
|
||||
"seed": "seed.alt-clients.radicle.xyz"
|
||||
},
|
||||
{
|
||||
"name": "radicle-interface",
|
||||
"urn": "rad:git:hnrkjajuucc6zp5eknt3s9xykqsrus44cjimy",
|
||||
"seed": "seed.alt-clients.radicle.xyz"
|
||||
},
|
||||
{
|
||||
"name": "radicle-client-services",
|
||||
"urn": "rad:git:hnrkk9c4zt9thuxhwi1ukxqcrs5tmhbtcsony",
|
||||
"seed": "seed.alt-clients.radicle.xyz"
|
||||
},
|
||||
{
|
||||
"name": "solmate",
|
||||
"urn": "rad:git:hnrkbgczcfh9ycjtdfynmqyg478bqrso1hnty",
|
||||
"seed": "willow.radicle.garden"
|
||||
},
|
||||
{
|
||||
"name": "openzeppelin-contracts",
|
||||
"urn": "rad:git:hnrkbx9xzjg8tf9hj9uh5qdxwuyjddsxyaw6o",
|
||||
"seed": "willow.radicle.garden"
|
||||
},
|
||||
{
|
||||
"name": "haskell-language-server",
|
||||
"urn": "rad:git:hnrkf1jaje1e93rua64zgphg7zb14qo9zexgy",
|
||||
"seed": "pine.radicle.garden"
|
||||
},
|
||||
{
|
||||
"name": "rx",
|
||||
"urn": "rad:git:hnrk8aoks6w3tdbcwt9dqnor9aqy8tjha3k5o",
|
||||
"seed": "maple.radicle.garden"
|
||||
},
|
||||
{
|
||||
"name": "py_ecc",
|
||||
"urn": "rad:git:hnrkba67x8poqt4d9qgoc4au39ro9dx7pdhfo",
|
||||
"seed": "maple.radicle.garden"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ipfs": { "gateway": "https://ipfs.io/ipfs/" },
|
||||
"abi": {
|
||||
"registrar": [
|
||||
|
||||
@@ -35,6 +35,7 @@ export class Config {
|
||||
reverseRegistrar: { address: string };
|
||||
orgs: { subgraph: string; contractHash: string; pinned: string[] };
|
||||
users: { pinned: string[] };
|
||||
projects: { pinned: { urn: string; name: string; seed: string }[] };
|
||||
seeds: { pinned: Record<string, { emoji: string }> };
|
||||
gasLimits: { createOrg: number };
|
||||
provider: ethers.providers.JsonRpcProvider;
|
||||
@@ -120,6 +121,7 @@ export class Config {
|
||||
this.provider = provider;
|
||||
this.signer = null;
|
||||
this.gasLimits = gasLimits;
|
||||
this.projects = config.projects;
|
||||
this.abi = config.abi;
|
||||
this.ceramic = {
|
||||
client: ceramic,
|
||||
|
||||
@@ -432,4 +432,20 @@ export class Project implements ProjectInfo {
|
||||
|
||||
return new Project(urn, info, seed, peers, remote.heads, profile, anchors);
|
||||
}
|
||||
|
||||
static async getMulti(projs: { urn: Urn; seed: string }[]): Promise<{ info: ProjectInfo; seed: Host }[]> {
|
||||
const promises = [];
|
||||
|
||||
for (const proj of projs) {
|
||||
const seed = { host: proj.seed, port: null };
|
||||
promises.push(Project.getInfo(proj.urn, seed).then(info => {
|
||||
return { info, seed };
|
||||
}));
|
||||
}
|
||||
const results = await Promise.allSettled(promises);
|
||||
const isFulfilled = <T>(input: PromiseSettledResult<T>):
|
||||
input is PromiseFulfilledResult<T> => input.status === 'fulfilled';
|
||||
|
||||
return results.filter(isFulfilled).map(r => r.value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user