mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-31 01:00:47 +08:00
Show pinned orgs on the front page
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
+21
-1
@@ -34,6 +34,22 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
header .nav {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
margin-left: 1.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
header .nav a {
|
||||
display: inline-block;
|
||||
padding: 0.5rem 0.5rem;
|
||||
margin-right: 1.5rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-foreground-6);
|
||||
}
|
||||
header .nav a:hover {
|
||||
color: var(--color-foreground);
|
||||
}
|
||||
.logo {
|
||||
display: flex;
|
||||
}
|
||||
@@ -54,7 +70,7 @@
|
||||
.search {
|
||||
height: 42px;
|
||||
width: 16rem;
|
||||
margin-left: 1rem;
|
||||
margin-left: 1.5rem;
|
||||
display: inline-block;
|
||||
}
|
||||
.address {
|
||||
@@ -122,6 +138,10 @@
|
||||
<div class="search">
|
||||
<Search />
|
||||
</div>
|
||||
<div class="nav">
|
||||
<a use:link href="/orgs/">Orgs</a>
|
||||
<a use:link href="/registrations">Register</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
|
||||
<main>
|
||||
<div>
|
||||
<Orgs {config} />
|
||||
<Orgs {config} pinned />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
import List from './List.svelte';
|
||||
|
||||
export let config: Config;
|
||||
export let pinned = false;
|
||||
|
||||
const getOrgs = pinned && (config.orgs.pinned.length > 0)
|
||||
? Org.getMulti(config.orgs.pinned, config)
|
||||
: Org.getAll(config);
|
||||
const onCreate = () => modal = Create;
|
||||
let modal: typeof SvelteComponent | null = null;
|
||||
|
||||
@@ -32,14 +36,14 @@
|
||||
}
|
||||
|
||||
.padding {
|
||||
padding: 0 3rem 3rem 3rem;
|
||||
padding: 0 2rem 2rem 2rem;
|
||||
}
|
||||
|
||||
.my-orgs {
|
||||
margin-bottom: 3rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.orgs-empty {
|
||||
margin-left: 3rem;
|
||||
margin-left: 2rem;
|
||||
padding: 1rem 0 2rem 0;
|
||||
font-style: italic;
|
||||
color: var(--color-foreground-faded);
|
||||
@@ -50,7 +54,7 @@
|
||||
}
|
||||
|
||||
.loading {
|
||||
padding: 3rem 0;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -90,7 +94,7 @@
|
||||
<span><strong>Orgs</strong> of the Radicle network</span>
|
||||
</header>
|
||||
|
||||
{#await Org.getAll(config)}
|
||||
{#await getOrgs}
|
||||
<div class="loading">
|
||||
<Loading center />
|
||||
</div>
|
||||
|
||||
+37
-13
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ethers } from 'ethers';
|
||||
import { onMount } from 'svelte';
|
||||
import { Link } from 'svelte-routing';
|
||||
import type { Org } from '@app/base/orgs/Org';
|
||||
import Avatar from '@app/Avatar.svelte';
|
||||
@@ -10,32 +12,43 @@
|
||||
export let config: Config;
|
||||
export let orgs: Org[];
|
||||
|
||||
const orgsAddresses = orgs.map(org => org.address);
|
||||
const orgMembers: Record<string, string[]> = {};
|
||||
const getProfiles = Profile.getMulti(orgs.map(o => o.name ?? o.address), config);
|
||||
|
||||
onMount(async () => {
|
||||
const promises = orgs.map(org => org.getMembers(config).then(members => {
|
||||
orgMembers[ethers.utils.getAddress(org.address)] = members;
|
||||
}));
|
||||
|
||||
Promise.all(promises);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.org {
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
width: 10rem;
|
||||
height: 10rem;
|
||||
margin-left: 3rem;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
.org:hover .org-label {
|
||||
color: var(--color-background);
|
||||
background: var(--color-secondary);
|
||||
.org:hover {
|
||||
background: var(--color-foreground-background-lighter);
|
||||
}
|
||||
.org-avatar {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
.org-label {
|
||||
color: var(--color-foreground-90);
|
||||
display: inline-block;
|
||||
font-weight: var(--font-weight-medium);
|
||||
margin-top: 0.75rem;
|
||||
border-radius: 0.75rem;
|
||||
padding: 0rem 0.5rem;
|
||||
@@ -43,6 +56,10 @@
|
||||
overflow-x: hidden;
|
||||
max-width: 8rem;
|
||||
}
|
||||
.org-members {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-foreground-faded);
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -53,15 +70,15 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
{#await Profile.getMulti(orgsAddresses, config)}
|
||||
{#await getProfiles}
|
||||
<div class="loading">
|
||||
<Loading center />
|
||||
</div>
|
||||
{:then profiles}
|
||||
<div class="list">
|
||||
{#each profiles as profile}
|
||||
<div class="org">
|
||||
<Link to={`/orgs/${profile.nameOrAddress}`}>
|
||||
<Link to={`/orgs/${profile.nameOrAddress}`}>
|
||||
<div class="org">
|
||||
<div class="org-avatar">
|
||||
<Avatar source={profile.avatar ?? profile.address} />
|
||||
</div>
|
||||
@@ -72,8 +89,15 @@
|
||||
{formatAddress(profile.address)}
|
||||
{/if}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div class="org-members">
|
||||
{#if orgMembers[profile.address]?.length}
|
||||
{orgMembers[profile.address].length} members
|
||||
{:else}
|
||||
{formatAddress(profile.address)}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
{:else}
|
||||
<slot />
|
||||
{/each}
|
||||
|
||||
@@ -172,6 +172,13 @@ export class Org {
|
||||
}
|
||||
}
|
||||
|
||||
static async getMulti(ids: string[], config: Config): Promise<Array<Org>> {
|
||||
const results = await Promise.all(ids.map(addr => Org.get(addr, config)));
|
||||
const orgs = results.filter((org): org is Org => org != null);
|
||||
|
||||
return orgs;
|
||||
}
|
||||
|
||||
static async getAll(config: Config): Promise<Array<Org>> {
|
||||
const result = await utils.querySubgraph(config.orgs.subgraph, GetOrgs, {});
|
||||
const orgs: Org[] = [];
|
||||
|
||||
+10
-2
@@ -12,7 +12,14 @@
|
||||
},
|
||||
"orgs": {
|
||||
"subgraph": "https://gateway.thegraph.com/api/1758a78ae257ad4906f9c638e4a68c19/subgraphs/id/0x2f0963e77ca6ac0c2dad1bf4147b6b40e0dd8728-0",
|
||||
"contractHash": "0x5c34bb0755876de98e801805e6685456eea739ad0abba1cc450a7ee0f2a70b74"
|
||||
"contractHash": "0x5c34bb0755876de98e801805e6685456eea739ad0abba1cc450a7ee0f2a70b74",
|
||||
"pinned": [
|
||||
"alt-clients.radicle.eth",
|
||||
"community.radicle.eth",
|
||||
"upstream.radicle.eth",
|
||||
"gnosisguild.radicle.eth",
|
||||
"dxdao.radicle.eth"
|
||||
]
|
||||
},
|
||||
"safe": {
|
||||
"api": "https://safe-transaction.gnosis.io",
|
||||
@@ -37,7 +44,8 @@
|
||||
},
|
||||
"orgs": {
|
||||
"subgraph": "https://api.thegraph.com/subgraphs/name/radicle-dev/radicle-orgs-rinkeby",
|
||||
"contractHash": "0x5c34bb0755876de98e801805e6685456eea739ad0abba1cc450a7ee0f2a70b74"
|
||||
"contractHash": "0x5c34bb0755876de98e801805e6685456eea739ad0abba1cc450a7ee0f2a70b74",
|
||||
"pinned": []
|
||||
},
|
||||
"safe": {
|
||||
"api": "https://safe-transaction.rinkeby.gnosis.io",
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ export class Config {
|
||||
radToken: { address: string };
|
||||
orgFactory: { address: string };
|
||||
reverseRegistrar: { address: string };
|
||||
orgs: { subgraph: string; contractHash: string };
|
||||
orgs: { subgraph: string; contractHash: string; pinned: string[] };
|
||||
gasLimits: { createOrg: number };
|
||||
provider: ethers.providers.JsonRpcProvider;
|
||||
signer: ethers.Signer & TypedDataSigner | WalletConnectSigner | null;
|
||||
|
||||
@@ -264,6 +264,9 @@ export async function resolveEnsProfile(addressOrName: string, profileType: Prof
|
||||
|
||||
if (name) {
|
||||
const resolver = await config.provider.getResolver(name);
|
||||
if (! resolver) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (profileType === ProfileType.Full) {
|
||||
const registration = await getRegistration(name, config, resolver);
|
||||
|
||||
Reference in New Issue
Block a user