Tighten mine-only inbox swipe archive
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
4426d96610
commit
826da2973d
5 changed files with 29 additions and 16 deletions
|
|
@ -83,7 +83,7 @@ describe("SwipeToArchive", () => {
|
||||||
expect(onClick).not.toHaveBeenCalled();
|
expect(onClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
vi.advanceTimersByTime(210);
|
vi.advanceTimersByTime(140);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(onArchive).toHaveBeenCalledTimes(1);
|
expect(onArchive).toHaveBeenCalledTimes(1);
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ interface SwipeToArchiveProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMMIT_THRESHOLD = 0.4;
|
const COMMIT_THRESHOLD = 0.32;
|
||||||
const MAX_SWIPE = 0.92;
|
const MAX_SWIPE = 0.88;
|
||||||
const COMMIT_DELAY_MS = 210;
|
const COMMIT_DELAY_MS = 140;
|
||||||
|
|
||||||
export function SwipeToArchive({
|
export function SwipeToArchive({
|
||||||
children,
|
children,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
getInboxWorkItems,
|
getInboxWorkItems,
|
||||||
getRecentTouchedIssues,
|
getRecentTouchedIssues,
|
||||||
getUnreadTouchedIssues,
|
getUnreadTouchedIssues,
|
||||||
|
isMineInboxTab,
|
||||||
loadLastInboxTab,
|
loadLastInboxTab,
|
||||||
RECENT_ISSUES_LIMIT,
|
RECENT_ISSUES_LIMIT,
|
||||||
saveLastInboxTab,
|
saveLastInboxTab,
|
||||||
|
|
@ -400,4 +401,11 @@ describe("inbox helpers", () => {
|
||||||
localStorage.setItem("paperclip:inbox:last-tab", "new");
|
localStorage.setItem("paperclip:inbox:last-tab", "new");
|
||||||
expect(loadLastInboxTab()).toBe("mine");
|
expect(loadLastInboxTab()).toBe("mine");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("enables swipe archive only on the mine tab", () => {
|
||||||
|
expect(isMineInboxTab("mine")).toBe(true);
|
||||||
|
expect(isMineInboxTab("recent")).toBe(false);
|
||||||
|
expect(isMineInboxTab("unread")).toBe(false);
|
||||||
|
expect(isMineInboxTab("all")).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,10 @@ export function saveLastInboxTab(tab: InboxTab) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isMineInboxTab(tab: InboxTab): boolean {
|
||||||
|
return tab === "mine";
|
||||||
|
}
|
||||||
|
|
||||||
export function getLatestFailedRunsByAgent(runs: HeartbeatRun[]): HeartbeatRun[] {
|
export function getLatestFailedRunsByAgent(runs: HeartbeatRun[]): HeartbeatRun[] {
|
||||||
const sorted = [...runs].sort(
|
const sorted = [...runs].sort(
|
||||||
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ import {
|
||||||
getInboxWorkItems,
|
getInboxWorkItems,
|
||||||
getLatestFailedRunsByAgent,
|
getLatestFailedRunsByAgent,
|
||||||
getRecentTouchedIssues,
|
getRecentTouchedIssues,
|
||||||
|
isMineInboxTab,
|
||||||
InboxApprovalFilter,
|
InboxApprovalFilter,
|
||||||
saveLastInboxTab,
|
saveLastInboxTab,
|
||||||
shouldShowInboxSection,
|
shouldShowInboxSection,
|
||||||
|
|
@ -520,6 +521,7 @@ export function Inbox() {
|
||||||
pathSegment === "mine" || pathSegment === "recent" || pathSegment === "all" || pathSegment === "unread"
|
pathSegment === "mine" || pathSegment === "recent" || pathSegment === "all" || pathSegment === "unread"
|
||||||
? pathSegment
|
? pathSegment
|
||||||
: "mine";
|
: "mine";
|
||||||
|
const canArchiveFromTab = isMineInboxTab(tab);
|
||||||
const issueLinkState = useMemo(
|
const issueLinkState = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createIssueDetailLocationState(
|
createIssueDetailLocationState(
|
||||||
|
|
@ -911,7 +913,7 @@ export function Inbox() {
|
||||||
}, [dismiss]);
|
}, [dismiss]);
|
||||||
|
|
||||||
const nonIssueUnreadState = (key: string): NonIssueUnreadState => {
|
const nonIssueUnreadState = (key: string): NonIssueUnreadState => {
|
||||||
if (tab !== "mine") return null;
|
if (!canArchiveFromTab) return null;
|
||||||
const isRead = readItems.has(key);
|
const isRead = readItems.has(key);
|
||||||
const isFading = fadingNonIssueItems.has(key);
|
const isFading = fadingNonIssueItems.has(key);
|
||||||
if (isFading) return "fading";
|
if (isFading) return "fading";
|
||||||
|
|
@ -951,7 +953,7 @@ export function Inbox() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard shortcuts are only active on the "mine" tab
|
// Keyboard shortcuts are only active on the "mine" tab
|
||||||
if (tab !== "mine") return;
|
if (!canArchiveFromTab) return;
|
||||||
|
|
||||||
const itemCount = workItemsToRender.length;
|
const itemCount = workItemsToRender.length;
|
||||||
if (itemCount === 0) return;
|
if (itemCount === 0) return;
|
||||||
|
|
@ -1033,7 +1035,7 @@ export function Inbox() {
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
}, [
|
}, [
|
||||||
workItemsToRender, selectedIndex, tab, navigate, issueLinkState,
|
workItemsToRender, selectedIndex, canArchiveFromTab, navigate, issueLinkState,
|
||||||
getWorkItemKey, archivingIssueIds, archivingNonIssueIds,
|
getWorkItemKey, archivingIssueIds, archivingNonIssueIds,
|
||||||
fadingOutIssues, readItems,
|
fadingOutIssues, readItems,
|
||||||
archiveIssueMutation, markReadMutation, markUnreadMutation,
|
archiveIssueMutation, markReadMutation, markUnreadMutation,
|
||||||
|
|
@ -1219,7 +1221,6 @@ export function Inbox() {
|
||||||
</div>,
|
</div>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const isMineTab = tab === "mine";
|
|
||||||
const isSelected = selectedIndex === index;
|
const isSelected = selectedIndex === index;
|
||||||
|
|
||||||
if (item.kind === "approval") {
|
if (item.kind === "approval") {
|
||||||
|
|
@ -1235,7 +1236,7 @@ export function Inbox() {
|
||||||
isPending={approveMutation.isPending || rejectMutation.isPending}
|
isPending={approveMutation.isPending || rejectMutation.isPending}
|
||||||
unreadState={nonIssueUnreadState(approvalKey)}
|
unreadState={nonIssueUnreadState(approvalKey)}
|
||||||
onMarkRead={() => handleMarkNonIssueRead(approvalKey)}
|
onMarkRead={() => handleMarkNonIssueRead(approvalKey)}
|
||||||
onArchive={isMineTab ? () => handleArchiveNonIssue(approvalKey) : undefined}
|
onArchive={canArchiveFromTab ? () => handleArchiveNonIssue(approvalKey) : undefined}
|
||||||
archiveDisabled={isArchiving}
|
archiveDisabled={isArchiving}
|
||||||
className={
|
className={
|
||||||
isArchiving
|
isArchiving
|
||||||
|
|
@ -1244,7 +1245,7 @@ export function Inbox() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
elements.push(wrapItem(approvalKey, isSelected, isMineTab ? (
|
elements.push(wrapItem(approvalKey, isSelected, canArchiveFromTab ? (
|
||||||
<SwipeToArchive
|
<SwipeToArchive
|
||||||
key={approvalKey}
|
key={approvalKey}
|
||||||
disabled={isArchiving}
|
disabled={isArchiving}
|
||||||
|
|
@ -1271,7 +1272,7 @@ export function Inbox() {
|
||||||
isRetrying={retryingRunIds.has(item.run.id)}
|
isRetrying={retryingRunIds.has(item.run.id)}
|
||||||
unreadState={nonIssueUnreadState(runKey)}
|
unreadState={nonIssueUnreadState(runKey)}
|
||||||
onMarkRead={() => handleMarkNonIssueRead(runKey)}
|
onMarkRead={() => handleMarkNonIssueRead(runKey)}
|
||||||
onArchive={isMineTab ? () => handleArchiveNonIssue(runKey) : undefined}
|
onArchive={canArchiveFromTab ? () => handleArchiveNonIssue(runKey) : undefined}
|
||||||
archiveDisabled={isArchiving}
|
archiveDisabled={isArchiving}
|
||||||
className={
|
className={
|
||||||
isArchiving
|
isArchiving
|
||||||
|
|
@ -1280,7 +1281,7 @@ export function Inbox() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
elements.push(wrapItem(runKey, isSelected, isMineTab ? (
|
elements.push(wrapItem(runKey, isSelected, canArchiveFromTab ? (
|
||||||
<SwipeToArchive
|
<SwipeToArchive
|
||||||
key={runKey}
|
key={runKey}
|
||||||
disabled={isArchiving}
|
disabled={isArchiving}
|
||||||
|
|
@ -1304,7 +1305,7 @@ export function Inbox() {
|
||||||
isPending={approveJoinMutation.isPending || rejectJoinMutation.isPending}
|
isPending={approveJoinMutation.isPending || rejectJoinMutation.isPending}
|
||||||
unreadState={nonIssueUnreadState(joinKey)}
|
unreadState={nonIssueUnreadState(joinKey)}
|
||||||
onMarkRead={() => handleMarkNonIssueRead(joinKey)}
|
onMarkRead={() => handleMarkNonIssueRead(joinKey)}
|
||||||
onArchive={isMineTab ? () => handleArchiveNonIssue(joinKey) : undefined}
|
onArchive={canArchiveFromTab ? () => handleArchiveNonIssue(joinKey) : undefined}
|
||||||
archiveDisabled={isArchiving}
|
archiveDisabled={isArchiving}
|
||||||
className={
|
className={
|
||||||
isArchiving
|
isArchiving
|
||||||
|
|
@ -1313,7 +1314,7 @@ export function Inbox() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
elements.push(wrapItem(joinKey, isSelected, isMineTab ? (
|
elements.push(wrapItem(joinKey, isSelected, canArchiveFromTab ? (
|
||||||
<SwipeToArchive
|
<SwipeToArchive
|
||||||
key={joinKey}
|
key={joinKey}
|
||||||
disabled={isArchiving}
|
disabled={isArchiving}
|
||||||
|
|
@ -1370,7 +1371,7 @@ export function Inbox() {
|
||||||
}
|
}
|
||||||
onMarkRead={() => markReadMutation.mutate(issue.id)}
|
onMarkRead={() => markReadMutation.mutate(issue.id)}
|
||||||
onArchive={
|
onArchive={
|
||||||
isMineTab
|
canArchiveFromTab
|
||||||
? () => archiveIssueMutation.mutate(issue.id)
|
? () => archiveIssueMutation.mutate(issue.id)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
@ -1383,7 +1384,7 @@ export function Inbox() {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
elements.push(wrapItem(`issue:${issue.id}`, isSelected, isMineTab ? (
|
elements.push(wrapItem(`issue:${issue.id}`, isSelected, canArchiveFromTab ? (
|
||||||
<SwipeToArchive
|
<SwipeToArchive
|
||||||
key={`issue:${issue.id}`}
|
key={`issue:${issue.id}`}
|
||||||
disabled={isArchiving || archiveIssueMutation.isPending}
|
disabled={isArchiving || archiveIssueMutation.isPending}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue