From afb0173c0560ee7ce2ceb3ace7474400f9e58a14 Mon Sep 17 00:00:00 2001 From: iliya Date: Thu, 5 Mar 2026 13:32:58 +0200 Subject: [PATCH] feat: improve task sorting and enhance UI component behavior - Updated task sorting logic in TeamTaskReader to handle non-numeric IDs with stable lexicographic ordering. - Refactored ActivityItem to remove unnecessary whitespace for cleaner code. - Enhanced AttachmentPreviewItem to improve accessibility by adding pointer-events-none class for disabled state. - Modified MessageComposer to ensure the summary remains compact during message sending. - Added cleanup logic in useResizableColumns to handle global listeners and styles during drag events. --- src/main/services/team/TeamTaskReader.ts | 12 ++++++++++-- .../components/team/activity/ActivityItem.tsx | 1 - .../team/attachments/AttachmentPreviewItem.tsx | 2 +- .../components/team/messages/MessageComposer.tsx | 3 ++- src/renderer/hooks/useResizableColumns.ts | 11 +++++++++++ 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/main/services/team/TeamTaskReader.ts b/src/main/services/team/TeamTaskReader.ts index e6b8912c..ef445afa 100644 --- a/src/main/services/team/TeamTaskReader.ts +++ b/src/main/services/team/TeamTaskReader.ts @@ -271,8 +271,16 @@ export class TeamTaskReader { } } - // Sort by numeric ID so kanban default order is deterministic (#1, #2, ..., #10, #11) - tasks.sort((a, b) => Number(a.id) - Number(b.id)); + // Sort by numeric ID so kanban default order is deterministic (#1, #2, ..., #10, #11). + // Fall back to stable lexicographic ordering for unexpected non-numeric IDs. + tasks.sort((a, b) => { + const aIsNumeric = /^\d+$/.test(a.id); + const bIsNumeric = /^\d+$/.test(b.id); + if (aIsNumeric && bIsNumeric) return Number(a.id) - Number(b.id); + if (aIsNumeric) return -1; + if (bIsNumeric) return 1; + return a.id.localeCompare(b.id, undefined, { numeric: true, sensitivity: 'base' }); + }); return tasks; } diff --git a/src/renderer/components/team/activity/ActivityItem.tsx b/src/renderer/components/team/activity/ActivityItem.tsx index 14416cd2..fe1e9eeb 100644 --- a/src/renderer/components/team/activity/ActivityItem.tsx +++ b/src/renderer/components/team/activity/ActivityItem.tsx @@ -176,7 +176,6 @@ function linkifyMentionsInMarkdown(text: string, memberColorMap: Map` in plain text as clickable inline elements with TaskTooltip. */ function linkifyTaskIds(text: string, onClick: (taskId: string) => void): React.ReactNode[] { return text.split(/(#\d+)/g).map((part, i) => { diff --git a/src/renderer/components/team/attachments/AttachmentPreviewItem.tsx b/src/renderer/components/team/attachments/AttachmentPreviewItem.tsx index 75c7cd4a..819ebaf6 100644 --- a/src/renderer/components/team/attachments/AttachmentPreviewItem.tsx +++ b/src/renderer/components/team/attachments/AttachmentPreviewItem.tsx @@ -21,7 +21,7 @@ export const AttachmentPreviewItem = ({ return (
{disabled ? ( -
+
) : null} diff --git a/src/renderer/components/team/messages/MessageComposer.tsx b/src/renderer/components/team/messages/MessageComposer.tsx index 84a4eb6f..47e45bf4 100644 --- a/src/renderer/components/team/messages/MessageComposer.tsx +++ b/src/renderer/components/team/messages/MessageComposer.tsx @@ -168,7 +168,8 @@ export const MessageComposer = ({ if (!canSend) return; pendingSendRef.current = true; const serialized = serializeChipsWithText(trimmed, chipDraft.chips); - onSend(recipient, serialized, serialized, attachments.length > 0 ? attachments : undefined); + // Summary should stay compact (no expanded chip markdown) + onSend(recipient, serialized, trimmed, attachments.length > 0 ? attachments : undefined); }, [canSend, recipient, trimmed, onSend, attachments, chipDraft.chips]); // Clear draft only after send completes successfully (sending: true → false, no error) diff --git a/src/renderer/hooks/useResizableColumns.ts b/src/renderer/hooks/useResizableColumns.ts index 332c836f..ee55fde2 100644 --- a/src/renderer/hooks/useResizableColumns.ts +++ b/src/renderer/hooks/useResizableColumns.ts @@ -95,6 +95,17 @@ export function useResizableColumns({ }); }, [handlePointerMove, storageKey]); + // Safety: if the board unmounts or storageKey changes mid-drag, clean up global listeners/styles. + useEffect(() => { + return () => { + draggingRef.current = null; + document.removeEventListener('pointermove', handlePointerMove); + document.removeEventListener('pointerup', handlePointerUp); + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + }; + }, [handlePointerMove, handlePointerUp]); + const getHandleProps = useCallback( (leftColumnId: string) => ({ onPointerDown: (e: React.PointerEvent) => {