Add delegate info to peer selector

This commit also adds a highlight to the currently selected peer and
branch.

Signed-off-by: Sebastian Martinez <sebastinez@me.com>
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Sebastian Martinez
2022-02-23 15:09:21 +01:00
committed by Alexis Sellier
parent d69907be45
commit 856b53dae8
7 changed files with 149 additions and 21 deletions
+6
View File
@@ -31,6 +31,7 @@
--color-light: #add0e4;
--color-yellow: #ffff99;
--color-yellow-background: #ffff9911;
--color-yellow-background-lighter: #ffff9922;
--color-positive: #53db53;
--color-positive-background: #53db5311;
--color-positive-1: #11332b;
@@ -51,6 +52,7 @@
--color-foreground-faded: #777788;
--color-foreground-subtle: #444455;
--color-foreground-subtler: #333344;
--color-foreground-even-subtler: #292936;
--color-foreground-background: #121a21;
--color-foreground-background-subtle: #10171e;
--color-foreground-background-lighter: #151f24;
@@ -337,6 +339,10 @@ label.input {
font-size: 0.75rem;
line-height: 1.6;
}
.badge.primary {
color: var(--color-primary);
background: var(--color-primary-background);
}
.badge.tertiary {
color: var(--color-tertiary);
background: var(--color-tertiary-background);
+13 -5
View File
@@ -1,7 +1,8 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
export let items: string[];
export let items: { key: string; value: string; badge: string | null }[];
export let selected: string | null;
export let visible = false;
const dispatch = createEventDispatcher();
@@ -17,15 +18,18 @@
margin-top: 0.5rem;
border-radius: 0.25rem;
position: absolute;
box-shadow: 8px 8px 24px var(--color-shadow);
box-shadow: 16px 16px 32px 32px var(--color-shadow);
}
.dropdown-item {
cursor: pointer;
padding: 0.5rem 1rem;
}
.dropdown-item:hover {
.dropdown-item:hover, .selected {
background-color: var(--color-foreground-background-lighter);
}
.dropdown .badge {
margin: 0;
}
@media (max-width: 720px) {
.dropdown {
@@ -37,8 +41,12 @@
{#if visible}
<div class="dropdown">
{#each items as item}
<div class="dropdown-item" on:click={() => onSelect(item)}>{item}</div>
{#each items as {key, value, badge}}
<div class="dropdown-item" class:selected={value === selected} on:click={() => onSelect(value)} title={value}>{@html key}
{#if badge}
<span class="badge primary">{badge}</span>
{/if}
</div>
{/each}
</div>
{/if}
+2 -1
View File
@@ -17,7 +17,7 @@
let branchLabel: string | null = null;
$: branchList = Object.keys(branches).sort();
$: branchList = Object.keys(branches).sort().map(b => ({ key: b, value: b, badge: null }));
$: showSelector = branchList.length > 1;
$: head = branches[project.defaultBranch];
$: commit = getOid(revision, branches) || head;
@@ -81,6 +81,7 @@
</div>
<Dropdown
items={branchList}
selected={branchLabel}
visible={branchesDropdown}
on:select={(e) => switchBranch(e.detail)} />
</span>
+6 -6
View File
@@ -73,16 +73,16 @@
}
.clone {
color: var(--color-primary);
background-color: var(--color-primary-background);
color: var(--color-yellow);
background-color: var(--color-yellow-background);
font-family: var(--font-family-monospace);
padding: 0.5rem 0.75rem;
border-radius: 0.25rem;
cursor: pointer;
user-select: none;
}
.clone:hoverr {
background-color: var(--color-primary-background-lighter);
.clone:hover {
background-color: var(--color-yellow-background-lighter);
}
.commit-count {
cursor: pointer;
@@ -103,8 +103,8 @@
display: block;
}
.clone-dropdown input {
color: var(--color-primary);
background: var(--color-primary-background);
color: var(--color-yellow);
background: var(--color-yellow-background);
}
.dropdown input {
font-size: 0.75rem;
+80
View File
@@ -0,0 +1,80 @@
import PeerSelector from "./PeerSelector.svelte";
import { mount } from "radicle-svelte-unit-test";
import { styles } from "@test/support/index";
const defaultProps = {
peer: "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
peers: [
{
"id": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
"name": "sebastinez",
"delegate": true
}
],
toggleDropdown: () => console.log("toggle"),
};
describe('PeerSelector', function () {
it("Render correctly with default props", () => {
mount(PeerSelector, {
props: defaultProps
}, styles);
cy.get("span.peer-id").should("has.text", "sebastinez");
cy.get("span.badge").should("has.text", "delegate");
});
it("Test Peer selection", () => {
mount(PeerSelector, {
props: {
...defaultProps, peers: [
{
"id": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
"name": "sebastinez",
"delegate": false
},
{
"id": "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1",
"name": "cloudhead",
"delegate": true
},
],
peersDropdown: true
}, callbacks: {
peerChanged: cy.stub().as("peerChanged")
}
}, styles);
cy.get("div.dropdown > div").last().click();
cy.get("@peerChanged")
.should("be.calledOnce")
.its("firstCall.args.0.detail")
.should("equal", "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1");
});
it("If no peers are provided, no dropdown should be showed", () => {
mount(PeerSelector, {
props: {
...defaultProps,
peers: [],
peersDropdown: true
}
}, styles);
cy.get("div.dropdown > div.dropdown-item").should("not.exist");
});
it("If peer identity is not being resolved, fallback to peer id", () => {
mount(PeerSelector, {
props: {
...defaultProps,
peers: [
{
"id": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
}
],
peersDropdown: true
}
}, styles);
cy.get("span.peer-id").should("has.text", "hyyg55…p7ofue");
cy.get("span.badge").should("not.exist");
cy.get("div.dropdown > .dropdown-item")
.first()
.should("contain", "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue");
});
});
+32 -5
View File
@@ -1,14 +1,29 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { createEventDispatcher, onMount } from "svelte";
import Icon from "@app/Icon.svelte";
import Dropdown from "@app/Dropdown.svelte";
import { formatSeedId } from "@app/utils";
import type { Peer } from "@app/project";
export let peer: string | null = null;
export let peers: (string | { id: string })[];
export let peers: Peer[];
export let toggleDropdown: (input: string) => void;
export let peersDropdown = false;
let meta: Peer | undefined;
// List of items to be created for the Dropdown component.
let items: { key: string; value: string; badge: string | null }[] = [];
onMount(() => {
meta = peers.find(p => p.id === peer);
items = peers.map(p => {
if (! p.name) console.debug("Not able to resolve peer identity for: ", p.id);
let key = p.name ? `<strong>${p.name}</strong> ${p.id}` : p.id;
return { key, value: p.id, badge: p.delegate ? "delegate" : null };
});
});
const dispatch = createEventDispatcher();
const switchPeer = (peer: string) => {
dispatch("peerChanged", peer);
@@ -32,8 +47,11 @@
.selector .peer.not-allowed {
cursor: not-allowed;
}
.selector .badge {
margin: 0;
}
.peer-id {
margin-left: 0.5rem;
margin: 0 0.5rem;
}
.peer:hover {
background-color: var(--color-foreground-background-lighter);
@@ -52,14 +70,23 @@
<span>
<div on:click={() => toggleDropdown("peer")} class="stat peer" class:not-allowed={!peers}>
<Icon name="fork" width={15} height={15} />
{#if peer}
{#if meta}
<span class="peer-id">
{meta.name ?? formatSeedId(meta.id)}
</span>
{#if meta.delegate}
<span class="badge primary">delegate</span>
{/if}
<!-- If the delegate metadata is not found -->
{:else if peer}
<span class="peer-id">
{formatSeedId(peer)}
</span>
{/if}
</div>
<Dropdown
items={peers.map((p) => typeof(p) == "string" ? p : p.id)}
{items}
selected={peer}
visible={peersDropdown}
on:select={(e) => switchPeer(e.detail)}
/>
+10 -4
View File
@@ -86,6 +86,12 @@ export interface Remote {
heads: Branches;
}
export interface Peer {
id: PeerId;
name: string;
delegate: boolean;
}
export interface Browser {
content: ProjectContent;
revision: string | null;
@@ -203,12 +209,12 @@ export class Project implements ProjectInfo {
maintainers: Urn[];
delegates: PeerId[];
seed: Seed;
peers: (string | { id: string })[];
peers: Peer[];
branches: Branches;
profile: Profile | null;
anchors: string[];
constructor(urn: string, info: ProjectInfo, seed: Seed, peers: (string | { id: string })[], branches: Branches, profile: Profile | null, anchors: string[]) {
constructor(urn: string, info: ProjectInfo, seed: Seed, peers: Peer[], branches: Branches, profile: Profile | null, anchors: string[]) {
this.urn = urn;
this.head = info.head;
this.name = info.name;
@@ -258,7 +264,7 @@ export class Project implements ProjectInfo {
return api.get(`projects/${urn}/remotes/${peer}`, {}, host);
}
static async getRemotes(urn: string, host: api.Host): Promise<(PeerId | { id: string })[]> {
static async getRemotes(urn: string, host: api.Host): Promise<Peer[]> {
return api.get(`projects/${urn}/remotes`, {}, host);
}
@@ -331,7 +337,7 @@ export class Project implements ProjectInfo {
// Older versions of http-api don't include the URN.
if (! info.urn) info.urn = urn;
const peers: (PeerId | { id: string })[] = info.delegates
const peers: Peer[] = info.delegates
? await Project.getRemotes(urn, seed.api)
: [];