Compare commits

...

1 Commits

Author SHA1 Message Date
rahulramesha
48acacfc64 add individual call for each group 2024-07-25 20:17:44 +05:30
8 changed files with 80 additions and 41 deletions

View File

@@ -26,6 +26,7 @@ import { IQuickActionProps, TRenderQuickActions } from "../list/list-view-types"
import { getSourceFromDropPayload } from "../utils";
import { KanBan } from "./default";
import { KanBanSwimLanes } from "./swimlanes";
import { IssuePaginationOptions } from "@plane/types";
export type KanbanStoreType =
| EIssuesStoreType.PROJECT
@@ -53,7 +54,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
membership: { currentProjectRole },
} = useUser();
const { captureIssueEvent } = useEventTracker();
const { issueMap, issuesFilter, issues } = useIssues(storeType);
const { issueMap, issuesFilter, issues } = useIssues(EIssuesStoreType.PROJECT);
const {
issue: { getIssueById },
} = useIssueDetail();
@@ -67,7 +68,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
archiveIssue,
restoreIssue,
updateFilters,
} = useIssuesActions(storeType);
} = useIssuesActions(EIssuesStoreType.PROJECT);
const deleteAreaRef = useRef<HTMLDivElement | null>(null);
const [isDragOverDelete, setIsDragOverDelete] = useState(false);
@@ -82,24 +83,21 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
const orderBy = displayFilters?.order_by;
useEffect(() => {
fetchIssues("init-loader", { canGroup: true, perPageCount: sub_group_by ? 10 : 30 }, viewId);
}, [fetchIssues, storeType, group_by, sub_group_by, viewId]);
// useEffect(() => {
// fetchIssues("init-loader", { canGroup: true, perPageCount: sub_group_by ? 10 : 30 }, viewId);
// }, [fetchIssues, storeType, group_by, sub_group_by, viewId]);
const fetchMoreIssues = useCallback(
(groupId?: string, subgroupId?: string) => {
(groupId?: string, subgroupId?: string, options?: IssuePaginationOptions) => {
if (issues?.getIssueLoader(groupId, subgroupId) !== "pagination") {
fetchNextIssues(groupId, subgroupId);
fetchNextIssues(groupId, subgroupId, options);
}
},
[fetchNextIssues]
);
const debouncedFetchMoreIssues = debounce(
(groupId?: string, subgroupId?: string) => fetchMoreIssues(groupId, subgroupId),
300,
{ leading: true, trailing: false }
);
const debouncedFetchMoreIssues = (groupId?: string, subgroupId?: string, options?: IssuePaginationOptions) =>
fetchMoreIssues(groupId, subgroupId, options);
const groupedIssueIds = issues?.groupedIssueIds;
@@ -222,7 +220,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
const kanbanFilters = issuesFilter?.issueFilters?.kanbanFilters || { group_by: [], sub_group_by: [] };
return (
<IssueLayoutHOC layout={EIssueLayoutTypes.KANBAN}>
<>
<DeleteIssueModal
dataId={draggedIssueId}
isOpen={deleteIssueModal}
@@ -279,6 +277,6 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
</div>
</div>
</div>
</IssueLayoutHOC>
</>
);
});

View File

@@ -11,6 +11,7 @@ import {
TIssueKanbanFilters,
TIssueGroupByOptions,
TIssueOrderByOptions,
IssuePaginationOptions,
} from "@plane/types";
// constants
// hooks
@@ -43,7 +44,7 @@ export interface IKanBan {
quickActions: TRenderQuickActions;
kanbanFilters: TIssueKanbanFilters;
handleKanbanFilters: any;
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
loadMoreIssues: (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => void;
enableQuickIssueCreate?: boolean;
quickAddCallback?: (projectId: string | null | undefined, data: TIssue) => Promise<TIssue | undefined>;
disableIssueCreation?: boolean;

View File

@@ -14,6 +14,7 @@ import {
TSubGroupedIssues,
TIssueGroupByOptions,
TIssueOrderByOptions,
IssuePaginationOptions,
} from "@plane/types";
import { TOAST_TYPE, setToast } from "@plane/ui";
import { highlightIssueOnDrop } from "@/components/issues/issue-layouts/utils";
@@ -32,7 +33,7 @@ import { KanbanIssueBlocksList, KanBanQuickAddIssueForm } from ".";
interface IKanbanGroup {
groupId: string;
issuesMap: IIssueMap;
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
displayProperties: IIssueDisplayProperties | undefined;
sub_group_by: TIssueGroupByOptions | undefined;
group_by: TIssueGroupByOptions | undefined;
@@ -44,7 +45,7 @@ groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
quickActions: TRenderQuickActions;
enableQuickIssueCreate?: boolean;
quickAddCallback?: (projectId: string | null | undefined, data: TIssue) => Promise<TIssue | undefined>;
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
loadMoreIssues: (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => void;
disableIssueCreation?: boolean;
canEditProperties: (projectId: string | undefined) => boolean;
groupByVisibilityToggle?: boolean;
@@ -88,9 +89,15 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
const containerRef = sub_group_by && scrollableContainerRef ? scrollableContainerRef : columnRef;
const loadMoreIssuesInThisGroup = useCallback(() => {
loadMoreIssues(groupId, sub_group_id === "null"? undefined: sub_group_id)
}, [loadMoreIssues, groupId, sub_group_id])
loadMoreIssues(groupId, sub_group_id === "null" ? undefined : sub_group_id);
}, [loadMoreIssues, groupId, sub_group_id, sub_group_by]);
useEffect(() => {
loadMoreIssues(groupId, sub_group_id === "null" ? undefined : sub_group_id, {
canGroup: true,
perPageCount: sub_group_by ? 10 : 30,
});
}, [loadMoreIssues, groupId, sub_group_id, sub_group_by]);
const isPaginating = !!getIssueLoader(groupId, sub_group_id);
useIntersectionObserver(
@@ -222,7 +229,6 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
const nextPageResults = getPaginationData(groupId, sub_group_id)?.nextPageResults;
const loadMore = isPaginating ? (
<KanbanIssueBlockLoader />
) : (
@@ -235,8 +241,13 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
</div>
);
const shouldLoadMore = nextPageResults === undefined ? issueIds?.length < groupIssueCount : !!nextPageResults;
const canOverlayBeVisible = orderBy !== "sort_order" || isDropDisabled;
const shouldLoadMore =
nextPageResults === undefined
? groupIssueCount !== undefined
? issueIds?.length < groupIssueCount
: true
: !!nextPageResults;
const canOverlayBeVisible = orderBy !== "sort_order" || isDropDisabled;
const shouldOverlayBeVisible = isDraggingOverColumn && canOverlayBeVisible;
return (
@@ -270,7 +281,7 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
canDropOverIssue={!canOverlayBeVisible}
/>
{shouldLoadMore && (isSubGroup ? <>{loadMore}</> : <KanbanIssueBlockLoader ref={setIntersectionElement} />)}
{shouldLoadMore && (isSubGroup ? <>{loadMore}</> : <KanbanIssueBlockLoader ref={setIntersectionElement} />)}
{enableQuickIssueCreate && !disableIssueCreation && (
<div className="w-full bg-custom-background-90 py-0.5 sticky bottom-0">

View File

@@ -11,6 +11,7 @@ import {
TIssueKanbanFilters,
TIssueGroupByOptions,
TIssueOrderByOptions,
IssuePaginationOptions,
} from "@plane/types";
// hooks
import { useCycle, useLabel, useMember, useModule, useProject, useProjectState } from "@/hooks/store";
@@ -104,7 +105,7 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
quickAddCallback?: (projectId: string | null | undefined, data: TIssue) => Promise<TIssue | undefined>;
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
loadMoreIssues: (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => void;
}
const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
@@ -223,7 +224,7 @@ export interface IKanBanSwimLanes {
quickActions: TRenderQuickActions;
kanbanFilters: TIssueKanbanFilters;
handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void;
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
loadMoreIssues: (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => void;
showEmptyGroup: boolean;
handleOnDrop: (source: GroupDropLocation, destination: GroupDropLocation) => Promise<void>;
disableIssueCreation?: boolean;

View File

@@ -21,7 +21,11 @@ interface IssueActions {
options: IssuePaginationOptions,
viewId?: string
) => Promise<TIssuesResponse | undefined>;
fetchNextIssues: (groupId?: string, subGroupId?: string) => Promise<TIssuesResponse | undefined>;
fetchNextIssues: (
groupId?: string,
subGroupId?: string,
options?: IssuePaginationOptions
) => Promise<TIssuesResponse | undefined>;
removeIssue: (projectId: string | undefined | null, issueId: string) => Promise<void>;
createIssue?: (projectId: string | undefined | null, data: Partial<TIssue>) => Promise<TIssue | undefined>;
quickAddIssue?: (projectId: string | undefined | null, data: TIssue) => Promise<TIssue | undefined>;
@@ -76,16 +80,16 @@ const useProjectIssueActions = () => {
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
const fetchIssues = useCallback(
async (loadType: TLoader, options: IssuePaginationOptions) => {
async (loadType: TLoader, options: IssuePaginationOptions, groupId?: string, subGroupId?: string) => {
if (!workspaceSlug || !projectId) return;
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options);
},
[issues.fetchIssues, workspaceSlug, projectId]
);
const fetchNextIssues = useCallback(
async (groupId?: string, subGroupId?: string) => {
async (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => {
if (!workspaceSlug || !projectId) return;
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString(), groupId, subGroupId);
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString(), groupId, subGroupId, options);
},
[issues.fetchIssues, workspaceSlug, projectId]
);

View File

@@ -482,7 +482,12 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
* @param groupId
* @param subGroupId
*/
onfetchNexIssues(issuesResponse: TIssuesResponse, groupId?: string, subGroupId?: string) {
onfetchNexIssues(
issuesResponse: TIssuesResponse,
groupId?: string,
subGroupId?: string,
options?: IssuePaginationOptions
) {
// Process the Issue Response to get the following data from it
const { issueList, groupedIssues, groupedIssueCount } = this.processIssueResponse(issuesResponse);
@@ -496,7 +501,7 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
});
// store Pagination data like next cursor etc
this.storePreviousPaginationValues(issuesResponse, undefined, groupId, subGroupId);
this.storePreviousPaginationValues(issuesResponse, options, groupId, subGroupId);
}
/**

View File

@@ -282,7 +282,7 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore {
subGroupId?: string
) {
// if cursor exists, use the cursor. If it doesn't exist construct the cursor based on per page count
const pageCursor = cursor ? cursor : groupId ? `${options.perPageCount}:1:0` : `${options.perPageCount}:0:0`;
const pageCursor = cursor ? cursor : groupId ? `${options.perPageCount}:0:0` : `${options.perPageCount}:0:0`;
// pagination params
const paginationParams: Partial<Record<TIssueParams, string | boolean>> = {

View File

@@ -15,7 +15,9 @@ export interface IProjectIssues extends IBaseIssuesStore {
workspaceSlug: string,
projectId: string,
loadType: TLoader,
option: IssuePaginationOptions
option: IssuePaginationOptions,
groupId?: string,
subGroupId?: string
) => Promise<TIssuesResponse | undefined>;
fetchIssuesWithExistingPagination: (
workspaceSlug: string,
@@ -26,7 +28,8 @@ export interface IProjectIssues extends IBaseIssuesStore {
workspaceSlug: string,
projectId: string,
groupId?: string,
subGroupId?: string
subGroupId?: string,
options?: IssuePaginationOptions
) => Promise<TIssuesResponse | undefined>;
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
@@ -83,6 +86,8 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
projectId: string,
loadType: TLoader = "init-loader",
options: IssuePaginationOptions,
groupId?: string,
subGroupId?: string,
isExistingPaginationOptions: boolean = false
) => {
try {
@@ -93,14 +98,14 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
this.clear(!isExistingPaginationOptions);
// get params from pagination options
const params = this.issueFilterStore?.getFilterParams(options, projectId, undefined, undefined, undefined);
const params = this.issueFilterStore?.getFilterParams(options, projectId, undefined, groupId, subGroupId);
// call the fetch issues API with the params
const response = await this.issueService.getIssues(workspaceSlug, projectId, params, {
signal: this.controller.signal,
});
// after fetching issues, call the base method to process the response further
this.onfetchIssues(response, options, workspaceSlug, projectId);
this.onfetchNexIssues(response, groupId, subGroupId, options);
return response;
} catch (error) {
// set loader to undefined if errored out
@@ -119,17 +124,23 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
* @param subGroupId
* @returns
*/
fetchNextIssues = async (workspaceSlug: string, projectId: string, groupId?: string, subGroupId?: string) => {
fetchNextIssues = async (
workspaceSlug: string,
projectId: string,
groupId?: string,
subGroupId?: string,
options?: IssuePaginationOptions
) => {
const cursorObject = this.getPaginationData(groupId, subGroupId);
// if there are no pagination options and the next page results do not exist the return
if (!this.paginationOptions || (cursorObject && !cursorObject?.nextPageResults)) return;
if (!this.paginationOptions && !options) return;
try {
// set Loader
this.setLoader("pagination", groupId, subGroupId);
// get params from stored pagination options
const params = this.issueFilterStore?.getFilterParams(
this.paginationOptions,
options ?? this.paginationOptions!,
projectId,
this.getNextCursor(groupId, subGroupId),
groupId,
@@ -139,7 +150,7 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
// after the next page of issues are fetched, call the base method to process the response
this.onfetchNexIssues(response, groupId, subGroupId);
this.onfetchNexIssues(response, groupId, subGroupId, options);
return response;
} catch (error) {
// set Loader as undefined if errored out
@@ -162,7 +173,15 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
loadType: TLoader = "mutation"
) => {
if (!this.paginationOptions) return;
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions, true);
return await this.fetchIssues(
workspaceSlug,
projectId,
loadType,
this.paginationOptions,
undefined,
undefined,
true
);
};
// Using aliased names as they cannot be overridden in other stores