Skip to content

Commit bb024f6

Browse files
committed
Open the limit page on the right of current tab (#236)
1 parent 83256a1 commit bb024f6

File tree

7 files changed

+94
-60
lines changed

7 files changed

+94
-60
lines changed

src/api/chrome/tab.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,25 @@ export function getTab(id: number): Promise<ChromeTab> {
1414
}))
1515
}
1616

17+
export function resetTabUrl(tabId: number, url: string): Promise<void> {
18+
return new Promise(resolve => chrome.tabs.update(tabId, {
19+
url: url,
20+
highlighted: true,
21+
}, () => resolve()))
22+
}
23+
24+
export async function getRightOf(target: ChromeTab): Promise<ChromeTab> {
25+
if (!target) return null
26+
const { windowId, index } = target
27+
return new Promise(resolve => chrome.tabs.query({ windowId }, tabs => {
28+
const rightTab = tabs
29+
?.sort?.((a, b) => (a?.index ?? -1) - (b?.index ?? -1))
30+
?.filter?.(t => t.index > index)
31+
?.[0]
32+
resolve(rightTab)
33+
}))
34+
}
35+
1736
export function getCurrentTab(): Promise<ChromeTab> {
1837
return new Promise(resolve => chrome.tabs.getCurrent(tab => {
1938
handleError("getCurrentTab")
@@ -34,13 +53,15 @@ export function createTab(param: chrome.tabs.CreateProperties | string): Promise
3453
*
3554
* Must not be invocked in background.js
3655
*/
37-
export async function createTabAfterCurrent(url: string): Promise<ChromeTab> {
38-
const tab = await getCurrentTab()
39-
if (!tab) {
56+
export async function createTabAfterCurrent(url: string, currentTab?: ChromeTab): Promise<ChromeTab> {
57+
if (!currentTab) {
58+
currentTab = await getCurrentTab()
59+
}
60+
if (!currentTab) {
4061
// Current tab not found
4162
return createTab(url)
4263
} else {
43-
const { windowId, index: currentIndex } = tab
64+
const { windowId, index: currentIndex } = currentTab
4465
return createTab({
4566
url,
4667
windowId,

src/app/components/common/input-filter-item.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ import { defineComponent, h, Ref, ref } from "vue"
1111
const _default = defineComponent({
1212
name: "InputFilterItem",
1313
props: {
14-
placeholder: String
14+
defaultValue: {
15+
type: String,
16+
required: false,
17+
},
18+
placeholder: String,
1519
},
1620
emits: {
1721
search: (_text: string) => true
1822
},
1923
setup(props, ctx) {
20-
const modelValue: Ref<string> = ref("")
24+
const modelValue: Ref<string> = ref(props.defaultValue)
2125
return () => h(ElInput, {
2226
class: 'filter-item',
2327
modelValue: modelValue.value,

src/app/components/dashboard/components/calendar-heat-map.ts

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,24 @@ function getXAxisLabelMap(data: _Value[]): { [x: string]: string } {
109109
return result
110110
}
111111

112+
const titleText = (totalHours: number) => t(msg => totalHours
113+
? msg.dashboard.heatMap.title0
114+
: msg.dashboard.heatMap.title1,
115+
{ hour: totalHours }
116+
)
117+
118+
type HeatmapItem = HeatmapSeriesOption["data"][number]
119+
120+
const cvtHeatmapItem = (d: _Value): HeatmapItem => {
121+
let item: HeatmapItem = { value: d, itemStyle: undefined, label: undefined, emphasis: undefined }
122+
const minutes = d[2]
123+
if (!minutes) {
124+
item.itemStyle = { color: 'transparent' }
125+
item.emphasis = { disabled: true }
126+
}
127+
return item
128+
}
129+
112130
function optionOf(data: _Value[], days: string[]): EcOption {
113131
const totalMinutes = data.map(d => d[2] || 0).reduce((a, b) => a + b, 0)
114132
const totalHours = Math.floor(totalMinutes / 60)
@@ -117,15 +135,8 @@ function optionOf(data: _Value[], days: string[]): EcOption {
117135
return {
118136
title: {
119137
...BASE_TITLE_OPTION,
120-
text: t(msg => totalHours
121-
? msg.dashboard.heatMap.title0
122-
: msg.dashboard.heatMap.title1,
123-
{ hour: totalHours }
124-
),
125-
textStyle: {
126-
fontSize: '14px',
127-
color: textColor
128-
}
138+
text: titleText(totalHours),
139+
textStyle: { fontSize: '14px', color: textColor }
129140
},
130141
tooltip: {
131142
position: 'top',
@@ -145,16 +156,13 @@ function optionOf(data: _Value[], days: string[]): EcOption {
145156
formatter: (x: string) => xAxisLabelMap[x] || '',
146157
interval: 0,
147158
margin: 14,
148-
color: textColor
159+
color: textColor,
149160
},
150161
},
151162
yAxis: {
152163
type: 'category',
153164
data: days,
154-
axisLabel: {
155-
padding: /* T R B L */[0, 12, 0, 0],
156-
color: textColor
157-
},
165+
axisLabel: { padding: /* T R B L */[0, 12, 0, 0], color: textColor },
158166
axisLine: { show: false },
159167
axisTick: { show: false, alignWithLabel: true }
160168
},
@@ -168,28 +176,12 @@ function optionOf(data: _Value[], days: string[]): EcOption {
168176
right: '2%',
169177
top: 'center',
170178
dimension: 2,
171-
textStyle: {
172-
color: textColor
173-
}
179+
textStyle: { color: textColor }
174180
}],
175181
series: [{
176182
name: 'Daily Focus',
177183
type: 'heatmap',
178-
data: data.map(d => {
179-
let item = { value: d, itemStyle: undefined, label: undefined, emphasis: undefined, tooltip: undefined, silent: false }
180-
const minutes = d[2]
181-
if (minutes) {
182-
} else {
183-
item.itemStyle = {
184-
color: 'transparent',
185-
}
186-
item.emphasis = {
187-
disabled: true
188-
}
189-
item.silent = true
190-
}
191-
return item
192-
}),
184+
data: data.map(cvtHeatmapItem),
193185
progressive: 5,
194186
progressiveThreshold: 10,
195187
}]

src/app/components/habit/component/chart/wrapper.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,19 @@ function getYAxiasValue(milliseconds: number, periodSize: number) {
8989
return periodSize === 8 ? hours : minutes
9090
}
9191

92+
type BarItem = BarSeriesOption["data"][number]
93+
94+
const cvt2Item = (row: timer.period.Row, periodSize: number): BarItem => {
95+
const startTime = row.startTime.getTime()
96+
const endTime = row.endTime.getTime()
97+
const x = (startTime + endTime) / 2
98+
const milliseconds = row.milliseconds
99+
return [x, getYAxiasValue(milliseconds, periodSize), startTime, endTime, milliseconds]
100+
}
101+
92102
function generateOptions(data: timer.period.Row[], averageByDate: boolean, periodSize: number): EcOption {
93103
const periodData: timer.period.Row[] = averageByDate ? averageByDay(data, periodSize) : data
94-
const valueData: any[] = []
95-
periodData.forEach((item) => {
96-
const startTime = item.startTime.getTime()
97-
const endTime = item.endTime.getTime()
98-
const x = (startTime + endTime) / 2
99-
const milliseconds = item.milliseconds
100-
valueData.push([x, getYAxiasValue(milliseconds, periodSize), startTime, endTime, milliseconds])
101-
})
102-
104+
const valueData: BarItem[] = periodData.map(i => cvt2Item(i, periodSize))
103105
const xAxisMin = periodData[0].startTime.getTime()
104106
const xAxisMax = periodData[periodData.length - 1].endTime.getTime()
105107
const xAxisAxisLabelFormatter = averageByDate ? '{HH}:{mm}' : formatXAxis
@@ -123,9 +125,7 @@ function generateOptions(data: timer.period.Row[], averageByDate: boolean, perio
123125
name: TITLE, // file name
124126
excludeComponents: ['toolbox'],
125127
pixelRatio: 1,
126-
iconStyle: {
127-
borderColor: secondaryTextColor
128-
}
128+
iconStyle: { borderColor: secondaryTextColor }
129129
}
130130
}
131131
},

src/app/components/limit/filter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const _default = defineComponent({
4444
})
4545
return () => [
4646
h(InputFilterItem, {
47+
defaultValue: props.url,
4748
placeholder: urlPlaceholder,
4849
onSearch(searchVal: string) {
4950
url.value = searchVal

src/app/components/limit/index.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,15 @@ import { t } from "@app/locale"
1717
import { ElMessage } from "element-plus"
1818
import { handleWindowVisibleChange } from "@util/window"
1919

20+
const initialUrl = () => {
21+
// Init with url parameter
22+
const urlParam = useRoute().query['url'] as string
23+
useRouter().replace({ query: {} })
24+
return urlParam ? decodeURIComponent(urlParam) : ''
25+
}
26+
2027
const _default = defineComponent(() => {
21-
const url: Ref<string> = ref('')
28+
const url: Ref<string> = ref(initialUrl())
2229
const onlyEnabled: Ref<boolean> = ref(false)
2330
const data: Ref<timer.limit.Item[]> = ref([])
2431
// Init and query
@@ -29,10 +36,6 @@ const _default = defineComponent(() => {
2936
queryData()
3037
// Query data if the window become visible
3138
handleWindowVisibleChange(queryData)
32-
// Init with url parameter
33-
const urlParam = useRoute().query['url'] as string
34-
useRouter().replace({ query: {} })
35-
urlParam && (url.value = decodeURIComponent(urlParam))
3639

3740
const modify: Ref<ModifyInstance> = ref()
3841
const test: Ref<TestInstance> = ref()

src/background/limit-processor.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
* https://opensource.org/licenses/MIT
66
*/
77

8-
import { createTab, listTabs, sendMsg2Tab } from "@api/chrome/tab"
8+
import { createTabAfterCurrent, getRightOf, listTabs, resetTabUrl, sendMsg2Tab } from "@api/chrome/tab"
99
import { LIMIT_ROUTE } from "@app/router/constants"
1010
import { getAppPageUrl } from "@util/constant/url"
1111
import MessageDispatcher from "./message-dispatcher"
1212
import { matches } from "@util/limit"
1313
import limitService from "@service/limit-service"
14+
import { isBrowserUrl } from "@util/pattern"
1415

1516
function processLimitWaking(rules: timer.limit.Item[], tab: ChromeTab) {
1617
const { url } = tab
@@ -23,12 +24,24 @@ function processLimitWaking(rules: timer.limit.Item[], tab: ChromeTab) {
2324
.catch(err => console.error(`Failed to wake with limit rule: rules=${JSON.stringify(rules)}, msg=${err.msg}`))
2425
}
2526

27+
async function processOpenPage(limittedUrl: string, sender: ChromeMessageSender) {
28+
const originTab = sender?.tab
29+
if (!originTab) return
30+
const realUrl = getAppPageUrl(true, LIMIT_ROUTE, { url: encodeURI(limittedUrl) })
31+
const baseUrl = getAppPageUrl(true, LIMIT_ROUTE)
32+
const rightTab = await getRightOf(originTab)
33+
const rightUrl = rightTab?.url
34+
if (rightUrl && isBrowserUrl(rightUrl) && rightUrl.includes(baseUrl)) {
35+
// Reset url
36+
await resetTabUrl(rightTab.id, realUrl)
37+
} else {
38+
await createTabAfterCurrent(realUrl, sender?.tab)
39+
}
40+
}
41+
2642
export default function init(dispatcher: MessageDispatcher) {
2743
dispatcher
28-
.register<string>(
29-
'openLimitPage',
30-
(url: string) => createTab(getAppPageUrl(true, LIMIT_ROUTE, { url: encodeURI(url) }))
31-
)
44+
.register<string>('openLimitPage', processOpenPage)
3245
.register<timer.limit.Item[]>(
3346
'limitWaking',
3447
async data => {

0 commit comments

Comments
 (0)