fix(board): show live feed history
This commit is contained in:
@@ -44,6 +44,7 @@ import {
|
|||||||
streamApprovalsApiV1BoardsBoardIdApprovalsStreamGet,
|
streamApprovalsApiV1BoardsBoardIdApprovalsStreamGet,
|
||||||
updateApprovalApiV1BoardsBoardIdApprovalsApprovalIdPatch,
|
updateApprovalApiV1BoardsBoardIdApprovalsApprovalIdPatch,
|
||||||
} from "@/api/generated/approvals/approvals";
|
} from "@/api/generated/approvals/approvals";
|
||||||
|
import { listTaskCommentFeedApiV1ActivityTaskCommentsGet } from "@/api/generated/activity/activity";
|
||||||
import { getBoardSnapshotApiV1BoardsBoardIdSnapshotGet } from "@/api/generated/boards/boards";
|
import { getBoardSnapshotApiV1BoardsBoardIdSnapshotGet } from "@/api/generated/boards/boards";
|
||||||
import {
|
import {
|
||||||
createBoardMemoryApiV1BoardsBoardIdMemoryPost,
|
createBoardMemoryApiV1BoardsBoardIdMemoryPost,
|
||||||
@@ -307,6 +308,12 @@ export default function BoardDetailPage() {
|
|||||||
const openedTaskIdFromUrlRef = useRef<string | null>(null);
|
const openedTaskIdFromUrlRef = useRef<string | null>(null);
|
||||||
const [comments, setComments] = useState<TaskComment[]>([]);
|
const [comments, setComments] = useState<TaskComment[]>([]);
|
||||||
const [liveFeed, setLiveFeed] = useState<TaskComment[]>([]);
|
const [liveFeed, setLiveFeed] = useState<TaskComment[]>([]);
|
||||||
|
const [isLiveFeedHistoryLoading, setIsLiveFeedHistoryLoading] =
|
||||||
|
useState(false);
|
||||||
|
const [liveFeedHistoryError, setLiveFeedHistoryError] = useState<
|
||||||
|
string | null
|
||||||
|
>(null);
|
||||||
|
const liveFeedHistoryLoadedRef = useRef(false);
|
||||||
const [isCommentsLoading, setIsCommentsLoading] = useState(false);
|
const [isCommentsLoading, setIsCommentsLoading] = useState(false);
|
||||||
const [commentsError, setCommentsError] = useState<string | null>(null);
|
const [commentsError, setCommentsError] = useState<string | null>(null);
|
||||||
const [newComment, setNewComment] = useState("");
|
const [newComment, setNewComment] = useState("");
|
||||||
@@ -345,6 +352,71 @@ export default function BoardDetailPage() {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
liveFeedHistoryLoadedRef.current = false;
|
||||||
|
setIsLiveFeedHistoryLoading(false);
|
||||||
|
setLiveFeedHistoryError(null);
|
||||||
|
setLiveFeed([]);
|
||||||
|
}, [boardId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLiveFeedOpen) return;
|
||||||
|
if (!isSignedIn || !boardId) return;
|
||||||
|
if (liveFeedHistoryLoadedRef.current) return;
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
setIsLiveFeedHistoryLoading(true);
|
||||||
|
setLiveFeedHistoryError(null);
|
||||||
|
|
||||||
|
const fetchHistory = async () => {
|
||||||
|
try {
|
||||||
|
const result = await listTaskCommentFeedApiV1ActivityTaskCommentsGet({
|
||||||
|
board_id: boardId,
|
||||||
|
limit: 200,
|
||||||
|
});
|
||||||
|
if (cancelled) return;
|
||||||
|
if (result.status !== 200) {
|
||||||
|
throw new Error("Unable to load live feed.");
|
||||||
|
}
|
||||||
|
const items = result.data.items ?? [];
|
||||||
|
liveFeedHistoryLoadedRef.current = true;
|
||||||
|
|
||||||
|
const mapped: TaskComment[] = items.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
message: item.message ?? null,
|
||||||
|
agent_id: item.agent_id ?? null,
|
||||||
|
task_id: item.task_id ?? null,
|
||||||
|
created_at: item.created_at,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setLiveFeed((prev) => {
|
||||||
|
const map = new Map<string, TaskComment>();
|
||||||
|
[...prev, ...mapped].forEach((item) => map.set(item.id, item));
|
||||||
|
const merged = [...map.values()];
|
||||||
|
merged.sort((a, b) => {
|
||||||
|
const aTime = apiDatetimeToMs(a.created_at) ?? 0;
|
||||||
|
const bTime = apiDatetimeToMs(b.created_at) ?? 0;
|
||||||
|
return bTime - aTime;
|
||||||
|
});
|
||||||
|
return merged.slice(0, 50);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (cancelled) return;
|
||||||
|
setLiveFeedHistoryError(
|
||||||
|
err instanceof Error ? err.message : "Unable to load live feed.",
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
if (cancelled) return;
|
||||||
|
setIsLiveFeedHistoryLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void fetchHistory();
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [boardId, isLiveFeedOpen, isSignedIn]);
|
||||||
|
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
@@ -2365,7 +2437,13 @@ export default function BoardDetailPage() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-y-auto px-6 py-4">
|
<div className="flex-1 overflow-y-auto px-6 py-4">
|
||||||
{orderedLiveFeed.length === 0 ? (
|
{isLiveFeedHistoryLoading && orderedLiveFeed.length === 0 ? (
|
||||||
|
<p className="text-sm text-slate-500">Loading feed…</p>
|
||||||
|
) : liveFeedHistoryError ? (
|
||||||
|
<div className="rounded-lg border border-slate-200 bg-white p-4 text-sm text-slate-700 shadow-sm">
|
||||||
|
{liveFeedHistoryError}
|
||||||
|
</div>
|
||||||
|
) : orderedLiveFeed.length === 0 ? (
|
||||||
<p className="text-sm text-slate-500">
|
<p className="text-sm text-slate-500">
|
||||||
Waiting for new comments…
|
Waiting for new comments…
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user