Unify unread badge and archive X into single column on Mine tab
The unread dot and dismiss X now share the same rightmost column on the Mine tab. When an issue is unread the blue dot shows first; clicking it marks the issue as read and reveals the X on hover for archiving. Read/unread state stays in sync across all inbox tabs. Desktop dismiss animation polished with scale + slide. Co-Authored-By: Paperclip <noreply@paperclip.ing> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
995f5b0b66
commit
49c7fb7fbd
2 changed files with 33 additions and 41 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import type { ReactNode } from "react";
|
||||
import type { Issue } from "@paperclipai/shared";
|
||||
import { Link } from "@/lib/router";
|
||||
import { X } from "lucide-react";
|
||||
import { cn } from "../lib/utils";
|
||||
import { StatusIcon } from "./StatusIcon";
|
||||
|
||||
|
|
@ -17,6 +18,8 @@ interface IssueRowProps {
|
|||
trailingMeta?: ReactNode;
|
||||
unreadState?: UnreadState | null;
|
||||
onMarkRead?: () => void;
|
||||
onArchive?: () => void;
|
||||
archiveDisabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
|
|
@ -31,6 +34,8 @@ export function IssueRow({
|
|||
trailingMeta,
|
||||
unreadState = null,
|
||||
onMarkRead,
|
||||
onArchive,
|
||||
archiveDisabled,
|
||||
className,
|
||||
}: IssueRowProps) {
|
||||
const issuePathId = issue.identifier ?? issue.id;
|
||||
|
|
@ -113,6 +118,26 @@ export function IssueRow({
|
|||
)}
|
||||
/>
|
||||
</button>
|
||||
) : onArchive ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onArchive();
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== "Enter" && event.key !== " ") return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onArchive();
|
||||
}}
|
||||
disabled={archiveDisabled}
|
||||
className="inline-flex h-4 w-4 items-center justify-center rounded-md text-muted-foreground opacity-0 transition-opacity hover:text-foreground group-hover:opacity-100 disabled:pointer-events-none disabled:opacity-30"
|
||||
aria-label="Dismiss from inbox"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
) : (
|
||||
<span className="inline-flex h-4 w-4" aria-hidden="true" />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -94,35 +94,6 @@ function readIssueIdFromRun(run: HeartbeatRun): string | null {
|
|||
return null;
|
||||
}
|
||||
|
||||
function InboxArchiveButton({
|
||||
onArchive,
|
||||
disabled,
|
||||
}: {
|
||||
onArchive: () => void;
|
||||
disabled: boolean;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onArchive();
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== "Enter" && event.key !== " ") return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onArchive();
|
||||
}}
|
||||
disabled={disabled}
|
||||
className="rounded-md p-1 text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground group-hover:opacity-100 disabled:pointer-events-none disabled:opacity-30"
|
||||
aria-label="Archive from mine"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function FailedRunInboxRow({
|
||||
run,
|
||||
|
|
@ -957,8 +928,8 @@ export function Inbox() {
|
|||
issueLinkState={issueLinkState}
|
||||
className={
|
||||
isArchiving
|
||||
? "pointer-events-none -translate-x-3 opacity-0 transition-transform transition-opacity duration-200"
|
||||
: "transition-transform transition-opacity duration-200"
|
||||
? "pointer-events-none -translate-x-4 scale-[0.98] opacity-0 transition-all duration-200 ease-out"
|
||||
: "transition-all duration-200 ease-out"
|
||||
}
|
||||
desktopMetaLeading={(
|
||||
<>
|
||||
|
|
@ -987,19 +958,15 @@ export function Inbox() {
|
|||
: `updated ${timeAgo(issue.updatedAt)}`
|
||||
}
|
||||
unreadState={
|
||||
isMineTab
|
||||
? null
|
||||
: isUnread ? "visible" : isFading ? "fading" : "hidden"
|
||||
isUnread ? "visible" : isFading ? "fading" : "hidden"
|
||||
}
|
||||
onMarkRead={() => markReadMutation.mutate(issue.id)}
|
||||
desktopTrailing={
|
||||
isMineTab ? (
|
||||
<InboxArchiveButton
|
||||
onArchive={() => archiveIssueMutation.mutate(issue.id)}
|
||||
disabled={isArchiving || archiveIssueMutation.isPending}
|
||||
/>
|
||||
) : undefined
|
||||
onArchive={
|
||||
isMineTab
|
||||
? () => archiveIssueMutation.mutate(issue.id)
|
||||
: undefined
|
||||
}
|
||||
archiveDisabled={isArchiving || archiveIssueMutation.isPending}
|
||||
trailingMeta={
|
||||
issue.lastExternalCommentAt
|
||||
? `commented ${timeAgo(issue.lastExternalCommentAt)}`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue