205 lines
6.2 KiB
CoffeeScript
Executable File
205 lines
6.2 KiB
CoffeeScript
Executable File
Unread =
|
|
init: ->
|
|
return if g.VIEW isnt 'thread' or !Conf['Unread Count'] and !Conf['Unread Favicon'] and !Conf['Desktop Notifications']
|
|
|
|
@db = new DataBoard 'lastReadPosts', @sync
|
|
@hr = $.el 'hr',
|
|
id: 'unread-line'
|
|
@posts = new RandomAccessList
|
|
@postsQuotingYou = []
|
|
|
|
Thread.callbacks.push
|
|
name: 'Unread'
|
|
cb: @node
|
|
|
|
node: ->
|
|
Unread.thread = @
|
|
Unread.title = d.title
|
|
Unread.lastReadPost = Unread.db.get
|
|
boardID: @board.ID
|
|
threadID: @ID
|
|
defaultValue: 0
|
|
$.on d, '4chanXInitFinished', Unread.ready
|
|
$.on d, 'ThreadUpdate', Unread.onUpdate
|
|
$.on d, 'scroll visibilitychange', Unread.read
|
|
$.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line']
|
|
|
|
ready: ->
|
|
$.off d, '4chanXInitFinished', Unread.ready
|
|
posts = []
|
|
posts.push post for ID, post of Unread.thread.posts when post.isReply
|
|
Unread.addPosts posts
|
|
QuoteThreading.force() if Conf['Quote Threading']
|
|
Unread.scroll() if Conf['Scroll to Last Read Post']
|
|
|
|
scroll: ->
|
|
# Let the header's onload callback handle it.
|
|
return if (hash = location.hash.match /\d+/) and hash[0] of Unread.thread.posts
|
|
if post = Unread.posts.first
|
|
# Scroll to a non-hidden, non-OP post that's before the first unread post.
|
|
while root = $.x 'preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root
|
|
break unless (post = Get.postFromRoot root).isHidden
|
|
return unless root
|
|
down = true
|
|
else
|
|
# Scroll to the last read post.
|
|
posts = Object.keys Unread.thread.posts
|
|
{root} = Unread.thread.posts[posts[posts.length - 1]].nodes
|
|
|
|
# Scroll to the target unless we scrolled past it.
|
|
Header.scrollTo root, down if Header.getBottomOf(root) < 0
|
|
|
|
sync: ->
|
|
lastReadPost = Unread.db.get
|
|
boardID: Unread.thread.board.ID
|
|
threadID: Unread.thread.ID
|
|
defaultValue: 0
|
|
return unless Unread.lastReadPost < lastReadPost
|
|
Unread.lastReadPost = lastReadPost
|
|
|
|
post = Unread.posts.first
|
|
while post
|
|
break if ({ID} = post) > Unread.lastReadPost
|
|
post = post.next
|
|
Unread.posts.rm ID
|
|
|
|
Unread.readArray Unread.postsQuotingYou
|
|
Unread.setLine() if Conf['Unread Line']
|
|
Unread.update()
|
|
|
|
addPosts: (posts) ->
|
|
for post in posts
|
|
{ID} = post
|
|
continue if ID <= Unread.lastReadPost or post.isHidden or QR.db.get {
|
|
boardID: post.board.ID
|
|
threadID: post.thread.ID
|
|
postID: ID
|
|
}
|
|
Unread.posts.push post unless post.prev or post.next
|
|
Unread.addPostQuotingYou post
|
|
if Conf['Unread Line']
|
|
# Force line on visible threads if there were no unread posts previously.
|
|
Unread.setLine Unread.posts.first in posts
|
|
Unread.read()
|
|
Unread.update()
|
|
|
|
addPostQuotingYou: (post) ->
|
|
return unless QR.db
|
|
for quotelink in post.nodes.quotelinks when QR.db.get Get.postDataFromLink quotelink
|
|
Unread.postsQuotingYou.push post
|
|
Unread.openNotification post
|
|
return
|
|
|
|
openNotification: (post) ->
|
|
return unless Header.areNotificationsEnabled
|
|
name = if Conf['Anonymize']
|
|
'Anonymous'
|
|
else
|
|
$('.nameBlock', post.nodes.info).textContent.trim()
|
|
notif = new Notification "#{name} replied to you",
|
|
body: post.info.comment
|
|
icon: Favicon.logo
|
|
notif.onclick = ->
|
|
Header.scrollToIfNeeded post.nodes.root, true
|
|
window.focus()
|
|
notif.onshow = ->
|
|
setTimeout ->
|
|
notif.close()
|
|
, 7 * $.SECOND
|
|
|
|
onUpdate: (e) ->
|
|
if e.detail[404]
|
|
Unread.update()
|
|
else
|
|
Unread.addPosts e.detail.newPosts
|
|
|
|
readSinglePost: (post) ->
|
|
{ID} = post
|
|
return unless Unread.posts[ID]
|
|
if post is Unread.posts.first
|
|
Unread.lastReadPost = ID
|
|
Unread.saveLastReadPost()
|
|
Unread.posts.rm ID
|
|
if (i = Unread.postsQuotingYou.indexOf post) isnt -1
|
|
Unread.postsQuotingYou.splice i, 1
|
|
Unread.update()
|
|
|
|
readArray: (arr) ->
|
|
for post, i in arr
|
|
break if post.ID > Unread.lastReadPost
|
|
arr.splice 0, i
|
|
|
|
read: $.debounce 100, (e) ->
|
|
return if d.hidden or !Unread.posts.length
|
|
height = doc.clientHeight
|
|
|
|
{posts} = Unread
|
|
while post = posts.first
|
|
break unless Header.getBottomOf(post.nodes.root) > -1 # post is not completely read
|
|
{ID} = post
|
|
posts.rm ID
|
|
|
|
if Conf['Mark Quotes of You'] and post.info.yours
|
|
QuoteYou.lastRead = post.nodes.root
|
|
|
|
return unless ID
|
|
|
|
Unread.lastReadPost = ID if Unread.lastReadPost < ID or !Unread.lastReadPost
|
|
Unread.saveLastReadPost()
|
|
Unread.readArray Unread.postsQuotingYou
|
|
Unread.update() if e
|
|
|
|
saveLastReadPost: $.debounce 2 * $.SECOND, ->
|
|
return if Unread.thread.isDead
|
|
Unread.db.set
|
|
boardID: Unread.thread.board.ID
|
|
threadID: Unread.thread.ID
|
|
val: Unread.lastReadPost
|
|
|
|
setLine: (force) ->
|
|
return unless d.hidden or force is true
|
|
return $.rm Unread.hr unless post = Unread.posts.first
|
|
if $.x 'preceding-sibling::div[contains(@class,"replyContainer")]', post.nodes.root # not the first reply
|
|
$.before post.nodes.root, Unread.hr
|
|
|
|
update: <% if (type === 'crx') { %>(dontrepeat) <% } %>->
|
|
count = Unread.posts.length
|
|
|
|
if Conf['Unread Count']
|
|
d.title = "#{if Conf['Quoted Title'] and Unread.postsQuotingYou.length then '(!) ' else ''}#{if count or !Conf['Hide Unread Count at (0)'] then "(#{count}) " else ''}#{if g.DEAD then "/#{g.BOARD}/ - 404" else "#{Unread.title}"}"
|
|
<% if (type === 'crx') { %>
|
|
# XXX Chrome bug where it doesn't always update the tab title.
|
|
# crbug.com/124381
|
|
# Call it one second later,
|
|
# but don't display outdated unread count.
|
|
return if dontrepeat
|
|
setTimeout ->
|
|
d.title = ''
|
|
Unread.update true
|
|
, $.SECOND
|
|
<% } %>
|
|
|
|
return unless Conf['Unread Favicon']
|
|
|
|
Favicon.el.href =
|
|
if g.DEAD
|
|
if Unread.postsQuotingYou[0]
|
|
Favicon.unreadDeadY
|
|
else if count
|
|
Favicon.unreadDead
|
|
else
|
|
Favicon.dead
|
|
else
|
|
if count
|
|
if Unread.postsQuotingYou[0]
|
|
Favicon.unreadY
|
|
else
|
|
Favicon.unread
|
|
else
|
|
Favicon.default
|
|
|
|
<% if (type === 'userscript') { %>
|
|
# `favicon.href = href` doesn't work on Firefox.
|
|
$.add d.head, Favicon.el
|
|
<% } %>
|