mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-18 19:30:27 +08:00
Tweak copyable IDs
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
import ExtendedTextarea from "@app/components/ExtendedTextarea.svelte";
|
||||
import IconButton from "@app/components/IconButton.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
import Markdown from "@app/components/Markdown.svelte";
|
||||
import NodeId from "@app/components/NodeId.svelte";
|
||||
import ReactionSelector from "@app/components/ReactionSelector.svelte";
|
||||
@@ -136,15 +137,10 @@
|
||||
{/if}
|
||||
<div class="card-header" class:card-header-no-icon={isReply}>
|
||||
<slot class="icon" name="icon" />
|
||||
<NodeId
|
||||
stylePopoverPositionLeft="-13px"
|
||||
nodeId={authorId}
|
||||
alias={authorAlias} />
|
||||
<NodeId nodeId={authorId} alias={authorAlias} />
|
||||
<slot name="caption">{caption}</slot>
|
||||
{#if id}
|
||||
<span class="global-oid">
|
||||
{utils.formatObjectId(id)}
|
||||
</span>
|
||||
<Id {id} />
|
||||
{/if}
|
||||
<span class="timestamp" title={utils.absoluteTimestamp(timestamp)}>
|
||||
{utils.formatTimestamp(timestamp)}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { SvelteComponent } from "svelte";
|
||||
import Clipboard from "@app/components/Clipboard.svelte";
|
||||
|
||||
export let id: string;
|
||||
export let style: "commit" | "oid";
|
||||
|
||||
let clipboard: SvelteComponent;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.id {
|
||||
cursor: pointer;
|
||||
color: var(--color-foreground-emphasized);
|
||||
overflow-wrap: anywhere;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.125rem;
|
||||
width: fit-content;
|
||||
}
|
||||
.id:hover {
|
||||
color: var(--color-foreground-emphasized-hover);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={() => {
|
||||
clipboard.copy();
|
||||
}}
|
||||
class="id">
|
||||
<span
|
||||
class="txt-overflow"
|
||||
class:global-commit={style === "commit"}
|
||||
class:global-oid={style === "oid"}
|
||||
style="color: inherit">
|
||||
<slot>{id}</slot>
|
||||
</span>
|
||||
<Clipboard bind:this={clipboard} text={id} />
|
||||
</div>
|
||||
@@ -1,32 +1,26 @@
|
||||
<script lang="ts">
|
||||
import debounce from "lodash/debounce";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
export let onShow: () => void = () => {};
|
||||
export let stylePopoverPositionLeft: string | undefined = undefined;
|
||||
export let stylePopoverPositionRight: string | undefined = undefined;
|
||||
export let stylePopoverPositionTop: string | undefined = undefined;
|
||||
export let stylePopoverPositionBottom: string | undefined = undefined;
|
||||
export let stylePopoverPadding: string | undefined = "1rem";
|
||||
export let stylePopoverPositionLeft: string | undefined = undefined;
|
||||
|
||||
let visible: boolean = false;
|
||||
|
||||
const setVisible = debounce((value: boolean) => {
|
||||
visible = value;
|
||||
if (visible) {
|
||||
onShow();
|
||||
}
|
||||
}, 500);
|
||||
}, 50);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.popover {
|
||||
background: var(--color-background-float);
|
||||
border-radius: var(--border-radius-regular);
|
||||
border: 1px solid var(--color-border-hint);
|
||||
padding: 1rem;
|
||||
box-shadow: var(--elevation-low);
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
@@ -42,19 +36,11 @@
|
||||
<slot name="toggle" />
|
||||
|
||||
{#if visible}
|
||||
<!-- If this component is used inside a button (see `NodeId`, for example)
|
||||
we don’t want clicks in the popover to trigger button actions. So we
|
||||
stop propagation of click events. -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div style:position="absolute" on:click|stopPropagation>
|
||||
<div style:position="absolute">
|
||||
<div
|
||||
class="popover"
|
||||
style:padding={stylePopoverPadding}
|
||||
style:left={stylePopoverPositionLeft}
|
||||
style:right={stylePopoverPositionRight}
|
||||
style:bottom={stylePopoverPositionBottom}
|
||||
style:top={stylePopoverPositionTop}>
|
||||
style:bottom={stylePopoverPositionBottom}>
|
||||
<slot name="popover" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
<script lang="ts">
|
||||
import type { ComponentProps } from "svelte";
|
||||
|
||||
import { debounce } from "lodash";
|
||||
|
||||
import { formatObjectId } from "@app/lib/utils";
|
||||
import { toClipboard } from "@app/lib/utils";
|
||||
|
||||
import IconSmall from "./IconSmall.svelte";
|
||||
|
||||
export let id: string;
|
||||
export let clipboard: string = id;
|
||||
export let shorten: boolean = true;
|
||||
export let style: "oid" | "commit" | "none" = "oid";
|
||||
export let subject: string | undefined = undefined;
|
||||
export let ariaLabel: string | undefined = undefined;
|
||||
|
||||
let icon: ComponentProps<IconSmall>["name"] = "clipboard";
|
||||
const text = subject ? `Click to copy ${subject}` : "Click to copy";
|
||||
let tooltip = text;
|
||||
|
||||
const restoreIcon = debounce(() => {
|
||||
icon = "clipboard";
|
||||
tooltip = text;
|
||||
visible = false;
|
||||
}, 1000);
|
||||
|
||||
async function copy() {
|
||||
await toClipboard(clipboard);
|
||||
icon = "checkmark";
|
||||
tooltip = "Copied to clipboard";
|
||||
restoreIcon();
|
||||
}
|
||||
|
||||
let visible: boolean = false;
|
||||
export let debounceTimeout = 50;
|
||||
|
||||
const setVisible = debounce((value: boolean) => {
|
||||
visible = value;
|
||||
}, debounceTimeout);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.popover {
|
||||
position: absolute;
|
||||
bottom: 1.5rem;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
background: var(--color-background-float);
|
||||
color: var(--color-foreground-default);
|
||||
border: 1px solid var(--color-border-hint);
|
||||
border-radius: var(--border-radius-small);
|
||||
box-shadow: var(--elevation-low);
|
||||
font-family: var(--font-family-sans-serif);
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: var(--font-weight-regular);
|
||||
white-space: nowrap;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.target-commit:hover {
|
||||
color: var(--color-foreground-contrast);
|
||||
}
|
||||
.target-oid:hover {
|
||||
color: var(--color-foreground-emphasized-hover);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:mouseenter={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
on:mouseleave={() => {
|
||||
setVisible(false);
|
||||
}}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="target-{style} global-{style}"
|
||||
style:cursor="pointer"
|
||||
aria-label={ariaLabel}
|
||||
on:click={async () => {
|
||||
await copy();
|
||||
setVisible(true);
|
||||
}}
|
||||
role="button"
|
||||
tabindex="0">
|
||||
<slot>
|
||||
{#if shorten}
|
||||
{formatObjectId(id)}
|
||||
{:else}
|
||||
{id}
|
||||
{/if}
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
{#if visible}
|
||||
<div style:position="absolute">
|
||||
<div class="popover">
|
||||
<IconSmall name={icon} />
|
||||
{tooltip}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,13 +2,10 @@
|
||||
import { formatNodeId } from "@app/lib/utils";
|
||||
|
||||
import Avatar from "./Avatar.svelte";
|
||||
import HoverPopover from "./HoverPopover.svelte";
|
||||
import CopyableId from "./CopyableId.svelte";
|
||||
import Id from "./Id.svelte";
|
||||
|
||||
export let nodeId: string;
|
||||
export let alias: string | undefined = undefined;
|
||||
export let large: boolean = false;
|
||||
export let stylePopoverPositionLeft = "-4.5rem";
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -22,32 +19,10 @@
|
||||
font-weight: var(--font-weight-semibold);
|
||||
font-size: var(--font-size-small);
|
||||
}
|
||||
.large {
|
||||
height: 1.25rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.popover-avatar {
|
||||
height: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-family: var(--font-family-monospace);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
font-size: var(--font-size-small);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.popover-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<HoverPopover
|
||||
{stylePopoverPositionLeft}
|
||||
stylePopoverPadding="0.5rem 0.5rem 0.5rem 0.75rem"
|
||||
stylePopoverPositionTop="-4.5rem">
|
||||
<div slot="toggle" class="avatar-alias" class:large>
|
||||
<Id id={nodeId} subject={formatNodeId(nodeId)} style="none">
|
||||
<div class="avatar-alias">
|
||||
<Avatar {nodeId} />
|
||||
{#if alias}
|
||||
{alias}
|
||||
@@ -55,19 +30,4 @@
|
||||
{formatNodeId(nodeId)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div slot="popover">
|
||||
<div class="popover-container">
|
||||
<div class="popover-avatar">
|
||||
<Avatar {nodeId} />
|
||||
{#if alias}
|
||||
{alias}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<CopyableId id={nodeId} style="oid">
|
||||
{formatNodeId(nodeId)}
|
||||
</CopyableId>
|
||||
</div>
|
||||
</div>
|
||||
</HoverPopover>
|
||||
</Id>
|
||||
|
||||
+17
-15
@@ -11,9 +11,9 @@
|
||||
import { isDelegate } from "@app/lib/roles";
|
||||
|
||||
import AppLayout from "@app/App/AppLayout.svelte";
|
||||
import CopyableId from "@app/components/CopyableId.svelte";
|
||||
import IconButton from "@app/components/IconButton.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
import Loading from "@app/components/Loading.svelte";
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
import ProjectCard from "@app/components/ProjectCard.svelte";
|
||||
@@ -128,25 +128,27 @@
|
||||
{#each externalAddresses as address}
|
||||
<!-- If there are externalAddresses this is probably a remote node -->
|
||||
<!-- in that case, we show all the defined externalAddresses as a listing -->
|
||||
<CopyableId id={`${nid}@${address}`} style="oid">
|
||||
{truncateId(nid)}@{address}
|
||||
</CopyableId>
|
||||
<Id
|
||||
ariaLabel="node-id"
|
||||
shorten={false}
|
||||
id="{truncateId(nid)}@{address}"
|
||||
clipboard={`${nid}@${address}`} />
|
||||
{:else}
|
||||
<!-- else this is probably a local node -->
|
||||
<!-- So we show only the nid -->
|
||||
<CopyableId id={nid} style="oid">
|
||||
<div class="global-hide-on-small-desktop-up">
|
||||
{truncateId(nid)}
|
||||
</div>
|
||||
<div class="global-hide-on-mobile-down">
|
||||
{nid}
|
||||
</div>
|
||||
</CopyableId>
|
||||
<div class="global-hide-on-small-desktop-up">
|
||||
<Id ariaLabel="node-id" id={truncateId(nid)} shorten={false} />
|
||||
</div>
|
||||
<div class="global-hide-on-mobile-down">
|
||||
<Id ariaLabel="node-id" id={nid} shorten={false} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="version">
|
||||
{version}
|
||||
</div>
|
||||
<Id ariaLabel="version" id={version} shorten={false} style="none">
|
||||
<div class="version">
|
||||
{version}
|
||||
</div>
|
||||
</Id>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
import { twemoji } from "@app/lib/utils";
|
||||
|
||||
import CommitLink from "@app/views/projects/components/CommitLink.svelte";
|
||||
import CompactCommitAuthorship from "@app/components/CompactCommitAuthorship.svelte";
|
||||
import ExpandButton from "@app/components/ExpandButton.svelte";
|
||||
import IconButton from "@app/components/IconButton.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
{/if}
|
||||
<div class="global-hide-on-small-desktop-up">
|
||||
<CompactCommitAuthorship {commit}>
|
||||
<CommitLink {baseUrl} {projectId} commitId={commit.id} />
|
||||
<Id id={commit.id} style="commit" />
|
||||
</CompactCommitAuthorship>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,7 +96,7 @@
|
||||
<div style="display: flex; gap: 0.5rem; height: 21px; align-items: center;">
|
||||
<div class="global-hide-on-mobile-down">
|
||||
<CompactCommitAuthorship {commit}>
|
||||
<CommitLink {baseUrl} {projectId} commitId={commit.id} />
|
||||
<Id id={commit.id} style="commit" />
|
||||
</CompactCommitAuthorship>
|
||||
</div>
|
||||
<IconButton title="Browse repo at this commit">
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
font-size: var(--font-size-small);
|
||||
font-family: var(--font-family-monospace);
|
||||
}
|
||||
.summary {
|
||||
display: flex;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import CobCommitTeaser from "@app/views/projects/Cob/CobCommitTeaser.svelte";
|
||||
import CommentComponent from "@app/components/Comment.svelte";
|
||||
import CommitLink from "@app/views/projects/components/CommitLink.svelte";
|
||||
import DiffStatBadge from "@app/components/DiffStatBadge.svelte";
|
||||
import DropdownList from "@app/components/DropdownList.svelte";
|
||||
import DropdownListItem from "@app/components/DropdownList/DropdownListItem.svelte";
|
||||
@@ -35,6 +34,7 @@
|
||||
import ReactionSelector from "@app/components/ReactionSelector.svelte";
|
||||
import Reactions from "@app/components/Reactions.svelte";
|
||||
import Thread from "@app/components/Thread.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
|
||||
export let baseUrl: BaseUrl;
|
||||
export let initiallyExpanded: boolean = false;
|
||||
@@ -300,7 +300,7 @@
|
||||
<ExpandButton {expanded} on:toggle={() => (expanded = !expanded)} />
|
||||
<span>
|
||||
Revision
|
||||
<span class="global-oid">{utils.formatObjectId(revisionId)}</span>
|
||||
<Id id={revisionId} />
|
||||
</span>
|
||||
</div>
|
||||
<div class="revision-data">
|
||||
@@ -409,21 +409,16 @@
|
||||
style:padding="0 0.375rem">
|
||||
<IconSmall name="patch" />
|
||||
</div>
|
||||
<NodeId
|
||||
stylePopoverPositionLeft="-13px"
|
||||
nodeId={revisionAuthor.id}
|
||||
alias={revisionAuthor.alias} />
|
||||
<NodeId nodeId={revisionAuthor.id} alias={revisionAuthor.alias} />
|
||||
{#if patchId === revisionId}
|
||||
opened this patch on base
|
||||
<CommitLink {baseUrl} {projectId} commitId={revisionBase} />
|
||||
<Id id={revisionBase} style="commit" />
|
||||
{:else}
|
||||
updated to
|
||||
<span class="global-oid">
|
||||
{utils.formatObjectId(revisionId)}
|
||||
</span>
|
||||
<Id id={revisionId} />
|
||||
{#if previousRevBase && previousRevBase !== revisionBase}
|
||||
with base
|
||||
<CommitLink {baseUrl} {projectId} commitId={revisionBase} />
|
||||
<Id id={revisionBase} style="commit" />
|
||||
{/if}
|
||||
{/if}
|
||||
<span
|
||||
@@ -556,20 +551,14 @@
|
||||
</div>
|
||||
|
||||
<NodeId
|
||||
stylePopoverPositionLeft="-13px"
|
||||
nodeId={element.inner.author.id}
|
||||
alias={element.inner.author.alias}>
|
||||
</NodeId>
|
||||
|
||||
merged revision
|
||||
<span class="global-oid">
|
||||
{utils.formatObjectId(element.inner.revision)}
|
||||
</span>
|
||||
<Id id={element.inner.revision} />
|
||||
at commit
|
||||
<CommitLink
|
||||
{baseUrl}
|
||||
{projectId}
|
||||
commitId={element.inner.commit} />
|
||||
<Id id={element.inner.commit} style="commit" />
|
||||
<span
|
||||
class="timestamp"
|
||||
title={utils.absoluteTimestamp(revisionTimestamp)}>
|
||||
@@ -593,9 +582,7 @@
|
||||
body={review.summary ?? ""}>
|
||||
<div slot="caption">
|
||||
{formatVerdict(review.verdict)}
|
||||
<span class="global-oid">
|
||||
{utils.formatObjectId(revisionId)}
|
||||
</span>
|
||||
<Id id={revisionId} />
|
||||
</div>
|
||||
<div slot="icon" style:color={verdictIconColor(review.verdict)}>
|
||||
{#if review.verdict === "accept"}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
<script lang="ts">
|
||||
import type { BaseUrl, Commit, Node, Project } from "@http-client";
|
||||
|
||||
import { formatCommit } from "@app/lib/utils";
|
||||
|
||||
import Button from "@app/components/Button.svelte";
|
||||
import Changeset from "@app/views/projects/Changeset.svelte";
|
||||
import CommitAuthorship from "@app/views/projects/Commit/CommitAuthorship.svelte";
|
||||
import CopyableId from "@app/components/CopyableId.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
import Layout from "./Layout.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
import Share from "./Share.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
|
||||
export let baseUrl: BaseUrl;
|
||||
export let node: Node;
|
||||
@@ -73,9 +71,7 @@
|
||||
</div>
|
||||
</span>
|
||||
<CommitAuthorship {header}>
|
||||
<CopyableId id={header.id} style="commit">
|
||||
{formatCommit(header.id)}
|
||||
</CopyableId>
|
||||
<Id id={header.id} style="commit" ariaLabel="commit-id" />
|
||||
</CommitAuthorship>
|
||||
</div>
|
||||
{#if header.description}
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
import { twemoji } from "@app/lib/utils";
|
||||
|
||||
import CommitAuthorship from "./CommitAuthorship.svelte";
|
||||
import CommitLink from "@app/views/projects/components/CommitLink.svelte";
|
||||
import ExpandButton from "@app/components/ExpandButton.svelte";
|
||||
import IconButton from "@app/components/IconButton.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
|
||||
export let baseUrl: BaseUrl;
|
||||
export let commit: CommitHeader;
|
||||
@@ -104,7 +104,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<CommitAuthorship header={commit}>
|
||||
<CommitLink {baseUrl} {projectId} commitId={commit.id} />
|
||||
<Id id={commit.id} style="commit" />
|
||||
</CommitAuthorship>
|
||||
</div>
|
||||
<div class="right">
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
import CobHeader from "@app/views/projects/Cob/CobHeader.svelte";
|
||||
import CobStateButton from "@app/views/projects/Cob/CobStateButton.svelte";
|
||||
import CommentToggleInput from "@app/components/CommentToggleInput.svelte";
|
||||
import CopyableId from "@app/components/CopyableId.svelte";
|
||||
import Embeds from "@app/views/projects/Cob/Embeds.svelte";
|
||||
import ErrorModal from "@app/modals/ErrorModal.svelte";
|
||||
import ExtendedTextarea from "@app/components/ExtendedTextarea.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
import LabelInput from "./Cob/LabelInput.svelte";
|
||||
import Layout from "./Layout.svelte";
|
||||
@@ -547,14 +547,9 @@
|
||||
{issue.state.reason}
|
||||
</Badge>
|
||||
{/if}
|
||||
<NodeId
|
||||
stylePopoverPositionLeft="-13px"
|
||||
nodeId={issue.author.id}
|
||||
alias={issue.author.alias} />
|
||||
<NodeId nodeId={issue.author.id} alias={issue.author.alias} />
|
||||
opened
|
||||
<CopyableId id={issue.id} style="oid">
|
||||
{utils.formatObjectId(issue.id)}
|
||||
</CopyableId>
|
||||
<Id id={issue.id} />
|
||||
<span title={utils.absoluteTimestamp(issue.discussion[0].timestamp)}>
|
||||
{utils.formatTimestamp(issue.discussion[0].timestamp)}
|
||||
</span>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { BaseUrl, Issue } from "@http-client";
|
||||
|
||||
import {
|
||||
absoluteTimestamp,
|
||||
formatObjectId,
|
||||
formatTimestamp,
|
||||
} from "@app/lib/utils";
|
||||
import { absoluteTimestamp, formatTimestamp } from "@app/lib/utils";
|
||||
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
@@ -14,6 +10,7 @@
|
||||
|
||||
import CommentCounter from "../CommentCounter.svelte";
|
||||
import Labels from "../Cob/Labels.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
|
||||
export let baseUrl: BaseUrl;
|
||||
export let issue: Issue;
|
||||
@@ -123,12 +120,9 @@
|
||||
{/if}
|
||||
<div
|
||||
style="display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;">
|
||||
<NodeId
|
||||
stylePopoverPositionLeft="-13px"
|
||||
nodeId={issue.author.id}
|
||||
alias={issue.author.alias} />
|
||||
<NodeId nodeId={issue.author.id} alias={issue.author.alias} />
|
||||
opened
|
||||
<span class="global-oid">{formatObjectId(issue.id)}</span>
|
||||
<Id id={issue.id} />
|
||||
<span title={absoluteTimestamp(issue.discussion[0].timestamp)}>
|
||||
{formatTimestamp(issue.discussion[0].timestamp)}
|
||||
</span>
|
||||
|
||||
@@ -52,12 +52,13 @@
|
||||
import * as role from "@app/lib/roles";
|
||||
import * as router from "@app/lib/router";
|
||||
import * as utils from "@app/lib/utils";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import capitalize from "lodash/capitalize";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import partial from "lodash/partial";
|
||||
import uniqBy from "lodash/uniqBy";
|
||||
import { HttpdClient } from "@http-client";
|
||||
import { closeFocused } from "@app/components/Popover.svelte";
|
||||
import { experimental } from "@app/lib/appearance";
|
||||
import { httpdStore } from "@app/lib/httpd";
|
||||
import { parseEmbedIntoMap } from "@app/lib/file";
|
||||
|
||||
@@ -68,12 +69,12 @@
|
||||
import CobStateButton from "@app/views/projects/Cob/CobStateButton.svelte";
|
||||
import CommentToggleInput from "@app/components/CommentToggleInput.svelte";
|
||||
import CompareButton from "@app/views/projects/Patch/CompareButton.svelte";
|
||||
import CopyableId from "@app/components/CopyableId.svelte";
|
||||
import DiffStatBadge from "@app/components/DiffStatBadge.svelte";
|
||||
import Embeds from "@app/views/projects/Cob/Embeds.svelte";
|
||||
import ErrorModal from "@app/modals/ErrorModal.svelte";
|
||||
import ExtendedTextarea from "@app/components/ExtendedTextarea.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
import LabelInput from "@app/views/projects/Cob/LabelInput.svelte";
|
||||
import Layout from "@app/views/projects/Layout.svelte";
|
||||
@@ -89,7 +90,6 @@
|
||||
import RevisionSelector from "@app/views/projects/Patch/RevisionSelector.svelte";
|
||||
import Share from "@app/views/projects/Share.svelte";
|
||||
import TextInput from "@app/components/TextInput.svelte";
|
||||
import { closeFocused } from "@app/components/Popover.svelte";
|
||||
|
||||
export let baseUrl: BaseUrl;
|
||||
export let node: Node;
|
||||
@@ -775,14 +775,9 @@
|
||||
insertions={stats.insertions}
|
||||
deletions={stats.deletions} />
|
||||
</Link>
|
||||
<NodeId
|
||||
stylePopoverPositionLeft="-13px"
|
||||
nodeId={patch.author.id}
|
||||
alias={patch.author.alias} />
|
||||
<NodeId nodeId={patch.author.id} alias={patch.author.alias} />
|
||||
opened
|
||||
<CopyableId id={patch.id} style="oid">
|
||||
{utils.formatObjectId(patch.id)}
|
||||
</CopyableId>
|
||||
<Id id={patch.id} />
|
||||
<span title={utils.absoluteTimestamp(patch.revisions[0].timestamp)}>
|
||||
{utils.formatTimestamp(patch.revisions[0].timestamp)}
|
||||
</span>
|
||||
|
||||
@@ -2,20 +2,17 @@
|
||||
import type { BaseUrl } from "@http-client";
|
||||
import type { Patch } from "@http-client";
|
||||
|
||||
import {
|
||||
absoluteTimestamp,
|
||||
formatObjectId,
|
||||
formatTimestamp,
|
||||
} from "@app/lib/utils";
|
||||
import { absoluteTimestamp, formatTimestamp } from "@app/lib/utils";
|
||||
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
import NodeId from "@app/components/NodeId.svelte";
|
||||
|
||||
import Labels from "../Cob/Labels.svelte";
|
||||
import DiffStatBadgeLoader from "../DiffStatBadgeLoader.svelte";
|
||||
import CommentCounter from "../CommentCounter.svelte";
|
||||
import DiffStatBadgeLoader from "../DiffStatBadgeLoader.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
import Labels from "../Cob/Labels.svelte";
|
||||
|
||||
export let projectId: string;
|
||||
export let baseUrl: BaseUrl;
|
||||
@@ -137,17 +134,12 @@
|
||||
{/if}
|
||||
<div
|
||||
style="display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;">
|
||||
<NodeId
|
||||
stylePopoverPositionLeft="-13px"
|
||||
nodeId={patch.author.id}
|
||||
alias={patch.author.alias} />
|
||||
<NodeId nodeId={patch.author.id} alias={patch.author.alias} />
|
||||
{patch.revisions.length > 1 ? "updated" : "opened"}
|
||||
<span class="global-oid">{formatObjectId(patch.id)}</span>
|
||||
<Id id={patch.id} />
|
||||
{#if patch.revisions.length > 1}
|
||||
<span class="global-hide-on-mobile-down">
|
||||
to <span class="global-oid">
|
||||
{formatObjectId(patch.revisions[patch.revisions.length - 1].id)}
|
||||
</span>
|
||||
to <Id id={patch.revisions[patch.revisions.length - 1].id} />
|
||||
</span>
|
||||
{/if}
|
||||
<span title={absoluteTimestamp(latestRevision.timestamp)}>
|
||||
|
||||
@@ -61,7 +61,12 @@
|
||||
<ShareButton slot="popover" />
|
||||
</Popover>
|
||||
{:else}
|
||||
<Button variant="outline" size="regular" on:click={copy}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="regular"
|
||||
on:click={async () => {
|
||||
await copy();
|
||||
}}>
|
||||
<IconSmall name={shareIcon} />
|
||||
<span class="global-hide-on-small-desktop-down">Copy link</span>
|
||||
</Button>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts">
|
||||
import type { BaseUrl, Node, Project } from "@http-client";
|
||||
|
||||
import { capitalize } from "lodash";
|
||||
import { isLocal } from "@app/lib/utils";
|
||||
|
||||
import IconButton from "@app/components/IconButton.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import NodeId from "@app/components/NodeId.svelte";
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
import ScopePolicyExplainer from "@app/components/ScopePolicyExplainer.svelte";
|
||||
import { isLocal } from "@app/lib/utils";
|
||||
import { capitalize } from "lodash";
|
||||
|
||||
export let disablePopovers: boolean = false;
|
||||
export let project: Project;
|
||||
@@ -86,7 +87,7 @@
|
||||
<div class="nids">
|
||||
{#each project.delegates as { id: nodeId, alias }}
|
||||
<div style:width="fit-content">
|
||||
<NodeId {alias} {nodeId} stylePopoverPositionLeft="-0.8rem" />
|
||||
<NodeId {alias} {nodeId} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -119,11 +120,5 @@
|
||||
policy={node.config.policy} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="item txt-overflow" style:padding-top="0.5rem">
|
||||
Radicle version
|
||||
<span class="txt-missing txt-overflow txt-monospace" title={node.version}>
|
||||
{node.version}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,15 +6,14 @@
|
||||
import { formatNodeId } from "@app/lib/utils";
|
||||
import { httpdStore } from "@app/lib/httpd";
|
||||
|
||||
import NodeId from "@app/components/NodeId.svelte";
|
||||
import Avatar from "@app/components/Avatar.svelte";
|
||||
import Badge from "@app/components/Badge.svelte";
|
||||
import Button from "@app/components/Button.svelte";
|
||||
import DropdownList from "@app/components/DropdownList.svelte";
|
||||
import DropdownListItem from "@app/components/DropdownList/DropdownListItem.svelte";
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
import Button from "@app/components/Button.svelte";
|
||||
import Avatar from "@app/components/Avatar.svelte";
|
||||
import Popover from "@app/components/Popover.svelte";
|
||||
|
||||
export let peers: Array<{ remote: Remote; selected: boolean; route: Route }>;
|
||||
export let project: Project;
|
||||
@@ -48,22 +47,27 @@
|
||||
popoverPositionTop="2.5rem"
|
||||
popoverBorderRadius="var(--border-radius-small)">
|
||||
<Button
|
||||
ariaLabel="Change peer"
|
||||
slot="toggle"
|
||||
let:expanded
|
||||
let:toggle
|
||||
styleBorderRadius="var(--border-radius-tiny) 0 0 var(--border-radius-tiny)"
|
||||
on:click={toggle}
|
||||
title="Change peer"
|
||||
title={selectedPeer ? formatNodeId(selectedPeer.id) : "Change peer"}
|
||||
disabled={!peers}>
|
||||
{#if !selectedPeer}
|
||||
<IconSmall name="delegate" />
|
||||
{/if}
|
||||
|
||||
{#if selectedPeer}
|
||||
<NodeId
|
||||
nodeId={selectedPeer.id}
|
||||
alias={selectedPeer.alias}
|
||||
stylePopoverPositionLeft="-0.75rem" />
|
||||
<div style:height="1rem">
|
||||
<Avatar nodeId={selectedPeer.id} />
|
||||
</div>
|
||||
<span
|
||||
style:font-family="var(--font-family-monospace)"
|
||||
class:no-alias={!selectedPeer.alias}>
|
||||
{selectedPeer.alias || formatNodeId(selectedPeer.id)}
|
||||
</span>
|
||||
{#if selectedPeer.delegate}
|
||||
<Badge size="tiny" variant="delegate">
|
||||
<IconSmall name="badge" />
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
import Badge from "@app/components/Badge.svelte";
|
||||
import CloneButton from "@app/views/projects/Header/CloneButton.svelte";
|
||||
import CopyableId from "@app/components/CopyableId.svelte";
|
||||
import IconSmall from "@app/components/IconSmall.svelte";
|
||||
import Id from "@app/components/Id.svelte";
|
||||
import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
import SeedButton from "@app/views/projects/Header/SeedButton.svelte";
|
||||
@@ -90,7 +90,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="id">
|
||||
<CopyableId id={project.id} style="oid" />
|
||||
<Id shorten={false} id={project.id} ariaLabel="project-id" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="description" use:twemoji>
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { BaseUrl } from "@http-client";
|
||||
|
||||
import { formatCommit } from "@app/lib/utils";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
|
||||
export let baseUrl: BaseUrl;
|
||||
export let projectId: string;
|
||||
export let commitId: string;
|
||||
</script>
|
||||
|
||||
<Link
|
||||
route={{
|
||||
resource: "project.commit",
|
||||
node: baseUrl,
|
||||
project: projectId,
|
||||
commit: commitId,
|
||||
}}>
|
||||
<span class="global-commit">
|
||||
{formatCommit(commitId)}
|
||||
</span>
|
||||
</Link>
|
||||
@@ -33,7 +33,7 @@ test("copy to clipboard", async ({ page, browserName, context }) => {
|
||||
|
||||
// Project ID.
|
||||
{
|
||||
await page.locator(".id > .clipboard").click();
|
||||
await page.getByLabel("project-id").click();
|
||||
const clipboardContent = await page.evaluate<string>(
|
||||
"navigator.clipboard.readText()",
|
||||
);
|
||||
@@ -63,7 +63,7 @@ test("copy to clipboard", async ({ page, browserName, context }) => {
|
||||
await page.goto("/nodes/radicle.local");
|
||||
// Node address.
|
||||
{
|
||||
await page.locator(".clipboard").first().click();
|
||||
await page.getByRole("button", { name: "node-id" }).first().click();
|
||||
await expectClipboard(`${nodeRemote}`, page);
|
||||
}
|
||||
|
||||
|
||||
@@ -288,13 +288,13 @@ test("peer and branch switching", async ({ page }) => {
|
||||
|
||||
// Alice's peer.
|
||||
{
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.getByLabel("Change peer").click();
|
||||
await page
|
||||
.getByRole("link", {
|
||||
name: "alice delegate",
|
||||
})
|
||||
.click();
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("alice Delegate");
|
||||
await expect(page.getByLabel("Change peer")).toHaveText("alice Delegate");
|
||||
|
||||
// Default `main` branch.
|
||||
{
|
||||
@@ -356,8 +356,8 @@ test("peer and branch switching", async ({ page }) => {
|
||||
{
|
||||
await page.getByRole("link", { name: "source-browsing" }).nth(1).click();
|
||||
|
||||
await expect(page.getByTitle("Change peer")).not.toContainText("alice");
|
||||
await expect(page.getByTitle("Change peer")).not.toContainText("bob");
|
||||
await expect(page.getByLabel("Change peer")).not.toContainText("alice");
|
||||
await expect(page.getByLabel("Change peer")).not.toContainText("bob");
|
||||
|
||||
await expect(page.getByTitle("Change branch")).toBeVisible();
|
||||
await expect(
|
||||
@@ -372,10 +372,10 @@ test("peer and branch switching", async ({ page }) => {
|
||||
|
||||
// Bob's peer.
|
||||
{
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.getByLabel("Change peer").click();
|
||||
await page.getByRole("link", { name: "bob" }).click();
|
||||
await expect(page.getByTitle("Change peer")).toContainText("bob");
|
||||
await expect(page.getByTitle("Change peer")).not.toHaveText("delegate");
|
||||
await expect(page.getByLabel("Change peer")).toContainText("bob");
|
||||
await expect(page.getByLabel("Change peer")).not.toHaveText("delegate");
|
||||
|
||||
// Default `main` branch.
|
||||
{
|
||||
@@ -402,7 +402,7 @@ test("peer and branch switching", async ({ page }) => {
|
||||
test("only one modal can be open at a time", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingUrl);
|
||||
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.getByLabel("Change peer").click();
|
||||
await page
|
||||
.getByRole("link", {
|
||||
name: "alice delegate",
|
||||
@@ -427,7 +427,7 @@ test("only one modal can be open at a time", async ({ page }) => {
|
||||
await expect(page.getByText("bob")).not.toBeVisible();
|
||||
await expect(page.getByText("feature/branch")).toBeVisible();
|
||||
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.getByLabel("Change peer").click();
|
||||
await expect(page.getByText("Code font")).not.toBeVisible();
|
||||
await expect(page.getByText("Use the Radicle CLI")).not.toBeVisible();
|
||||
await expect(page.getByText("bob")).toBeVisible();
|
||||
|
||||
@@ -13,7 +13,7 @@ const commitUrl = `${sourceBrowsingUrl}/commits/${bobHead}`;
|
||||
|
||||
test("navigation from commit list", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingUrl);
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.getByLabel("Change peer").click();
|
||||
await page.getByRole("link", { name: "bob" }).click();
|
||||
await page
|
||||
.getByRole("link", { name: `Commits ${bobMainCommitCount}` })
|
||||
@@ -43,9 +43,7 @@ test("modified file", async ({ page }) => {
|
||||
// Commit header.
|
||||
{
|
||||
await expect(page.getByText("Update readme")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: shortBobHead }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByLabel("commit-id")).toHaveText(shortBobHead);
|
||||
}
|
||||
|
||||
// Diff header.
|
||||
|
||||
@@ -20,14 +20,14 @@ test("peer and branch switching", async ({ page }) => {
|
||||
|
||||
// Alice's peer.
|
||||
{
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.getByLabel("Change peer").click();
|
||||
await page
|
||||
.getByRole("link", {
|
||||
name: "alice delegate",
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("alice Delegate");
|
||||
await expect(page.getByLabel("Change peer")).toHaveText("alice Delegate");
|
||||
|
||||
await expect(page.getByText("Thursday, November 17, 2022")).toBeVisible();
|
||||
await expect(page.locator(".list .teaser")).toHaveCount(
|
||||
@@ -65,10 +65,10 @@ test("peer and branch switching", async ({ page }) => {
|
||||
|
||||
// Bob's peer.
|
||||
{
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.getByLabel("Change peer").click();
|
||||
await page.getByRole("link", { name: "bob" }).click();
|
||||
|
||||
await expect(page.getByTitle("Change peer")).toContainText("bob");
|
||||
await expect(page.getByLabel("Change peer")).toContainText("bob");
|
||||
|
||||
await expect(page.getByText("Wednesday, December 21, 2022")).toBeVisible();
|
||||
await expect(page.locator(".list").first().locator(".teaser")).toHaveCount(
|
||||
@@ -155,9 +155,9 @@ test("relative timestamps", async ({ page }) => {
|
||||
.getByRole("link", { name: `Commits ${aliceMainCommitCount}` })
|
||||
.click();
|
||||
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.getByLabel("Change peer").click();
|
||||
await page.getByRole("link", { name: "bob" }).click();
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("bob");
|
||||
await expect(page.getByLabel("Change peer")).toHaveText("bob");
|
||||
const latestCommit = page.locator(".teaser").first();
|
||||
await expect(latestCommit).toContainText(
|
||||
`Bob Belcher committed ${shortBobHead} now`,
|
||||
|
||||
Reference in New Issue
Block a user