Skip to content

Commit a0b4e6b

Browse files
committed
feat(issues): Infinity query issues & sort them by updated time
1 parent d1697e4 commit a0b4e6b

File tree

2 files changed

+41
-28
lines changed

2 files changed

+41
-28
lines changed

src/api/redmine.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TIssue } from "../types/redmine";
22
import instance from "./axios.config";
33

4-
export const getAllMyOpenIssues = async (offset = 0): Promise<TIssue[]> => {
5-
return instance.get(`/issues.json?limit=100&offset=${offset}&status_id=open&assigned_to_id=me`).then((res) => res.data.issues);
4+
export const getAllMyOpenIssues = async (offset = 0, limit = 100): Promise<TIssue[]> => {
5+
return instance.get(`/issues.json?offset=${offset}&limit=${limit}&status_id=open&assigned_to_id=me&sort=updated_on:desc`).then((res) => res.data.issues);
66
};

src/pages/IssuesPage.tsx

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { faSearch } from "@fortawesome/free-solid-svg-icons";
22
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3-
import { useQuery } from "@tanstack/react-query";
4-
import { useState } from "react";
3+
import { useInfiniteQuery } from "@tanstack/react-query";
4+
import { useEffect, useState } from "react";
55
import { getAllMyOpenIssues } from "../api/redmine";
66
import InputField from "../components/general/InputField";
77
import LoadingSpinner from "../components/general/LoadingSpinner";
@@ -31,29 +31,40 @@ const IssuesPage = () => {
3131

3232
const [search, setSearch] = useState("");
3333

34-
const issuesQuery = useQuery(["issues"], () => getAllMyOpenIssues());
35-
const filteredIssues = searching && search ? issuesQuery.data?.filter((issue) => new RegExp(search, "i").test(`#${issue.id} ${issue.subject}`)) : issuesQuery.data;
36-
const groupedIssues = filteredIssues?.reduce(
37-
(
38-
result: {
39-
[id: number]: {
40-
project: TReference;
41-
issues: TIssue[];
42-
};
34+
const issuesQuery = useInfiniteQuery({
35+
queryKey: ["issues"],
36+
queryFn: ({ pageParam = 0 }) => getAllMyOpenIssues(pageParam * 100, 100),
37+
getNextPageParam: (lastPage, allPages) => (lastPage.length === 100 ? allPages.length : undefined),
38+
});
39+
useEffect(() => {
40+
if (issuesQuery.hasNextPage && !issuesQuery.isFetchingNextPage) issuesQuery.fetchNextPage();
41+
}, [issuesQuery.hasNextPage, issuesQuery.isFetchingNextPage, issuesQuery.fetchNextPage]);
42+
const filteredIssues = searching && search ? issuesQuery.data?.pages?.flat().filter((issue) => new RegExp(search, "i").test(`#${issue.id} ${issue.subject}`)) : issuesQuery.data?.pages?.flat();
43+
const groupedIssues = Object.values(
44+
filteredIssues?.reduce(
45+
(
46+
result: {
47+
[id: number]: {
48+
project: TReference;
49+
issues: TIssue[];
50+
sort: number;
51+
};
52+
},
53+
issue
54+
) => {
55+
if (!(issue.project.id in result)) {
56+
result[issue.project.id] = {
57+
project: issue.project,
58+
issues: [],
59+
sort: Object.keys(result).length,
60+
};
61+
}
62+
result[issue.project.id].issues.push(issue);
63+
return result;
4364
},
44-
issue
45-
) => {
46-
if (!(issue.project.id in result)) {
47-
result[issue.project.id] = {
48-
project: issue.project,
49-
issues: [],
50-
};
51-
}
52-
result[issue.project.id].issues.push(issue);
53-
return result;
54-
},
55-
{}
56-
);
65+
{}
66+
) ?? {}
67+
).sort(({ sort: a }, { sort: b }) => a - b);
5768

5869
const { data: issues, setData: setIssues } = useStorage<IssuesData>("issues", {});
5970

@@ -63,9 +74,11 @@ const IssuesPage = () => {
6374
<div className="flex flex-col gap-y-2">
6475
{issuesQuery.isLoading && <LoadingSpinner />}
6576
{issuesQuery.isError && <Toast type="error" message="Failed to load issues" allowClose={false} />}
66-
{Object.entries(groupedIssues ?? {}).map(([_, { project, issues: groupIssues }]) => (
77+
{groupedIssues.map(({ project, issues: groupIssues }) => (
6778
<>
68-
<h5 className="text-xs text-slate-500 dark:text-slate-300 truncate">{project.name}</h5>
79+
<a href={`${settings.redmineURL}/projects/${project.id}`} target="_blank" tabIndex={-1}>
80+
<h5 className="text-xs text-slate-500 dark:text-slate-300 truncate">{project.name}</h5>
81+
</a>
6982
{groupIssues.map((issue) => {
7083
const issueData =
7184
issue.id in issues

0 commit comments

Comments
 (0)