From 8610c2f277cb8bd11ca6f9d35dae80181e81c562 Mon Sep 17 00:00:00 2001 From: Koustav Date: Wed, 12 Mar 2025 19:33:36 +0530 Subject: [PATCH 1/5] added feature to clean up old data --- package-lock.json | 26 ++++--- src/background.index.ts | 14 ++++ src/background/services/data.ts | 69 +++++++++++++++++++ .../CleanUpTimeLineStorage.tsx | 48 +++++++++++++ src/popup/pages/PreferencesPage.tsx | 2 + src/shared/db/helper.ts | 7 ++ src/shared/db/types.ts | 1 + 7 files changed, 156 insertions(+), 11 deletions(-) create mode 100644 src/background/services/data.ts create mode 100644 src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx create mode 100644 src/shared/db/helper.ts diff --git a/package-lock.json b/package-lock.json index 6fed2db..6433691 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "codealike-plugin", - "version": "2.0.2", + "version": "2.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "codealike-plugin", - "version": "2.0.2", - "license": "GPL3", + "version": "2.0.4", + "license": "MIT", "dependencies": { "chart.js": "^3.2.1", "idb": "^6.1.2", @@ -7774,7 +7774,6 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -11020,13 +11019,15 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "dev": true, + "requires": {} }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-node": { "version": "1.8.2", @@ -12290,7 +12291,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-prettier": { "version": "4.2.1", @@ -12362,7 +12364,8 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-sort-keys-fix": { "version": "1.1.2", @@ -13757,7 +13760,8 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true + "dev": true, + "requires": {} }, "jest-regex-util": { "version": "29.2.0", @@ -15164,7 +15168,6 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -15661,7 +15664,8 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "dev": true + "dev": true, + "requires": {} }, "supports-color": { "version": "7.2.0", diff --git a/src/background.index.ts b/src/background.index.ts index 9314127..7868a5f 100644 --- a/src/background.index.ts +++ b/src/background.index.ts @@ -14,6 +14,7 @@ import { DebugTab } from './shared/db/types'; import { WAKE_UP_BACKGROUND } from './shared/messages'; import Port = chrome.runtime.Port; +import { manageLocalStoreSpace } from './background/services/data'; interface Service { name: string; @@ -30,6 +31,10 @@ const ASYNC_POLL_INTERVAL_MINUTES = 1; const ASYNC_STATS_INTERVAL_ALARM_NAME = 'send-stats'; const ASYNC_STATS_INTERVAL_MINUTES = 1; +const ASYNC_CLEAN_TIMELINE_ALARM_NAME = 'clean-timeline'; +const ASYNC_CLEAN_TIMELINE_MINUTES = 1440; // 24 hours * 60 minutes - one day +const DEFAULT_CUTOFF_DAYS = 60; + function findDebuggingTabIndexFromId(tabIdOrUrl: number | string | undefined) { if (tabIdOrUrl !== undefined) { for (let i = 0; i < debuggingTabs.length; i++) { @@ -78,6 +83,10 @@ const asyncPollAlarmHandler = async (): Promise => { const sendStatsAlarmHandler = async (): Promise => await sendWebActivityAutomatically(); +const cleanTimeLineAlarmHandler = async (): Promise => { + await manageLocalStoreSpace(DEFAULT_CUTOFF_DAYS) +} + const ChromeServiceDefinition: Array = [ { handler: asyncPollAlarmHandler, @@ -89,6 +98,11 @@ const ChromeServiceDefinition: Array = [ intervalInMinutes: ASYNC_STATS_INTERVAL_MINUTES, name: ASYNC_STATS_INTERVAL_ALARM_NAME, }, + { + handler: cleanTimeLineAlarmHandler, + intervalInMinutes: ASYNC_CLEAN_TIMELINE_MINUTES, + name: ASYNC_CLEAN_TIMELINE_ALARM_NAME + } ]; ChromeServiceDefinition.forEach((service) => { diff --git a/src/background/services/data.ts b/src/background/services/data.ts new file mode 100644 index 0000000..23b2565 --- /dev/null +++ b/src/background/services/data.ts @@ -0,0 +1,69 @@ +import { connect, TimeTrackerStoreTables } from '../../shared/db/idb'; +import { logMessage } from '../tables/logs'; + +const getIdsToDelete = async (cutOffDate: Date) => { + await logMessage(`Cut Off Date: ${cutOffDate.getTime()}`); + const db = await connect(); + const timeline = await db.getAll( + TimeTrackerStoreTables.Timeline, + ); + const idsToDelete: number[] = []; + + for (const data of timeline) { + const recordDate = new Date(data.date).getTime(); + if (recordDate < cutOffDate.getTime()) { + const id: number = data.id as number; + // Should be deleted + idsToDelete.push(id); + } + } + + return idsToDelete; +}; + +const deleteRecordsById = async (idsToDelete: number[]): Promise => { + try { + + const db = await connect(); + const transaction = db.transaction( + TimeTrackerStoreTables.Timeline, + 'readwrite', + ); + const store = transaction.objectStore(TimeTrackerStoreTables.Timeline); + + for (const id of idsToDelete) { + // Using any as delete function expects string but we store id as number + // this throws lint error + const delId: any = id; + await store.delete(delId); + } + + await transaction.done; + await logMessage('Records deleted successfully.'); + return 'Records deleted successfully.'; + } catch (error) { + console.error('Error deleting IDB records:', error); + return 'Failed to delete records.'; + } +}; + +const manageLocalStoreSpace = async ( + defaultCutOff: number, +): Promise => { + await logMessage(`Handling Old Data Cleanup`); + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - defaultCutOff); + const idsToDelete = await getIdsToDelete(cutoffDate); + + await logMessage( + `Records to Delete: ${JSON.stringify(idsToDelete, null, 2)}`, + ); + + if (idsToDelete.length > 0) { + return await deleteRecordsById(idsToDelete); + } + + return 'No data available for deletion beyond the 60 days timeframe.' +}; + +export { manageLocalStoreSpace }; diff --git a/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx b/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx new file mode 100644 index 0000000..4869e4e --- /dev/null +++ b/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx @@ -0,0 +1,48 @@ +import * as React from "react"; +import { Panel, PanelBody, PanelHeader } from "../../../blocks/Panel"; +import { Button, ButtonType } from "../../../blocks/Button"; +import { handleClearTimeLineData } from "../../../shared/db/helper"; + +export const CleanUpTimeLineStorage: React.FC = () => { + const [state, setState] = React.useState<{ + status: boolean, + statusText: string, + }>({ + status: false, + statusText: '', + }); + + const handleClearData = (() => { + handleClearTimeLineData() + .then((res) => { + setState({ + status: true, + statusText: res + }) + }) + }); + + const { status, statusText } = state; + return ( + + Clean Up Old Timeline Data + +

Timeline records older than 60 days will be removed from the database.

+
+
+ {status + && (

{statusText}

) + } +
+ +
+
+
+ ) +} \ No newline at end of file diff --git a/src/popup/pages/PreferencesPage.tsx b/src/popup/pages/PreferencesPage.tsx index 4633116..b540799 100644 --- a/src/popup/pages/PreferencesPage.tsx +++ b/src/popup/pages/PreferencesPage.tsx @@ -8,6 +8,7 @@ import { Input } from './../../blocks/Input'; import {IgnoredDomainSetting} from '../components/IgnoredDomainsSetting/IgnoredDomainSetting'; import { WhitelistDomainSetting } from '../components/WhitelistDomainsSetting/WhitelistDomainSetting'; import {UserTokenSetting} from "../components/UserTokenSetting/UserTokenSetting"; +import { CleanUpTimeLineStorage } from '../components/CleanUpTimeLineStorage/CleanUpTimeLineStorage'; export const PreferencesPage: FC = () => { const [isWhitelistShown, hideWhitelist] = React.useState(true); @@ -45,6 +46,7 @@ export const PreferencesPage: FC = () => { {(isWhitelistShown ? : )} + ); }; diff --git a/src/shared/db/helper.ts b/src/shared/db/helper.ts new file mode 100644 index 0000000..6469e53 --- /dev/null +++ b/src/shared/db/helper.ts @@ -0,0 +1,7 @@ +import { manageLocalStoreSpace } from "../../background/services/data"; + +const DEFAULT_CUTOFF_DAYS = 60; + +export const handleClearTimeLineData = async () => { + return await manageLocalStoreSpace(DEFAULT_CUTOFF_DAYS) +}; \ No newline at end of file diff --git a/src/shared/db/types.ts b/src/shared/db/types.ts index e31518c..43e4420 100644 --- a/src/shared/db/types.ts +++ b/src/shared/db/types.ts @@ -32,6 +32,7 @@ export interface TimelineRecord { secure: boolean; activityPeriodStart: number; activityPeriodEnd: number; + id?: number; } export type ActiveTabState = { From e94804c85ba6f52aa8ca1e677fd0581797875d67 Mon Sep 17 00:00:00 2001 From: Koustav Date: Wed, 19 Mar 2025 14:31:08 +0530 Subject: [PATCH 2/5] added input box to clear data for custom days --- src/background/services/data.ts | 2 +- .../CleanUpTimeLineStorage.tsx | 59 ++++++++++++++----- src/shared/db/helper.ts | 6 +- 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/background/services/data.ts b/src/background/services/data.ts index 23b2565..f26ff9d 100644 --- a/src/background/services/data.ts +++ b/src/background/services/data.ts @@ -63,7 +63,7 @@ const manageLocalStoreSpace = async ( return await deleteRecordsById(idsToDelete); } - return 'No data available for deletion beyond the 60 days timeframe.' + return `No data available for deletion beyond the ${defaultCutOff} days timeframe.` }; export { manageLocalStoreSpace }; diff --git a/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx b/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx index 4869e4e..529cc87 100644 --- a/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx +++ b/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx @@ -2,46 +2,77 @@ import * as React from "react"; import { Panel, PanelBody, PanelHeader } from "../../../blocks/Panel"; import { Button, ButtonType } from "../../../blocks/Button"; import { handleClearTimeLineData } from "../../../shared/db/helper"; +import { Input } from "../../../blocks/Input"; export const CleanUpTimeLineStorage: React.FC = () => { const [state, setState] = React.useState<{ + cutOffDate: string status: boolean, statusText: string, }>({ + cutOffDate: '', status: false, statusText: '', }); - const handleClearData = (() => { - handleClearTimeLineData() + const handleClearData = React.useCallback(() => { + const days = Number(state.cutOffDate); + if (days < 7) { + setState((prev) => ({ + ...prev, + status: true, + statusText: 'Cut Off Date cannot be lower than 7' + })); + return; + } + + handleClearTimeLineData(days) .then((res) => { setState({ + cutOffDate: '', status: true, - statusText: res + statusText: res, }) }) - }); + }, [state]); - const { status, statusText } = state; + const handleCutOffDate = React.useCallback( + (e: React.ChangeEvent) => { + setState((prev) => ({ + ...prev, + cutOffDate: e.target.value, + })); + }, + [], + ); + + const { cutOffDate, status, statusText } = state; return ( Clean Up Old Timeline Data -

Timeline records older than 60 days will be removed from the database.

-
-
- {status - && (

{statusText}

) - } -
-
+
+ {status + && (

{statusText}

) + } +
) diff --git a/src/shared/db/helper.ts b/src/shared/db/helper.ts index 6469e53..724198b 100644 --- a/src/shared/db/helper.ts +++ b/src/shared/db/helper.ts @@ -1,7 +1,5 @@ import { manageLocalStoreSpace } from "../../background/services/data"; -const DEFAULT_CUTOFF_DAYS = 60; - -export const handleClearTimeLineData = async () => { - return await manageLocalStoreSpace(DEFAULT_CUTOFF_DAYS) +export const handleClearTimeLineData = async (days: number) => { + return await manageLocalStoreSpace(days) }; \ No newline at end of file From 31a750e29f1bc9c03593bb22e17cd748cc7974f0 Mon Sep 17 00:00:00 2001 From: Koustav Date: Fri, 28 Mar 2025 15:53:59 +0530 Subject: [PATCH 3/5] number box pr commit resolve --- src/background/services/data.ts | 2 +- .../CleanUpTimeLineStorage.tsx | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/background/services/data.ts b/src/background/services/data.ts index f26ff9d..01a648a 100644 --- a/src/background/services/data.ts +++ b/src/background/services/data.ts @@ -63,7 +63,7 @@ const manageLocalStoreSpace = async ( return await deleteRecordsById(idsToDelete); } - return `No data available for deletion beyond the ${defaultCutOff} days timeframe.` + return `No timeline data available for keeping beyond the ${defaultCutOff} days period.` }; export { manageLocalStoreSpace }; diff --git a/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx b/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx index 529cc87..019d797 100644 --- a/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx +++ b/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx @@ -17,22 +17,22 @@ export const CleanUpTimeLineStorage: React.FC = () => { const handleClearData = React.useCallback(() => { const days = Number(state.cutOffDate); - if (days < 7) { + if (days <= 1) { setState((prev) => ({ ...prev, status: true, - statusText: 'Cut Off Date cannot be lower than 7' + statusText: 'Cut Off Date cannot be lower than 1' })); return; } handleClearTimeLineData(days) .then((res) => { - setState({ - cutOffDate: '', + setState((prev) => ({ + ...prev, status: true, statusText: res, - }) + })); }) }, [state]); @@ -51,13 +51,14 @@ export const CleanUpTimeLineStorage: React.FC = () => { Clean Up Old Timeline Data -

Enter the number of days (7-60) you wish to retain your data. Data older than this period will be removed.

+

Enter the number of days you wish to retain your data. Data older than this period will be removed.

+

After clicking the Clear button, only the timeline data from your local storage will be cleared. The synced data will remain in the server.

{status && (

{statusText}

) From c647c98f17b5360bc64723ab276a95496559fc14 Mon Sep 17 00:00:00 2001 From: Koustav Date: Fri, 28 Mar 2025 16:36:32 +0530 Subject: [PATCH 4/5] made minimum day 1 for data clean up --- .../CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx b/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx index 019d797..aa23542 100644 --- a/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx +++ b/src/popup/components/CleanUpTimeLineStorage/CleanUpTimeLineStorage.tsx @@ -17,7 +17,7 @@ export const CleanUpTimeLineStorage: React.FC = () => { const handleClearData = React.useCallback(() => { const days = Number(state.cutOffDate); - if (days <= 1) { + if (days < 1) { setState((prev) => ({ ...prev, status: true, @@ -59,6 +59,7 @@ export const CleanUpTimeLineStorage: React.FC = () => { value={cutOffDate} onChange={handleCutOffDate} type="number" + min={1} />