-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
sidebar-indicators@page.js
125 lines (103 loc) · 3.17 KB
/
sidebar-indicators@page.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import select from 'select-dom'
import { on, off } from 'delegated-events'
import {
NEW_TIMESTAMP_STORAGE_KEY,
NEW_TIMESTAMP_STORAGE_AREA_NAME,
READ_TIMESTAMP_STORAGE_KEY,
READ_TIMESTAMP_STORAGE_AREA_NAME,
SAVED_SEARCHES_READ,
SAVED_SEARCHES_WRITE,
} from './constants'
import bridge from '@page/environment/bridge'
import getLoggedInUserId from '@libs/getLoggedInUserId'
import { STORAGE_CHANGED } from '@constants'
const CLASSNAME_INDICATOR = 'sf-new'
const SELECTOR_KEYWORD_LINK = '#savedsearchs > ul > li > a'
export default context => {
const {
elementCollection,
registerBroadcastListener,
unregisterBroadcastListener,
} = context
elementCollection.add({
savedSearchs: '#savedsearchs',
})
async function checkUnread(keyword) {
const userId = getLoggedInUserId()
const hasUnread = await bridge.postMessage({
action: SAVED_SEARCHES_READ,
payload: { userId, keyword },
})
return hasUnread
}
async function markAsRead(keyword) {
const userId = getLoggedInUserId()
await bridge.postMessage({
action: SAVED_SEARCHES_WRITE,
payload: { userId, keyword },
})
}
function getItems() {
const savedSearchs = elementCollection.get('savedSearchs')
// 这些元素不缓存在 ElementCollection 里,因为可能会动态增删
const elements = select.all('.label', savedSearchs)
return elements.map(element => ({
element,
keyword: element.textContent.trim(),
}))
}
async function updateIndicators() {
for (const { element, keyword } of getItems()) {
const hasNew = await checkUnread(keyword)
element.classList.toggle(CLASSNAME_INDICATOR, hasNew)
}
}
function removeIndicators() {
for (const { element } of getItems()) {
element.classList.remove(CLASSNAME_INDICATOR)
}
}
async function onClick(event) {
const keywordLink = event.path.find(element => element.matches(SELECTOR_KEYWORD_LINK))
const label = select('.label', keywordLink)
const keyword = label.textContent.trim()
label.classList.remove(CLASSNAME_INDICATOR)
await markAsRead(keyword)
await updateIndicators()
}
function isRelatedStorageChange({ storageAreaName, key }) {
if (
storageAreaName === NEW_TIMESTAMP_STORAGE_AREA_NAME &&
key === NEW_TIMESTAMP_STORAGE_KEY
) {
return true
}
if (
storageAreaName === READ_TIMESTAMP_STORAGE_AREA_NAME &&
key === READ_TIMESTAMP_STORAGE_KEY
) {
return true
}
return false
}
function onBroadcast({ action, payload }) {
if (action === STORAGE_CHANGED && isRelatedStorageChange(payload)) {
updateIndicators()
}
}
return {
applyWhen: () => elementCollection.ready('savedSearchs'),
onLoad() {
updateIndicators()
// 饭否也使用了事件代理,并且使用了 event.stopPropagation()
// 我们使用 capture 避免受影响
on('click', SELECTOR_KEYWORD_LINK, onClick, { capture: true })
registerBroadcastListener(onBroadcast)
},
onUnload() {
removeIndicators()
off('click', SELECTOR_KEYWORD_LINK, onClick, { capture: true })
unregisterBroadcastListener(onBroadcast)
},
}
}