From 7264fb19cbc1fe803a80075f7d36882e73ac76bb Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Wed, 25 Sep 2024 20:10:02 +0530 Subject: [PATCH 1/8] Fix sync of local updates --- web/core/local-db/storage.sqlite.ts | 2 +- web/core/local-db/utils/load-issues.ts | 2 +- web/core/local-db/utils/schemas.ts | 1 + web/core/local-db/utils/utils.ts | 2 +- web/core/services/issue/issue.service.ts | 13 +------------ web/core/store/issue/helpers/base-issues.store.ts | 3 +++ .../store/issue/issue-details/sub_issues.store.ts | 6 ++++++ web/core/store/issue/issue.store.ts | 5 ++++- 8 files changed, 18 insertions(+), 16 deletions(-) diff --git a/web/core/local-db/storage.sqlite.ts b/web/core/local-db/storage.sqlite.ts index 97f09a49ebd..aba286edba9 100644 --- a/web/core/local-db/storage.sqlite.ts +++ b/web/core/local-db/storage.sqlite.ts @@ -252,7 +252,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 != 1 order by datetime(updated_at) desc limit 1 ` ); if (lastUpdatedIssue.length) { diff --git a/web/core/local-db/utils/load-issues.ts b/web/core/local-db/utils/load-issues.ts index 057e82747b8..44fe143b89f 100644 --- a/web/core/local-db/utils/load-issues.ts +++ b/web/core/local-db/utils/load-issues.ts @@ -43,7 +43,7 @@ export const deleteIssueFromLocal = async (issue_id: any) => { persistence.db.exec("COMMIT;"); }; -export const updateIssue = async (issue: TIssue) => { +export const updateIssue = async (issue: TIssue & { is_local_update: number }) => { if (document.hidden || !rootStore.user.localDBEnabled) return; const issue_id = issue.id; 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/utils.ts b/web/core/local-db/utils/utils.ts index 4c355ac95c9..ef40dbda498 100644 --- a/web/core/local-db/utils/utils.ts +++ b/web/core/local-db/utils/utils.ts @@ -44,7 +44,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 From c15d22ac8efa3083bc686d6403b52ff4217d8aea Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Wed, 25 Sep 2024 22:51:33 +0530 Subject: [PATCH 2/8] Escape single quotes!! --- web/core/local-db/utils/load-issues.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/core/local-db/utils/load-issues.ts b/web/core/local-db/utils/load-issues.ts index 44fe143b89f..58268875829 100644 --- a/web/core/local-db/utils/load-issues.ts +++ b/web/core/local-db/utils/load-issues.ts @@ -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; }) From 4f2c29ce4b9cf4c88ea9e4d4aaab906aacf06f6c Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Wed, 25 Sep 2024 22:58:35 +0530 Subject: [PATCH 3/8] Fix last updated time query --- web/core/local-db/storage.sqlite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/local-db/storage.sqlite.ts b/web/core/local-db/storage.sqlite.ts index aba286edba9..183277b20cc 100644 --- a/web/core/local-db/storage.sqlite.ts +++ b/web/core/local-db/storage.sqlite.ts @@ -252,7 +252,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}' AND is_local_update != 1 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) { From c0c8ed9eec0e1c559319a7072b32c2d60c68688f Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Wed, 25 Sep 2024 23:53:16 +0530 Subject: [PATCH 4/8] Move console.logs out --- web/core/local-db/storage.sqlite.ts | 22 +++++++++----------- web/core/local-db/utils/query-constructor.ts | 9 ++++---- web/core/local-db/utils/tables.ts | 3 ++- web/core/local-db/utils/utils.ts | 10 ++++++--- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/web/core/local-db/storage.sqlite.ts b/web/core/local-db/storage.sqlite.ts index 183277b20cc..4011fc89bde 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 }; @@ -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; @@ -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(); @@ -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/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/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 ef40dbda498..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") { From ed1470fca9aa5faadbc2bf55b29bef8ba94ae971 Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Thu, 26 Sep 2024 12:50:43 +0530 Subject: [PATCH 5/8] Fix issue title not rendering line breaks when disabled --- web/core/components/issues/title-input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 (
From 9c6aaec9fc3c73d18d07a735fd36c12d2163741b Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Thu, 26 Sep 2024 13:21:51 +0530 Subject: [PATCH 6/8] Add a todo --- web/core/local-db/utils/load-issues.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/local-db/utils/load-issues.ts b/web/core/local-db/utils/load-issues.ts index 58268875829..1524edea7d3 100644 --- a/web/core/local-db/utils/load-issues.ts +++ b/web/core/local-db/utils/load-issues.ts @@ -42,7 +42,7 @@ export const deleteIssueFromLocal = async (issue_id: any) => { persistence.db.exec(deleteMetaQuery); persistence.db.exec("COMMIT;"); }; - +// @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; From a00bdf24f367adc256f606e59e2089317a4acd3c Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Thu, 26 Sep 2024 13:25:54 +0530 Subject: [PATCH 7/8] Fix build errors --- web/ce/constants/issues.ts | 2 +- web/core/local-db/storage.sqlite.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/ce/constants/issues.ts b/web/ce/constants/issues.ts index a139dc86a14..da88f1a2da9 100644 --- a/web/ce/constants/issues.ts +++ b/web/ce/constants/issues.ts @@ -32,4 +32,4 @@ export const filterActivityOnSelectedFilters = ( activity.filter((activity) => filter.includes(activity.activity_type as TActivityFilters)); // boolean to decide if the local db cache is enabled -export const ENABLE_LOCAL_DB_CACHE = false; +export const ENABLE_LOCAL_DB_CACHE = true; diff --git a/web/core/local-db/storage.sqlite.ts b/web/core/local-db/storage.sqlite.ts index 4011fc89bde..70a96942fe4 100644 --- a/web/core/local-db/storage.sqlite.ts +++ b/web/core/local-db/storage.sqlite.ts @@ -69,7 +69,7 @@ export class Storage { await this._initialize(workspaceSlug); return true; } catch (err) { - error(err); + logError(err); this.status = "error"; return false; } @@ -138,7 +138,7 @@ export class Storage { await this.setOption("DB_VERSION", DB_VERSION.toString()); } catch (err) { - error(err); + logError(err); throw err; } From 75a022c35a81ab943c702931195019d190f3ce9a Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Thu, 26 Sep 2024 13:53:41 +0530 Subject: [PATCH 8/8] Disable local --- web/ce/constants/issues.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/ce/constants/issues.ts b/web/ce/constants/issues.ts index da88f1a2da9..a139dc86a14 100644 --- a/web/ce/constants/issues.ts +++ b/web/ce/constants/issues.ts @@ -32,4 +32,4 @@ export const filterActivityOnSelectedFilters = ( activity.filter((activity) => filter.includes(activity.activity_type as TActivityFilters)); // boolean to decide if the local db cache is enabled -export const ENABLE_LOCAL_DB_CACHE = true; +export const ENABLE_LOCAL_DB_CACHE = false;