-
Notifications
You must be signed in to change notification settings - Fork 137
/
Copy pathUnread.coffee
266 lines (233 loc) · 8.59 KB
/
Unread.coffee
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
Unread =
init: ->
return unless g.VIEW is 'thread' and (
Conf['Unread Count'] or
Conf['Unread Favicon'] or
Conf['Unread Line'] or
Conf['Remember Last Read Post'] or
Conf['Desktop Notifications'] or
Conf['Quote Threading']
)
if Conf['Remember Last Read Post']
$.sync 'Remember Last Read Post', (enabled) -> Conf['Remember Last Read Post'] = enabled
@db = new DataBoard 'lastReadPosts', @sync
@hr = $.el 'hr',
id: 'unread-line'
className: 'unread-line'
@posts = new Set()
@postsQuotingYou = new Set()
@order = new RandomAccessList()
@position = null
Callbacks.Thread.push
name: 'Unread'
cb: @node
Callbacks.Post.push
name: 'Unread'
cb: @addPost
node: ->
Unread.thread = @
Unread.title = d.title
Unread.lastReadPost = Unread.db?.get(
boardID: @board.ID
threadID: @ID
) or 0
Unread.readCount = 0
Unread.readCount++ for ID in @posts.keys when +ID <= Unread.lastReadPost
$.one d, '4chanXInitFinished', Unread.ready
$.on d, 'PostsInserted', Unread.onUpdate
$.on d, 'ThreadUpdate', (e) -> Unread.update() if e.detail[404]
resetLink = $.el 'a',
href: 'javascript:;'
className: 'unread-reset'
textContent: 'Mark all unread'
$.on resetLink, 'click', Unread.reset
Header.menu.addEntry
el: resetLink
order: 70
ready: ->
Unread.scroll() if Conf['Remember Last Read Post'] and Conf['Scroll to Last Read Post']
Unread.setLine true
Unread.read()
Unread.update()
$.on d, 'scroll visibilitychange', Unread.read
$.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line']
positionPrev: ->
if Unread.position then Unread.position.prev else Unread.order.last
scroll: ->
# Let the header's onload callback handle it.
return if (hash = location.hash.match /\d+/) and hash[0] of Unread.thread.posts
position = Unread.positionPrev()
while position
{bottom} = position.data.nodes
if !bottom.getBoundingClientRect().height
# Don't try to scroll to posts with display: none
position = position.prev
else
Header.scrollToIfNeeded bottom, true
break
return
reset: ->
return unless Unread.lastReadPost?
Unread.posts = new Set()
Unread.postsQuotingYou = new Set()
Unread.order = new RandomAccessList()
Unread.position = null
Unread.lastReadPost = 0
Unread.readCount = 0
Unread.thread.posts.forEach (post) -> Unread.addPost.call post
$.forceSync 'Remember Last Read Post'
if Conf['Remember Last Read Post'] and (!Unread.thread.isDead or Unread.thread.isArchived)
Unread.db.set
boardID: Unread.thread.board.ID
threadID: Unread.thread.ID
val: 0
Unread.updatePosition()
Unread.setLine()
Unread.update()
sync: ->
return unless Unread.lastReadPost?
lastReadPost = Unread.db.get
boardID: Unread.thread.board.ID
threadID: Unread.thread.ID
defaultValue: 0
return unless Unread.lastReadPost < lastReadPost
Unread.lastReadPost = lastReadPost
postIDs = Unread.thread.posts.keys
for i in [Unread.readCount...postIDs.length] by 1
ID = +postIDs[i]
unless Unread.thread.posts.get(ID).isFetchedQuote
break if ID > Unread.lastReadPost
Unread.posts.delete ID
Unread.postsQuotingYou.delete ID
Unread.readCount++
Unread.updatePosition()
Unread.setLine()
Unread.update()
addPost: ->
return if @isFetchedQuote or @isClone
Unread.order.push @
return if @ID <= Unread.lastReadPost or @isHidden or QuoteYou.isYou(@)
Unread.posts.add (Unread.posts.last = @ID)
Unread.addPostQuotingYou @
Unread.position ?= Unread.order[@ID]
addPostQuotingYou: (post) ->
for quotelink in post.nodes.quotelinks when QuoteYou.db?.get Get.postDataFromLink quotelink
Unread.postsQuotingYou.add (Unread.postsQuotingYou.last = post.ID)
Unread.openNotification post
return
openNotification: (post, predicate=' replied to you') ->
return unless Header.areNotificationsEnabled
notif = new Notification "#{post.info.nameBlock}#{predicate}",
body: post.commentDisplay()
icon: Favicon.logo
notif.onclick = ->
Header.scrollToIfNeeded post.nodes.bottom, true
window.focus()
notif.onshow = ->
setTimeout ->
notif.close()
, 7 * $.SECOND
onUpdate: ->
$.queueTask -> # ThreadUpdater may scroll immediately after inserting posts
Unread.setLine()
Unread.read()
Unread.update()
readSinglePost: (post) ->
{ID} = post
return unless Unread.posts.has ID
Unread.posts.delete ID
Unread.postsQuotingYou.delete ID
Unread.updatePosition()
Unread.saveLastReadPost()
Unread.update()
read: $.debounce 100, (e) ->
# Update the lastReadPost when hidden posts are added to the thread.
if !Unread.posts.size and Unread.readCount isnt Unread.thread.posts.keys.length
Unread.saveLastReadPost()
return if d.hidden or !Unread.posts.size
count = 0
while Unread.position
{ID, data} = Unread.position
{bottom} = data.nodes
break unless !bottom.getBoundingClientRect().height or # post has been hidden
Header.getBottomOf(bottom) > -1 # post is completely read
count++
Unread.posts.delete ID
Unread.postsQuotingYou.delete ID
Unread.position = Unread.position.next
return unless count
Unread.updatePosition()
Unread.saveLastReadPost()
(Unread.update() if e)
updatePosition: ->
while Unread.position and !Unread.posts.has Unread.position.ID
Unread.position = Unread.position.next
return
saveLastReadPost: $.debounce 2 * $.SECOND, ->
$.forceSync 'Remember Last Read Post'
return unless Conf['Remember Last Read Post'] and Unread.db
postIDs = Unread.thread.posts.keys
for i in [Unread.readCount...postIDs.length] by 1
ID = +postIDs[i]
unless Unread.thread.posts.get(ID).isFetchedQuote
break if Unread.posts.has ID
Unread.lastReadPost = ID
Unread.readCount++
return if Unread.thread.isDead and !Unread.thread.isArchived
Unread.db.set
boardID: Unread.thread.board.ID
threadID: Unread.thread.ID
val: Unread.lastReadPost
setLine: (force) ->
return unless Conf['Unread Line']
if Unread.hr.hidden or d.hidden or (force is true)
oldPosition = Unread.linePosition
if (Unread.linePosition = Unread.positionPrev())
if Unread.linePosition isnt oldPosition
node = Unread.linePosition.data.nodes.bottom
node = node.nextSibling if node.nextSibling?.tagName is 'BR'
$.after node, Unread.hr
else
$.rm Unread.hr
Unread.hr.hidden = Unread.linePosition is Unread.order.last
update: ->
count = Unread.posts.size
countQuotingYou = Unread.postsQuotingYou.size
if Conf['Unread Count']
titleQuotingYou = if Conf['Quoted Title'] and countQuotingYou then '(!) ' else ''
titleCount = if count or !Conf['Hide Unread Count at (0)'] then "(#{count}) " else ''
titleDead = if Unread.thread.isDead
Unread.title.replace '-', (if Unread.thread.isArchived then '- Archived -' else '- 404 -')
else
Unread.title
d.title = "#{titleQuotingYou}#{titleCount}#{titleDead}"
Unread.saveThreadWatcherCount()
if Conf['Unread Favicon'] and g.SITE.software is 'yotsuba'
{isDead} = Unread.thread
Favicon.set (
if countQuotingYou
(if isDead then 'unreadDeadY' else 'unreadY')
else if count
(if isDead then 'unreadDead' else 'unread')
else
(if isDead then 'dead' else 'default')
)
saveThreadWatcherCount: $.debounce 2 * $.SECOND, ->
$.forceSync 'Remember Last Read Post'
if Conf['Remember Last Read Post'] and (!Unread.thread.isDead or Unread.thread.isArchived)
quotingYou = if !Conf['Require OP Quote Link'] and QuoteYou.isYou(Unread.thread.OP) then Unread.posts else Unread.postsQuotingYou
if !quotingYou.size
quotingYou.last = 0
else if !quotingYou.has(quotingYou.last)
quotingYou.last = 0
posts = Unread.thread.posts.keys
for i in [posts.length - 1 .. 0] by -1
if quotingYou.has(+posts[i])
quotingYou.last = posts[i]
break
ThreadWatcher.update g.SITE.ID, Unread.thread.board.ID, Unread.thread.ID,
last: Unread.thread.lastPost
isDead: Unread.thread.isDead
isArchived: Unread.thread.isArchived
unread: Unread.posts.size
quotingYou: (quotingYou.last or 0)