Compare commits

..

1 Commits

Author SHA1 Message Date
Aaryan Khandelwal 83298a7a69 chore: generalize page root component 2024-12-18 15:19:48 +05:30
11 changed files with 139 additions and 339 deletions
+2 -4
View File
@@ -91,7 +91,6 @@ def issue_on_results(issues, group_by, sub_group_by):
Case(
When(
votes__isnull=False,
votes__deleted_at__isnull=True,
then=JSONObject(
vote=F("votes__vote"),
actor_details=JSONObject(
@@ -118,14 +117,13 @@ def issue_on_results(issues, group_by, sub_group_by):
default=None,
output_field=JSONField(),
),
filter=Q(votes__isnull=False,votes__deleted_at__isnull=True),
filter=Q(votes__isnull=False),
distinct=True,
),
reaction_items=ArrayAgg(
Case(
When(
issue_reactions__isnull=False,
issue_reactions__deleted_at__isnull=True,
then=JSONObject(
reaction=F("issue_reactions__reaction"),
actor_details=JSONObject(
@@ -152,7 +150,7 @@ def issue_on_results(issues, group_by, sub_group_by):
default=None,
output_field=JSONField(),
),
filter=Q(issue_reactions__isnull=False, issue_reactions__deleted_at__isnull=True),
filter=Q(issue_reactions__isnull=False),
distinct=True,
),
).values(*required_fields, "vote_items", "reaction_items")
+2 -12
View File
@@ -701,7 +701,6 @@ class IssueRetrievePublicEndpoint(BaseAPIView):
Case(
When(
votes__isnull=False,
votes__deleted_at__isnull=True,
then=JSONObject(
vote=F("votes__vote"),
actor_details=JSONObject(
@@ -733,11 +732,7 @@ class IssueRetrievePublicEndpoint(BaseAPIView):
output_field=JSONField(),
),
filter=Case(
When(
votes__isnull=False,
votes__deleted_at__isnull=True,
then=True,
),
When(votes__isnull=False, then=True),
default=False,
output_field=JSONField(),
),
@@ -747,7 +742,6 @@ class IssueRetrievePublicEndpoint(BaseAPIView):
Case(
When(
issue_reactions__isnull=False,
issue_reactions__deleted_at__isnull=True,
then=JSONObject(
reaction=F("issue_reactions__reaction"),
actor_details=JSONObject(
@@ -781,11 +775,7 @@ class IssueRetrievePublicEndpoint(BaseAPIView):
output_field=JSONField(),
),
filter=Case(
When(
issue_reactions__isnull=False,
issue_reactions__deleted_at__isnull=True,
then=True,
),
When(issue_reactions__isnull=False, then=True),
default=False,
output_field=JSONField(),
),
-1
View File
@@ -1,2 +1 @@
export * from "./projects";
export * from "./project-activity";
-25
View File
@@ -1,25 +0,0 @@
export interface TProjectActivity {
id: string;
content: string;
createdAt: string;
updatedAt: string;
userId: string;
projectId: string;
created_at: string;
field: string;
verb: string;
actor_detail: {
display_name: string;
id: string;
};
workspace_detail: {
slug: string;
};
project_detail: {
name: string;
};
new_value: string;
old_value: string;
project: string;
new_identifier?: string;
}
+15 -29
View File
@@ -4,7 +4,7 @@ import { useSearchParams } from "next/navigation";
// editor
import { EditorRefApi } from "@plane/editor";
// types
import { TPage } from "@plane/types";
import { TDocumentPayload, TPage, TPageVersion } from "@plane/types";
// ui
import { setToast, TOAST_TYPE } from "@plane/ui";
// components
@@ -14,21 +14,25 @@ import { useProjectPages } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { usePageFallback } from "@/hooks/use-page-fallback";
import { useQueryParams } from "@/hooks/use-query-params";
// services
import { ProjectPageService, ProjectPageVersionService } from "@/services/page";
const projectPageService = new ProjectPageService();
const projectPageVersionService = new ProjectPageVersionService();
// store
import { IPage } from "@/store/pages/page";
type TPageRootProps = {
descriptionHandler: {
fetch: () => Promise<any>;
update: (data: TDocumentPayload) => Promise<void>;
};
page: IPage;
projectId: string;
versionHistoryHandler: {
fetchAll: (pageId: string) => Promise<TPageVersion[] | undefined>;
fetchDetails: (pageId: string, versionId: string) => Promise<TPageVersion | undefined>;
};
workspaceSlug: string;
};
export const PageRoot = observer((props: TPageRootProps) => {
const { projectId, workspaceSlug, page } = props;
const { descriptionHandler, page, projectId, versionHistoryHandler, workspaceSlug } = props;
// states
const [editorReady, setEditorReady] = useState(false);
const [hasConnectionFailed, setHasConnectionFailed] = useState(false);
@@ -43,16 +47,13 @@ export const PageRoot = observer((props: TPageRootProps) => {
// store hooks
const { createPage } = useProjectPages();
// derived values
const { access, description_html, name, isContentEditable, updateDescription } = page;
const { access, description_html, name, isContentEditable } = page;
// page fallback
usePageFallback({
editorRef,
fetchPageDescription: async () => {
if (!page.id) return;
return await projectPageService.fetchDescriptionBinary(workspaceSlug, projectId, page.id);
},
fetchPageDescription: async () => await descriptionHandler.fetch(),
hasConnectionFailed,
updatePageDescription: async (data) => await updateDescription(data),
updatePageDescription: async (data) => await descriptionHandler.update(data),
});
// update query params
const { updateQueryParams } = useQueryParams();
@@ -105,23 +106,8 @@ export const PageRoot = observer((props: TPageRootProps) => {
activeVersion={version}
currentVersionDescription={currentVersionDescription ?? null}
editorComponent={PagesVersionEditor}
fetchAllVersions={async (pageId) => {
if (!workspaceSlug || !projectId) return;
return await projectPageVersionService.fetchAllVersions(
workspaceSlug.toString(),
projectId.toString(),
pageId
);
}}
fetchVersionDetails={async (pageId, versionId) => {
if (!workspaceSlug || !projectId) return;
return await projectPageVersionService.fetchVersionById(
workspaceSlug.toString(),
projectId.toString(),
pageId,
versionId
);
}}
fetchAllVersions={async (pageId) => await versionHistoryHandler.fetchAll(pageId)}
fetchVersionDetails={async (pageId, versionId) => await versionHistoryHandler.fetchDetails(pageId, versionId)}
handleRestore={handleRestoreVersion}
isOpen={isVersionsOverlayOpen}
onClose={handleCloseVersionsOverlay}
+113 -141
View File
@@ -3,49 +3,37 @@ import * as Comlink from "comlink";
import set from "lodash/set";
// plane
import { EIssueGroupBYServerToProperty } from "@plane/constants";
import { TIssue } from "@plane/types";
// lib
import { rootStore } from "@/lib/store-context";
// services
import { IssueService } from "@/services/issue/issue.service";
//
import { ARRAY_FIELDS, BOOLEAN_FIELDS } from "./utils/constants";
import { getSubIssuesWithDistribution } from "./utils/data.utils";
import createIndexes from "./utils/indexes";
import { addIssuesBulk, syncDeletesToLocal } from "./utils/load-issues";
import { loadWorkSpaceData } from "./utils/load-workspace";
import { issueFilterCountQueryConstructor, issueFilterQueryConstructor } from "./utils/query-constructor";
import { runQuery } from "./utils/query-executor";
import { deleteOption, formatLocalIssue, getLastSyncTime, getOption, setOption } from "./utils/storage.sqlite.utils";
import { createTables } from "./utils/tables";
import { clearOPFS, getGroupedIssueResults, getSubGroupedIssueResults, log, logError } from "./utils/utils";
/** Database version for schema management */
const DB_VERSION = 1;
/** Number of items per page for pagination */
const PAGE_SIZE = 500;
/** Number of items to process in a single batch */
const BATCH_SIZE = 50;
/** Project status type definition */
type TProjectStatus = {
issues: {
status: undefined | "loading" | "ready" | "error" | "syncing";
sync: Promise<void> | undefined;
};
issues: { status: undefined | "loading" | "ready" | "error" | "syncing"; sync: Promise<void> | undefined };
};
/** Database status type definition */
type TDBStatus = "initializing" | "ready" | "error" | undefined;
/**
* Storage class for managing local SQLite database operations
* Handles database initialization, synchronization, and CRUD operations for issues
*/
export class Storage {
public db: any;
private status: TDBStatus = undefined;
private dbName = "plane";
private projectStatus: Record<string, TProjectStatus> = {};
private workspaceSlug: string = "";
db: any;
status: TDBStatus = undefined;
dbName = "plane";
projectStatus: Record<string, TProjectStatus> = {};
workspaceSlug: string = "";
constructor() {
this.db = null;
@@ -55,20 +43,13 @@ export class Storage {
}
}
/**
* Closes the database connection
* @returns Promise<void>
*/
private closeDBConnection = async (): Promise<void> => {
closeDBConnection = async () => {
if (this.db) {
await this.db.close();
}
};
/**
* Resets the database state
*/
public reset = (): void => {
reset = () => {
if (this.db) {
this.db.close();
}
@@ -78,27 +59,17 @@ export class Storage {
this.workspaceSlug = "";
};
/**
* Clears the storage and resets the database
* @param force - Force clear storage
*/
public clearStorage = async (force = false): Promise<void> => {
clearStorage = async (force = false) => {
try {
await this.db?.close();
await clearOPFS(force);
this.reset();
} catch (error) {
logError(error);
console.error("Error clearing sqlite sync storage", error);
} catch (e) {
console.error("Error clearing sqlite sync storage", e);
}
};
/**
* Initializes the database for a given workspace
* @param workspaceSlug - Workspace identifier
* @returns Promise<boolean> - True if initialization is successful
*/
public initialize = async (workspaceSlug: string): Promise<boolean> => {
initialize = async (workspaceSlug: string): Promise<boolean> => {
if (!rootStore.user.localDBEnabled) return false; // return if the window gets hidden
if (workspaceSlug !== this.workspaceSlug) {
@@ -115,12 +86,7 @@ export class Storage {
}
};
/**
* Initializes the database for a given workspace
* @param workspaceSlug - Workspace identifier
* @returns Promise<boolean> - True if initialization is successful
*/
private _initialize = async (workspaceSlug: string): Promise<boolean> => {
_initialize = async (workspaceSlug: string): Promise<boolean> => {
if (this.status === "initializing") {
console.warn(`Initialization already in progress for workspace ${workspaceSlug}`);
return false;
@@ -156,7 +122,7 @@ export class Storage {
// Your SQLite code here.
await createTables();
await setOption("DB_VERSION", DB_VERSION.toString());
await this.setOption("DB_VERSION", DB_VERSION.toString());
return true;
} catch (error) {
this.status = "error";
@@ -165,10 +131,7 @@ export class Storage {
}
};
/**
* Synchronizes the workspace data
*/
public syncWorkspace = async (): Promise<void> => {
syncWorkspace = async () => {
if (!rootStore.user.localDBEnabled) return;
const syncInProgress = await this.getIsWriteInProgress("sync_workspace");
if (syncInProgress) {
@@ -177,27 +140,23 @@ export class Storage {
}
try {
await startSpan({ name: "LOAD_WS", attributes: { slug: this.workspaceSlug } }, async () => {
setOption("sync_workspace", new Date().toUTCString());
this.setOption("sync_workspace", new Date().toUTCString());
await loadWorkSpaceData(this.workspaceSlug);
deleteOption("sync_workspace");
this.deleteOption("sync_workspace");
});
} catch (e) {
logError(e);
deleteOption("sync_workspace");
this.deleteOption("sync_workspace");
}
};
/**
* Synchronizes the project data
* @param projectId - Project identifier
*/
public syncProject = async (projectId: string): Promise<void> => {
syncProject = async (projectId: string) => {
if (
// document.hidden ||
!rootStore.user.localDBEnabled
) {
return; // return if the window gets hidden
}
)
return false; // return if the window gets hidden
// Load labels, members, states, modules, cycles
await this.syncIssues(projectId);
@@ -213,13 +172,9 @@ export class Storage {
// this.setOption("workspace_synced_at", new Date().toISOString());
};
/**
* Synchronizes the issues for a project
* @param projectId - Project identifier
*/
public syncIssues = async (projectId: string): Promise<void> => {
syncIssues = async (projectId: string) => {
if (!rootStore.user.localDBEnabled || !this.db) {
return;
return false;
}
try {
const sync = startSpan({ name: `SYNC_ISSUES` }, () => this._syncIssues(projectId));
@@ -231,11 +186,7 @@ export class Storage {
}
};
/**
* Synchronizes the issues for a project
* @param projectId - Project identifier
*/
private _syncIssues = async (projectId: string): Promise<void> => {
_syncIssues = async (projectId: string) => {
const activeSpan = getActiveSpan();
log("### Sync started");
@@ -256,8 +207,8 @@ export class Storage {
description: true,
};
const syncedAt = await getLastSyncTime(projectId);
const projectSync = await getOption(projectId);
const syncedAt = await this.getLastSyncTime(projectId);
const projectSync = await this.getOption(projectId);
if (syncedAt) {
queryParams["updated_at__gt"] = syncedAt;
@@ -294,7 +245,7 @@ export class Storage {
if (status === "loading") {
await createIndexes();
}
setOption(projectId, "ready");
this.setOption(projectId, "ready");
this.setStatus(projectId, "ready");
this.setSync(projectId, undefined);
@@ -304,15 +255,31 @@ export class Storage {
});
};
/**
* Gets issues for a project
* @param workspaceSlug - Workspace identifier
* @param projectId - Project identifier
* @param queries - Query parameters
* @param config - Configuration options
* @returns Promise<any> - Issues data
*/
public getIssues = async (workspaceSlug: string, projectId: string, queries: any, config: any): Promise<any> => {
getIssueCount = async (projectId: string) => {
const count = await runQuery(`select count(*) as count from issues where project_id='${projectId}'`);
return count[0]["count"];
};
getLastUpdatedIssue = async (projectId: string) => {
const lastUpdatedIssue = await runQuery(
`select id, name, updated_at , sequence_id from issues WHERE project_id='${projectId}' AND is_local_update IS NULL order by datetime(updated_at) desc limit 1 `
);
if (lastUpdatedIssue.length) {
return lastUpdatedIssue[0];
}
return;
};
getLastSyncTime = async (projectId: string) => {
const issue = await this.getLastUpdatedIssue(projectId);
if (!issue) {
return false;
}
return issue.updated_at;
};
getIssues = async (workspaceSlug: string, projectId: string, queries: any, config: any) => {
log("#### Queries", queries);
const currentProjectStatus = this.getStatus(projectId);
@@ -420,12 +387,7 @@ export class Storage {
return out;
};
/**
* Gets an issue by ID
* @param issueId - Issue identifier
* @returns Promise<any | undefined> - Issue data or undefined
*/
public getIssue = async (issueId: string): Promise<any | undefined> => {
getIssue = async (issueId: string) => {
try {
if (!rootStore.user.localDBEnabled || this.status !== "ready") return;
@@ -441,15 +403,8 @@ export class Storage {
return;
};
/**
* Gets sub-issues for an issue
* @param workspaceSlug - Workspace identifier
* @param projectId - Project identifier
* @param issueId - Issue identifier
* @returns Promise<any> - Sub-issues data
*/
public getSubIssues = async (workspaceSlug: string, projectId: string, issueId: string): Promise<any> => {
const workspace_synced_at = await getOption("workspace_synced_at");
getSubIssues = async (workspaceSlug: string, projectId: string, issueId: string) => {
const workspace_synced_at = await this.getOption("workspace_synced_at");
if (!workspace_synced_at) {
const issueService = new IssueService();
return await issueService.subIssues(workspaceSlug, projectId, issueId);
@@ -457,57 +412,74 @@ export class Storage {
return await getSubIssuesWithDistribution(issueId);
};
/**
* Gets the status of a project
* @param projectId - Project identifier
* @returns Project status or undefined
*/
public getStatus = (projectId: string): "loading" | "ready" | "error" | "syncing" | undefined =>
this.projectStatus[projectId]?.issues?.status || undefined;
/**
* Sets the status of a project
* @param projectId - Project identifier
* @param status - New status
*/
public setStatus = (
projectId: string,
status: "loading" | "ready" | "error" | "syncing" | undefined = undefined
): void => {
getStatus = (projectId: string) => this.projectStatus[projectId]?.issues?.status || undefined;
setStatus = (projectId: string, status: "loading" | "ready" | "error" | "syncing" | undefined = undefined) => {
set(this.projectStatus, `${projectId}.issues.status`, status);
};
/**
* Gets the sync promise for a project
* @param projectId - Project identifier
* @returns Sync promise or undefined
*/
public getSync = (projectId: string): Promise<void> | undefined => this.projectStatus[projectId]?.issues?.sync;
/**
* Sets the sync promise for a project
* @param projectId - Project identifier
* @param sync - Sync promise
*/
public setSync = (projectId: string, sync: Promise<void> | undefined): void => {
getSync = (projectId: string) => this.projectStatus[projectId]?.issues?.sync;
setSync = (projectId: string, sync: Promise<void> | undefined) => {
set(this.projectStatus, `${projectId}.issues.sync`, sync);
};
/**
* Checks if a write operation is in progress
* @param op - Operation identifier
* @returns Promise<boolean> - True if write is in progress
*/
public getIsWriteInProgress = async (op: string): Promise<boolean> => {
const writeStartTime = (await getOption(op, "")) as string;
getOption = async (key: string, fallback?: string | boolean | number) => {
try {
const options = await runQuery(`select * from options where key='${key}'`);
if (options.length) {
return options[0].value;
}
return fallback;
} catch (e) {
return fallback;
}
};
setOption = async (key: string, value: string) => {
await runQuery(`insert or replace into options (key, value) values ('${key}', '${value}')`);
};
deleteOption = async (key: string) => {
await runQuery(` DELETE FROM options where key='${key}'`);
};
getOptions = async (keys: string[]) => {
const options = await runQuery(`select * from options where key in ('${keys.join("','")}')`);
return options.reduce((acc: any, option: any) => {
acc[option.key] = option.value;
return acc;
}, {});
};
getIsWriteInProgress = async (op: string) => {
const writeStartTime = await this.getOption(op, false);
if (writeStartTime) {
// Check if it has been more than 5seconds
const current = new Date();
const start = new Date(writeStartTime);
return current.getTime() - start.getTime() < 5000;
if (current.getTime() - start.getTime() < 5000) {
return true;
}
return false;
}
return false;
};
}
export const persistence = new Storage();
/**
* format the issue fetched from local db into an issue
* @param issue
* @returns
*/
export const formatLocalIssue = (issue: any) => {
const currIssue = issue;
ARRAY_FIELDS.forEach((field: string) => {
currIssue[field] = currIssue[field] ? JSON.parse(currIssue[field]) : [];
});
// Convert boolean fields to actual boolean values
BOOLEAN_FIELDS.forEach((field: string) => {
currIssue[field] = currIssue[field] === 1;
});
return currIssue as TIssue & { group_id?: string; total_issues: number; sub_group_id?: string };
};
@@ -1,116 +0,0 @@
// Moving functions from storage.sqlite.ts to storage.sqlite.utils.ts
import { TIssue } from "@plane/types";
import { ARRAY_FIELDS, BOOLEAN_FIELDS } from "./constants";
import { runQuery } from "./query-executor";
/**
* Formats an issue fetched from local db into the required format
* @param issue - Raw issue data from database
* @returns Formatted issue with proper types
*/
export const formatLocalIssue = (
issue: any
): TIssue & { group_id?: string; total_issues: number; sub_group_id?: string } => {
const currIssue = { ...issue };
// Parse array fields from JSON strings
ARRAY_FIELDS.forEach((field: string) => {
currIssue[field] = currIssue[field] ? JSON.parse(currIssue[field]) : [];
});
// Convert boolean fields to actual boolean values
BOOLEAN_FIELDS.forEach((field: string) => {
currIssue[field] = currIssue[field] === 1;
});
return currIssue;
};
/**
* Gets the last updated issue for a project
* @param projectId - Project identifier
* @returns Promise<any | undefined> - Last updated issue or undefined
*/
export const getLastUpdatedIssue = async (projectId: string): Promise<any | undefined> => {
const lastUpdatedIssue = await runQuery(
`select id, name, updated_at, sequence_id from issues WHERE project_id='${projectId}' AND is_local_update IS NULL order by datetime(updated_at) desc limit 1`
);
return lastUpdatedIssue.length ? lastUpdatedIssue[0] : undefined;
};
/**
* Gets the last sync time for a project
* @param projectId - Project identifier
* @returns Promise<string | false> - Last sync time or false
*/
export const getLastSyncTime = async (projectId: string): Promise<string | false> => {
const issue = await getLastUpdatedIssue(projectId);
if (!issue) {
return false;
}
return issue.updated_at;
};
/**
* Gets the count of issues for a project
* @param projectId - Project identifier
* @returns Promise<number> - Count of issues
*/
export const getIssueCount = async (projectId: string): Promise<number> => {
const count = await runQuery(`select count(*) as count from issues where project_id='${projectId}'`);
return count[0]["count"];
};
/**
* Gets an option value
* @param key - Option key
* @param fallback - Fallback value
* @returns Option value or fallback
*/
export const getOption = async (
key: string,
fallback?: string | boolean | number
): Promise<string | boolean | number | undefined> => {
try {
const options = await runQuery(`select * from options where key='${key}'`);
if (options.length) {
return options[0].value;
}
return fallback;
} catch (e) {
return fallback;
}
};
/**
* Sets an option value
* @param key - Option key
* @param value - Option value
*/
export const setOption = async (key: string, value: string): Promise<void> => {
await runQuery(`insert or replace into options (key, value) values ('${key}', '${value}')`);
};
/**
* Deletes an option
* @param key - Option key
*/
export const deleteOption = async (key: string): Promise<void> => {
await runQuery(` DELETE FROM options where key='${key}'`);
};
/**
* Gets multiple options
* @param keys - Option keys
* @returns Options data
*/
export const getOptions = async (keys: string[]): Promise<Record<string, string | boolean | number>> => {
const options = await runQuery(`select * from options where key in ('${keys.join("','")}')`);
return options.reduce((acc: any, option: any) => {
acc[option.key] = option.value;
return acc;
}, {});
};
+3 -3
View File
@@ -19,7 +19,7 @@ export const logError = (e: any) => {
};
export const logInfo = console.info;
export const addIssueToPersistenceLayer = async (issue: TIssue) => {
export const addIssueToPersistanceLayer = async (issue: TIssue) => {
try {
const issuePartial = pick({ ...JSON.parse(JSON.stringify(issue)) }, [
"id",
@@ -66,7 +66,7 @@ export const updatePersistentLayer = async (issueIds: string | string[]) => {
const issue = rootStore.issue.issues.getIssueById(issueId);
if (issue) {
addIssueToPersistenceLayer(issue);
addIssueToPersistanceLayer(issue);
}
});
};
@@ -174,7 +174,7 @@ export const clearOPFS = async (force = false) => {
return;
}
// ts-ignore
for await (const entry of (root as any)?.values()) {
for await (const entry of root.values()) {
if (entry.kind === "directory" && entry.name.startsWith(".ahp-")) {
// A lock with the same name as the directory protects it from
// being deleted.
-4
View File
@@ -72,10 +72,6 @@ export class DBClass {
async exec(props: string | TQueryProps) {
// @todo this will fail if the transaction is started any other way
// eg: BEGIN, OR BEGIN TRANSACTION
// Below code ensures the transactions are queued
// Ideally we should never have multiple transactions
// running at the same time, in rare cases, it shouldn't be waiting for more than 1ms
if (props === "BEGIN;") {
let promiseToAwait;
if (this.tp.length > 0) {
+2 -2
View File
@@ -5,7 +5,7 @@ import { TIssue, TInboxIssue, TInboxIssueStatus, TInboxDuplicateIssueDetails } f
// helpers
import { EInboxIssueStatus } from "@/helpers/inbox.helper";
// local db
import { addIssueToPersistenceLayer } from "@/local-db/utils/utils";
import { addIssueToPersistanceLayer } from "@/local-db/utils/utils";
// services
import { InboxIssueService } from "@/services/inbox";
import { IssueService } from "@/services/issue";
@@ -98,7 +98,7 @@ export class InboxIssueStore implements IInboxIssueStore {
// If issue accepted sync issue to local db
if (status === EInboxIssueStatus.ACCEPTED) {
addIssueToPersistenceLayer({ ...this.issue, ...inboxIssue.issue });
addIssueToPersistanceLayer({ ...this.issue, ...inboxIssue.issue });
}
} catch {
runInAction(() => set(this, "status", previousData.status));
@@ -23,7 +23,7 @@ import { EDraftIssuePaginationType } from "@/constants/workspace-drafts";
// helpers
import { getCurrentDateTimeInISO, convertToISODateString } from "@/helpers/date-time.helper";
// local-db
import { addIssueToPersistenceLayer } from "@/local-db/utils/utils";
import { addIssueToPersistanceLayer } from "@/local-db/utils/utils";
// services
import workspaceDraftService from "@/services/issue/workspace_draft.service";
// types
@@ -352,7 +352,7 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {
}
// sync issue to local db
addIssueToPersistenceLayer({ ...payload, ...response });
addIssueToPersistanceLayer({ ...payload, ...response });
// Update draft issue count in workspaceUserInfo
this.updateWorkspaceUserDraftIssueCount(workspaceSlug, -1);