mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-12 05:50:35 +08:00
Implement org profile fallback
When an org doesn't have its own profile, but its owner does, we fallback on that profile. This allows for "user" orgs, where a user keeps his projects anchored under an org, but all profile information under the user.
This commit is contained in:
+3
-1
@@ -66,7 +66,9 @@
|
||||
<span class="badge">contract</span>
|
||||
{:else if addressType === AddressType.EOA}
|
||||
<a href={`/users/${address}`} target="_blank">{addressLabel}</a>
|
||||
{:else if !noBadge}
|
||||
{:else if profile?.name} <!-- While we're waiting to find out what address type it is -->
|
||||
<a href={explorerLink(address, config)} target="_blank">{addressLabel}</a>
|
||||
{:else}
|
||||
<div class="loading"><Loading small /></div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
export let name: string;
|
||||
export let width: number | null = null;
|
||||
export let height: number | null = null;
|
||||
export let inline: boolean = false;
|
||||
export let inline = false;
|
||||
|
||||
const icons = [
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { TransactionResponse } from '@ethersproject/providers';
|
||||
import type { ContractReceipt } from '@ethersproject/contracts';
|
||||
import { OperationType } from "@gnosis.pm/safe-core-sdk-types";
|
||||
|
||||
import { Profile } from '@app/profile';
|
||||
import { assert } from '@app/error';
|
||||
import * as utils from '@app/utils';
|
||||
import type { Config } from '@app/config';
|
||||
@@ -141,6 +142,10 @@ export class Org {
|
||||
return projects;
|
||||
}
|
||||
|
||||
async getProfile(config: Config): Promise<Profile> {
|
||||
return Org.getProfile(this.address, config);
|
||||
}
|
||||
|
||||
static async getAnchor(orgAddr: string, urn: string, config: Config): Promise<string | null> {
|
||||
const org = new ethers.Contract(
|
||||
orgAddr,
|
||||
@@ -207,6 +212,22 @@ export class Org {
|
||||
}
|
||||
}
|
||||
|
||||
// Return only org profile if there is one, otherwise tries to get the profile
|
||||
// of its owner.
|
||||
static async getProfile(address: string, config: Config): Promise<Profile> {
|
||||
const profile = await Profile.get(address, config);
|
||||
|
||||
if (profile.ens) {
|
||||
return profile;
|
||||
}
|
||||
const org = await Org.get(address, config);
|
||||
|
||||
if (org) {
|
||||
return Profile.get(org.owner, config);
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
// Return only Orgs that have a specific user as owner
|
||||
static async getOrgsByOwner(owner: string, config: Config): Promise<Org[]> {
|
||||
const orgsResult = await utils.querySubgraph(config.orgs.subgraph, GetOrgsByOwner, { owners: [owner] });
|
||||
|
||||
+30
-20
@@ -85,6 +85,10 @@
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -127,7 +131,7 @@
|
||||
{:then org}
|
||||
{#if org}
|
||||
<main>
|
||||
{#await Profile.get(address, config)}
|
||||
{#await org.getProfile(config)}
|
||||
<div class="centered">
|
||||
<Loading center />
|
||||
</div>
|
||||
@@ -139,6 +143,9 @@
|
||||
<div class="info">
|
||||
<span class="title bold">
|
||||
{parseEnsLabel(profile.name, config) ?? address}
|
||||
{#if profile.address === org.owner}
|
||||
<span class="badge">org</span>
|
||||
{/if}
|
||||
</span>
|
||||
<div class="links">
|
||||
{#if profile.url}
|
||||
@@ -176,25 +183,28 @@
|
||||
{/if}
|
||||
</div>
|
||||
<!-- Name -->
|
||||
<div class="label">Name</div>
|
||||
<div>
|
||||
{#if profile.name}
|
||||
<Link to={`/registrations/${parseEnsLabel(profile.name, config)}`}>{profile.name}</Link>
|
||||
{:else}
|
||||
<span class="subtle">Not set</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
{#await isAuthorized(org)}
|
||||
<!-- Loading -->
|
||||
{:then authorized}
|
||||
{#if authorized}
|
||||
<button class="tiny secondary" on:click={setName}>
|
||||
Set
|
||||
</button>
|
||||
<!-- Only show the name if we aren't already using the name of the owner -->
|
||||
{#if profile.address === org.address}
|
||||
<div class="label">Name</div>
|
||||
<div>
|
||||
{#if profile.name}
|
||||
<Link to={`/registrations/${parseEnsLabel(profile.name, config)}`}>{profile.name}</Link>
|
||||
{:else}
|
||||
<span class="subtle">Not set</span>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{#await isAuthorized(org)}
|
||||
<!-- Loading -->
|
||||
{:then authorized}
|
||||
{#if authorized}
|
||||
<button class="tiny secondary" on:click={setName}>
|
||||
Set
|
||||
</button>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#await org.getMembers(config)}
|
||||
@@ -224,7 +234,7 @@
|
||||
{:then projects}
|
||||
{#each projects as project}
|
||||
<div class="project">
|
||||
<Project {project} org={org.address} {config} seed={profile.seed} />
|
||||
<Project {project} org={org.address} config={profile.config(config)} />
|
||||
</div>
|
||||
{/each}
|
||||
{:catch err}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import Loading from '@app/Loading.svelte';
|
||||
import Modal from '@app/Modal.svelte';
|
||||
import Avatar from '@app/Avatar.svelte';
|
||||
import { Profile } from '@app/profile';
|
||||
import { Org } from '@app/base/orgs/Org';
|
||||
|
||||
import Browser from './Browser.svelte';
|
||||
|
||||
@@ -16,13 +16,11 @@
|
||||
export let path: string;
|
||||
|
||||
let projectRoot = proj.path({ urn, org, commit });
|
||||
let getProject = new Promise<string | undefined>(resolve => {
|
||||
let getProject = new Promise<string | null>(resolve => {
|
||||
if (org) {
|
||||
Profile.get(org, config).then((p: Profile) => {
|
||||
resolve(p.seed);
|
||||
});
|
||||
Org.getProfile(org, config).then(p => resolve(p?.seed || null));
|
||||
} else {
|
||||
resolve(undefined);
|
||||
resolve(null);
|
||||
}
|
||||
}).then(async (seed) => {
|
||||
const cfg = seed ? config.withSeed(seed) : config;
|
||||
|
||||
@@ -16,15 +16,13 @@
|
||||
export let project: proj.Project;
|
||||
export let config: Config;
|
||||
export let org: string | undefined;
|
||||
export let seed: string | undefined;
|
||||
|
||||
let state: State = { status: Status.Loading };
|
||||
let info: proj.Info | null = null;
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const cfg = seed ? config.withSeed(seed) : config;
|
||||
const result = await proj.getInfo(project.id, cfg);
|
||||
const result = await proj.getInfo(project.id, config);
|
||||
state = { status: Status.Loaded };
|
||||
info = result;
|
||||
} catch (err) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<style>
|
||||
main {
|
||||
padding: 5rem 0;
|
||||
width: 36rem;
|
||||
}
|
||||
main > header {
|
||||
display: flex;
|
||||
@@ -66,7 +67,7 @@
|
||||
</div>
|
||||
<div class="info">
|
||||
<span class="title bold">
|
||||
<Address compact noAvatar noBadge {address} {config} resolve/>
|
||||
<Address compact noAvatar noBadge {profile} {address} {config} resolve/>
|
||||
</span>
|
||||
<div class="links">
|
||||
{#if profile.url}
|
||||
@@ -93,7 +94,7 @@
|
||||
{#await org.getProjects(config) then projects}
|
||||
{#each projects as project}
|
||||
<div class="project">
|
||||
<Project {project} org={org.address} {config} seed={profile.seed} />
|
||||
<Project {project} org={org.address} config={profile.config(config)} />
|
||||
</div>
|
||||
{/each}
|
||||
{:catch err}
|
||||
|
||||
@@ -20,6 +20,16 @@ export class Profile {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
// Get the ENS profile
|
||||
get ens(): Registration | null {
|
||||
return this.profile.ens;
|
||||
}
|
||||
|
||||
// Get the IDX profle
|
||||
get idx(): BasicProfile | null {
|
||||
return this.profile.idx;
|
||||
}
|
||||
|
||||
// Using undefined as return type if nothing to be returned since it works better with <a href> links
|
||||
get github(): string | undefined {
|
||||
if (this.profile?.ens?.github) return parseUsername(this.profile.ens.github);
|
||||
@@ -60,6 +70,15 @@ export class Profile {
|
||||
return this.profile?.ens?.seedApi ?? undefined;
|
||||
}
|
||||
|
||||
// Return the profile-specific config. This sets various URLs in the config,
|
||||
// based on profile data.
|
||||
config(config: Config): Config {
|
||||
if (this.seed) {
|
||||
return config.withSeed(this.seed);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
// Keeping this function private since the desired entrypoint is .get()
|
||||
private static async lookupAddress(address: string, config: Config): Promise<[IProfile, string]> {
|
||||
const profile: IProfile = { ens: null, idx: null };
|
||||
|
||||
Reference in New Issue
Block a user