diff --git a/web/core/components/issues/title-input.tsx b/web/core/components/issues/title-input.tsx index 9bbd9bb2e33..bf52924ceb6 100644 --- a/web/core/components/issues/title-input.tsx +++ b/web/core/components/issues/title-input.tsx @@ -100,7 +100,7 @@ export const IssueTitleInput: FC = observer((props) => { [setIsSubmitting] ); - if (disabled) return
{title}
; + if (disabled) return
{title}
; return (
diff --git a/web/core/local-db/storage.sqlite.ts b/web/core/local-db/storage.sqlite.ts index 97f09a49ebd..70a96942fe4 100644 --- a/web/core/local-db/storage.sqlite.ts +++ b/web/core/local-db/storage.sqlite.ts @@ -15,7 +15,7 @@ import { loadWorkSpaceData } from "./utils/load-workspace"; import { issueFilterCountQueryConstructor, issueFilterQueryConstructor } from "./utils/query-constructor"; import { runQuery } from "./utils/query-executor"; import { createTables } from "./utils/tables"; -import { getGroupedIssueResults, getSubGroupedIssueResults } from "./utils/utils"; +import { logError, getGroupedIssueResults, getSubGroupedIssueResults, logInfo, log } from "./utils/utils"; declare module "@sqlite.org/sqlite-wasm" { export function sqlite3Worker1Promiser(...args: any): any; @@ -24,9 +24,6 @@ declare module "@sqlite.org/sqlite-wasm" { const DB_VERSION = 1; const PAGE_SIZE = 1000; const BATCH_SIZE = 200; -const log = console.log; -const error = console.error; -const info = console.info; type TProjectStatus = { issues: { status: undefined | "loading" | "ready" | "error" | "syncing"; sync: Promise | undefined }; @@ -72,7 +69,7 @@ export class Storage { await this._initialize(workspaceSlug); return true; } catch (err) { - error(err); + logError(err); this.status = "error"; return false; } @@ -92,7 +89,7 @@ export class Storage { return false; } - info("Loading and initializing SQLite3 module..."); + logInfo("Loading and initializing SQLite3 module..."); this.workspaceSlug = workspaceSlug; this.dbName = workspaceSlug; @@ -141,7 +138,7 @@ export class Storage { await this.setOption("DB_VERSION", DB_VERSION.toString()); } catch (err) { - error(err); + logError(err); throw err; } @@ -179,15 +176,16 @@ export class Storage { this.setSync(projectId, sync); await sync; } catch (e) { + logError(e); this.setStatus(projectId, "error"); } }; _syncIssues = async (projectId: string) => { - console.log("### Sync started"); + log("### Sync started"); let status = this.getStatus(projectId); if (status === "loading" || status === "syncing") { - info(`Project ${projectId} is already loading or syncing`); + logInfo(`Project ${projectId} is already loading or syncing`); return; } const syncPromise = this.getSync(projectId); @@ -235,7 +233,7 @@ export class Storage { if (syncedAt) { await syncDeletesToLocal(this.workspaceSlug, projectId, { updated_at__gt: syncedAt }); } - console.log("### Time taken to add issues", performance.now() - start); + log("### Time taken to add issues", performance.now() - start); if (status === "loading") { await createIndexes(); @@ -252,7 +250,7 @@ export class Storage { getLastUpdatedIssue = async (projectId: string) => { const lastUpdatedIssue = await runQuery( - `select id, name, updated_at , sequence_id from issues where project_id='${projectId}' order by datetime(updated_at) desc limit 1` + `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) { @@ -270,7 +268,7 @@ export class Storage { }; getIssues = async (workspaceSlug: string, projectId: string, queries: any, config: any) => { - console.log("#### Queries", queries); + log("#### Queries", queries); const currentProjectStatus = this.getStatus(projectId); if ( @@ -280,7 +278,7 @@ export class Storage { currentProjectStatus === "error" || !rootStore.user.localDBEnabled ) { - info(`Project ${projectId} is loading, falling back to server`); + logInfo(`Project ${projectId} is loading, falling back to server`); const issueService = new IssueService(); return await issueService.getIssuesFromServer(workspaceSlug, projectId, queries); } @@ -307,7 +305,7 @@ export class Storage { const parsingStart = performance.now(); let issueResults = issuesRaw.map((issue: any) => formatLocalIssue(issue)); - console.log("#### Issue Results", issueResults.length); + log("#### Issue Results", issueResults.length); const parsingEnd = performance.now(); @@ -326,7 +324,7 @@ export class Storage { Parsing: parsingEnd - parsingStart, Grouping: groupingEnd - grouping, }; - console.log(issueResults); + log(issueResults); console.table(times); const total_pages = Math.ceil(total_count / Number(pageSize)); diff --git a/web/core/local-db/utils/load-issues.ts b/web/core/local-db/utils/load-issues.ts index 057e82747b8..1524edea7d3 100644 --- a/web/core/local-db/utils/load-issues.ts +++ b/web/core/local-db/utils/load-issues.ts @@ -42,8 +42,8 @@ export const deleteIssueFromLocal = async (issue_id: any) => { persistence.db.exec(deleteMetaQuery); persistence.db.exec("COMMIT;"); }; - -export const updateIssue = async (issue: TIssue) => { +// @todo: Update deletes the issue description from local. Implement a separate update. +export const updateIssue = async (issue: TIssue & { is_local_update: number }) => { if (document.hidden || !rootStore.user.localDBEnabled) return; const issue_id = issue.id; @@ -82,10 +82,10 @@ const stageIssueInserts = (issue: any) => { return ""; } if (typeof value === "object") { - return `'${JSON.stringify(value)}'`; + return `'${JSON.stringify(value).replace(/'/g, "''")}'`; } if (typeof value === "string") { - return `'${value}'`; + return `'${value.replace(/'/g, "''")}'`; } return value; }) diff --git a/web/core/local-db/utils/query-constructor.ts b/web/core/local-db/utils/query-constructor.ts index 556bbe521bb..864f160706e 100644 --- a/web/core/local-db/utils/query-constructor.ts +++ b/web/core/local-db/utils/query-constructor.ts @@ -6,6 +6,7 @@ import { singleFilterConstructor, translateQueryParams, } from "./query.utils"; +import { log } from "./utils"; export const SPECIAL_ORDER_BY = { labels__name: "labels", "-labels__name": "labels", @@ -47,7 +48,7 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st `; - console.log("###", sql); + log("###", sql); return sql; } @@ -63,7 +64,7 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st WHERE rank <= ${per_page} `; - console.log("###", sql); + log("###", sql); return sql; } @@ -119,7 +120,7 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st `; sql += ` group by i.id ${orderByString} LIMIT ${pageSize} OFFSET ${offset * 1 + page * pageSize};`; - console.log("######$$$", sql); + log("######$$$", sql); return sql; } @@ -148,7 +149,7 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st // Add offset and paging to query sql += ` LIMIT ${pageSize} OFFSET ${offset * 1 + page * pageSize};`; - console.log("$$$", sql); + log("$$$", sql); return sql; }; diff --git a/web/core/local-db/utils/schemas.ts b/web/core/local-db/utils/schemas.ts index 20501fbaf0d..068ab9234f3 100644 --- a/web/core/local-db/utils/schemas.ts +++ b/web/core/local-db/utils/schemas.ts @@ -32,6 +32,7 @@ export const issueSchema: Schema = { assignee_ids: "TEXT", module_ids: "TEXT", description_html: "TEXT", + is_local_update: "INTEGER", }; export const issueMetaSchema: Schema = { diff --git a/web/core/local-db/utils/tables.ts b/web/core/local-db/utils/tables.ts index d4ef132f4e1..32ad5d50cf8 100644 --- a/web/core/local-db/utils/tables.ts +++ b/web/core/local-db/utils/tables.ts @@ -11,6 +11,7 @@ import { memberSchema, optionsSchema, } from "./schemas"; +import { log } from "./utils"; const createTableSQLfromSchema = (tableName: string, schema: Schema) => { let sql = `CREATE TABLE IF NOT EXISTS ${tableName} (`; @@ -18,7 +19,7 @@ const createTableSQLfromSchema = (tableName: string, schema: Schema) => { .map((key) => `'${key}' ${schema[key]}`) .join(", "); sql += `);`; - console.log("#####", sql); + log("#####", sql); return sql; }; diff --git a/web/core/local-db/utils/utils.ts b/web/core/local-db/utils/utils.ts index 4c355ac95c9..619ec9af8d5 100644 --- a/web/core/local-db/utils/utils.ts +++ b/web/core/local-db/utils/utils.ts @@ -3,9 +3,13 @@ import { TIssue } from "@plane/types"; import { rootStore } from "@/lib/store-context"; import { updateIssue } from "./load-issues"; -export const log = console.log; - -// export const log = () => {}; +export const log = (...args: any) => { + if ((window as any).DEBUG) { + console.log(...args); + } +}; +export const logError = console.error; +export const logInfo = console.info; export const updatePersistentLayer = async (issueIds: string | string[]) => { if (typeof issueIds === "string") { @@ -44,7 +48,7 @@ export const updatePersistentLayer = async (issueIds: string | string[]) => { "module_ids", "type_id", ]); - updateIssue(issuePartial); + updateIssue({ ...issuePartial, is_local_update: 1 }); } }); }; diff --git a/web/core/services/issue/issue.service.ts b/web/core/services/issue/issue.service.ts index 0d925f6b97b..6b57c7bad19 100644 --- a/web/core/services/issue/issue.service.ts +++ b/web/core/services/issue/issue.service.ts @@ -14,7 +14,6 @@ import { persistence } from "@/local-db/storage.sqlite"; // services import { addIssue, addIssuesBulk, deleteIssueFromLocal } from "@/local-db/utils/load-issues"; -import { updatePersistentLayer } from "@/local-db/utils/utils"; import { APIService } from "@/services/api.service"; export class IssueService extends APIService { @@ -24,10 +23,7 @@ export class IssueService extends APIService { async createIssue(workspaceSlug: string, projectId: string, data: Partial): Promise { return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/`, data) - .then((response) => { - updatePersistentLayer(response?.data?.id); - return response?.data; - }) + .then((response) => response?.data) .catch((error) => { throw error?.response?.data; }); @@ -147,7 +143,6 @@ export class IssueService extends APIService { issues: string[]; } ) { - updatePersistentLayer(data.issues); return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/`, data) .then((response) => response?.data) .catch((error) => { @@ -177,7 +172,6 @@ export class IssueService extends APIService { relation?: "blocking" | null; } ) { - updatePersistentLayer(issueId); return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-relation/`, data) .then((response) => response?.data) .catch((error) => { @@ -218,7 +212,6 @@ export class IssueService extends APIService { } async patchIssue(workspaceSlug: string, projectId: string, issueId: string, data: Partial): Promise { - updatePersistentLayer(issueId); return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/`, data) .then((response) => response?.data) .catch((error) => { @@ -249,7 +242,6 @@ export class IssueService extends APIService { issueId: string, data: { sub_issue_ids: string[] } ): Promise { - updatePersistentLayer([issueId, ...data.sub_issue_ids]); return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/sub-issues/`, data) .then((response) => response?.data) .catch((error) => { @@ -271,7 +263,6 @@ export class IssueService extends APIService { issueId: string, data: Partial ): Promise { - updatePersistentLayer(issueId); return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-links/`, data) .then((response) => response?.data) .catch((error) => { @@ -286,7 +277,6 @@ export class IssueService extends APIService { linkId: string, data: Partial ): Promise { - updatePersistentLayer(issueId); return this.patch( `/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-links/${linkId}/`, data @@ -298,7 +288,6 @@ export class IssueService extends APIService { } async deleteIssueLink(workspaceSlug: string, projectId: string, issueId: string, linkId: string): Promise { - updatePersistentLayer(issueId); return this.delete( `/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-links/${linkId}/` ) diff --git a/web/core/store/issue/helpers/base-issues.store.ts b/web/core/store/issue/helpers/base-issues.store.ts index 88be87a457e..816072a2e07 100644 --- a/web/core/store/issue/helpers/base-issues.store.ts +++ b/web/core/store/issue/helpers/base-issues.store.ts @@ -31,6 +31,7 @@ import { } from "@plane/types"; import { EIssueLayoutTypes, ISSUE_PRIORITIES } from "@/constants/issue"; import { convertToISODateString } from "@/helpers/date-time.helper"; +import { updatePersistentLayer } from "@/local-db/utils/utils"; import { CycleService } from "@/services/cycle.service"; import { IssueArchiveService, IssueDraftService, IssueService } from "@/services/issue"; import { ModuleService } from "@/services/module.service"; @@ -529,6 +530,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore { // If shouldUpdateList is true, call fetchParentStats shouldUpdateList && (await this.fetchParentStats(workspaceSlug, projectId)); + updatePersistentLayer(response.id); + return response; } diff --git a/web/core/store/issue/issue-details/sub_issues.store.ts b/web/core/store/issue/issue-details/sub_issues.store.ts index df1a8828364..bdec29db9ec 100644 --- a/web/core/store/issue/issue-details/sub_issues.store.ts +++ b/web/core/store/issue/issue-details/sub_issues.store.ts @@ -13,6 +13,7 @@ import { TSubIssuesStateDistribution, } from "@plane/types"; // services +import { updatePersistentLayer } from "@/local-db/utils/utils"; import { IssueService } from "@/services/issue"; // store import { IIssueDetail } from "./root.store"; @@ -180,6 +181,7 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore { [parentIssueId, "sub_issues_count"], this.subIssues[parentIssueId].length ); + updatePersistentLayer([parentIssueId, ...issueIds]); return; }; @@ -277,6 +279,8 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore { ); }); + updatePersistentLayer([parentIssueId]); + return; }; @@ -310,6 +314,8 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore { ); }); + updatePersistentLayer([parentIssueId]); + return; }; diff --git a/web/core/store/issue/issue.store.ts b/web/core/store/issue/issue.store.ts index 95ca24f6692..eafed18fcff 100644 --- a/web/core/store/issue/issue.store.ts +++ b/web/core/store/issue/issue.store.ts @@ -1,4 +1,3 @@ -import isEmpty from "lodash/isEmpty"; import set from "lodash/set"; import update from "lodash/update"; import { action, makeObservable, observable, runInAction } from "mobx"; @@ -8,6 +7,8 @@ import { TIssue } from "@plane/types"; // helpers import { getCurrentDateTimeInISO } from "@/helpers/date-time.helper"; // services +import { deleteIssueFromLocal } from "@/local-db/utils/load-issues"; +import { updatePersistentLayer } from "@/local-db/utils/utils"; import { IssueService } from "@/services/issue"; export type IIssueStore = { @@ -84,6 +85,7 @@ export class IssueStore implements IIssueStore { set(this.issuesMap, [issueId, key], issue[key as keyof TIssue]); }); }); + updatePersistentLayer(issueId); }; /** @@ -96,6 +98,7 @@ export class IssueStore implements IIssueStore { runInAction(() => { delete this.issuesMap[issueId]; }); + deleteIssueFromLocal(issueId); }; // helper methods