From 73ef2209fd35fca3fd3e59c4b6728b97d25f72ef Mon Sep 17 00:00:00 2001 From: ZHY Date: Thu, 23 Jun 2022 00:20:42 +0800 Subject: [PATCH 01/72] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 69c00cc4b..ad85e4e19 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,11 @@ - 统计用户阅读本地文件的时间 - 限制每天浏览指定网站的时间 - 网站白名单,过滤不需要统计的网站 -- 自定义域名合并统计的规则 -- 分时段统计分析用户的上网行为并以直方图展示 +- 支持多个域名合并统计,用户自定义合并规则 +- 分时段统计用户的上网行为并以直方图展示 +- 仪表盘全局数据 - 报表导出 +- 支持夜间模式 ## 下载地址 From 86666cc83bdba6a01ccda3e2b2bdd46e3432abe4 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 25 Jun 2022 23:24:19 +0800 Subject: [PATCH 02/72] Change the menu icon of dashboard (#115) --- src/app/layout/icon/trend-icon.ts | 22 ++++++++++++++++++++++ src/app/layout/menu.ts | 9 +++++---- 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 src/app/layout/icon/trend-icon.ts diff --git a/src/app/layout/icon/trend-icon.ts b/src/app/layout/icon/trend-icon.ts new file mode 100644 index 000000000..a49a7902c --- /dev/null +++ b/src/app/layout/icon/trend-icon.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2022 Hengyang Zhang + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +import { defineComponent, h } from "vue" + +const _default = defineComponent({ + name: "TrendIcon", + setup() { + return () => h('svg', { + viewBox: "0 0 1024 1024", + xmlns: "http://www.w3.org/2000/svg" + }, h("path", { + d: "M99.760075 885.579417c-16.231683 0-29.42824-13.196557-29.42824-29.42824L70.331835 148.761092c0-16.238846 13.196557-29.441543 29.42824-29.441543 16.237823 0 29.440519 13.20372 29.440519 29.441543l0 677.955706 807.702746 0c16.23066 0 29.427216 13.202697 29.427216 29.434379s-13.196557 29.42824-29.427216 29.42824L99.760075 885.579417 99.760075 885.579417zM195.092303 726.586286c-6.382361 0-12.472056-2.029216-17.60803-5.867638-12.993942-9.730619-15.657608-28.214599-5.932106-41.208541l193.054901-257.921257c5.619998-7.498788 14.214746-11.799744 23.594371-11.799744 5.867638 0 11.544941 1.729387 16.413831 5.012154l173.612083 116.910687 305.917388-243.700371c5.266957-4.196579 11.597129-6.41613 18.313088-6.41613 9.025561 0 17.418718 4.040013 23.038716 11.095709 4.901637 6.141884 7.107885 13.829983 6.233981 21.648043-0.88209 7.806803-4.758374 14.809287-10.906397 19.717064l-322.80194 257.149685c-5.175883 4.13825-11.689227 6.40999-18.339694 6.40999-5.879917 0-11.565407-1.729387-16.439414-5.012154l-168.280658-113.320928L218.687698 714.77938C213.074863 722.285331 204.471928 726.586286 195.092303 726.586286L195.092303 726.586286z" + })) + } +}) + +export default _default \ No newline at end of file diff --git a/src/app/layout/menu.ts b/src/app/layout/menu.ts index 42a616002..5e1984c9d 100644 --- a/src/app/layout/menu.ts +++ b/src/app/layout/menu.ts @@ -4,7 +4,7 @@ * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ -import type { UnwrapRef } from "vue" +import type { ComponentOptionsMixin, DefineComponent, RendererElement, RendererNode, UnwrapRef, VNode } from "vue" import type ElementIcon from "../element-ui/icon" import type { RouteLocationNormalizedLoaded, Router } from "vue-router" import type { I18nKey } from "@app/locale" @@ -17,10 +17,11 @@ import { t } from "@app/locale" import { HOME_PAGE, MEAT_URL, TRANSLATION_ISSUE_PAGE, FEEDBACK_QUESTIONNAIRE } from "@util/constant/url" import { Aim, Calendar, ChatSquare, Folder, Food, HotWater, MagicStick, Rank, SetUp, Stopwatch, Sugar, Tickets, Timer, TrendCharts } from "@element-plus/icons-vue" import { locale } from "@util/i18n" +import TrendIcon from "./icon/trend-icon" type _MenuItem = { title: keyof MenuMessage - icon: ElementIcon + icon: ElementIcon | any route?: string href?: string index?: string @@ -83,7 +84,7 @@ function generateMenus(): _MenuGroup[] { children: [{ title: 'dashboard', route: '/data/dashboard', - icon: TrendCharts + icon: Stopwatch }, { title: 'dataReport', route: '/data/report', @@ -91,7 +92,7 @@ function generateMenus(): _MenuGroup[] { }, { title: 'dataHistory', route: '/data/history', - icon: Stopwatch + icon: TrendIcon }, { title: 'dataClear', route: '/data/manage', From da032bf43a5d3968cc66f6c1d4e0897637dedd7d Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 25 Jun 2022 23:27:23 +0800 Subject: [PATCH 03/72] Use white for the core of switch in dark mode (#115) --- src/app/styles/dark-theme.sass | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/styles/dark-theme.sass b/src/app/styles/dark-theme.sass index 8420bc3fb..02ee14c08 100644 --- a/src/app/styles/dark-theme.sass +++ b/src/app/styles/dark-theme.sass @@ -70,9 +70,6 @@ html[data-theme='dark']:root --el-mask-color-extra-light: rgba(0 0 0 .3) // timer --timer-app-container-bg-color: var(--el-fill-color-dark) - // element-plus - .el-switch__core .el-switch__action - background-color: var(--el-fill-color-darker) // menu --el-menu-bg-color: var(--el-fill-color-light) --el-menu-item-active-bg-color: var(--el-color-primary-light-5) From 82c0d4635681bc1cb313f6121198bae2d506e011 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 25 Jun 2022 23:30:40 +0800 Subject: [PATCH 04/72] Use primary color for background of active menu items (#115) --- src/app/styles/dark-theme.sass | 1 - src/app/styles/index.sass | 1 + src/app/styles/light-theme.sass | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/styles/dark-theme.sass b/src/app/styles/dark-theme.sass index 02ee14c08..5e3fa060d 100644 --- a/src/app/styles/dark-theme.sass +++ b/src/app/styles/dark-theme.sass @@ -72,4 +72,3 @@ html[data-theme='dark']:root --timer-app-container-bg-color: var(--el-fill-color-dark) // menu --el-menu-bg-color: var(--el-fill-color-light) - --el-menu-item-active-bg-color: var(--el-color-primary-light-5) diff --git a/src/app/styles/index.sass b/src/app/styles/index.sass index 35cd16851..5e66a628e 100644 --- a/src/app/styles/index.sass +++ b/src/app/styles/index.sass @@ -138,3 +138,4 @@ a // el-menu-item --el-menu-item-height: 48px --el-menu-active-color: var(--el-menu-text-color) + --el-menu-item-active-bg-color: var(--el-color-primary) diff --git a/src/app/styles/light-theme.sass b/src/app/styles/light-theme.sass index f17f4a37e..a15c8be46 100644 --- a/src/app/styles/light-theme.sass +++ b/src/app/styles/light-theme.sass @@ -9,5 +9,4 @@ --el-menu-bg-color: #1d222d --timer-app-container-bg-color: var(--el-fill-color-blank) --el-menu-text-color: #c1c6c8 - --el-menu-item-active-bg-color: #0a6cfa --el-menu-hover-bg-color: #262f3e From 81b9371df3f67d7d27ab0a856607dcb689c6d9ed Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 25 Jun 2022 23:32:23 +0800 Subject: [PATCH 05/72] Use danger color for deleting button (#115) --- .../components/report/table/columns/operation-delete-button.ts | 2 +- src/app/components/report/table/columns/operation.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/components/report/table/columns/operation-delete-button.ts b/src/app/components/report/table/columns/operation-delete-button.ts index fd2e0b3db..24e1a42fb 100644 --- a/src/app/components/report/table/columns/operation-delete-button.ts +++ b/src/app/components/report/table/columns/operation-delete-button.ts @@ -61,7 +61,7 @@ const _default = defineComponent({ }) return () => h(OperationPopupConfirmButton, { buttonIcon: Delete, - buttonType: "warning", + buttonType: "danger", buttonText: deleteButtonText, confirmText: deleteMsg.value, visible: props.visible, diff --git a/src/app/components/report/table/columns/operation.ts b/src/app/components/report/table/columns/operation.ts index 762318696..f69a48302 100644 --- a/src/app/components/report/table/columns/operation.ts +++ b/src/app/components/report/table/columns/operation.ts @@ -94,7 +94,7 @@ const _default = defineComponent({ // Add 2 whitelist h(OperationPopupConfirmButton, { buttonIcon: Plus, - buttonType: "danger", + buttonType: "warning", buttonText: add2WhitelistButtonText, confirmText: t(msg => msg.whitelist.addConfirmMsg, { url: row.host }), visible: canOperate.value && !props.whitelist?.includes(row.host), From ffe5962a9cbd1468d98de7c0e311e306dfd47c3b Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 25 Jun 2022 23:55:15 +0800 Subject: [PATCH 06/72] Optimize --- src/app/locale/components/merge-rule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/locale/components/merge-rule.ts b/src/app/locale/components/merge-rule.ts index 3d6cc2846..35c126f7b 100644 --- a/src/app/locale/components/merge-rule.ts +++ b/src/app/locale/components/merge-rule.ts @@ -40,7 +40,7 @@ const _default: Messages = { infoAlert2: '合并后域名可填具体的域名,或者填数字,或者不填', infoAlert3: '如果填数字,则表示合并后域名的级数。比如存在规则【 *.*.edu.cn >>> 3 】,那么 www.hust.edu.cn 将被合并至 hust.edu.cn', infoAlert4: '如果不填,则表示原域名不会被合并', - infoAlert5: '如果没有匹配任何规则,则默认会合并至 {psl} 的前一级' + infoAlert5: '如果没有命中任何规则,则默认会合并至 {psl} 的前一级' }, zh_TW: { resultOfOrigin: '不合並', From 5d58513482fe7db6b20e9155c76868535a0645e1 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 25 Jun 2022 23:57:29 +0800 Subject: [PATCH 07/72] Fix (#112) --- src/app/components/whitelist/components/add-button.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/whitelist/components/add-button.ts b/src/app/components/whitelist/components/add-button.ts index a7b43443b..fe097b5bb 100644 --- a/src/app/components/whitelist/components/add-button.ts +++ b/src/app/components/whitelist/components/add-button.ts @@ -36,7 +36,7 @@ const _default = defineComponent({ size: "small", class: "editable-item item-add-button", onClick: () => { - white.value + white.value = '' editing.value = true } }, From 019f6750269183a349a6d1d02a477df5c1b9dfa0 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 2 Jul 2022 11:01:31 +0800 Subject: [PATCH 08/72] Refacotr declarations --- global.d.ts | 199 ++++++++++-------- .../components/appearance/dark-mode-input.ts | 10 +- .../option/components/appearance/index.ts | 18 +- src/app/components/option/components/popup.ts | 14 +- .../option/components/statistics.ts | 10 +- src/app/components/report/index.ts | 2 +- src/app/components/report/table/index.ts | 2 +- src/app/index.ts | 2 +- src/app/locale/index.ts | 2 +- src/background/badge-text-manager.ts | 2 +- src/background/icon-and-alias-collector.ts | 2 +- src/background/timer/context.ts | 2 +- src/database/option-database.ts | 10 +- src/entity/dao/extension-meta.ts | 18 -- src/entity/dto/data-item.ts | 2 +- src/popup/common/query-result.ts | 2 +- src/popup/components/chart/index.ts | 2 +- src/popup/components/footer/index.ts | 10 +- .../components/footer/select/time-select.ts | 10 +- .../components/footer/select/type-select.ts | 10 +- src/popup/locale/messages.ts | 2 +- src/service/option-service.ts | 18 +- src/util/constant/option.ts | 6 +- src/util/constant/popup.ts | 2 +- src/util/constant/url.ts | 4 +- src/util/i18n/components/popup-duration.ts | 2 +- src/util/i18n/index.ts | 22 +- 27 files changed, 194 insertions(+), 191 deletions(-) delete mode 100644 src/entity/dao/extension-meta.ts diff --git a/global.d.ts b/global.d.ts index fd3305f68..190eefde7 100644 --- a/global.d.ts +++ b/global.d.ts @@ -4,102 +4,126 @@ * * @since 0.3.0 */ -declare namespace Timer { +declare namespace timer { type DataDimension = 'total' | 'focus' | 'time' type PopupDuration = "today" | "thisWeek" | "thisMonth" - /** - * Options used for the popup page - */ - type PopupOption = { - /** - * The max count of today's data to display in popup page - */ - popupMax: number - /** - * The default type to display - */ - defaultType: DataDimension - /** - * The default duration to search - * @since 0.6.0 - */ - defaultDuration: PopupDuration - /** - * Replace the host name with site name which is detected automatically from the title of site homepages, - * or modified manually by the user - * - * @since 0.5.0 - */ - displaySiteName: boolean - } + namespace option { - type AppearanceOptionDarkMode = - // Always on - | "on" - // Always off - | "off" - // Timed on - | "timed" - type AppearanceOption = { - /** - * Whether to display the whitelist button in the context menu - * - * @since 0.3.2 - */ - displayWhitelistMenu: boolean - /** - * Whether to display the badge text of focus time - * - * @since 0.3.3 - */ - displayBadgeText: boolean - /** - * The language of this extension - * - * @since 0.8.0 - */ - locale: LocaleOption /** - * Whether to print the info in the console - * - * @since 0.8.6 + * Options used for the popup page */ - printInConsole: boolean - /** - * The state of dark mode - * - * @since 1.1.0 - */ - darkMode: AppearanceOptionDarkMode + type PopupOption = { + /** + * The max count of today's data to display in popup page + */ + popupMax: number + /** + * The default type to display + */ + defaultType: DataDimension + /** + * The default duration to search + * @since 0.6.0 + */ + defaultDuration: PopupDuration + /** + * Replace the host name with site name which is detected automatically from the title of site homepages, + * or modified manually by the user + * + * @since 0.5.0 + */ + displaySiteName: boolean + } - /** - * The range of seconds to turn on dark mode. Required if {@param darkMode} is 'timed' - * - * @since 1.1.0 - */ - darkModeTimeStart?: number - darkModeTimeEnd?: number - } + type DarkMode = + // Always on + | "on" + // Always off + | "off" + // Timed on + | "timed" + type AppearanceOption = { + /** + * Whether to display the whitelist button in the context menu + * + * @since 0.3.2 + */ + displayWhitelistMenu: boolean + /** + * Whether to display the badge text of focus time + * + * @since 0.3.3 + */ + displayBadgeText: boolean + /** + * The language of this extension + * + * @since 0.8.0 + */ + locale: LocaleOption + /** + * Whether to print the info in the console + * + * @since 0.8.6 + */ + printInConsole: boolean + /** + * The state of dark mode + * + * @since 1.1.0 + */ + darkMode: DarkMode - type StatisticsOption = { - /** - * Count when idle - */ - countWhenIdle: boolean - /** - * Whether to collect the site name - * - * @since 0.5.0 - */ - collectSiteName: boolean + /** + * The range of seconds to turn on dark mode. Required if {@param darkMode} is 'timed' + * + * @since 1.1.0 + */ + darkModeTimeStart?: number + darkModeTimeEnd?: number + } + + type StatisticsOption = { + /** + * Count when idle + */ + countWhenIdle: boolean + /** + * Whether to collect the site name + * + * @since 0.5.0 + */ + collectSiteName: boolean + /** + * Whether to count the local files + * @since 0.7.0 + */ + countLocalFiles: boolean + } + + type AllOption = PopupOption & AppearanceOption & StatisticsOption /** - * Whether to count the local files - * @since 0.7.0 + * @since 0.8.0 */ - countLocalFiles: boolean + type LocaleOption = Locale | "default" } - type Option = PopupOption & AppearanceOption & StatisticsOption + namespace meta { + type SyncMeta = { + timer?: number + } + type ExtensionMeta = { + installTime?: number + appCounter?: { [routePath: string]: number } + popupCounter?: { + _total?: number + } + /** + * @since 1.2.0 + */ + sync?: SyncMeta + } + } /** * @since 0.8.0 @@ -110,8 +134,5 @@ declare namespace Timer { | 'ja' // @since 0.9.0 | 'zh_TW' - /** - * @since 0.8.0 - */ - type LocaleOption = Locale | "default" + } \ No newline at end of file diff --git a/src/app/components/option/components/appearance/dark-mode-input.ts b/src/app/components/option/components/appearance/dark-mode-input.ts index 5c45e5be0..d2d02c475 100644 --- a/src/app/components/option/components/appearance/dark-mode-input.ts +++ b/src/app/components/option/components/appearance/dark-mode-input.ts @@ -4,10 +4,10 @@ * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ -import { Ref, PropType, ComputedRef, watch } from "vue" +import type { Ref, PropType, ComputedRef } from "vue" import { ElOption, ElSelect, ElTimePicker } from "element-plus" -import { defineComponent, ref, h, computed } from "vue" +import { defineComponent, ref, h, watch, computed } from "vue" import { t } from "@app/locale" function computeSecondToDate(secondOfDate: number): Date { @@ -32,13 +32,13 @@ function computeDateToSecond(date: Date) { const _default = defineComponent({ name: "DarkModeInput", props: { - modelValue: String as PropType, + modelValue: String as PropType, startSecond: Number, endSecond: Number }, emits: ["change"], setup(props, ctx) { - const darkMode: Ref = ref(props.modelValue) + const darkMode: Ref = ref(props.modelValue) // @ts-ignore const start: Ref = ref(computeSecondToDate(props.startSecond)) // @ts-ignore @@ -58,7 +58,7 @@ const _default = defineComponent({ style: { width: '120px', marginLeft: '10px' }, onChange: async (newVal: string) => { const before = darkMode.value - darkMode.value = newVal as Timer.AppearanceOptionDarkMode + darkMode.value = newVal as timer.option.DarkMode handleChange() } }, { diff --git a/src/app/components/option/components/appearance/index.ts b/src/app/components/option/components/appearance/index.ts index f80b1173e..fd048ec6c 100644 --- a/src/app/components/option/components/appearance/index.ts +++ b/src/app/components/option/components/appearance/index.ts @@ -19,7 +19,7 @@ import { InfoFilled } from "@element-plus/icons-vue" import { localeSameAsBrowser } from "@util/i18n" import { toggle } from "@util/dark-mode" -const displayWhitelist = (option: Ref) => h(ElSwitch, { +const displayWhitelist = (option: Ref) => h(ElSwitch, { modelValue: option.value.displayWhitelistMenu, onChange: (newVal: boolean) => { option.value.displayWhitelistMenu = newVal @@ -27,7 +27,7 @@ const displayWhitelist = (option: Ref) => h(ElSwitch, { } }) -const displayBadgeText = (option: Ref) => h(ElSwitch, { +const displayBadgeText = (option: Ref) => h(ElSwitch, { modelValue: option.value.displayBadgeText, onChange: (newVal: boolean) => { option.value.displayBadgeText = newVal @@ -35,7 +35,7 @@ const displayBadgeText = (option: Ref) => h(ElSwitch, { } }) -const printInConsole = (option: Ref) => h(ElSwitch, { +const printInConsole = (option: Ref) => h(ElSwitch, { modelValue: option.value.printInConsole, onChange: (newVal: boolean) => { option.value.printInConsole = newVal @@ -43,21 +43,21 @@ const printInConsole = (option: Ref) => h(ElSwitch, { } }) -const allLocales: Timer.Locale[] = (["zh_CN", "zh_TW", "en", "ja"] as Timer.Locale[]) +const allLocales: timer.Locale[] = (["zh_CN", "zh_TW", "en", "ja"] as timer.Locale[]) // Keep the locale same as this browser first position .sort((a, _b) => a === localeSameAsBrowser ? -1 : 0) -const allLocaleOptions: Timer.LocaleOption[] = ["default", ...allLocales] +const allLocaleOptions: timer.option.LocaleOption[] = ["default", ...allLocales] -const locale = (option: Ref) => h(ElSelect, { +const locale = (option: Ref) => h(ElSelect, { modelValue: option.value.locale, size: 'small', style: { width: '120px' }, - onChange: async (newVal: Timer.LocaleOption) => { + onChange: async (newVal: timer.option.LocaleOption) => { option.value.locale = newVal await optionService.setAppearanceOption(option.value) // await maybe not work in Firefox, so calculate the real locale again // GG Firefox - const realLocale: Timer.Locale = newVal === "default" + const realLocale: timer.Locale = newVal === "default" ? localeSameAsBrowser : newVal ElMessageBox({ @@ -84,7 +84,7 @@ const locale = (option: Ref) => h(ElSelect, { const _default = defineComponent({ name: "AppearanceOptionContainer", setup(_props, ctx) { - const option: Ref = ref(defaultAppearance()) + const option: Ref = ref(defaultAppearance()) optionService.getAllOption().then(currentVal => { option.value = currentVal console.log(option.value) diff --git a/src/app/components/option/components/popup.ts b/src/app/components/option/components/popup.ts index 885efe798..8a2cf0c09 100644 --- a/src/app/components/option/components/popup.ts +++ b/src/app/components/option/components/popup.ts @@ -14,7 +14,7 @@ import { renderOptionItem, tagText } from "../common" import { defaultPopup } from "@util/constant/option" import { ALL_POPUP_DURATION } from "@util/constant/popup" -const popupMaxInput = (option: Ref) => h(ElInputNumber, { +const popupMaxInput = (option: Ref) => h(ElInputNumber, { modelValue: option.value.popupMax, size: 'small', min: 5, @@ -26,28 +26,28 @@ const popupMaxInput = (option: Ref) => h(ElInputNumber, { }) const typeOptions = () => ALL_DATA_ITEMS.map(item => h(ElOption, { value: item, label: t(msg => msg.item[item]) })) -const typeSelect = (option: Ref) => h(ElSelect, { +const typeSelect = (option: Ref) => h(ElSelect, { modelValue: option.value.defaultType, size: 'small', style: { width: '120px' }, - onChange: (val: Timer.DataDimension) => { + onChange: (val: timer.DataDimension) => { option.value.defaultType = val optionService.setPopupOption(option.value) } }, { default: typeOptions }) const durationOptions = () => ALL_POPUP_DURATION.map(item => h(ElOption, { value: item, label: t(msg => msg.option.popup.duration[item]) })) -const durationSelect = (option: Ref) => h(ElSelect, { +const durationSelect = (option: Ref) => h(ElSelect, { modelValue: option.value.defaultDuration, size: 'small', style: { width: t(msg => msg.option.popup.durationWidth) }, - onChange: (val: Timer.PopupDuration) => { + onChange: (val: timer.PopupDuration) => { option.value.defaultDuration = val optionService.setPopupOption(option.value) } }, { default: durationOptions }) -const displaySiteName = (option: Ref) => h(ElSwitch, { +const displaySiteName = (option: Ref) => h(ElSwitch, { modelValue: option.value.displaySiteName, onChange: (newVal: boolean) => { option.value.displaySiteName = newVal @@ -63,7 +63,7 @@ const displayDefaultLabel = `${defaultDurationLabel}/${defaultTypeLabel}` const _default = defineComponent({ name: "PopupOptionContainer", setup(_props, ctx) { - const option: Ref = ref(defaultPopup()) + const option: Ref = ref(defaultPopup()) optionService.getAllOption().then(currentVal => option.value = currentVal) ctx.expose({ async reset() { diff --git a/src/app/components/option/components/statistics.ts b/src/app/components/option/components/statistics.ts index 6b5331649..b5ae3c36b 100644 --- a/src/app/components/option/components/statistics.ts +++ b/src/app/components/option/components/statistics.ts @@ -12,23 +12,23 @@ import { defineComponent, h, Ref, ref } from "vue" import { t } from "@app/locale" import { renderOptionItem, tagText, tooltip } from "../common" -function updateOptionVal(key: keyof Timer.StatisticsOption, newVal: boolean, option: Ref) { +function updateOptionVal(key: keyof timer.option.StatisticsOption, newVal: boolean, option: Ref) { const value = option.value value[key] = newVal optionService.setStatisticsOption(value) } -const countWhenIdle = (option: Ref) => h(ElSwitch, { +const countWhenIdle = (option: Ref) => h(ElSwitch, { modelValue: option.value.countWhenIdle, onChange: (newVal: boolean) => updateOptionVal('countWhenIdle', newVal, option) }) -const countLocalFiles = (option: Ref) => h(ElSwitch, { +const countLocalFiles = (option: Ref) => h(ElSwitch, { modelValue: option.value.countLocalFiles, onChange: (newVal: boolean) => updateOptionVal("countLocalFiles", newVal, option) }) -const collectSiteName = (option: Ref) => h(ElSwitch, { +const collectSiteName = (option: Ref) => h(ElSwitch, { modelValue: option.value.collectSiteName, onChange: (newVal: boolean) => updateOptionVal('collectSiteName', newVal, option) }) @@ -36,7 +36,7 @@ const collectSiteName = (option: Ref) => h(ElSwitch, { const _default = defineComponent({ name: "StatisticsOptionContainer", setup(_props, ctx) { - const option: Ref = ref(defaultStatistics()) + const option: Ref = ref(defaultStatistics()) optionService.getAllOption().then(currentVal => option.value = currentVal) ctx.expose({ async reset() { diff --git a/src/app/components/report/index.ts b/src/app/components/report/index.ts index d2640ab2f..e0ed17818 100644 --- a/src/app/components/report/index.ts +++ b/src/app/components/report/index.ts @@ -198,7 +198,7 @@ export type ReportQuery = { /** * Sorted column */ - sc?: Timer.DataDimension + sc?: timer.DataDimension } const _default = defineComponent({ diff --git a/src/app/components/report/table/index.ts b/src/app/components/report/table/index.ts index 7564209be..a90269641 100644 --- a/src/app/components/report/table/index.ts +++ b/src/app/components/report/table/index.ts @@ -25,7 +25,7 @@ export enum ElSortDirect { } export type SortInfo = { - prop: Timer.DataDimension | 'host' + prop: timer.DataDimension | 'host' order: ElSortDirect } diff --git a/src/app/index.ts b/src/app/index.ts index 3c8583411..d1544674a 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -19,7 +19,7 @@ import { initLocale, locale as appLocale } from "@util/i18n" import { toggle, init as initTheme } from "@util/dark-mode" import optionService from "@service/option-service" -const locales: { [locale in Timer.Locale]: () => Promise<{ default: Language }> } = { +const locales: { [locale in timer.Locale]: () => Promise<{ default: Language }> } = { zh_CN: () => import('element-plus/lib/locale/lang/zh-cn'), zh_TW: () => import('element-plus/lib/locale/lang/zh-tw'), en: () => import('element-plus/lib/locale/lang/en'), diff --git a/src/app/locale/index.ts b/src/app/locale/index.ts index 4c1b9b304..60b3b1d03 100644 --- a/src/app/locale/index.ts +++ b/src/app/locale/index.ts @@ -19,7 +19,7 @@ export function t(key: I18nKey, param?: any) { /** * @since 0.8.8 */ -export function tWith(key: I18nKey, specLocale: Timer.Locale, param?: any) { +export function tWith(key: I18nKey, specLocale: timer.Locale, param?: any) { const props = { key, param } return _t(messages[specLocale], props) } diff --git a/src/background/badge-text-manager.ts b/src/background/badge-text-manager.ts index 238fd686f..7b5a100bb 100644 --- a/src/background/badge-text-manager.ts +++ b/src/background/badge-text-manager.ts @@ -56,7 +56,7 @@ class BadgeTextManager { async init() { this.timer = setInterval(() => !this.isPaused && updateFocus(), 1000) - const option: Partial = await optionDb.getOption() + const option: Partial = await optionDb.getOption() this.pauseOrResumeAccordingToOption(!!option.displayBadgeText) optionDb.addOptionChangeListener(({ displayBadgeText }) => this.pauseOrResumeAccordingToOption(displayBadgeText)) } diff --git a/src/background/icon-and-alias-collector.ts b/src/background/icon-and-alias-collector.ts index eeeee8d8c..f0d4c6375 100644 --- a/src/background/icon-and-alias-collector.ts +++ b/src/background/icon-and-alias-collector.ts @@ -21,7 +21,7 @@ const hostAliasDatabase = new HostAliasDatabase(storage) const optionDatabase = new OptionDatabase(storage) let collectAliasEnabled = defaultStatistics().collectSiteName -const setCollectAliasEnabled = (opt: Timer.Option) => collectAliasEnabled = opt.collectSiteName +const setCollectAliasEnabled = (opt: timer.option.AllOption) => collectAliasEnabled = opt.collectSiteName optionDatabase.getOption().then(setCollectAliasEnabled) optionDatabase.addOptionChangeListener(setCollectAliasEnabled) diff --git a/src/background/timer/context.ts b/src/background/timer/context.ts index fd07d48a0..29f997ab0 100644 --- a/src/background/timer/context.ts +++ b/src/background/timer/context.ts @@ -30,7 +30,7 @@ export class TimeInfo { let countWhenIdle: boolean = false -const setCountWhenIdle = (op: Timer.Option) => countWhenIdle = op.countWhenIdle +const setCountWhenIdle = (op: timer.option.AllOption) => countWhenIdle = op.countWhenIdle optionService.getAllOption().then(setCountWhenIdle) optionService.addOptionChangeListener(setCountWhenIdle) diff --git a/src/database/option-database.ts b/src/database/option-database.ts index a8106f6cd..ff6376181 100644 --- a/src/database/option-database.ts +++ b/src/database/option-database.ts @@ -25,27 +25,27 @@ class OptionDatabase extends BaseDatabase { await this.setOption(newVal) } - async getOption(): Promise> { + async getOption(): Promise> { const data = await this.storage.get(DB_KEY) const option = data[DB_KEY] if (!option) return {} - return option as Partial + return option as Partial } - async setOption(option: Timer.Option): Promise { + async setOption(option: timer.option.AllOption): Promise { option && await this.setByKey(DB_KEY, option) } /** * @since 0.3.2 */ - addOptionChangeListener(listener: (newVal: Timer.Option) => void) { + addOptionChangeListener(listener: (newVal: timer.option.AllOption) => void) { const storageListener = ( changes: { [key: string]: chrome.storage.StorageChange }, _areaName: "sync" | "local" | "managed" ) => { const optionInfo = changes[DB_KEY] - optionInfo && listener(optionInfo.newValue as Timer.Option) + optionInfo && listener(optionInfo.newValue as timer.option.AllOption) } chrome.storage.onChanged.addListener(storageListener) } diff --git a/src/entity/dao/extension-meta.ts b/src/entity/dao/extension-meta.ts deleted file mode 100644 index 43612f5af..000000000 --- a/src/entity/dao/extension-meta.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2022 Hengyang Zhang - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * Meta data of this extension - */ -export type ExtensionMeta = { - installTime?: number - appCounter?: { [routePath: string]: number } - popupCounter?: { - _total?: number - } - -} \ No newline at end of file diff --git a/src/entity/dto/data-item.ts b/src/entity/dto/data-item.ts index fed1ad29b..e4458f4a4 100644 --- a/src/entity/dto/data-item.ts +++ b/src/entity/dto/data-item.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -type DataDimension = Timer.DataDimension +type DataDimension = timer.DataDimension export const ALL_DATA_ITEMS: DataDimension[] = ['focus', 'total', 'time'] diff --git a/src/popup/common/query-result.ts b/src/popup/common/query-result.ts index 0552c6f4a..4bec5a8e9 100644 --- a/src/popup/common/query-result.ts +++ b/src/popup/common/query-result.ts @@ -10,7 +10,7 @@ import DataItem from "@entity/dto/data-item" export type PopupItem = DataItem & { isOther?: boolean } type QueryResult = { - type: Timer.DataDimension + type: timer.DataDimension mergeHost: boolean data: PopupItem[] // Filter items diff --git a/src/popup/components/chart/index.ts b/src/popup/components/chart/index.ts index 789ee239e..ad1bd5a18 100644 --- a/src/popup/components/chart/index.ts +++ b/src/popup/components/chart/index.ts @@ -32,7 +32,7 @@ const pie: ECharts = init(chartContainer) // Initialize let displaySiteName: boolean = defaultStatistics().collectSiteName -const setDisplaySiteName = (opt: Timer.Option) => displaySiteName = opt.displaySiteName +const setDisplaySiteName = (opt: timer.option.AllOption) => displaySiteName = opt.displaySiteName optionDatabase.getOption().then(setDisplaySiteName) optionDatabase.addOptionChangeListener(setDisplaySiteName) diff --git a/src/popup/components/footer/index.ts b/src/popup/components/footer/index.ts index 4217cdd3a..821d25fb1 100644 --- a/src/popup/components/footer/index.ts +++ b/src/popup/components/footer/index.ts @@ -27,7 +27,7 @@ type FooterParam = TimerQueryParam & { chartTitle: string } -function calculateDateRange(duration: Timer.PopupDuration): Date | Date[] { +function calculateDateRange(duration: timer.PopupDuration): Date | Date[] { const now = new Date() if (duration == 'today') { return now @@ -38,12 +38,12 @@ function calculateDateRange(duration: Timer.PopupDuration): Date | Date[] { } } -function calculateChartTitle(duration: Timer.PopupDuration): string { +function calculateChartTitle(duration: timer.PopupDuration): string { return t(msg => msg.title[duration]) } export function getQueryParam(): FooterParam { - const duration: Timer.PopupDuration = getSelectedTime() + const duration: timer.PopupDuration = getSelectedTime() const param: FooterParam = { date: calculateDateRange(duration), mergeHost: mergedHost(), @@ -66,7 +66,7 @@ function _default(handleQuery: (result: QueryResult) => void) { * @param type type * @returns total alert text */ -const getTotalInfo = (data: DataItem[], type: Timer.DataDimension) => { +const getTotalInfo = (data: DataItem[], type: timer.DataDimension) => { if (type === 'time') { const totalCount = data.map(d => d[type] || 0).reduce((a, b) => a + b, 0) return t(msg => msg.totalCount, { totalCount }) @@ -102,7 +102,7 @@ async function query() { } } result.push(other) - const type = queryParam.sort as Timer.DataDimension + const type = queryParam.sort as timer.DataDimension const data = result.filter(item => item[type]) const queryResult: QueryResult = { diff --git a/src/popup/components/footer/select/time-select.ts b/src/popup/components/footer/select/time-select.ts index fe1e10a79..c3982c159 100644 --- a/src/popup/components/footer/select/time-select.ts +++ b/src/popup/components/footer/select/time-select.ts @@ -34,9 +34,9 @@ timeSelect.onclick = () => isOpen ? hidePopup() : openPopup() /////////// Options const SELECTED_CLASS = 'selected' const optionList = document.getElementById('time-select-options') -const optionItems: Map = new Map() +const optionItems: Map = new Map() -function selected(item: Timer.PopupDuration): void { +function selected(item: timer.PopupDuration): void { currentSelected = item Array.from(optionItems.values()).forEach(item => item.classList.remove(SELECTED_CLASS)) optionItems.get(item).classList.add(SELECTED_CLASS) @@ -55,13 +55,13 @@ for (const item of ALL_POPUP_DURATION) { optionList.append(li) optionItems.set(item, li) } -let currentSelected: Timer.PopupDuration = undefined +let currentSelected: timer.PopupDuration = undefined -export function getSelectedTime(): Timer.PopupDuration { +export function getSelectedTime(): timer.PopupDuration { return currentSelected } -optionService.getAllOption().then((option: Timer.PopupOption) => selected(option.defaultDuration)) +optionService.getAllOption().then((option: timer.option.PopupOption) => selected(option.defaultDuration)) let handleSelected: () => void function _default(handleSelected_: () => void) { diff --git a/src/popup/components/footer/select/type-select.ts b/src/popup/components/footer/select/type-select.ts index 5a69a11ee..ae5160d41 100644 --- a/src/popup/components/footer/select/type-select.ts +++ b/src/popup/components/footer/select/type-select.ts @@ -34,9 +34,9 @@ typeSelect.onclick = () => isOpen ? hidePopup() : openPopup() /////////// Options const SELECTED_CLASS = 'selected' const optionList = document.getElementById('type-select-options') -const optionItems: Map = new Map() +const optionItems: Map = new Map() -function selected(item: Timer.DataDimension): void { +function selected(item: timer.DataDimension): void { currentSelected = item Array.from(optionItems.values()).forEach(item => item.classList.remove(SELECTED_CLASS)) optionItems.get(item).classList.add(SELECTED_CLASS) @@ -55,11 +55,11 @@ for (const item of ALL_DATA_ITEMS) { optionList.append(li) optionItems.set(item, li) } -let currentSelected: Timer.DataDimension = undefined +let currentSelected: timer.DataDimension = undefined -export function getSelectedType(): Timer.DataDimension { return currentSelected } +export function getSelectedType(): timer.DataDimension { return currentSelected } -optionService.getAllOption().then((option: Timer.PopupOption) => selected(option.defaultType)) +optionService.getAllOption().then((option: timer.option.PopupOption) => selected(option.defaultType)) let handleSelected: () => void function _default(handleSelected_: () => void) { diff --git a/src/popup/locale/messages.ts b/src/popup/locale/messages.ts index a6e005f99..7ba186a01 100644 --- a/src/popup/locale/messages.ts +++ b/src/popup/locale/messages.ts @@ -11,7 +11,7 @@ import itemMessages, { ItemMessage } from "@util/i18n/components/item" import popupDurationMessages, { PopupDurationMessage } from "@util/i18n/components/popup-duration" export type PopupMessage = { - title: { [key in Timer.PopupDuration]: string } + title: { [key in timer.PopupDuration]: string } mergeHostLabel: string viewMore: string fileName: string diff --git a/src/service/option-service.ts b/src/service/option-service.ts index 8e9bdf3b9..b422b042a 100644 --- a/src/service/option-service.ts +++ b/src/service/option-service.ts @@ -14,34 +14,34 @@ const defaultOption = () => { return { ...defaultAppearance(), ...defaultPopup(), ...defaultStatistics() } } -async function getAllOption(): Promise { - const exist: Partial = await db.getOption() - const result: Timer.Option = defaultOption() +async function getAllOption(): Promise { + const exist: Partial = await db.getOption() + const result: timer.option.AllOption = defaultOption() Object.entries(exist).forEach(([key, val]) => result[key] = val) return result } -async function setPopupOption(option: Timer.PopupOption): Promise { +async function setPopupOption(option: timer.option.PopupOption): Promise { await setOption(option) } -async function setAppearanceOption(option: Timer.AppearanceOption): Promise { +async function setAppearanceOption(option: timer.option.AppearanceOption): Promise { await setOption(option) } -async function setStatisticsOption(option: Timer.StatisticsOption): Promise { +async function setStatisticsOption(option: timer.option.StatisticsOption): Promise { await setOption(option) } -async function setOption(option: Partial): Promise { - const exist: Partial = await db.getOption() +async function setOption(option: Partial): Promise { + const exist: Partial = await db.getOption() const toSet = defaultOption() Object.entries(exist).forEach(([key, val]) => toSet[key] = val) Object.entries(option).forEach(([key, val]) => toSet[key] = val) await db.setOption(toSet) } -async function isDarkMode(targetVal?: Timer.AppearanceOption): Promise { +async function isDarkMode(targetVal?: timer.option.AppearanceOption): Promise { const option = targetVal || await getAllOption() const darkMode = option.darkMode if (darkMode === "on") { diff --git a/src/util/constant/option.ts b/src/util/constant/option.ts index 69de4d0c0..6fba4421b 100644 --- a/src/util/constant/option.ts +++ b/src/util/constant/option.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -export function defaultPopup(): Timer.PopupOption { +export function defaultPopup(): timer.option.PopupOption { // Use template return { popupMax: 10, @@ -18,7 +18,7 @@ export function defaultPopup(): Timer.PopupOption { } } -export function defaultAppearance(): Timer.AppearanceOption { +export function defaultAppearance(): timer.option.AppearanceOption { return { displayWhitelistMenu: true, // Change false to true @since 0.8.4 @@ -34,7 +34,7 @@ export function defaultAppearance(): Timer.AppearanceOption { } } -export function defaultStatistics(): Timer.StatisticsOption { +export function defaultStatistics(): timer.option.StatisticsOption { return { countWhenIdle: false, collectSiteName: true, diff --git a/src/util/constant/popup.ts b/src/util/constant/popup.ts index e9012e166..37f218d70 100644 --- a/src/util/constant/popup.ts +++ b/src/util/constant/popup.ts @@ -5,4 +5,4 @@ * https://opensource.org/licenses/MIT */ -export const ALL_POPUP_DURATION: Timer.PopupDuration[] = ["today", "thisWeek", "thisMonth"] \ No newline at end of file +export const ALL_POPUP_DURATION: timer.PopupDuration[] = ["today", "thisWeek", "thisMonth"] \ No newline at end of file diff --git a/src/util/constant/url.ts b/src/util/constant/url.ts index 5e0059cc2..be0a10194 100644 --- a/src/util/constant/url.ts +++ b/src/util/constant/url.ts @@ -48,7 +48,7 @@ export const TU_CAO_PAGE = 'https://support.qq.com/products/402895' /** * @since 0.9.6 */ -export const FEEDBACK_QUESTIONNAIRE: Partial<{ [locale in Timer.Locale]: string }> = { +export const FEEDBACK_QUESTIONNAIRE: Partial<{ [locale in timer.Locale]: string }> = { zh_CN: ZH_FEEDBACK_PAGE, zh_TW: 'https://docs.google.com/forms/d/e/1FAIpQLSdfvG6ExLj331YOLZIKO3x98k3kMxpkkLW1RgFuRGmUnZCGRQ/viewform?usp=sf_link', en: 'https://docs.google.com/forms/d/e/1FAIpQLSdNq4gnSY7uxYkyqOPqyYF3Bqlc3ZnWCLDi5DI5xGjPeVCNiw/viewform?usp=sf_link', @@ -57,7 +57,7 @@ export const FEEDBACK_QUESTIONNAIRE: Partial<{ [locale in Timer.Locale]: string /** * @since 0.9.6 */ -export const UNINSTALL_QUESTIONNAIRE: { [locale in Timer.Locale]: string } = { +export const UNINSTALL_QUESTIONNAIRE: { [locale in timer.Locale]: string } = { zh_CN: 'https://www.wjx.cn/vj/YDgY9Yz.aspx', zh_TW: 'https://docs.google.com/forms/d/e/1FAIpQLSdK93q-548dK-2naoS3DaArdc7tEGoUY9JQvaXP5Kpov8h6-A/viewform?usp=sf_link', ja: 'https://docs.google.com/forms/d/e/1FAIpQLSdsB3onZuleNf6j7KJJLbcote647WV6yeUr-9m7Db5QXakfpg/viewform?usp=sf_link', diff --git a/src/util/i18n/components/popup-duration.ts b/src/util/i18n/components/popup-duration.ts index 5b767d93f..7662a7ec6 100644 --- a/src/util/i18n/components/popup-duration.ts +++ b/src/util/i18n/components/popup-duration.ts @@ -7,7 +7,7 @@ import { Messages } from ".." -export type PopupDurationMessage = { [key in Timer.PopupDuration]: string } +export type PopupDurationMessage = { [key in timer.PopupDuration]: string } const _default: Messages = { zh_CN: { diff --git a/src/util/i18n/index.ts b/src/util/i18n/index.ts index 5980a3c82..b773c5478 100644 --- a/src/util/i18n/index.ts +++ b/src/util/i18n/index.ts @@ -10,20 +10,20 @@ import optionService from "@service/option-service" /** * Not to import this one if not necessary */ -export type FakedLocale = Timer.Locale +export type FakedLocale = timer.Locale /** * @since 0.2.2 */ -const FEEDBACK_LOCALE: Timer.Locale = "en" +const FEEDBACK_LOCALE: timer.Locale = "en" -export const defaultLocale: Timer.Locale = "zh_CN" +export const defaultLocale: timer.Locale = "zh_CN" export type Messages = { - [key in Timer.Locale]: T + [key in timer.Locale]: T } // Standardize the locale code according to the Chrome locale code -const chrome2I18n: { [key: string]: Timer.Locale } = { +const chrome2I18n: { [key: string]: timer.Locale } = { 'zh-CN': "zh_CN", 'zh-TW': "zh_TW", 'en-US': "en", @@ -39,7 +39,7 @@ const chrome2I18n: { [key: string]: Timer.Locale } = { * * They are different, so translate */ -export function chromeLocale2ExtensionLocale(chromeLocale: string): Timer.Locale { +export function chromeLocale2ExtensionLocale(chromeLocale: string): timer.Locale { if (!chromeLocale) { return defaultLocale } @@ -49,19 +49,19 @@ export function chromeLocale2ExtensionLocale(chromeLocale: string): Timer.Locale /** * @since 0.9.0 */ -export let localeSameAsBrowser: Timer.Locale = chromeLocale2ExtensionLocale(chrome.i18n.getUILanguage()) +export let localeSameAsBrowser: timer.Locale = chromeLocale2ExtensionLocale(chrome.i18n.getUILanguage()) /** * Real locale with locale option */ -export let locale: Timer.Locale = localeSameAsBrowser +export let locale: timer.Locale = localeSameAsBrowser -function handleLocaleOption(option: Timer.Option) { - const localOption: Timer.LocaleOption = option.locale +function handleLocaleOption(option: timer.option.AllOption) { + const localOption: timer.option.LocaleOption = option.locale if (!localOption || localOption === "default") { locale = chromeLocale2ExtensionLocale(chrome.i18n.getUILanguage()) } else { - locale = localOption as Timer.Locale + locale = localOption as timer.Locale } } From cb011f22fb97b60c58ea2cc72d4ac14278cab727 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 2 Jul 2022 13:56:06 +0800 Subject: [PATCH 09/72] Refacotr declarations --- global.d.ts | 29 +++++++++++++ src/background/chrome-synchronizer.ts | 37 +++++++++++++++++ src/background/index.ts | 5 ++- src/content-script/printer.ts | 3 +- src/database/archived-database.ts | 3 +- src/database/meta-database.ts | 9 ++-- src/database/timer-database.ts | 32 +++++++-------- src/entity/dao/waste-per-day.ts | 59 --------------------------- src/service/meta-service.ts | 5 +-- src/service/timer-service.ts | 8 ++-- src/util/waste-per-day.ts | 22 ++++++++++ test/database/timer-database.test.ts | 52 +++++++++++------------ test/entity/dao/waste-per-day.test.ts | 11 ----- test/util/waste-per-day.test.ts | 11 +++++ webpack/webpack.dev.ts | 4 +- 15 files changed, 160 insertions(+), 130 deletions(-) create mode 100644 src/background/chrome-synchronizer.ts delete mode 100644 src/entity/dao/waste-per-day.ts create mode 100644 src/util/waste-per-day.ts delete mode 100644 test/entity/dao/waste-per-day.test.ts create mode 100644 test/util/waste-per-day.test.ts diff --git a/global.d.ts b/global.d.ts index 190eefde7..bce0aab81 100644 --- a/global.d.ts +++ b/global.d.ts @@ -42,6 +42,7 @@ declare namespace timer { | "off" // Timed on | "timed" + type AppearanceOption = { /** * Whether to display the whitelist button in the context menu @@ -135,4 +136,32 @@ declare namespace timer { // @since 0.9.0 | 'zh_TW' + namespace stat { + /** + * Time waste per day + * + * @since 0.0.1 + */ + type WastePerDay = { + /** + * Duration of visit + */ + total: number + /** + * Duration of focus + */ + focus: number + /** + * Visit times + */ + time: number + } + + /** + * Waste data + * + * @since 0.3.3 + */ + type WasteData = { [host: string]: WastePerDay } + } } \ No newline at end of file diff --git a/src/background/chrome-synchronizer.ts b/src/background/chrome-synchronizer.ts new file mode 100644 index 000000000..bae926828 --- /dev/null +++ b/src/background/chrome-synchronizer.ts @@ -0,0 +1,37 @@ +import MetaDatabase from "@db/meta-database" +import TimerDatabase from "@db/timer-database" +import { MILL_PER_DAY } from "@util/time" + +const metaDb = new MetaDatabase(chrome.storage.local) +const local = chrome.storage.local +const sync = chrome.storage.sync + +async function syncData() { + const current = new Date().getTime() + const meta = (await metaDb.getMeta())?.sync + syncTimer(current, 11) +} + +async function syncTimer(current: number, lastSyncTs: number) { + const startTime = lastSyncTs + const endTime = current - MILL_PER_DAY + const localDb = new TimerDatabase(local) + const syncDb = new TimerDatabase(sync) + + const dateRange = [ + startTime ? new Date(startTime) : undefined, + new Date(endTime) + ] + + const localItems = await localDb.select({ date: dateRange }) + const remoteItems = await syncDb.select({ date: dateRange }) + localItems.forEach(item => { + syncDb.accumulate(item.host, item.date, item) + }) + + console.log(localItems, remoteItems) +} + +export default class ChromeSynchronizer { + sync = syncData +} \ No newline at end of file diff --git a/src/background/index.ts b/src/background/index.ts index 913614cca..64bb237e9 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -16,6 +16,7 @@ import ActiveTabListener from "./active-tab-listener" import badgeTextManager from "./badge-text-manager" import metaService from "@service/meta-service" import UninstallListener from "./uninstall-listener" +import ChromeSynchronizer from "./chrome-synchronizer" // Open the log of console openLog() @@ -51,4 +52,6 @@ chrome.runtime.onInstalled.addListener(async detail => { detail.reason === "install" && await metaService.updateInstallTime(new Date()) // Questionnaire for uninstall new UninstallListener().listen() -}) \ No newline at end of file +}) + +new ChromeSynchronizer().sync() \ No newline at end of file diff --git a/src/content-script/printer.ts b/src/content-script/printer.ts index b21954ce8..c5b8c589c 100644 --- a/src/content-script/printer.ts +++ b/src/content-script/printer.ts @@ -6,7 +6,6 @@ */ import TimerDatabase from "@db/timer-database" -import WastePerDay from "@entity/dao/waste-per-day" import { t2Chrome } from "@util/i18n/chrome/t" import { formatPeriod } from "@util/time" @@ -16,7 +15,7 @@ const timerDatabase = new TimerDatabase(chrome.storage.local) * Print info of today */ export default async function printInfo(host: string) { - const waste: WastePerDay = await timerDatabase.get(host, new Date()) + const waste: timer.stat.WastePerDay = await timerDatabase.get(host, new Date()) const hourMsg = t2Chrome(root => root.message.timeWithHour) const minuteMsg = t2Chrome(root => root.message.timeWithMinute) const secondMsg = t2Chrome(root => root.message.timeWithSecond) diff --git a/src/database/archived-database.ts b/src/database/archived-database.ts index 944014131..6a4bbb815 100644 --- a/src/database/archived-database.ts +++ b/src/database/archived-database.ts @@ -6,7 +6,6 @@ */ import { log } from "../common/logger" -import WastePerDay from "@entity/dao/waste-per-day" import DataItem from "@entity/dto/data-item" import BaseDatabase from "./common/base-database" import { ARCHIVED_PREFIX } from "./common/constant" @@ -79,7 +78,7 @@ class ArchivedDatabase extends BaseDatabase { const result: DataItem[] = Object.entries(items) .filter(([key]) => domains.has(key)) .map(([host, waste]) => { - const { focus, total, time } = waste as WastePerDay + const { focus, total, time } = waste as timer.stat.WastePerDay return { focus, total, time, host, date: '', mergedHosts: [] } }) return await Promise.resolve(result) diff --git a/src/database/meta-database.ts b/src/database/meta-database.ts index 5feae36c1..11fd1ead5 100644 --- a/src/database/meta-database.ts +++ b/src/database/meta-database.ts @@ -5,7 +5,6 @@ * https://opensource.org/licenses/MIT */ -import { ExtensionMeta } from "@entity/dao/extension-meta" import BaseDatabase from "./common/base-database" import { META_KEY } from "./common/constant" @@ -13,8 +12,8 @@ import { META_KEY } from "./common/constant" * @since 0.6.0 */ class MetaDatabase extends BaseDatabase { - async getMeta(): Promise { - const meta = (await this.storage.getOne(META_KEY)) as ExtensionMeta + async getMeta(): Promise { + const meta = (await this.storage.getOne(META_KEY)) as timer.meta.ExtensionMeta if (!meta) { return {} } else { @@ -23,7 +22,7 @@ class MetaDatabase extends BaseDatabase { } async importData(data: any): Promise { - const meta: ExtensionMeta = data[META_KEY] as ExtensionMeta + const meta: timer.meta.ExtensionMeta = data[META_KEY] as timer.meta.ExtensionMeta if (!meta) { return } @@ -45,7 +44,7 @@ class MetaDatabase extends BaseDatabase { await this.update(existMeta) } - async update(existMeta: ExtensionMeta): Promise { + async update(existMeta: timer.meta.ExtensionMeta): Promise { await this.storage.put(META_KEY, existMeta) } } diff --git a/src/database/timer-database.ts b/src/database/timer-database.ts index 5859d7f3c..b89896059 100644 --- a/src/database/timer-database.ts +++ b/src/database/timer-database.ts @@ -6,11 +6,11 @@ */ import { log } from "../common/logger" -import WastePerDay, { merge, WasteData } from "@entity/dao/waste-per-day" import DataItem from "@entity/dto/data-item" import { formatTime } from "@util/time" import BaseDatabase from "./common/base-database" import { ARCHIVED_PREFIX, DATE_FORMAT, REMAIN_WORD_PREFIX } from "./common/constant" +import { zero, merge, isNotZero } from "@util/waste-per-day" export type TimerCondition = { /** @@ -107,12 +107,12 @@ function processCondition(condition: TimerCondition): _TimerCondition { return result } -function mergeMigration(exist: WastePerDay | undefined, another: any) { - exist = exist || WastePerDay.zero() - return merge(exist, WastePerDay.of(another.total || 0, another.focus || 0, another.time || 0)) +function mergeMigration(exist: timer.stat.WastePerDay | undefined, another: any) { + exist = exist || zero() + return merge(exist, { total: another.total || 0, focus: another.focus || 0, time: another.time || 0 }) } -function migrate(exists: { [key: string]: WastePerDay }, data: any): { [key: string]: WastePerDay } { +function migrate(exists: { [key: string]: timer.stat.WastePerDay }, data: any): { [key: string]: timer.stat.WastePerDay } { const result = {} Object.entries(data) .filter(([key]) => /^20\d{2}[01]\d[0-3]\d.*/.test(key)) @@ -120,7 +120,7 @@ function migrate(exists: { [key: string]: WastePerDay }, data: any): { [key: str if (typeof value !== "object") return const exist = exists[key] const merged = mergeMigration(exist, value) - merged && merged.isNotZero() && (result[key] = mergeMigration(exist, value)) + merged && isNotZero(merged) && (result[key] = mergeMigration(exist, value)) }) return result } @@ -151,10 +151,10 @@ class TimerDatabase extends BaseDatabase { * @param host host * @since 0.1.3 */ - async accumulate(host: string, date: Date, item: WastePerDay): Promise { + async accumulate(host: string, date: Date | string, item: timer.stat.WastePerDay): Promise { const key = this.generateKey(host, date) const items = await this.storage.get(key) - const exist: WastePerDay = merge(items[key] as WastePerDay || new WastePerDay(), item) + const exist: timer.stat.WastePerDay = merge(items[key] as timer.stat.WastePerDay || zero(), item) const toUpdate = {} toUpdate[key] = exist log('toUpdate', toUpdate) @@ -168,7 +168,7 @@ class TimerDatabase extends BaseDatabase { * @param date date * @since 0.1.8 */ - async accumulateBatch(data: WasteData, date: Date): Promise { + async accumulateBatch(data: timer.stat.WasteData, date: Date): Promise { const hosts = Object.keys(data) if (!hosts.length) return const dateStr = formatTime(date, DATE_FORMAT) @@ -178,10 +178,10 @@ class TimerDatabase extends BaseDatabase { const items = await this.storage.get(Object.values(keys)) const toUpdate = {} - const afterUpdated: WasteData = {} + const afterUpdated: timer.stat.WasteData = {} Object.entries(keys).forEach(([host, key]) => { const item = data[host] - const exist: WastePerDay = merge(items[key] as WastePerDay || new WastePerDay(), item) + const exist: timer.stat.WastePerDay = merge(items[key] as timer.stat.WastePerDay || zero(), item) toUpdate[key] = afterUpdated[host] = exist }) await this.storage.set(toUpdate) @@ -203,7 +203,7 @@ class TimerDatabase extends BaseDatabase { for (let key in items) { const date = key.substring(0, 8) const host = key.substring(8) - const val: WastePerDay = items[key] + const val: timer.stat.WastePerDay = items[key] if (this.filterBefore(date, host, val, _cond)) { const { total, focus, time } = val result.push({ date, host, total, focus, time, mergedHosts: [] }) @@ -250,7 +250,7 @@ class TimerDatabase extends BaseDatabase { * @param condition query parameters * @return true if valid, or false */ - private filterBefore(date: string, host: string, val: WastePerDay, condition: _TimerCondition): boolean { + private filterBefore(date: string, host: string, val: timer.stat.WastePerDay, condition: _TimerCondition): boolean { const { focus, total, time } = val const { timeStart, timeEnd, totalStart, totalEnd, focusStart, focusEnd } = condition @@ -266,10 +266,10 @@ class TimerDatabase extends BaseDatabase { * * @since 0.0.5 */ - async get(host: string, date: Date): Promise { + async get(host: string, date: Date): Promise { const key = this.generateKey(host, date) const items = await this.storage.get(null) - return Promise.resolve(items[key] || new WastePerDay()) + return Promise.resolve(items[key] || zero()) } /** @@ -342,7 +342,7 @@ class TimerDatabase extends BaseDatabase { for (let key in items) { const date = key.substring(0, 8) const host = key.substring(8) - const val: WastePerDay = items[key] + const val: timer.stat.WastePerDay = items[key] if (this.filterBefore(date, host, val, _cond)) { count++ } diff --git a/src/entity/dao/waste-per-day.ts b/src/entity/dao/waste-per-day.ts deleted file mode 100644 index 9ca300956..000000000 --- a/src/entity/dao/waste-per-day.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2021 Hengyang Zhang - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * Time waste per day. Reconstructed at v0.0.9 - * - * @since 0.0.1 - */ -export default class WastePerDay { - /** - * Duration of visit - */ - total: number - /** - * Duration of focus - */ - focus: number - /** - * Visit times - */ - time: number - - constructor() { - this.total = 0 - this.focus = 0 - this.time = 0 - } - - isNotZero(): boolean { - return !!this.total || !!this.focus || !!this.time - } - - static of(total: number, focus: number, time: number) { - const result: WastePerDay = new WastePerDay() - result.total = total - result.focus = focus - result.time = time - return result - } - - static zero() { - return new WastePerDay() - } -} - -export function merge(a: WastePerDay, b: WastePerDay) { - return WastePerDay.of(a.total + b.total, a.focus + b.focus, a.time + b.time) -} - -/** - * Waste data - * - * @since 0.3.3 - */ -export type WasteData = { [host: string]: WastePerDay } \ No newline at end of file diff --git a/src/service/meta-service.ts b/src/service/meta-service.ts index ef92a4ac6..5458bd99d 100644 --- a/src/service/meta-service.ts +++ b/src/service/meta-service.ts @@ -6,18 +6,17 @@ */ import MetaDatabase from "@db/meta-database" -import { ExtensionMeta } from "@entity/dao/extension-meta" const storage = chrome.storage.local const db: MetaDatabase = new MetaDatabase(storage) async function getInstallTime() { - const meta: ExtensionMeta = await db.getMeta() + const meta: timer.meta.ExtensionMeta = await db.getMeta() return meta && meta.installTime ? new Date(meta.installTime) : undefined } async function updateInstallTime(installTime: Date) { - const meta: ExtensionMeta = await db.getMeta() + const meta: timer.meta.ExtensionMeta = await db.getMeta() if (meta?.installTime) { // Must not rewrite return diff --git a/src/service/timer-service.ts b/src/service/timer-service.ts index 7ec97bdf6..5a44ce6f1 100644 --- a/src/service/timer-service.ts +++ b/src/service/timer-service.ts @@ -12,11 +12,11 @@ import { log } from "../common/logger" import CustomizedHostMergeRuler from "./components/host-merge-ruler" import HostMergeRuleItem from "@entity/dto/host-merge-rule-item" import MergeRuleDatabase from "@db/merge-rule-database" -import WastePerDay, { WasteData } from "@entity/dao/waste-per-day" import IconUrlDatabase from "@db/icon-url-database" import HostAliasDatabase from "@db/host-alias-database" import { PageParam, PageResult, slicePageResult } from "./components/page-info" import whitelistHolder from './components/whitelist-holder' +import { wasteOf } from "@util/waste-per-day" const storage = chrome.storage.local @@ -75,16 +75,16 @@ export type HostSet = { */ class TimerService { - async addFocusAndTotal(data: { [host: string]: { run: number, focus: number } }): Promise { + async addFocusAndTotal(data: { [host: string]: { run: number, focus: number } }): Promise { const toUpdate = {} Object.entries(data) .filter(([host]) => whitelistHolder.notContains(host)) - .forEach(([host, item]) => toUpdate[host] = WastePerDay.of(item.run, item.focus, 0)) + .forEach(([host, item]) => toUpdate[host] = wasteOf(item.run, item.focus, 0)) return timerDatabase.accumulateBatch(toUpdate, new Date()) } async addOneTime(host: string) { - timerDatabase.accumulate(host, new Date(), WastePerDay.of(0, 0, 1)) + timerDatabase.accumulate(host, new Date(), wasteOf(0, 0, 1)) } /** diff --git a/src/util/waste-per-day.ts b/src/util/waste-per-day.ts new file mode 100644 index 000000000..9e5302f33 --- /dev/null +++ b/src/util/waste-per-day.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2022 Hengyang Zhang + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +export function isNotZero(target: timer.stat.WastePerDay): boolean { + return !!target.total || !!target.focus || !!target.time +} + +export function zero(): timer.stat.WastePerDay { + return { focus: 0, time: 0, total: 0 } +} + +export function merge(a: timer.stat.WastePerDay, b: timer.stat.WastePerDay): timer.stat.WastePerDay { + return { total: a.total + b.total, focus: a.focus + b.focus, time: a.time + b.time } +} + +export function wasteOf(total: number, focus: number, time: number): timer.stat.WastePerDay { + return { total, focus, time } +} \ No newline at end of file diff --git a/test/database/timer-database.test.ts b/test/database/timer-database.test.ts index c71065363..739d3ecc3 100644 --- a/test/database/timer-database.test.ts +++ b/test/database/timer-database.test.ts @@ -1,7 +1,7 @@ import { DATE_FORMAT } from "@db/common/constant" import TimerDatabase, { TimerCondition } from "@db/timer-database" -import WastePerDay from "@entity/dao/waste-per-day" import { formatTime, MILL_PER_DAY } from "@util/time" +import { wasteOf } from "@util/waste-per-day" import storage from "../__mock__/storage" const db = new TimerDatabase(storage.local) @@ -16,42 +16,42 @@ describe('timer-database', () => { beforeEach(async () => storage.local.clear()) test('1', async () => { - await db.accumulate(baidu, now, WastePerDay.of(100, 100, 0)) - const data: WastePerDay = await db.get(baidu, now) - expect(data).toEqual(WastePerDay.of(100, 100, 0)) + await db.accumulate(baidu, now, wasteOf(100, 100, 0)) + const data: timer.stat.WastePerDay = await db.get(baidu, now) + expect(data).toEqual(wasteOf(100, 100, 0)) }) test('2', async () => { - await db.accumulate(baidu, now, WastePerDay.of(100, 200, 0)) - await db.accumulate(baidu, now, WastePerDay.of(100, 200, 0)) + await db.accumulate(baidu, now, wasteOf(100, 200, 0)) + await db.accumulate(baidu, now, wasteOf(100, 200, 0)) let data = await db.get(baidu, now) - expect(data).toEqual(WastePerDay.of(200, 400, 0)) - await db.accumulate(baidu, now, WastePerDay.of(0, 0, 1)) + expect(data).toEqual(wasteOf(200, 400, 0)) + await db.accumulate(baidu, now, wasteOf(0, 0, 1)) data = await db.get(baidu, now) - expect(data).toEqual(WastePerDay.of(200, 400, 1)) + expect(data).toEqual(wasteOf(200, 400, 1)) }) test('3', async () => { await db.accumulateBatch( { - [google]: WastePerDay.of(11, 11, 0), - [baidu]: WastePerDay.of(1, 1, 0) + [google]: wasteOf(11, 11, 0), + [baidu]: wasteOf(1, 1, 0) }, now ) expect((await db.select()).length).toEqual(2) await db.accumulateBatch( { - [google]: WastePerDay.of(12, 12, 1), - [baidu]: WastePerDay.of(2, 2, 1) + [google]: wasteOf(12, 12, 1), + [baidu]: wasteOf(2, 2, 1) }, yesterday ) expect((await db.select()).length).toEqual(4) await db.accumulateBatch( { - [google]: WastePerDay.of(13, 13, 2), - [baidu]: WastePerDay.of(3, 3, 2) + [google]: wasteOf(13, 13, 2), + [baidu]: wasteOf(3, 3, 2) }, beforeYesterday ) expect((await db.select()).length).toEqual(6) @@ -115,8 +115,8 @@ describe('timer-database', () => { }) test('5', async () => { - await db.accumulate(baidu, now, WastePerDay.of(10, 10, 0)) - await db.accumulate(baidu, yesterday, WastePerDay.of(10, 12, 0)) + await db.accumulate(baidu, now, wasteOf(10, 10, 0)) + await db.accumulate(baidu, yesterday, wasteOf(10, 12, 0)) expect((await db.select()).length).toEqual(2) // Delete yesterday's data await db.deleteByUrlAndDate(baidu, yesterday) @@ -125,8 +125,8 @@ describe('timer-database', () => { await db.deleteByUrlAndDate(baidu, yesterday) expect((await db.get(baidu, now)).focus).toEqual(10) // Add one again, and another - await db.accumulate(baidu, beforeYesterday, WastePerDay.of(1, 1, 1)) - await db.accumulate(google, now, WastePerDay.of(0, 0, 0)) + await db.accumulate(baidu, beforeYesterday, wasteOf(1, 1, 1)) + await db.accumulate(google, now, wasteOf(0, 0, 0)) expect((await db.select()).length).toEqual(3) // Delete all the baidu await db.deleteByUrl(baidu) @@ -138,7 +138,7 @@ describe('timer-database', () => { const list = await db.select(cond) expect(list.length).toEqual(1) // Add one item of baidu again again - await db.accumulate(baidu, now, WastePerDay.of(1, 1, 1)) + await db.accumulate(baidu, now, wasteOf(1, 1, 1)) // But delete google await db.delete(list) // Then only one item of baidu @@ -154,7 +154,7 @@ describe('timer-database', () => { }) test('7', async () => { - const foo = WastePerDay.of(1, 1, 1) + const foo = wasteOf(1, 1, 1) await db.accumulate(baidu, now, foo) await db.accumulate(baidu, yesterday, foo) await db.accumulate(baidu, beforeYesterday, foo) @@ -166,7 +166,7 @@ describe('timer-database', () => { }) test("importData", async () => { - const foo = WastePerDay.of(1, 1, 1) + const foo = wasteOf(1, 1, 1) await db.accumulate(baidu, now, foo) const data2Import = await db.storage.get() storage.local.clear() @@ -216,10 +216,10 @@ describe('timer-database', () => { }) test("count", async () => { - await db.accumulate(baidu, now, WastePerDay.of(1, 1, 1)) - await db.accumulate(baidu, yesterday, WastePerDay.of(1, 2, 1)) - await db.accumulate(google, now, WastePerDay.of(1, 3, 1)) - await db.accumulate(google, yesterday, WastePerDay.of(1, 4, 1)) + await db.accumulate(baidu, now, wasteOf(1, 1, 1)) + await db.accumulate(baidu, yesterday, wasteOf(1, 2, 1)) + await db.accumulate(google, now, wasteOf(1, 3, 1)) + await db.accumulate(google, yesterday, wasteOf(1, 4, 1)) // Count by host expect(await db.count({ host: baidu, diff --git a/test/entity/dao/waste-per-day.test.ts b/test/entity/dao/waste-per-day.test.ts deleted file mode 100644 index 42aa40aca..000000000 --- a/test/entity/dao/waste-per-day.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import WastePerDay, { merge } from "@entity/dao/waste-per-day" - -test('default values of WastePerDay', () => { - const newOne = new WastePerDay() - expect(newOne.time).toBe(0) - expect(newOne.focus).toBe(0) - expect(newOne.total).toBe(0) - - const another = merge(newOne, WastePerDay.of(1, 1, 2)) - expect(another).toEqual(WastePerDay.of(1, 1, 2)) -}) \ No newline at end of file diff --git a/test/util/waste-per-day.test.ts b/test/util/waste-per-day.test.ts new file mode 100644 index 000000000..47c754456 --- /dev/null +++ b/test/util/waste-per-day.test.ts @@ -0,0 +1,11 @@ +import { merge, wasteOf, zero } from "@util/waste-per-day" + +test('default values of WastePerDay', () => { + const newOne = zero() + expect(newOne.time).toBe(0) + expect(newOne.focus).toBe(0) + expect(newOne.total).toBe(0) + + const another = merge(newOne, wasteOf(1, 1, 2)) + expect(another).toEqual(wasteOf(1, 1, 2)) +}) \ No newline at end of file diff --git a/webpack/webpack.dev.ts b/webpack/webpack.dev.ts index d376ed9b9..94a17e55d 100644 --- a/webpack/webpack.dev.ts +++ b/webpack/webpack.dev.ts @@ -5,12 +5,14 @@ import optionGenerator from "./webpack.common" import webpack from "webpack" const outputDir = path.join(__dirname, '..', 'dist_dev') -let manifest: any +let manifest: chrome.runtime.ManifestV2 const options = optionGenerator( outputDir, baseManifest => { baseManifest.name = 'IS DEV' + // Fix the crx id for development mode + baseManifest.key = "clbbddpinhgdejpoepalbfnkogbobfdb" manifest = baseManifest } ) From ec69e02dcc85e9a2cfc69117d8dd040e1b6a934a Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 2 Jul 2022 14:01:22 +0800 Subject: [PATCH 10/72] Sync timerData --- src/background/chrome-synchronizer.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/background/chrome-synchronizer.ts b/src/background/chrome-synchronizer.ts index bae926828..6c7cfaf8d 100644 --- a/src/background/chrome-synchronizer.ts +++ b/src/background/chrome-synchronizer.ts @@ -8,12 +8,15 @@ const sync = chrome.storage.sync async function syncData() { const current = new Date().getTime() - const meta = (await metaDb.getMeta())?.sync - syncTimer(current, 11) + const meta = await metaDb.getMeta() + if (meta.sync) { + meta.sync = {} + } + syncTimer(current, meta) } -async function syncTimer(current: number, lastSyncTs: number) { - const startTime = lastSyncTs +async function syncTimer(current: number, meta: timer.meta.ExtensionMeta) { + const startTime = meta.sync.timer const endTime = current - MILL_PER_DAY const localDb = new TimerDatabase(local) const syncDb = new TimerDatabase(sync) @@ -25,11 +28,11 @@ async function syncTimer(current: number, lastSyncTs: number) { const localItems = await localDb.select({ date: dateRange }) const remoteItems = await syncDb.select({ date: dateRange }) - localItems.forEach(item => { - syncDb.accumulate(item.host, item.date, item) - }) + await Promise.all(localItems.map(item => syncDb.accumulate(item.host, item.date, item))) + await Promise.all(remoteItems.map(item => localDb.accumulate(item.host, item.date, item))) - console.log(localItems, remoteItems) + meta.sync.timer = current + await metaDb.update(meta) } export default class ChromeSynchronizer { From d3e088f76dcae5e3e980f4859b8713fc53c4d189 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sat, 2 Jul 2022 20:03:57 +0800 Subject: [PATCH 11/72] Refacotr declarations --- global.d.ts | 62 ++++++++++++++----- .../dashboard/components/indicator/index.ts | 3 +- .../dashboard/components/top-k-visit.ts | 3 +- .../dashboard/components/week-on-week.ts | 7 +-- .../clear/filter/operation-button.ts | 7 +-- src/app/components/option/components/popup.ts | 6 +- src/app/components/report/index.ts | 21 +++---- .../components/report/table/columns/alias.ts | 3 +- .../components/report/table/columns/date.ts | 3 +- .../components/report/table/columns/focus.ts | 3 +- .../components/report/table/columns/host.ts | 3 +- .../report/table/columns/operation.ts | 3 +- .../components/report/table/columns/time.ts | 3 +- .../components/report/table/columns/total.ts | 3 +- src/app/components/report/table/index.ts | 13 ++-- .../trend/components/chart/index.ts | 3 +- .../trend/components/chart/wrapper.ts | 3 +- src/app/components/trend/index.ts | 3 +- src/app/index.ts | 1 + src/background/index.ts | 3 - src/common/timer.ts | 9 ++- src/content-script/printer.ts | 2 +- src/database/archived-database.ts | 18 +++--- src/database/timer-database.ts | 43 +++++++------ src/database/whitelist-database.ts | 1 - src/entity/dto/data-item.ts | 56 ----------------- src/popup/common/query-result.ts | 5 +- src/popup/components/chart/option.ts | 3 +- src/popup/components/footer/index.ts | 5 +- .../components/footer/select/type-select.ts | 12 ++-- .../chrome-synchronizer.ts | 5 +- src/service/timer-service.ts | 53 ++++++++-------- src/util/stat.ts | 35 +++++++++++ src/util/waste-per-day.ts | 22 ------- test/database/archived-database.test.ts | 8 +-- test/database/timer-database.test.ts | 52 ++++++++-------- test/util/stat.test.ts | 11 ++++ test/util/waste-per-day.test.ts | 11 ---- 38 files changed, 240 insertions(+), 267 deletions(-) delete mode 100644 src/entity/dto/data-item.ts rename src/{background => service}/chrome-synchronizer.ts (96%) create mode 100644 src/util/stat.ts delete mode 100644 src/util/waste-per-day.ts create mode 100644 test/util/stat.test.ts delete mode 100644 test/util/waste-per-day.test.ts diff --git a/global.d.ts b/global.d.ts index bce0aab81..d2657f432 100644 --- a/global.d.ts +++ b/global.d.ts @@ -5,7 +5,6 @@ * @since 0.3.0 */ declare namespace timer { - type DataDimension = 'total' | 'focus' | 'time' type PopupDuration = "today" | "thisWeek" | "thisMonth" namespace option { @@ -20,7 +19,7 @@ declare namespace timer { /** * The default type to display */ - defaultType: DataDimension + defaultType: stat.Dimension /** * The default duration to search * @since 0.6.0 @@ -137,31 +136,62 @@ declare namespace timer { | 'zh_TW' namespace stat { + /** + * The dimension to statistics + */ + type Dimension = + // Running time + | 'total' + // Focus time + | 'focus' + // Visit count + | 'time' + /** * Time waste per day * * @since 0.0.1 */ - type WastePerDay = { + type Result = { [item in timer.stat.Dimension]: number } + + /** + * Waste data + * + * @since 0.3.3 + */ + type ResultSet = { [host: string]: Result } + + /** + * The unique key of each data row + */ + type RowKey = { + host: string + // Absent if date merged + date?: string + } + + /** + * Row of each statistics result + */ + type Row = RowKey & Result & { /** - * Duration of visit + * The merged domains + * + * Can't be empty if merged + * + * @since 0.1.5 */ - total: number + mergedHosts: Row[] /** - * Duration of focus + * Icon url + * + * Must be undefined if merged */ - focus: number + iconUrl?: string /** - * Visit times + * The alias name of this Site, always is the title of its homepage by detected */ - time: number + alias?: string } - - /** - * Waste data - * - * @since 0.3.3 - */ - type WasteData = { [host: string]: WastePerDay } } } \ No newline at end of file diff --git a/src/app/components/dashboard/components/indicator/index.ts b/src/app/components/dashboard/components/indicator/index.ts index 2ceb0acfb..df274fba6 100644 --- a/src/app/components/dashboard/components/indicator/index.ts +++ b/src/app/components/dashboard/components/indicator/index.ts @@ -5,7 +5,6 @@ * https://opensource.org/licenses/MIT */ -import DataItem from "@entity/dto/data-item" import PeriodDatabase from "@db/period-database" import timerService from "@service/timer-service" import { getStartOfDay, MILL_PER_DAY } from "@util/time" @@ -38,7 +37,7 @@ function calculateInstallDays(installTime: Date, now: Date): number { } async function query(): Promise<_Value> { - const allData: DataItem[] = await timerService.select() + const allData: timer.stat.Row[] = await timerService.select() const hostSet = new Set() let visits = 0 let browsingTime = 0 diff --git a/src/app/components/dashboard/components/top-k-visit.ts b/src/app/components/dashboard/components/top-k-visit.ts index 96dfe2591..b7e2dffd4 100644 --- a/src/app/components/dashboard/components/top-k-visit.ts +++ b/src/app/components/dashboard/components/top-k-visit.ts @@ -22,7 +22,6 @@ import timerService, { SortDirect } from "@service/timer-service" import { MILL_PER_DAY } from "@util/time" import { ElLoading } from "element-plus" import { defineComponent, h, onMounted, ref } from "vue" -import DataItem from "@entity/dto/data-item" import { BASE_TITLE_OPTION } from "../common" import { t } from "@app/locale" import { getPrimaryTextColor } from "@util/style" @@ -108,7 +107,7 @@ const _default = defineComponent({ sortOrder: SortDirect.DESC, mergeDate: true, } - const top: DataItem[] = (await timerService.selectByPage(query, { pageNum: 1, pageSize: TOP_NUM })).list + const top: timer.stat.Row[] = (await timerService.selectByPage(query, { pageNum: 1, pageSize: TOP_NUM })).list const data: _Value[] = top.map(({ time, host }) => ({ name: host, value: time })) for (let realSize = top.length; realSize < TOP_NUM; realSize++) { data.push({ name: '', value: 0 }) diff --git a/src/app/components/dashboard/components/week-on-week.ts b/src/app/components/dashboard/components/week-on-week.ts index 8d0aec858..44e00bd34 100644 --- a/src/app/components/dashboard/components/week-on-week.ts +++ b/src/app/components/dashboard/components/week-on-week.ts @@ -22,7 +22,6 @@ import { formatPeriodCommon, MILL_PER_DAY } from "@util/time" import { ElLoading } from "element-plus" import { defineComponent, h, onMounted, ref } from "vue" import timerService from "@service/timer-service" -import DataItem from "@entity/dto/data-item" import { groupBy, sum } from "@util/array" import { BASE_TITLE_OPTION } from "../common" import { t } from "@app/locale" @@ -48,7 +47,7 @@ type _Value = { host: string } -function optionOf(lastPeriodItems: DataItem[], thisPeriodItems: DataItem[]): EcOption { +function optionOf(lastPeriodItems: timer.stat.Row[], thisPeriodItems: timer.stat.Row[]): EcOption { const textColor = getPrimaryTextColor() const lastPeriodMap: { [host: string]: number } = groupBy(lastPeriodItems, item => item.host, @@ -176,9 +175,9 @@ const _default = defineComponent({ date: [lastPeriodStart, lastPeriodEnd], mergeDate: true, } - const lastPeriodItems: DataItem[] = await timerService.select(query) + const lastPeriodItems: timer.stat.Row[] = await timerService.select(query) query.date = [thisPeriodStart, thisPeriodEnd] - const thisPeriodItems: DataItem[] = await timerService.select(query) + const thisPeriodItems: timer.stat.Row[] = await timerService.select(query) const option = optionOf(lastPeriodItems, thisPeriodItems) chartWrapper.render(option, loading) }) diff --git a/src/app/components/data-manage/clear/filter/operation-button.ts b/src/app/components/data-manage/clear/filter/operation-button.ts index 6191c9951..b7da973f4 100644 --- a/src/app/components/data-manage/clear/filter/operation-button.ts +++ b/src/app/components/data-manage/clear/filter/operation-button.ts @@ -9,7 +9,6 @@ import { ElButton, ElMessage, ElMessageBox, ElTooltip } from "element-plus" import ElementIcon from "@src/app/element-ui/icon" import { Ref, h } from "vue" import TimerDatabase, { TimerCondition } from "@db/timer-database" -import DataItem from "@entity/dto/data-item" import { ItemMessage } from "@util/i18n/components/item" import { t } from "@src/app/locale" import { DataManageMessage } from "@src/app/locale/components/data-manage" @@ -33,7 +32,7 @@ type _Props = BaseFilterProps & { confirm: { message: keyof DataManageMessage - operation: (result: DataItem[]) => Promise + operation: (result: timer.stat.Row[]) => Promise resultMessage: keyof DataManageMessage } @@ -99,7 +98,7 @@ function checkParam(props: _Props): TimerCondition | undefined { return condition } -function generateParamAndSelect(props: _Props): Promise | undefined { +function generateParamAndSelect(props: _Props): Promise | undefined { const condition = checkParam(props) if (!condition) { ElMessage({ message: t(msg => msg.dataManage.paramError), type: 'warning' }) @@ -121,7 +120,7 @@ const operationCancelMsg = t(msg => msg.dataManage.operationCancel) const operationConfirmMsg = t(msg => msg.dataManage.operationConfirm) const handleClick = async (props: _Props) => { - const result: DataItem[] = await generateParamAndSelect(props) + const result: timer.stat.Row[] = await generateParamAndSelect(props) const count = result.length const confirmMsg = t(msg => msg.dataManage[props.confirm.message], { count }) diff --git a/src/app/components/option/components/popup.ts b/src/app/components/option/components/popup.ts index 8a2cf0c09..11dcebe43 100644 --- a/src/app/components/option/components/popup.ts +++ b/src/app/components/option/components/popup.ts @@ -9,10 +9,10 @@ import { ElDivider, ElInputNumber, ElOption, ElSelect, ElSwitch } from "element- import { t } from "@app/locale" import { defineComponent, h, Ref, ref } from "vue" import optionService from "@service/option-service" -import { ALL_DATA_ITEMS } from "@entity/dto/data-item" import { renderOptionItem, tagText } from "../common" import { defaultPopup } from "@util/constant/option" import { ALL_POPUP_DURATION } from "@util/constant/popup" +import { ALL_DIMENSIONS } from "@util/stat" const popupMaxInput = (option: Ref) => h(ElInputNumber, { modelValue: option.value.popupMax, @@ -25,12 +25,12 @@ const popupMaxInput = (option: Ref) => h(ElInputNumber } }) -const typeOptions = () => ALL_DATA_ITEMS.map(item => h(ElOption, { value: item, label: t(msg => msg.item[item]) })) +const typeOptions = () => ALL_DIMENSIONS.map(item => h(ElOption, { value: item, label: t(msg => msg.item[item]) })) const typeSelect = (option: Ref) => h(ElSelect, { modelValue: option.value.defaultType, size: 'small', style: { width: '120px' }, - onChange: (val: timer.DataDimension) => { + onChange: (val: timer.stat.Dimension) => { option.value.defaultType = val optionService.setPopupOption(option.value) } diff --git a/src/app/components/report/index.ts b/src/app/components/report/index.ts index e0ed17818..2c82a6d6c 100644 --- a/src/app/components/report/index.ts +++ b/src/app/components/report/index.ts @@ -14,7 +14,6 @@ import type { ReportFilterOption } from "./filter" import { computed, defineComponent, h, reactive, ref } from "vue" import { I18nKey, t } from "@app/locale" -import DataItem from "@entity/dto/data-item" import timerService, { SortDirect } from "@service/timer-service" import whitelistService from "@service/whitelist-service" import './styles/element' @@ -33,7 +32,7 @@ import TimerDatabase from "@db/timer-database" const timerDatabase = new TimerDatabase(chrome.storage.local) -async function queryData(queryParam: Ref, data: Ref, page: UnwrapRef) { +async function queryData(queryParam: Ref, data: Ref, page: UnwrapRef) { const loading = ElLoadingService({ target: `.container-card>.el-card__body`, text: "LOADING..." }) const pageInfo = { pageSize: page.size, pageNum: page.num } const pageResult = await timerService.selectByPage(queryParam.value, pageInfo) @@ -43,7 +42,7 @@ async function queryData(queryParam: Ref, data: Ref loading.close() } -async function handleAliasChange(host: string, newAlias: string, data: Ref) { +async function handleAliasChange(host: string, newAlias: string, data: Ref) { newAlias = newAlias?.trim?.() if (!newAlias) { await hostAliasService.remove(host) @@ -71,7 +70,7 @@ type _ExportInfo = { * @param rows row data * @returns data with json format */ -const generateJsonData = (rows: DataItem[]) => rows.map(row => { +const generateJsonData = (rows: timer.stat.Row[]) => rows.map(row => { const data: _ExportInfo = { host: row.host } data.date = row.date data.alias = row.alias @@ -86,7 +85,7 @@ const generateJsonData = (rows: DataItem[]) => rows.map(row => { * @param rows row data * @returns data with csv format */ -function generateCsvData(rows: DataItem[], mergeDate: boolean, mergeHost: boolean): string[][] { +function generateCsvData(rows: timer.stat.Row[], mergeDate: boolean, mergeHost: boolean): string[][] { const columnName: string[] = [] if (!mergeDate) { columnName.push(t(msg => msg.item.date)) @@ -116,7 +115,7 @@ function generateCsvData(rows: DataItem[], mergeDate: boolean, mergeHost: boolea return data } -async function computeBatchDeleteMsg(selected: DataItem[], mergeDate: boolean, dateRange: Date[]): Promise { +async function computeBatchDeleteMsg(selected: timer.stat.Row[], mergeDate: boolean, dateRange: Date[]): Promise { // host => total focus const hostFocus: { [host: string]: number } = groupBy(selected, a => a.host, @@ -169,7 +168,7 @@ async function computeBatchDeleteMsg(selected: DataItem[], mergeDate: boolean, d return t(key, i18nParam) } -async function deleteBatch(selected: DataItem[], mergeDate: boolean, dateRange: Date[]) { +async function deleteBatch(selected: timer.stat.Row[], mergeDate: boolean, dateRange: Date[]) { if (!mergeDate) { // If not merge date // Delete batch @@ -198,7 +197,7 @@ export type ReportQuery = { /** * Sorted column */ - sc?: timer.DataDimension + sc?: timer.stat.Dimension } const _default = defineComponent({ @@ -220,7 +219,7 @@ const _default = defineComponent({ const mergeDate: Ref = ref(false) const mergeHost: Ref = ref(mh === "true" || mh === "1") const displayBySecond: Ref = ref(false) - const data: Ref = ref([]) + const data: Ref = ref([]) const whitelist: Ref> = ref([]) const sort: UnwrapRef = reactive({ prop: sc || 'focus', @@ -278,7 +277,7 @@ const _default = defineComponent({ format === 'csv' && exportCsv(generateCsvData(rows, mergeDate.value, mergeHost.value), fileName) }, onBatchDelete: async (filterOption: ReportFilterOption) => { - const selected: DataItem[] = tableEl?.value?.getSelected?.() || [] + const selected: timer.stat.Row[] = tableEl?.value?.getSelected?.() || [] if (!selected?.length) { ElMessage({ type: "info", message: t(msg => msg.report.batchDelete.noSelectedMsg) }) return @@ -321,7 +320,7 @@ const _default = defineComponent({ sort.prop = sortInfo.prop query() }), - onItemDelete: (_deleted: DataItem) => query(), + onItemDelete: (_deleted: timer.stat.Row) => query(), onWhitelistChange: (_host: string, _addOrRemove: boolean) => queryWhiteList(whitelist), onAliasChange: (host: string, newAlias: string) => handleAliasChange(host, newAlias, data) }), diff --git a/src/app/components/report/table/columns/alias.ts b/src/app/components/report/table/columns/alias.ts index 61fc18b69..f3c148adc 100644 --- a/src/app/components/report/table/columns/alias.ts +++ b/src/app/components/report/table/columns/alias.ts @@ -10,7 +10,6 @@ */ import { ElTableColumn } from "element-plus" import { defineComponent, h } from "vue" -import DataItem from "@entity/dto/data-item" import { t } from "@app/locale" import ReportAliasInfo from "./alias-info" @@ -25,7 +24,7 @@ const _default = defineComponent({ minWidth: 140, align: "center" }, { - default: ({ row }: { row: DataItem }) => h(ReportAliasInfo, { + default: ({ row }: { row: timer.stat.Row }) => h(ReportAliasInfo, { modelValue: row.alias, onChange: (newAlias: string) => ctx.emit("aliasChange", row.host, newAlias) }) diff --git a/src/app/components/report/table/columns/date.ts b/src/app/components/report/table/columns/date.ts index 1c8f6ecd9..baee04571 100644 --- a/src/app/components/report/table/columns/date.ts +++ b/src/app/components/report/table/columns/date.ts @@ -10,7 +10,6 @@ */ import { ElTableColumn } from "element-plus" import { defineComponent, h } from "vue" -import DataItem from "@entity/dto/data-item" import { t } from "@app/locale" import { dateFormatter } from "../../formatter" @@ -26,7 +25,7 @@ const _default = defineComponent({ align: "center", sortable: "custom" }, { - default: ({ row }: { row: DataItem }) => h('span', dateFormatter(row.date)) + default: ({ row }: { row: timer.stat.Row }) => h('span', dateFormatter(row.date)) }) } }) diff --git a/src/app/components/report/table/columns/focus.ts b/src/app/components/report/table/columns/focus.ts index e582de8db..38c0f7790 100644 --- a/src/app/components/report/table/columns/focus.ts +++ b/src/app/components/report/table/columns/focus.ts @@ -7,7 +7,6 @@ import { t } from "@app/locale" import { ElTableColumn } from "element-plus" -import DataItem from "@entity/dto/data-item" import { defineComponent, h } from "vue" import { periodFormatter } from "../../formatter" @@ -28,7 +27,7 @@ const _default = defineComponent({ align: "center", sortable: "custom" }, { - default: ({ row }: { row: DataItem }) => periodFormatter(row.focus, props.displayBySecond || false) + default: ({ row }: { row: timer.stat.Row }) => periodFormatter(row.focus, props.displayBySecond || false) }) } }) diff --git a/src/app/components/report/table/columns/host.ts b/src/app/components/report/table/columns/host.ts index 0ccf988f4..b58ce23c6 100644 --- a/src/app/components/report/table/columns/host.ts +++ b/src/app/components/report/table/columns/host.ts @@ -7,7 +7,6 @@ import { ElTableColumn } from "element-plus" import { h, defineComponent } from "vue" -import DataItem from "@entity/dto/data-item" import { t } from "@app/locale" import HostAlert from "@app/components/common/host-alert" import HostMergedAlert from './host-merged-alert' @@ -31,7 +30,7 @@ const _default = defineComponent({ sortable: "custom", align: "center" }, { - default: ({ row }: { row: DataItem }) => props.mergeHost + default: ({ row }: { row: timer.stat.Row }) => props.mergeHost ? h(HostMergedAlert, { mergedHost: row.host }, () => row.mergedHosts.map(origin => diff --git a/src/app/components/report/table/columns/operation.ts b/src/app/components/report/table/columns/operation.ts index f69a48302..193ca45f0 100644 --- a/src/app/components/report/table/columns/operation.ts +++ b/src/app/components/report/table/columns/operation.ts @@ -12,7 +12,6 @@ */ import { computed, defineComponent, h, PropType } from "vue" import { ElButton, ElMessage, ElTableColumn } from "element-plus" -import DataItem from "@entity/dto/data-item" import TimerDatabase from "@db/timer-database" import whitelistService from "@service/whitelist-service" import { t } from "@app/locale" @@ -62,7 +61,7 @@ const _default = defineComponent({ label: columnLabel, align: "center" }, { - default: ({ row }: { row: DataItem }) => [ + default: ({ row }: { row: timer.stat.Row }) => [ // Trend h(ElButton, { icon: Stopwatch, diff --git a/src/app/components/report/table/columns/time.ts b/src/app/components/report/table/columns/time.ts index 98e817fad..b9f3c2692 100644 --- a/src/app/components/report/table/columns/time.ts +++ b/src/app/components/report/table/columns/time.ts @@ -8,7 +8,6 @@ import { defineComponent, h } from "vue" import { ElTableColumn } from "element-plus" import { t } from "@app/locale" -import DataItem from "@entity/dto/data-item" const columnLabel = t(msg => msg.item.time) @@ -22,7 +21,7 @@ const _default = defineComponent({ align: 'center', sortable: 'custom' }, { - default: ({ row }: { row: DataItem }) => row.time?.toString?.() || '0' + default: ({ row }: { row: timer.stat.Row }) => row.time?.toString?.() || '0' }) } }) diff --git a/src/app/components/report/table/columns/total.ts b/src/app/components/report/table/columns/total.ts index 32fab98f5..a69f6ed47 100644 --- a/src/app/components/report/table/columns/total.ts +++ b/src/app/components/report/table/columns/total.ts @@ -7,7 +7,6 @@ import { t } from "@app/locale" import { ElTableColumn } from "element-plus" -import DataItem from "@entity/dto/data-item" import { defineComponent, h } from "vue" import { periodFormatter } from "../../formatter" @@ -28,7 +27,7 @@ const _default = defineComponent({ align: "center", sortable: "custom" }, { - default: ({ row }: { row: DataItem }) => periodFormatter(row.total, props.displayBySecond || false) + default: ({ row }: { row: timer.stat.Row }) => periodFormatter(row.total, props.displayBySecond || false) }) } }) diff --git a/src/app/components/report/table/index.ts b/src/app/components/report/table/index.ts index a90269641..15d14377f 100644 --- a/src/app/components/report/table/index.ts +++ b/src/app/components/report/table/index.ts @@ -9,7 +9,6 @@ import type { PropType } from "vue" import { ElTable } from "element-plus" import { defineComponent, h } from "vue" -import DataItem from "@entity/dto/data-item" import SelectionColumn from "./columns/selection" import DateColumn from "./columns/date" import HostColumn from "./columns/host" @@ -25,14 +24,14 @@ export enum ElSortDirect { } export type SortInfo = { - prop: timer.DataDimension | 'host' + prop: timer.stat.Dimension | 'host' order: ElSortDirect } const _default = defineComponent({ name: "ReportTable", props: { - data: Array as PropType, + data: Array as PropType, defaultSort: Object as PropType, mergeDate: Boolean, mergeHost: Boolean, @@ -42,9 +41,9 @@ const _default = defineComponent({ }, emits: ["sortChange", "aliasChange", "itemDelete", "whitelistChange"], setup(props, ctx) { - let selectedRows: DataItem[] = [] + let selectedRows: timer.stat.Row[] = [] ctx.expose({ - getSelected(): DataItem[] { + getSelected(): timer.stat.Row[] { return selectedRows || [] } }) @@ -55,7 +54,7 @@ const _default = defineComponent({ defaultSort: props.defaultSort, style: { width: '100%' }, highlightCurrentRow: true, - "onSelection-change": (data: DataItem[]) => selectedRows = data, + "onSelection-change": (data: timer.stat.Row[]) => selectedRows = data, fit: true, onSortChange: (newSortInfo: SortInfo) => ctx.emit("sortChange", newSortInfo) }, () => { @@ -75,7 +74,7 @@ const _default = defineComponent({ mergeHost: props.mergeHost, dateRange: props.dateRange, whitelist: props.whitelist, - onDelete: (row: DataItem) => ctx.emit("itemDelete", row), + onDelete: (row: timer.stat.Row) => ctx.emit("itemDelete", row), onWhitelistChange: (host: string, addOrRemove: boolean) => ctx.emit("whitelistChange", host, addOrRemove) })) return result diff --git a/src/app/components/trend/components/chart/index.ts b/src/app/components/trend/components/chart/index.ts index d2429d8de..4f687eb5b 100644 --- a/src/app/components/trend/components/chart/index.ts +++ b/src/app/components/trend/components/chart/index.ts @@ -9,7 +9,6 @@ import type { Ref } from "vue" import { defineComponent, h, onMounted, ref } from "vue" import HostOptionInfo from "../../host-option-info" -import DataItem from "@entity/dto/data-item" import ChartWrapper from "./wrapper" const _default = defineComponent({ @@ -19,7 +18,7 @@ const _default = defineComponent({ const chartWrapper: ChartWrapper = new ChartWrapper() ctx.expose({ - render: (host: HostOptionInfo, dateRange: Date[], row: DataItem[]) => chartWrapper.render(host, dateRange, row), + render: (host: HostOptionInfo, dateRange: Date[], row: timer.stat.Row[]) => chartWrapper.render(host, dateRange, row), }) onMounted(() => chartWrapper.init(elRef.value)) diff --git a/src/app/components/trend/components/chart/wrapper.ts b/src/app/components/trend/components/chart/wrapper.ts index 09ef12cae..677d346e5 100644 --- a/src/app/components/trend/components/chart/wrapper.ts +++ b/src/app/components/trend/components/chart/wrapper.ts @@ -28,7 +28,6 @@ import TooltipComponent from "@echarts/component/tooltip" import { t } from "@app/locale" import { formatPeriodCommon, formatTime, MILL_PER_DAY } from "@util/time" import HostOptionInfo from "../../host-option-info" -import DataItem from "@entity/dto/data-item" import hostAliasService from "@service/host-alias-service" import HostAlias from "@entity/dao/host-alias" import { getPrimaryTextColor, getSecondaryTextColor } from "@util/style" @@ -201,7 +200,7 @@ class ChartWrapper { this.instance = init(container) } - async render(host: HostOptionInfo, dateRange: Date[], row: DataItem[]) { + async render(host: HostOptionInfo, dateRange: Date[], row: timer.stat.Row[]) { // 1. x-axis data let xAxisData: string[], allDates: string[] if (!dateRange || dateRange.length !== 2) { diff --git a/src/app/components/trend/index.ts b/src/app/components/trend/index.ts index 750f0bb7a..bd9c176ca 100644 --- a/src/app/components/trend/index.ts +++ b/src/app/components/trend/index.ts @@ -15,7 +15,6 @@ import TrendChart from "./components/chart" import TrendFilter from "./components/filter" import HostOptionInfo from "./host-option-info" import timerService, { SortDirect, TimerQueryParam } from "@service/timer-service" -import DataItem from "@entity/dto/data-item" type _Queries = { host: string @@ -31,7 +30,7 @@ function initWithQuery(hostOption: Ref) { host && (hostOption.value = new HostOptionInfo(host, merge === "1")) } -async function query(hostOption: Ref, dateRange: Ref): Promise { +async function query(hostOption: Ref, dateRange: Ref): Promise { const hostVal = hostOption.value?.host if (!hostVal) { return [] diff --git a/src/app/index.ts b/src/app/index.ts index d1544674a..3103389a7 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -18,6 +18,7 @@ import ElementPlus from 'element-plus' import { initLocale, locale as appLocale } from "@util/i18n" import { toggle, init as initTheme } from "@util/dark-mode" import optionService from "@service/option-service" +import "@src/common/timer" const locales: { [locale in timer.Locale]: () => Promise<{ default: Language }> } = { zh_CN: () => import('element-plus/lib/locale/lang/zh-cn'), diff --git a/src/background/index.ts b/src/background/index.ts index 64bb237e9..65138fa77 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -16,7 +16,6 @@ import ActiveTabListener from "./active-tab-listener" import badgeTextManager from "./badge-text-manager" import metaService from "@service/meta-service" import UninstallListener from "./uninstall-listener" -import ChromeSynchronizer from "./chrome-synchronizer" // Open the log of console openLog() @@ -53,5 +52,3 @@ chrome.runtime.onInstalled.addListener(async detail => { // Questionnaire for uninstall new UninstallListener().listen() }) - -new ChromeSynchronizer().sync() \ No newline at end of file diff --git a/src/common/timer.ts b/src/common/timer.ts index 00c33f5f3..52cff8440 100644 --- a/src/common/timer.ts +++ b/src/common/timer.ts @@ -6,6 +6,7 @@ */ import { getUsedStorage } from "@db/memory-detector" +import ChromeSynchronizer from "@service/chrome-synchronizer" import { openLog, closeLog } from "./logger" /** @@ -24,12 +25,18 @@ export type Timer = { openLog: () => string closeLog: () => string showMemory: () => void + syncData: () => void } /** * @since 0.0.8 */ -const timer = { openLog, closeLog, showMemory } as Timer +const timer = { + openLog, + closeLog, + showMemory, + syncData: new ChromeSynchronizer().sync +} as Timer declare global { interface Window { diff --git a/src/content-script/printer.ts b/src/content-script/printer.ts index c5b8c589c..9e085c4b8 100644 --- a/src/content-script/printer.ts +++ b/src/content-script/printer.ts @@ -15,7 +15,7 @@ const timerDatabase = new TimerDatabase(chrome.storage.local) * Print info of today */ export default async function printInfo(host: string) { - const waste: timer.stat.WastePerDay = await timerDatabase.get(host, new Date()) + const waste: timer.stat.Result = await timerDatabase.get(host, new Date()) const hourMsg = t2Chrome(root => root.message.timeWithHour) const minuteMsg = t2Chrome(root => root.message.timeWithMinute) const secondMsg = t2Chrome(root => root.message.timeWithSecond) diff --git a/src/database/archived-database.ts b/src/database/archived-database.ts index 6a4bbb815..a54319d8b 100644 --- a/src/database/archived-database.ts +++ b/src/database/archived-database.ts @@ -5,8 +5,8 @@ * https://opensource.org/licenses/MIT */ +import { rowOf } from "@util/stat" import { log } from "../common/logger" -import DataItem from "@entity/dto/data-item" import BaseDatabase from "./common/base-database" import { ARCHIVED_PREFIX } from "./common/constant" @@ -27,7 +27,7 @@ class ArchivedDatabase extends BaseDatabase { return Promise.resolve(result) } - private generateKey(row: DataItem): string { + private generateKey(row: timer.stat.Row): string { return ARCHIVED_PREFIX + row.host } @@ -36,7 +36,7 @@ class ArchivedDatabase extends BaseDatabase { * * @param rows site rows, the host and date mustn't be null */ - async updateArchived(rows: DataItem[]): Promise { + async updateArchived(rows: timer.stat.Row[]): Promise { const domainSet: Set = new Set() rows = rows.filter(({ date, host }) => !!host && !!date) rows.forEach(({ host }) => domainSet.add(host)) @@ -47,17 +47,17 @@ class ArchivedDatabase extends BaseDatabase { const { host, focus, total, time } = row let archive = archiveMap[host] - !archive && (archiveMap[host] = archive = new DataItem({ host })) + !archive && (archiveMap[host] = archive = rowOf({ host })) archive.focus += focus || 0 archive.total += total || 0 archive.time += time || 0 }) - const archivedValues = Object.values(archiveMap) as DataItem[] + const archivedValues = Object.values(archiveMap) as timer.stat.Row[] return this.rewrite(archivedValues) } - private async rewrite(toWrite: DataItem[]): Promise { + private async rewrite(toWrite: timer.stat.Row[]): Promise { const promises = toWrite.map(tw => { const object = {} const { total, focus, time } = tw @@ -73,12 +73,12 @@ class ArchivedDatabase extends BaseDatabase { * * @param domains the domains which the key belongs to */ - async selectArchived(domains: Set): Promise { + async selectArchived(domains: Set): Promise { const items = await this.refresh() - const result: DataItem[] = Object.entries(items) + const result: timer.stat.Row[] = Object.entries(items) .filter(([key]) => domains.has(key)) .map(([host, waste]) => { - const { focus, total, time } = waste as timer.stat.WastePerDay + const { focus, total, time } = waste as timer.stat.Result return { focus, total, time, host, date: '', mergedHosts: [] } }) return await Promise.resolve(result) diff --git a/src/database/timer-database.ts b/src/database/timer-database.ts index b89896059..3c8d0637a 100644 --- a/src/database/timer-database.ts +++ b/src/database/timer-database.ts @@ -6,11 +6,10 @@ */ import { log } from "../common/logger" -import DataItem from "@entity/dto/data-item" import { formatTime } from "@util/time" import BaseDatabase from "./common/base-database" import { ARCHIVED_PREFIX, DATE_FORMAT, REMAIN_WORD_PREFIX } from "./common/constant" -import { zero, merge, isNotZero } from "@util/waste-per-day" +import { createZeroResult, mergeResult, isNotZeroResult } from "@util/stat" export type TimerCondition = { /** @@ -107,12 +106,12 @@ function processCondition(condition: TimerCondition): _TimerCondition { return result } -function mergeMigration(exist: timer.stat.WastePerDay | undefined, another: any) { - exist = exist || zero() - return merge(exist, { total: another.total || 0, focus: another.focus || 0, time: another.time || 0 }) +function mergeMigration(exist: timer.stat.Result | undefined, another: any) { + exist = exist || createZeroResult() + return mergeResult(exist, { total: another.total || 0, focus: another.focus || 0, time: another.time || 0 }) } -function migrate(exists: { [key: string]: timer.stat.WastePerDay }, data: any): { [key: string]: timer.stat.WastePerDay } { +function migrate(exists: { [key: string]: timer.stat.Result }, data: any): { [key: string]: timer.stat.Result } { const result = {} Object.entries(data) .filter(([key]) => /^20\d{2}[01]\d[0-3]\d.*/.test(key)) @@ -120,7 +119,7 @@ function migrate(exists: { [key: string]: timer.stat.WastePerDay }, data: any): if (typeof value !== "object") return const exist = exists[key] const merged = mergeMigration(exist, value) - merged && isNotZero(merged) && (result[key] = mergeMigration(exist, value)) + merged && isNotZeroResult(merged) && (result[key] = mergeMigration(exist, value)) }) return result } @@ -151,10 +150,10 @@ class TimerDatabase extends BaseDatabase { * @param host host * @since 0.1.3 */ - async accumulate(host: string, date: Date | string, item: timer.stat.WastePerDay): Promise { + async accumulate(host: string, date: Date | string, item: timer.stat.Result): Promise { const key = this.generateKey(host, date) const items = await this.storage.get(key) - const exist: timer.stat.WastePerDay = merge(items[key] as timer.stat.WastePerDay || zero(), item) + const exist: timer.stat.Result = mergeResult(items[key] as timer.stat.Result || createZeroResult(), item) const toUpdate = {} toUpdate[key] = exist log('toUpdate', toUpdate) @@ -168,7 +167,7 @@ class TimerDatabase extends BaseDatabase { * @param date date * @since 0.1.8 */ - async accumulateBatch(data: timer.stat.WasteData, date: Date): Promise { + async accumulateBatch(data: timer.stat.ResultSet, date: Date): Promise { const hosts = Object.keys(data) if (!hosts.length) return const dateStr = formatTime(date, DATE_FORMAT) @@ -178,32 +177,36 @@ class TimerDatabase extends BaseDatabase { const items = await this.storage.get(Object.values(keys)) const toUpdate = {} - const afterUpdated: timer.stat.WasteData = {} + const afterUpdated: timer.stat.ResultSet = {} Object.entries(keys).forEach(([host, key]) => { const item = data[host] - const exist: timer.stat.WastePerDay = merge(items[key] as timer.stat.WastePerDay || zero(), item) + const exist: timer.stat.Result = mergeResult(items[key] as timer.stat.Result || createZeroResult(), item) toUpdate[key] = afterUpdated[host] = exist }) await this.storage.set(toUpdate) return afterUpdated } + async accumulateBatch4Sync(data: timer.stat.Row[]) { + + } + /** * Select * * @param condition condition */ - async select(condition?: TimerCondition): Promise { + async select(condition?: TimerCondition): Promise { log("select:{condition}", condition) condition = condition || {} const _cond: _TimerCondition = processCondition(condition) const items = await this.refresh() - let result: DataItem[] = [] + let result: timer.stat.Row[] = [] for (let key in items) { const date = key.substring(0, 8) const host = key.substring(8) - const val: timer.stat.WastePerDay = items[key] + const val: timer.stat.Result = items[key] if (this.filterBefore(date, host, val, _cond)) { const { total, focus, time } = val result.push({ date, host, total, focus, time, mergedHosts: [] }) @@ -250,7 +253,7 @@ class TimerDatabase extends BaseDatabase { * @param condition query parameters * @return true if valid, or false */ - private filterBefore(date: string, host: string, val: timer.stat.WastePerDay, condition: _TimerCondition): boolean { + private filterBefore(date: string, host: string, val: timer.stat.Result, condition: _TimerCondition): boolean { const { focus, total, time } = val const { timeStart, timeEnd, totalStart, totalEnd, focusStart, focusEnd } = condition @@ -266,10 +269,10 @@ class TimerDatabase extends BaseDatabase { * * @since 0.0.5 */ - async get(host: string, date: Date): Promise { + async get(host: string, date: Date): Promise { const key = this.generateKey(host, date) const items = await this.storage.get(null) - return Promise.resolve(items[key] || zero()) + return Promise.resolve(items[key] || createZeroResult()) } /** @@ -290,7 +293,7 @@ class TimerDatabase extends BaseDatabase { * @param rows site rows, the host and date mustn't be null * @since 0.0.9 */ - async delete(rows: DataItem[]): Promise { + async delete(rows: timer.stat.Row[]): Promise { const keys: string[] = rows.filter(({ date, host }) => !!host && !!date).map(({ host, date }) => this.generateKey(host, date)) return this.storage.remove(keys) } @@ -342,7 +345,7 @@ class TimerDatabase extends BaseDatabase { for (let key in items) { const date = key.substring(0, 8) const host = key.substring(8) - const val: timer.stat.WastePerDay = items[key] + const val: timer.stat.Result = items[key] if (this.filterBefore(date, host, val, _cond)) { count++ } diff --git a/src/database/whitelist-database.ts b/src/database/whitelist-database.ts index 8e3c7e36e..04d9260c2 100644 --- a/src/database/whitelist-database.ts +++ b/src/database/whitelist-database.ts @@ -7,7 +7,6 @@ import BaseDatabase from "./common/base-database" import { WHITELIST_KEY } from "./common/constant" - class WhitelistDatabase extends BaseDatabase { private update(selectAll: string[]): Promise { diff --git a/src/entity/dto/data-item.ts b/src/entity/dto/data-item.ts deleted file mode 100644 index e4458f4a4..000000000 --- a/src/entity/dto/data-item.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2021 Hengyang Zhang - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -type DataDimension = timer.DataDimension - -export const ALL_DATA_ITEMS: DataDimension[] = ['focus', 'total', 'time'] - -export type DataItemVal = { [item in DataDimension]: number } - -/** - * The unique key of data item - */ -export type DataItemUk = { - host: string - date?: string -} - -export default class DataItem implements DataItemVal { - host: string - date: string - total: number - focus: number - time: number - /** - * The merged domains - * - * Can't be empty if merged - * - * @since 0.1.5 - */ - mergedHosts: DataItem[] - /** - * Icon url - * - * Must be undefined if merged - */ - iconUrl?: string - /** - * The alias name of this Site, always is the title of its homepage by detected - */ - alias?: string - - constructor(key: DataItemUk, item?: DataItemVal) { - this.host = key.host - this.date = key.date || '' - - this.total = item && item.total || 0 - this.focus = item && item.focus || 0 - this.time = item && item.time || 0 - this.mergedHosts = [] - } -} \ No newline at end of file diff --git a/src/popup/common/query-result.ts b/src/popup/common/query-result.ts index 4bec5a8e9..70b8acdfc 100644 --- a/src/popup/common/query-result.ts +++ b/src/popup/common/query-result.ts @@ -5,12 +5,11 @@ * https://opensource.org/licenses/MIT */ -import DataItem from "@entity/dto/data-item" -export type PopupItem = DataItem & { isOther?: boolean } +export type PopupItem = timer.stat.Row & { isOther?: boolean } type QueryResult = { - type: timer.DataDimension + type: timer.stat.Dimension mergeHost: boolean data: PopupItem[] // Filter items diff --git a/src/popup/components/chart/option.ts b/src/popup/components/chart/option.ts index 00dbee8b0..1037340dc 100644 --- a/src/popup/components/chart/option.ts +++ b/src/popup/components/chart/option.ts @@ -15,7 +15,6 @@ import type { } from "echarts/components" import type QueryResult from "@popup/common/query-result" -import DataItem from "@entity/dto/data-item" import { formatPeriodCommon, formatTime } from "@util/time" import { t } from "@popup/locale" import { getPrimaryTextColor, getSecondaryTextColor } from "@util/style" @@ -49,7 +48,7 @@ const legend2LabelStyle = (legend: string) => { const toolTipFormatter = ({ type }: QueryResult, params: any) => { const format = params instanceof Array ? params[0] : params const { name, value, percent } = format - const data = format.data as DataItem + const data = format.data as timer.stat.Row const host = data.host let dimensionName = name if (host && host !== name) { diff --git a/src/popup/components/footer/index.ts b/src/popup/components/footer/index.ts index 821d25fb1..be857892c 100644 --- a/src/popup/components/footer/index.ts +++ b/src/popup/components/footer/index.ts @@ -17,7 +17,6 @@ import './meat' import { updateTotal } from "./total-info" import timerService, { FillFlagParam, SortDirect, TimerQueryParam } from "@service/timer-service" -import DataItem from "@entity/dto/data-item" import { locale, t } from "@popup/locale" import QueryResult, { PopupItem } from "@popup/common/query-result" import { formatPeriodCommon, getMonthTime, getWeekTime } from "@util/time" @@ -66,7 +65,7 @@ function _default(handleQuery: (result: QueryResult) => void) { * @param type type * @returns total alert text */ -const getTotalInfo = (data: DataItem[], type: timer.DataDimension) => { +const getTotalInfo = (data: timer.stat.Row[], type: timer.stat.Dimension) => { if (type === 'time') { const totalCount = data.map(d => d[type] || 0).reduce((a, b) => a + b, 0) return t(msg => msg.totalCount, { totalCount }) @@ -102,7 +101,7 @@ async function query() { } } result.push(other) - const type = queryParam.sort as timer.DataDimension + const type = queryParam.sort as timer.stat.Dimension const data = result.filter(item => item[type]) const queryResult: QueryResult = { diff --git a/src/popup/components/footer/select/type-select.ts b/src/popup/components/footer/select/type-select.ts index ae5160d41..bfb93060d 100644 --- a/src/popup/components/footer/select/type-select.ts +++ b/src/popup/components/footer/select/type-select.ts @@ -6,7 +6,7 @@ */ // Type select -import { ALL_DATA_ITEMS } from "@entity/dto/data-item" +import { ALL_DIMENSIONS } from "@util/stat" import optionService from "@service/option-service" import { t } from "@popup/locale" import { toHideStyle, toShowStyle } from "./common" @@ -34,16 +34,16 @@ typeSelect.onclick = () => isOpen ? hidePopup() : openPopup() /////////// Options const SELECTED_CLASS = 'selected' const optionList = document.getElementById('type-select-options') -const optionItems: Map = new Map() +const optionItems: Map = new Map() -function selected(item: timer.DataDimension): void { +function selected(item: timer.stat.Dimension): void { currentSelected = item Array.from(optionItems.values()).forEach(item => item.classList.remove(SELECTED_CLASS)) optionItems.get(item).classList.add(SELECTED_CLASS) typeSelectInput.value = t(msg => msg.item[item]) } -for (const item of ALL_DATA_ITEMS) { +for (const item of ALL_DIMENSIONS) { const li = document.createElement('li') li.classList.add('el-select-dropdown__item') li.innerText = t(msg => msg.item[item]) @@ -55,9 +55,9 @@ for (const item of ALL_DATA_ITEMS) { optionList.append(li) optionItems.set(item, li) } -let currentSelected: timer.DataDimension = undefined +let currentSelected: timer.stat.Dimension = undefined -export function getSelectedType(): timer.DataDimension { return currentSelected } +export function getSelectedType(): timer.stat.Dimension { return currentSelected } optionService.getAllOption().then((option: timer.option.PopupOption) => selected(option.defaultType)) diff --git a/src/background/chrome-synchronizer.ts b/src/service/chrome-synchronizer.ts similarity index 96% rename from src/background/chrome-synchronizer.ts rename to src/service/chrome-synchronizer.ts index 6c7cfaf8d..ce81c5bcc 100644 --- a/src/background/chrome-synchronizer.ts +++ b/src/service/chrome-synchronizer.ts @@ -9,9 +9,7 @@ const sync = chrome.storage.sync async function syncData() { const current = new Date().getTime() const meta = await metaDb.getMeta() - if (meta.sync) { - meta.sync = {} - } + !meta.sync && (meta.sync = {}) syncTimer(current, meta) } @@ -28,6 +26,7 @@ async function syncTimer(current: number, meta: timer.meta.ExtensionMeta) { const localItems = await localDb.select({ date: dateRange }) const remoteItems = await syncDb.select({ date: dateRange }) + await Promise.all(localItems.map(item => syncDb.accumulate(item.host, item.date, item))) await Promise.all(remoteItems.map(item => localDb.accumulate(item.host, item.date, item))) diff --git a/src/service/timer-service.ts b/src/service/timer-service.ts index 5a44ce6f1..4dd91cf6e 100644 --- a/src/service/timer-service.ts +++ b/src/service/timer-service.ts @@ -7,7 +7,6 @@ import TimerDatabase, { TimerCondition } from "@db/timer-database" import ArchivedDatabase from "@db/archived-database" -import DataItem from "@entity/dto/data-item" import { log } from "../common/logger" import CustomizedHostMergeRuler from "./components/host-merge-ruler" import HostMergeRuleItem from "@entity/dto/host-merge-rule-item" @@ -16,7 +15,7 @@ import IconUrlDatabase from "@db/icon-url-database" import HostAliasDatabase from "@db/host-alias-database" import { PageParam, PageResult, slicePageResult } from "./components/page-info" import whitelistHolder from './components/whitelist-holder' -import { wasteOf } from "@util/waste-per-day" +import { resultOf, rowOf } from "@util/stat" const storage = chrome.storage.local @@ -43,7 +42,7 @@ export type TimerQueryParam = TimerCondition & { /** * The name of sorted column */ - sort?: keyof DataItem + sort?: keyof timer.stat.Row /** * 1 asc, -1 desc */ @@ -75,16 +74,16 @@ export type HostSet = { */ class TimerService { - async addFocusAndTotal(data: { [host: string]: { run: number, focus: number } }): Promise { + async addFocusAndTotal(data: { [host: string]: { run: number, focus: number } }): Promise { const toUpdate = {} Object.entries(data) .filter(([host]) => whitelistHolder.notContains(host)) - .forEach(([host, item]) => toUpdate[host] = wasteOf(item.run, item.focus, 0)) + .forEach(([host, item]) => toUpdate[host] = resultOf(item.run, item.focus, 0)) return timerDatabase.accumulateBatch(toUpdate, new Date()) } async addOneTime(host: string) { - timerDatabase.accumulate(host, new Date(), wasteOf(0, 0, 1)) + timerDatabase.accumulate(host, new Date(), resultOf(0, 0, 1)) } /** @@ -122,7 +121,7 @@ class TimerService { * @param rows rows * @since 0.0.9 */ - async archive(rows: DataItem[]): Promise { + async archive(rows: timer.stat.Row[]): Promise { await archivedDatabase.updateArchived(rows) return timerDatabase.delete(rows) } @@ -140,7 +139,7 @@ class TimerService { return count } - private processSort(origin: DataItem[], param: TimerQueryParam) { + private processSort(origin: timer.stat.Row[], param: TimerQueryParam) { const { sort, sortOrder } = param if (!sort) return @@ -153,19 +152,19 @@ class TimerService { }) } - private async fillIconUrl(items: DataItem[]): Promise { + private async fillIconUrl(items: timer.stat.Row[]): Promise { const hosts = items.map(o => o.host) const iconUrlMap = await iconUrlDatabase.get(...hosts) items.forEach(dataItem => dataItem.iconUrl = iconUrlMap[dataItem.host]) } - private async fillAlias(items: DataItem[]): Promise { + private async fillAlias(items: timer.stat.Row[]): Promise { const hosts: string[] = items.map(o => o.host) const aliasMap = await hostAliasDatabase.get(...hosts) items.forEach(dataItem => dataItem.alias = aliasMap[dataItem.host]?.name) } - async select(param?: TimerQueryParam, flagParam?: FillFlagParam): Promise { + async select(param?: TimerQueryParam, flagParam?: FillFlagParam): Promise { log("service: select:{param}", param) // Need match full host after merged @@ -194,14 +193,14 @@ class TimerService { flagParam?.alias && await this.fillAlias(origin) } // Filter merged host if full host - fullHost && (origin = origin.filter(DataItem => DataItem.host === fullHost)) + fullHost && (origin = origin.filter(dataItem => dataItem.host === fullHost)) return origin } - async selectByPage(param?: TimerQueryParam, page?: PageParam): Promise> { + async selectByPage(param?: TimerQueryParam, page?: PageParam): Promise> { log("selectByPage:{param},{page}", param, page) - const origin: DataItem[] = await this.select(param) - const result: PageResult = slicePageResult(origin, page) + const origin: timer.stat.Row[] = await this.select(param) + const result: PageResult = slicePageResult(origin, page) const list = result.list if (param.mergeHost) { for (const beforeMerge of list) await this.fillIconUrl(beforeMerge.mergedHosts) @@ -212,13 +211,13 @@ class TimerService { return result } - private filter(origin: DataItem[], param: TimerCondition) { + private filter(origin: timer.stat.Row[], param: TimerCondition) { const paramHost = (param.host || '').trim() return paramHost ? origin.filter(o => o.host.includes(paramHost)) : origin } - private async mergeHost(origin: DataItem[]): Promise { - const newDataItems = [] + private async mergeHost(origin: timer.stat.Row[]): Promise { + const newRows = [] const map = {} // Generate ruler @@ -235,26 +234,26 @@ class TimerService { mergedHosts.push(o) }) for (let key in map) { - newDataItems.push(map[key]) + newRows.push(map[key]) } - return newDataItems + return newRows } - private mergeDate(origin: DataItem[]): DataItem[] { - const newDataItems = [] + private mergeDate(origin: timer.stat.Row[]): timer.stat.Row[] { + const newRows = [] const map = {} origin.forEach(o => this.merge(map, o, o.host).date = '') for (let key in map) { - newDataItems.push(map[key]) + newRows.push(map[key]) } - return newDataItems + return newRows } - private merge(map: {}, origin: DataItem, key: string): DataItem { - let exist: DataItem = map[key] + private merge(map: {}, origin: timer.stat.Row, key: string): timer.stat.Row { + let exist: timer.stat.Row = map[key] if (exist === undefined) { - exist = map[key] = new DataItem({ host: origin.host, date: origin.date }) + exist = map[key] = rowOf({ host: origin.host, date: origin.date }) exist.mergedHosts = origin.mergedHosts || [] } exist.time += origin.time diff --git a/src/util/stat.ts b/src/util/stat.ts new file mode 100644 index 000000000..fe421cad6 --- /dev/null +++ b/src/util/stat.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2022 Hengyang Zhang + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +export function isNotZeroResult(target: timer.stat.Result): boolean { + return !!target.total || !!target.focus || !!target.time +} + +export function createZeroResult(): timer.stat.Result { + return { focus: 0, time: 0, total: 0 } +} + +export function mergeResult(a: timer.stat.Result, b: timer.stat.Result): timer.stat.Result { + return { total: a.total + b.total, focus: a.focus + b.focus, time: a.time + b.time } +} + +export function resultOf(total: number, focus: number, time: number): timer.stat.Result { + return { total, focus, time } +} + +export function rowOf(key: timer.stat.RowKey, item?: timer.stat.Result): timer.stat.Row { + return { + host: key.host, + date: key.date, + total: item && item.total || 0, + focus: item && item.focus || 0, + time: item && item.time || 0, + mergedHosts: [] + } +} + +export const ALL_DIMENSIONS: timer.stat.Dimension[] = ['focus', 'total', 'time'] diff --git a/src/util/waste-per-day.ts b/src/util/waste-per-day.ts deleted file mode 100644 index 9e5302f33..000000000 --- a/src/util/waste-per-day.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2022 Hengyang Zhang - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -export function isNotZero(target: timer.stat.WastePerDay): boolean { - return !!target.total || !!target.focus || !!target.time -} - -export function zero(): timer.stat.WastePerDay { - return { focus: 0, time: 0, total: 0 } -} - -export function merge(a: timer.stat.WastePerDay, b: timer.stat.WastePerDay): timer.stat.WastePerDay { - return { total: a.total + b.total, focus: a.focus + b.focus, time: a.time + b.time } -} - -export function wasteOf(total: number, focus: number, time: number): timer.stat.WastePerDay { - return { total, focus, time } -} \ No newline at end of file diff --git a/test/database/archived-database.test.ts b/test/database/archived-database.test.ts index 6b7e742df..03f16917b 100644 --- a/test/database/archived-database.test.ts +++ b/test/database/archived-database.test.ts @@ -1,6 +1,6 @@ import ArchivedDatabase from "@db/archived-database" import { DATE_FORMAT } from "@db/common/constant" -import DataItem from "@entity/dto/data-item" +import { rowOf } from "@util/stat" import { formatTime, MILL_PER_DAY } from "@util/time" import storage from "../__mock__/storage" @@ -15,10 +15,10 @@ describe('archived-database', () => { beforeEach(async () => storage.local.clear()) test('1', async () => { await archivedDatabase.updateArchived([ - new DataItem({ host: baidu, date: now }, { focus: 1, total: 1, time: 0 }), - new DataItem({ host: baidu, date: yesterday }, { focus: 1, total: 0, time: 2 }), + rowOf({ host: baidu, date: now }, { focus: 1, total: 1, time: 0 }), + rowOf({ host: baidu, date: yesterday }, { focus: 1, total: 0, time: 2 }), ]) - await archivedDatabase.updateArchived([new DataItem({ host: baidu, date: beforeYesterday }, { focus: 0, total: 1, time: 2 })]) + await archivedDatabase.updateArchived([rowOf({ host: baidu, date: beforeYesterday }, { focus: 0, total: 1, time: 2 })]) const result = await archivedDatabase.selectArchived(new Set([baidu])) expect(result.length).toEqual(1) const baiduInfo = result[0] diff --git a/test/database/timer-database.test.ts b/test/database/timer-database.test.ts index 739d3ecc3..1e66bba60 100644 --- a/test/database/timer-database.test.ts +++ b/test/database/timer-database.test.ts @@ -1,7 +1,7 @@ import { DATE_FORMAT } from "@db/common/constant" import TimerDatabase, { TimerCondition } from "@db/timer-database" import { formatTime, MILL_PER_DAY } from "@util/time" -import { wasteOf } from "@util/waste-per-day" +import { resultOf } from "@util/stat" import storage from "../__mock__/storage" const db = new TimerDatabase(storage.local) @@ -16,42 +16,42 @@ describe('timer-database', () => { beforeEach(async () => storage.local.clear()) test('1', async () => { - await db.accumulate(baidu, now, wasteOf(100, 100, 0)) - const data: timer.stat.WastePerDay = await db.get(baidu, now) - expect(data).toEqual(wasteOf(100, 100, 0)) + await db.accumulate(baidu, now, resultOf(100, 100, 0)) + const data: timer.stat.Result = await db.get(baidu, now) + expect(data).toEqual(resultOf(100, 100, 0)) }) test('2', async () => { - await db.accumulate(baidu, now, wasteOf(100, 200, 0)) - await db.accumulate(baidu, now, wasteOf(100, 200, 0)) + await db.accumulate(baidu, now, resultOf(100, 200, 0)) + await db.accumulate(baidu, now, resultOf(100, 200, 0)) let data = await db.get(baidu, now) - expect(data).toEqual(wasteOf(200, 400, 0)) - await db.accumulate(baidu, now, wasteOf(0, 0, 1)) + expect(data).toEqual(resultOf(200, 400, 0)) + await db.accumulate(baidu, now, resultOf(0, 0, 1)) data = await db.get(baidu, now) - expect(data).toEqual(wasteOf(200, 400, 1)) + expect(data).toEqual(resultOf(200, 400, 1)) }) test('3', async () => { await db.accumulateBatch( { - [google]: wasteOf(11, 11, 0), - [baidu]: wasteOf(1, 1, 0) + [google]: resultOf(11, 11, 0), + [baidu]: resultOf(1, 1, 0) }, now ) expect((await db.select()).length).toEqual(2) await db.accumulateBatch( { - [google]: wasteOf(12, 12, 1), - [baidu]: wasteOf(2, 2, 1) + [google]: resultOf(12, 12, 1), + [baidu]: resultOf(2, 2, 1) }, yesterday ) expect((await db.select()).length).toEqual(4) await db.accumulateBatch( { - [google]: wasteOf(13, 13, 2), - [baidu]: wasteOf(3, 3, 2) + [google]: resultOf(13, 13, 2), + [baidu]: resultOf(3, 3, 2) }, beforeYesterday ) expect((await db.select()).length).toEqual(6) @@ -115,8 +115,8 @@ describe('timer-database', () => { }) test('5', async () => { - await db.accumulate(baidu, now, wasteOf(10, 10, 0)) - await db.accumulate(baidu, yesterday, wasteOf(10, 12, 0)) + await db.accumulate(baidu, now, resultOf(10, 10, 0)) + await db.accumulate(baidu, yesterday, resultOf(10, 12, 0)) expect((await db.select()).length).toEqual(2) // Delete yesterday's data await db.deleteByUrlAndDate(baidu, yesterday) @@ -125,8 +125,8 @@ describe('timer-database', () => { await db.deleteByUrlAndDate(baidu, yesterday) expect((await db.get(baidu, now)).focus).toEqual(10) // Add one again, and another - await db.accumulate(baidu, beforeYesterday, wasteOf(1, 1, 1)) - await db.accumulate(google, now, wasteOf(0, 0, 0)) + await db.accumulate(baidu, beforeYesterday, resultOf(1, 1, 1)) + await db.accumulate(google, now, resultOf(0, 0, 0)) expect((await db.select()).length).toEqual(3) // Delete all the baidu await db.deleteByUrl(baidu) @@ -138,7 +138,7 @@ describe('timer-database', () => { const list = await db.select(cond) expect(list.length).toEqual(1) // Add one item of baidu again again - await db.accumulate(baidu, now, wasteOf(1, 1, 1)) + await db.accumulate(baidu, now, resultOf(1, 1, 1)) // But delete google await db.delete(list) // Then only one item of baidu @@ -154,7 +154,7 @@ describe('timer-database', () => { }) test('7', async () => { - const foo = wasteOf(1, 1, 1) + const foo = resultOf(1, 1, 1) await db.accumulate(baidu, now, foo) await db.accumulate(baidu, yesterday, foo) await db.accumulate(baidu, beforeYesterday, foo) @@ -166,7 +166,7 @@ describe('timer-database', () => { }) test("importData", async () => { - const foo = wasteOf(1, 1, 1) + const foo = resultOf(1, 1, 1) await db.accumulate(baidu, now, foo) const data2Import = await db.storage.get() storage.local.clear() @@ -216,10 +216,10 @@ describe('timer-database', () => { }) test("count", async () => { - await db.accumulate(baidu, now, wasteOf(1, 1, 1)) - await db.accumulate(baidu, yesterday, wasteOf(1, 2, 1)) - await db.accumulate(google, now, wasteOf(1, 3, 1)) - await db.accumulate(google, yesterday, wasteOf(1, 4, 1)) + await db.accumulate(baidu, now, resultOf(1, 1, 1)) + await db.accumulate(baidu, yesterday, resultOf(1, 2, 1)) + await db.accumulate(google, now, resultOf(1, 3, 1)) + await db.accumulate(google, yesterday, resultOf(1, 4, 1)) // Count by host expect(await db.count({ host: baidu, diff --git a/test/util/stat.test.ts b/test/util/stat.test.ts new file mode 100644 index 000000000..e5ac78040 --- /dev/null +++ b/test/util/stat.test.ts @@ -0,0 +1,11 @@ +import { mergeResult, resultOf, createZeroResult } from "@util/stat" + +test('default values of WastePerDay', () => { + const newOne = createZeroResult() + expect(newOne.time).toBe(0) + expect(newOne.focus).toBe(0) + expect(newOne.total).toBe(0) + + const another = mergeResult(newOne, resultOf(1, 1, 2)) + expect(another).toEqual(resultOf(1, 1, 2)) +}) \ No newline at end of file diff --git a/test/util/waste-per-day.test.ts b/test/util/waste-per-day.test.ts deleted file mode 100644 index 47c754456..000000000 --- a/test/util/waste-per-day.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { merge, wasteOf, zero } from "@util/waste-per-day" - -test('default values of WastePerDay', () => { - const newOne = zero() - expect(newOne.time).toBe(0) - expect(newOne.focus).toBe(0) - expect(newOne.total).toBe(0) - - const another = merge(newOne, wasteOf(1, 1, 2)) - expect(another).toEqual(wasteOf(1, 1, 2)) -}) \ No newline at end of file From d7b308f3dec4f804e0cb9748a1b0522eba6950d7 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sun, 3 Jul 2022 17:21:50 +0800 Subject: [PATCH 12/72] Clean code --- src/common/timer.ts | 5 +--- src/database/timer-database.ts | 15 ++++++++++-- src/service/chrome-synchronizer.ts | 39 ------------------------------ 3 files changed, 14 insertions(+), 45 deletions(-) delete mode 100644 src/service/chrome-synchronizer.ts diff --git a/src/common/timer.ts b/src/common/timer.ts index 52cff8440..40fbf0413 100644 --- a/src/common/timer.ts +++ b/src/common/timer.ts @@ -6,7 +6,6 @@ */ import { getUsedStorage } from "@db/memory-detector" -import ChromeSynchronizer from "@service/chrome-synchronizer" import { openLog, closeLog } from "./logger" /** @@ -25,7 +24,6 @@ export type Timer = { openLog: () => string closeLog: () => string showMemory: () => void - syncData: () => void } /** @@ -34,8 +32,7 @@ export type Timer = { const timer = { openLog, closeLog, - showMemory, - syncData: new ChromeSynchronizer().sync + showMemory } as Timer declare global { diff --git a/src/database/timer-database.ts b/src/database/timer-database.ts index 3c8d0637a..78accaf1a 100644 --- a/src/database/timer-database.ts +++ b/src/database/timer-database.ts @@ -187,8 +187,19 @@ class TimerDatabase extends BaseDatabase { return afterUpdated } - async accumulateBatch4Sync(data: timer.stat.Row[]) { - + async accumulateBatch4Sync(data: timer.stat.Row[]): Promise { + const validRows = data.filter(item => item.date && item.host) + const keyRowMap: { [key: string]: timer.stat.Row } = {} + validRows.forEach(row => keyRowMap[this.generateKey(row.host, row.date)] = row) + const exists = await this.storage.get(Object.keys(keyRowMap)) + const afterUpdated = {} + Object.entries(keyRowMap).forEach(([key, row]) => { + const merged = mergeResult(exists[key] as timer.stat.Result || createZeroResult(), row) + afterUpdated[key] = merged + }) + console.log(data.length, Object.keys(afterUpdated).length) + await this.storage.set(afterUpdated) + return Object.keys(afterUpdated).length } /** diff --git a/src/service/chrome-synchronizer.ts b/src/service/chrome-synchronizer.ts deleted file mode 100644 index ce81c5bcc..000000000 --- a/src/service/chrome-synchronizer.ts +++ /dev/null @@ -1,39 +0,0 @@ -import MetaDatabase from "@db/meta-database" -import TimerDatabase from "@db/timer-database" -import { MILL_PER_DAY } from "@util/time" - -const metaDb = new MetaDatabase(chrome.storage.local) -const local = chrome.storage.local -const sync = chrome.storage.sync - -async function syncData() { - const current = new Date().getTime() - const meta = await metaDb.getMeta() - !meta.sync && (meta.sync = {}) - syncTimer(current, meta) -} - -async function syncTimer(current: number, meta: timer.meta.ExtensionMeta) { - const startTime = meta.sync.timer - const endTime = current - MILL_PER_DAY - const localDb = new TimerDatabase(local) - const syncDb = new TimerDatabase(sync) - - const dateRange = [ - startTime ? new Date(startTime) : undefined, - new Date(endTime) - ] - - const localItems = await localDb.select({ date: dateRange }) - const remoteItems = await syncDb.select({ date: dateRange }) - - await Promise.all(localItems.map(item => syncDb.accumulate(item.host, item.date, item))) - await Promise.all(remoteItems.map(item => localDb.accumulate(item.host, item.date, item))) - - meta.sync.timer = current - await metaDb.update(meta) -} - -export default class ChromeSynchronizer { - sync = syncData -} \ No newline at end of file From f5324dd9d6345dee33da7598fb11bf01004b60d6 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sun, 3 Jul 2022 17:47:38 +0800 Subject: [PATCH 13/72] Jump to report page after clicking the heat-map (#116) --- global.d.ts | 7 ------ .../dashboard/components/calendar-heat-map.ts | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/global.d.ts b/global.d.ts index d2657f432..ce55c819f 100644 --- a/global.d.ts +++ b/global.d.ts @@ -109,19 +109,12 @@ declare namespace timer { } namespace meta { - type SyncMeta = { - timer?: number - } type ExtensionMeta = { installTime?: number appCounter?: { [routePath: string]: number } popupCounter?: { _total?: number } - /** - * @since 1.2.0 - */ - sync?: SyncMeta } } diff --git a/src/app/components/dashboard/components/calendar-heat-map.ts b/src/app/components/dashboard/components/calendar-heat-map.ts index 64de1e821..e56980302 100644 --- a/src/app/components/dashboard/components/calendar-heat-map.ts +++ b/src/app/components/dashboard/components/calendar-heat-map.ts @@ -7,6 +7,7 @@ import type { ECharts, ComposeOption } from "echarts/core" import type { HeatmapSeriesOption } from "echarts/charts" +import type { ReportQuery } from "@app/components/report" import type { TitleComponentOption, TooltipComponentOption, GridComponentOption, VisualMapComponentOption } from "echarts/components" import TitleComponent from "@echarts/component/title" @@ -37,6 +38,8 @@ import { defineComponent, h, onMounted, ref, Ref } from "vue" import { groupBy, rotate } from "@util/array" import { BASE_TITLE_OPTION } from "../common" import { getPrimaryTextColor } from "@util/style" +import { getAppPageUrl } from "@util/constant/url" +import { REPORT_ROUTE } from "@app/router/constants" const WEEK_NUM = 53 @@ -193,6 +196,27 @@ function optionOf(data: _Value[], days: string[]): EcOption { } } +/** + * Click to jump to the report page + * + * @since 1.1.1 + */ +function handleClick(value: _Value): void { + const [_1, _2, minutes, currentDate] = value + if (!minutes) { + return + } + + const currentYear = parseInt(currentDate.substr(0, 4)) + const currentMonth = parseInt(currentDate.substr(4, 2)) - 1 + const currentDay = parseInt(currentDate.substr(6, 2)) + const currentTs = (new Date(currentYear, currentMonth, currentDay).getTime() + 1000).toString() + const query: ReportQuery = { ds: currentTs, de: currentTs } + + const url = getAppPageUrl(false, REPORT_ROUTE, query) + chrome.tabs.create({ url }) +} + class ChartWrapper { instance: ECharts allDates: string[] @@ -208,6 +232,7 @@ class ChartWrapper { init(container: HTMLDivElement) { this.instance = init(container) + this.instance.on("click", params => handleClick(params.value as _Value)) } render(value: { [date: string]: number }, days: string[], loading: { close: () => void }) { From 8f90975a2f7e4ef9b577993f0212c2f383be34b2 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sun, 3 Jul 2022 17:48:38 +0800 Subject: [PATCH 14/72] Clean code --- src/util/i18n/chrome/message.ts | 3 ++- src/util/i18n/components/app.ts | 2 +- src/util/i18n/components/calendar.ts | 2 +- src/util/i18n/components/content-script.ts | 2 +- src/util/i18n/components/context-menus.ts | 2 +- src/util/i18n/components/initial.ts | 2 +- src/util/i18n/components/item.ts | 2 +- src/util/i18n/components/locale.ts | 2 +- src/util/i18n/components/popup-duration.ts | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/util/i18n/chrome/message.ts b/src/util/i18n/chrome/message.ts index af696a49e..7fd068721 100644 --- a/src/util/i18n/chrome/message.ts +++ b/src/util/i18n/chrome/message.ts @@ -5,7 +5,8 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." + import appMessages, { AppMessage } from "../components/app" import contentScriptMessages, { ContentScriptMessage } from "../components/content-script" import contextMenusMessages, { ContextMenusMessage } from "../components/context-menus" diff --git a/src/util/i18n/components/app.ts b/src/util/i18n/components/app.ts index a55743a71..e8a02be63 100644 --- a/src/util/i18n/components/app.ts +++ b/src/util/i18n/components/app.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." export type AppMessage = { name: string diff --git a/src/util/i18n/components/calendar.ts b/src/util/i18n/components/calendar.ts index a37665396..715aaada9 100644 --- a/src/util/i18n/components/calendar.ts +++ b/src/util/i18n/components/calendar.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." export type CalendarMessage = { weekDays: string diff --git a/src/util/i18n/components/content-script.ts b/src/util/i18n/components/content-script.ts index b3441f4df..666eb9fc9 100644 --- a/src/util/i18n/components/content-script.ts +++ b/src/util/i18n/components/content-script.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." export type ContentScriptMessage = { openTimesConsoleLog: string diff --git a/src/util/i18n/components/context-menus.ts b/src/util/i18n/components/context-menus.ts index 4c6fe55d8..922966a42 100644 --- a/src/util/i18n/components/context-menus.ts +++ b/src/util/i18n/components/context-menus.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." /** * Used for menu diff --git a/src/util/i18n/components/initial.ts b/src/util/i18n/components/initial.ts index e06ea8c62..d71460215 100644 --- a/src/util/i18n/components/initial.ts +++ b/src/util/i18n/components/initial.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." /** * Locales for initial data diff --git a/src/util/i18n/components/item.ts b/src/util/i18n/components/item.ts index 6133aabd3..d067c2440 100644 --- a/src/util/i18n/components/item.ts +++ b/src/util/i18n/components/item.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." export type ItemMessage = { date: string diff --git a/src/util/i18n/components/locale.ts b/src/util/i18n/components/locale.ts index 062e1c99d..7d17f23a9 100644 --- a/src/util/i18n/components/locale.ts +++ b/src/util/i18n/components/locale.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." /** * Meta info of locales diff --git a/src/util/i18n/components/popup-duration.ts b/src/util/i18n/components/popup-duration.ts index 7662a7ec6..e453c76b3 100644 --- a/src/util/i18n/components/popup-duration.ts +++ b/src/util/i18n/components/popup-duration.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -import { Messages } from ".." +import type { Messages } from ".." export type PopupDurationMessage = { [key in timer.PopupDuration]: string } From 387d4ddf6a984dad363816d1302438f977c79d3f Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sun, 3 Jul 2022 17:58:58 +0800 Subject: [PATCH 15/72] Optimize alert in console --- src/content-script/printer.ts | 2 ++ src/util/i18n/chrome/message.ts | 1 + src/util/i18n/components/content-script.ts | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/src/content-script/printer.ts b/src/content-script/printer.ts index 9e085c4b8..19156a06b 100644 --- a/src/content-script/printer.ts +++ b/src/content-script/printer.ts @@ -28,6 +28,8 @@ export default async function printInfo(host: string) { const info1 = t2Chrome(root => root.message.usedTimeInConsoleLog) .replace('{focus}', formatPeriod(waste.focus, msg)) .replace('{total}', formatPeriod(waste.total, msg)) + const info2 = t2Chrome(root => root.message.closeAlert) console.log(info0) console.log(info1) + console.log(info2) } \ No newline at end of file diff --git a/src/util/i18n/chrome/message.ts b/src/util/i18n/chrome/message.ts index 7fd068721..1da710807 100644 --- a/src/util/i18n/chrome/message.ts +++ b/src/util/i18n/chrome/message.ts @@ -58,6 +58,7 @@ const placeholder: ChromeMessage = { message: { openTimesConsoleLog: '', usedTimeInConsoleLog: '', + closeAlert: '', timeWithHour: '', timeWithMinute: '', timeWithSecond: '', diff --git a/src/util/i18n/components/content-script.ts b/src/util/i18n/components/content-script.ts index 666eb9fc9..e82b58d4e 100644 --- a/src/util/i18n/components/content-script.ts +++ b/src/util/i18n/components/content-script.ts @@ -10,6 +10,7 @@ import type { Messages } from ".." export type ContentScriptMessage = { openTimesConsoleLog: string usedTimeInConsoleLog: string + closeAlert: string timeWithHour: string timeWithMinute: string timeWithSecond: string @@ -20,6 +21,7 @@ const _default: Messages = { zh_CN: { openTimesConsoleLog: '今天您打开了 {time} 次 {host}。', usedTimeInConsoleLog: '它今天在您的电脑上运行了 {total},其中您花费了 {focus}来浏览它。', + closeAlert: '你可以在【网费很贵】的选项中关闭以上提示!', timeWithHour: '{hour} 小时 {minute} 分 {second} 秒', timeWithMinute: '{minute} 分 {second} 秒', timeWithSecond: '{second} 秒', @@ -29,6 +31,7 @@ const _default: Messages = { zh_TW: { openTimesConsoleLog: '今天您打開了 {time} 次 {host}。', usedTimeInConsoleLog: '它今天在您的電腦上運行了 {total},其中您花費了 {focus}來瀏覽它。', + closeAlert: '你可以在【網費很貴】的選項中關閉以上提示!', timeWithHour: '{hour} 小時 {minute} 分 {second} 秒', timeWithMinute: '{minute} 分 {second} 秒', timeWithSecond: '{second} 秒', @@ -38,6 +41,7 @@ const _default: Messages = { en: { openTimesConsoleLog: 'You have open {host} for {time} time(s) today.', usedTimeInConsoleLog: 'And it runs on your PC for {total} today, and is browsed for {focus}.', + closeAlert: 'You can turn off the above tips in the option of Timer!', timeWithHour: '{hour} hour(s) {minute} minute(s) {second} second(s)', timeWithMinute: '{minute} minute(s) {second} second(s)', timeWithSecond: '{second} second(s)', @@ -47,6 +51,7 @@ const _default: Messages = { ja: { openTimesConsoleLog: '今日、あなたは {host} を {time} 回開きました。', usedTimeInConsoleLog: 'それは今日あなたのコンピュータで {total} 実行されました、そのうちあなたはそれを閲覧するために {focus} を費やしました。', + closeAlert: 'Timer のオプションで上記のヒントをオフにすることができます!', timeWithHour: '{hour} 時間 {minute} 分 {second} 秒', timeWithMinute: '{minute} 分 {second} 秒', timeWithSecond: '{second} 秒', From a5816b58b3cd22b1f532c53994fb5e958345f374 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sun, 3 Jul 2022 18:03:07 +0800 Subject: [PATCH 16/72] Optimize alias detector --- src/util/site.ts | 3 +-- test/util/site.test.ts | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/site.ts b/src/util/site.ts index 4fa75f279..f4fb083de 100644 --- a/src/util/site.ts +++ b/src/util/site.ts @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -const SEPARATORS = /[-\|–_]/ +const SEPARATORS = /[-\|–_::,]/ const INVALID_SITE_NAME = /(登录)|(我的)|(个人)|(主页)|(首页)/ @@ -34,5 +34,4 @@ export function extractSiteName(title: string, host?: string) { .sort?.((a, b) => a.length - b.length)[0] ?.trim() || undefined - } \ No newline at end of file diff --git a/test/util/site.test.ts b/test/util/site.test.ts index 12540f20e..e82fc2e0c 100644 --- a/test/util/site.test.ts +++ b/test/util/site.test.ts @@ -14,4 +14,6 @@ test('extract site name', () => { expect(extractSiteName('Office 365 登录 | Microsoft Office')).toEqual('Microsoft Office') expect(extractSiteName('首页 - 知乎')).toEqual('知乎') expect(extractSiteName('哔哩哔哩 (゜-゜)つロ 干杯~-bilibili', 'www.bilibili.com')).toEqual('bilibili') + + expect(extractSiteName('SurveyMonkey: The World’s Most Popular Free Online Survey Tool')).toEqual('SurveyMonkey') }) \ No newline at end of file From 80828799965d7ec8a344c6654d432c350d4d24dd Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Sun, 3 Jul 2022 18:03:23 +0800 Subject: [PATCH 17/72] v1.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8fc6d1eab..e050f80c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "timer", - "version": "1.1.0", + "version": "1.1.1", "description": "Web timer", "homepage": "https://github.com/sheepzh/timer", "scripts": { From c36105de13fbb342022d9f089c1d80a7d0769feb Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Mon, 4 Jul 2022 00:01:49 +0800 Subject: [PATCH 18/72] Fix color --- src/popup/components/chart/option.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popup/components/chart/option.ts b/src/popup/components/chart/option.ts index 1037340dc..d9c77e810 100644 --- a/src/popup/components/chart/option.ts +++ b/src/popup/components/chart/option.ts @@ -165,7 +165,7 @@ export function pieOptions(props: PipProps, container: HTMLDivElement): EcOption left: 15, top: 20, bottom: 20, - textStyle: { color: secondaryColor } + textStyle: { color: textColor } }, series: [{ ...staticOptions.series[0], From f08f53aebe45a1d0520b17fc54cd25a4d0ddfc92 Mon Sep 17 00:00:00 2001 From: "returnzhy1996@outlook.com" Date: Mon, 4 Jul 2022 00:34:24 +0800 Subject: [PATCH 19/72] Fix i18n of popup --- public/popup.html | 4 +- src/popup/components/footer/index.ts | 9 +- src/popup/components/footer/select/common.ts | 15 --- .../components/footer/select/time-select.ts | 104 ++++++++++-------- .../components/footer/select/type-select.ts | 7 +- src/popup/style/index.sass | 6 + 6 files changed, 75 insertions(+), 70 deletions(-) delete mode 100644 src/popup/components/footer/select/common.ts diff --git a/public/popup.html b/public/popup.html index 0af46d1b6..871c9ae3e 100644 --- a/public/popup.html +++ b/public/popup.html @@ -85,7 +85,7 @@ -