mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-15 11:20:36 +08:00
Restyle reactions and highlight own reactions
This commit is contained in:
@@ -67,6 +67,8 @@
|
||||
|
||||
--color-surface-brand-primary: var(--color-brand-bg);
|
||||
--color-surface-brand-secondary: var(--color-brand-hover);
|
||||
--color-surface-brand-subtle: var(--color-accent-blue-100);
|
||||
--color-surface-brand-mid: var(--color-accent-blue-200);
|
||||
--color-border-brand: var(--color-brand-hover);
|
||||
--color-text-brand: var(--color-brand-text-light);
|
||||
}
|
||||
@@ -124,6 +126,9 @@
|
||||
--color-code-error: var(--color-semantic-red-400);
|
||||
--color-code-functions: var(--color-accent-emerald-400);
|
||||
|
||||
--color-surface-brand-subtle: var(--color-accent-blue-900);
|
||||
--color-surface-brand-mid: var(--color-accent-blue-800);
|
||||
|
||||
--color-text-brand: var(--color-brand-text-dark);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
beforeTimestamp?: Snippet;
|
||||
id?: string;
|
||||
rid: string;
|
||||
currentUserNid?: string;
|
||||
author: Author;
|
||||
body?: string;
|
||||
reactions?: Reaction[];
|
||||
@@ -46,6 +47,7 @@
|
||||
beforeTimestamp,
|
||||
id,
|
||||
rid,
|
||||
currentUserNid,
|
||||
author,
|
||||
body = $bindable(),
|
||||
reactions,
|
||||
@@ -243,7 +245,7 @@
|
||||
}
|
||||
}} />
|
||||
{/if}
|
||||
<Reactions handleReaction={reactOnComment} {reactions} />
|
||||
<Reactions handleReaction={reactOnComment} {currentUserNid} {reactions} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -433,6 +433,7 @@
|
||||
<ThreadComponent
|
||||
inline
|
||||
rid={codeComments.rid}
|
||||
currentUserNid={codeComments.config.publicKey}
|
||||
{thread}
|
||||
reactOnComment={codeComments.reactOnComment}
|
||||
createReply={(codeComments.canReply ?? true)
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
<ThreadComponent
|
||||
{thread}
|
||||
{rid}
|
||||
currentUserNid={config.publicKey}
|
||||
canEditComment={partial(
|
||||
roles.isDelegateOrAuthor,
|
||||
config.publicKey,
|
||||
|
||||
@@ -2,57 +2,105 @@
|
||||
import type { Author } from "@bindings/cob/Author";
|
||||
import type { Reaction } from "@bindings/cob/Reaction";
|
||||
|
||||
import { emojiToTwemoji } from "@app/lib/utils";
|
||||
import { emojiToTwemoji, publicKeyFromDid, truncateId } from "@app/lib/utils";
|
||||
|
||||
interface Props {
|
||||
reactions: Reaction[];
|
||||
currentUserNid?: string;
|
||||
handleReaction?: (authors: Author[], reaction: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const { reactions, handleReaction }: Props = $props();
|
||||
const { reactions, currentUserNid, handleReaction }: Props = $props();
|
||||
|
||||
function authorsToTooltip(authors: Author[]) {
|
||||
return authors.map(a => a.alias ?? a.did).join("\n");
|
||||
}
|
||||
|
||||
function isMine(authors: Author[]) {
|
||||
if (!currentUserNid) return false;
|
||||
return authors.some(a => publicKeyFromDid(a.did) === currentUserNid);
|
||||
}
|
||||
|
||||
function formatAuthor(a: Author) {
|
||||
const pk = publicKeyFromDid(a.did);
|
||||
if (currentUserNid && pk === currentUserNid) {
|
||||
return "You";
|
||||
}
|
||||
return a.alias ?? truncateId(pk);
|
||||
}
|
||||
|
||||
function formatAuthors(authors: Author[]) {
|
||||
if (authors.length > 3) return `${authors.length}`;
|
||||
return authors.map(formatAuthor).join(", ");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.reactions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
gap: 0.375rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.reaction {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 2px 5px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-surface-subtle);
|
||||
}
|
||||
.reaction.interactive {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.1s ease-in-out;
|
||||
}
|
||||
.reaction.interactive:focus {
|
||||
outline: none;
|
||||
}
|
||||
.reaction.interactive:focus-visible {
|
||||
outline: 2px solid var(--color-border-brand);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.reaction.interactive:hover {
|
||||
background-color: var(--color-surface-mid);
|
||||
}
|
||||
.reaction.mine {
|
||||
background-color: var(--color-surface-brand-subtle);
|
||||
border-color: var(--color-surface-brand-mid);
|
||||
}
|
||||
.reaction.mine .count {
|
||||
color: var(--color-text-brand);
|
||||
}
|
||||
.reaction.interactive.mine:hover {
|
||||
background-color: var(--color-surface-brand-mid);
|
||||
}
|
||||
.reaction :global(.txt-emoji) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="reactions">
|
||||
{#each reactions as { emoji, authors }}
|
||||
<div title={authorsToTooltip(authors)}>
|
||||
{#if handleReaction}
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class="reaction txt-body-s-regular"
|
||||
onclick={async () => {
|
||||
if (handleReaction) {
|
||||
await handleReaction(authors, emoji);
|
||||
}
|
||||
}}>
|
||||
<span>{@html emojiToTwemoji(emoji, ["21a9"])}</span>
|
||||
<span>{authors.length}</span>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="reaction txt-body-s-regular" style="padding: 2px 4px;">
|
||||
<span>{@html emojiToTwemoji(emoji, ["21a9"])}</span>
|
||||
<span>{authors.length}</span>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||
<div
|
||||
role={handleReaction ? "button" : undefined}
|
||||
tabindex={handleReaction ? 0 : undefined}
|
||||
title={isMine(authors) && handleReaction
|
||||
? "Remove reaction"
|
||||
: authorsToTooltip(authors)}
|
||||
class="reaction txt-body-s-regular"
|
||||
class:interactive={handleReaction}
|
||||
class:mine={isMine(authors)}
|
||||
onclick={handleReaction
|
||||
? () => handleReaction(authors, emoji)
|
||||
: undefined}>
|
||||
<span>{@html emojiToTwemoji(emoji, ["21a9"])}</span>
|
||||
<span class="count">{formatAuthors(authors)}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -412,6 +412,7 @@
|
||||
<CommentComponent
|
||||
disableAttachments
|
||||
rid={repo.rid}
|
||||
currentUserNid={config.publicKey}
|
||||
disallowEmptyBody={!("draft" in review) &&
|
||||
review.verdict === undefined}
|
||||
emptyBodyTooltip="Summary is mandatory when verdict is None"
|
||||
|
||||
@@ -177,6 +177,7 @@
|
||||
<CommentComponent
|
||||
caption={revision.id === patchId ? "opened patch" : "created revision"}
|
||||
{rid}
|
||||
currentUserNid={config.publicKey}
|
||||
id={revision.id}
|
||||
lastEdit={revision.description.length > 1
|
||||
? revision.description.at(-1)
|
||||
|
||||
@@ -402,6 +402,7 @@
|
||||
<div class="issue-body">
|
||||
<CommentComponent
|
||||
rid={repo.rid}
|
||||
currentUserNid={config.publicKey}
|
||||
id={issue.id}
|
||||
lastEdit={issue.body.edits.length > 1
|
||||
? issue.body.edits.at(-1)
|
||||
|
||||
Reference in New Issue
Block a user