Only have projects on home page

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier
2022-08-24 10:06:13 +02:00
parent c4769c8127
commit dbfa9862dd
6 changed files with 164 additions and 78 deletions
+5 -11
View File
@@ -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
View File
@@ -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}
+58 -35
View File
@@ -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>
+44
View File
@@ -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": [
+2
View File
@@ -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,
+16
View File
@@ -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);
}
}