Skip to content

Commit

Permalink
Support daily and weekly visit limit (#336)
Browse files Browse the repository at this point in the history
Time limit related functions
  • Loading branch information
sheepzh authored Jan 17, 2025
2 parents cc014e6 + 4595783 commit 2e21d46
Show file tree
Hide file tree
Showing 102 changed files with 1,635 additions and 1,466 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"selectchanged",
"sheepzh",
"Treemap",
"Vnode",
"vueuse",
"webcomponents",
"webstore",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,4 @@
"engines": {
"node": ">=20"
}
}
}
4 changes: 3 additions & 1 deletion src/api/chrome/common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export function handleError(scene: string) {
export function handleError(scene: string): string {
try {
const lastError = chrome.runtime.lastError
lastError && console.log(`Errored when ${scene}: ${lastError.message}`)
return lastError?.message
} catch (e) {
console.info("Can't execute here")
}
return null
}
6 changes: 3 additions & 3 deletions src/api/chrome/tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ export function listTabs(query?: chrome.tabs.QueryInfo): Promise<ChromeTab[]> {
}))
}

export function sendMsg2Tab<T = any, R = any>(tabId: number, code: timer.mq.ReqCode, data: T): Promise<R> {
export function sendMsg2Tab<T = any, R = any>(tabId: number, code: timer.mq.ReqCode, data?: T): Promise<R> {
const request: timer.mq.Request<T> = { code, data }
return new Promise((resolve, reject) => {
chrome.tabs.sendMessage<timer.mq.Request<T>, timer.mq.Response>(tabId, request, response => {
handleError('sendMsg2Tab')
const sendError = handleError('sendMsg2Tab')
const resCode = response?.code
resCode === 'success' && resolve(response.data)
resCode === "fail" && reject(new Error(response?.msg))
reject(new Error(response?.msg ?? sendError ?? 'Unknown error'))
})
})
}
Expand Down
14 changes: 0 additions & 14 deletions src/background/content-script-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import { createTab } from "@api/chrome/tab"
import { ANALYSIS_ROUTE, LIMIT_ROUTE } from "@app/router/constants"
import whitelistHolder from "@service/components/whitelist-holder"
import itemService from "@service/item-service"
import limitService from "@service/limit-service"
import optionService from "@service/option-service"
import { getAppPageUrl } from "@util/constant/url"
Expand Down Expand Up @@ -44,26 +43,13 @@ const handleOpenLimitPage = (sender: ChromeMessageSender) => {
*/
export default function init(dispatcher: MessageDispatcher) {
dispatcher
// Increase the visit time
.register<string | { host: string, url: string }, void>('cs.incVisitCount', async (param) => {
let host: string, url: string = undefined
if (typeof param === 'string') {
host = param
} else {
host = param?.host
url = param?.url
}
itemService.addOneTime(host, url)
})
// Judge is in whitelist
.register<{ host?: string, url?: string }, boolean>('cs.isInWhitelist', ({ host, url } = {}) => whitelistHolder.contains(host, url))
// Need to print the information of today
.register<void, boolean>('cs.printTodayInfo', async () => {
const option = await optionService.getAllOption()
return !!option.printInConsole
})
// Get today info
.register<string, timer.core.Result>('cs.getTodayInfo', host => itemService.getResult(host, new Date()))
// cs.getLimitedRules
.register<string, timer.limit.Item[]>('cs.getLimitedRules', url => limitService.getLimited(url))
.register<string, timer.limit.Item[]>('cs.getRelatedRules', url => limitService.getRelated(url))
Expand Down
8 changes: 6 additions & 2 deletions src/background/limit-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,12 @@ const processAskHitVisit = async (item: timer.limit.Item) => {
tabs = tabs?.filter(({ url }) => matches(item?.cond, url))
const { visitTime = 0 } = item || {}
for (const { id } of tabs) {
const tabFocus = await sendMsg2Tab(id, "askVisitTime", undefined)
if (tabFocus && tabFocus > visitTime * MILL_PER_SECOND) return true
try {
const tabFocus = await sendMsg2Tab(id, "askVisitTime")
if (tabFocus && tabFocus > visitTime * MILL_PER_SECOND) return true
} catch {
// Ignored
}
}
return false
}
Expand Down
22 changes: 20 additions & 2 deletions src/background/timer/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ async function handleTime(host: string, url: string, dateRange: [number, number]
return focusTime
}

async function handleEvent(event: timer.core.Event, sender: ChromeMessageSender): Promise<void> {
async function handleTrackTimeEvent(event: timer.core.Event, sender: ChromeMessageSender): Promise<void> {
const { url, start, end, ignoreTabCheck } = event
const windowId = sender?.tab?.windowId
const tabId = sender?.tab?.id
Expand Down Expand Up @@ -58,6 +58,24 @@ async function sendLimitedMessage(items: timer.limit.Item[]) {
)
}

async function handleVisit(host: string, url: string) {
await itemService.addOneTime(host, url)
const metLimits = await limitService.incVisit(host, url)
// If time limited after this operation, send messages
metLimits?.length && sendLimitedMessage(metLimits)
}

async function handleIncVisitEvent(param: { host: string, url: string }): Promise<void> {
const { host, url } = param || {}
const { protocol } = extractHostname(url) || {}
if (protocol === "file" && !option.countLocalFiles) return
await handleVisit(host, url)

}

export default function initTrackServer(messageDispatcher: MessageDispatcher) {
messageDispatcher.register<timer.core.Event, void>('cs.trackTime', handleEvent)
messageDispatcher
.register<timer.core.Event, void>('cs.trackTime', handleTrackTimeEvent)
.register<{ host: string, url: string }, void>('cs.incVisitCount', handleIncVisitEvent)
.register<string, timer.core.Result>('cs.getTodayInfo', host => itemService.getResult(host, new Date()))
}
7 changes: 4 additions & 3 deletions src/content-script/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,14 @@ async function main() {
const isWhitelist = await sendMsg2Runtime('cs.isInWhitelist', { host, url })
if (isWhitelist) return

sendMsg2Runtime('cs.incVisitCount', { host, url })

await initLocale()
const needPrintInfo = await sendMsg2Runtime('cs.printTodayInfo')
!!needPrintInfo && printInfo(host)
injectPolyfill()
processLimit(url)
await processLimit(url)

// Increase visit count at the end
await sendMsg2Runtime('cs.incVisitCount', { host, url })
}

main()
13 changes: 3 additions & 10 deletions src/content-script/limit/common.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
export type LimitReason =
& Required<Pick<timer.limit.Rule, 'id' | 'cond'>>
& Pick<timer.limit.Item, 'allowDelay'>
& Partial<Pick<timer.limit.Item, 'delayCount'>>
& Partial<Pick<timer.limit.Item, 'delayCount' | 'allowDelay'>>
& {
type: LimitType
type: timer.limit.ReasonType
getVisitTime?: () => number
}

Expand All @@ -16,16 +15,10 @@ export function isSameReason(a: LimitReason, b: LimitReason): boolean {
return true
}

export type LimitType =
| "DAILY"
| "WEEKLY"
| "VISIT"
| "PERIOD"

export interface MaskModal {
addReason(...reasons: LimitReason[]): void
removeReason(...reasons: LimitReason[]): void
removeReasonsByType(...types: LimitType[]): void
removeReasonsByType(...types: timer.limit.ReasonType[]): void
addDelayHandler(handler: () => void): void
}

Expand Down
21 changes: 3 additions & 18 deletions src/content-script/limit/modal/Main.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
import { useRequest, useWindowVisible } from "@hooks"
import limitService from "@service/limit-service"
import { defineComponent, watch } from "vue"
import { defineComponent } from "vue"
import Alert from "./components/Alert"
import Footer from "./components/Footer"
import Reason from "./components/Reason"
import { provideRule, useReason } from "./context"
import { provideRule } from "./context"
import "./style"

const _default = defineComponent(() => {
const reason = useReason()
const windowVisible = useWindowVisible()

const { data: rule, refresh } = useRequest(async () => {
if (!windowVisible.value) return null
const reasonId = reason.value?.id
if (!reasonId) return null
const rules = await limitService.select({ id: reasonId, filterDisabled: false })
return rules?.[0]
})

provideRule(rule)

watch([reason, windowVisible], refresh)
provideRule()

return () => (
<div id="app">
Expand Down
28 changes: 21 additions & 7 deletions src/content-script/limit/modal/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { sendMsg2Runtime } from "@api/chrome/runtime"
import Trend from "@app/Layout/icons/Trend"
import { judgeVerificationRequired, processVerification } from "@app/util/limit"
import { type LimitType } from "@cs/limit/common"
import { TAG_NAME } from "@cs/limit/element"
import { t } from "@cs/locale"
import { Plus, Timer } from "@element-plus/icons-vue"
import optionService from "@service/option-service"
import { meetTimeLimit } from "@util/limit"
import { ElButton } from "element-plus"
import { computed, defineComponent } from "vue"
import { useDelayHandler, useReason, useRule } from "../context"

const DELAY_ENABLED: LimitType[] = ['DAILY', 'VISIT', 'WEEKLY']

async function handleMore5Minutes(rule: timer.limit.Item, callback: () => void) {
let promise: Promise<void> = undefined
const ele = document.querySelector(TAG_NAME).shadowRoot.querySelector('body')
Expand All @@ -27,9 +25,25 @@ async function handleMore5Minutes(rule: timer.limit.Item, callback: () => void)
const _default = defineComponent(() => {
const reason = useReason()
const rule = useRule()
const allowDelay = computed(() => {
const { type, allowDelay } = reason.value || {}
return DELAY_ENABLED.includes(type) && allowDelay
const showDelay = computed(() => {
const { type, allowDelay, delayCount } = reason.value || {}
if (!allowDelay) return false

const { time, weekly, visit, waste, weeklyWaste } = rule.value || {}
let realLimit = 0, realWaste = 0
if (type === 'DAILY') {
realLimit = time
realWaste = waste
} else if (type === 'WEEKLY') {
realLimit = weekly
realWaste = weeklyWaste
} else if (type === 'VISIT') {
realLimit = visit
realWaste = reason.value?.getVisitTime?.()
} else {
return false
}
return meetTimeLimit(realLimit, realWaste, allowDelay, delayCount)
})

const delayHandler = useDelayHandler()
Expand All @@ -45,7 +59,7 @@ const _default = defineComponent(() => {
{t(msg => msg.menu.siteAnalysis)}
</ElButton>
<ElButton
v-show={allowDelay.value}
v-show={showDelay.value}
type="primary"
round
icon={<Plus />}
Expand Down
Loading

0 comments on commit 2e21d46

Please sign in to comment.