           currentFilter: @state.currentFilter
 
 
-  setBeatmapsetDiscussion: (_e, {beatmapsetDiscussion, callback}) ->
-    @setState
-      beatmapsetDiscussion: beatmapsetDiscussion
-      callback
+  checkNew: ->
+    @nextTimeout ?= @checkNewTimeoutDefault
+
+    Timeout.clear @checkNewTimeout
+
+    @checkNewAjax = $.ajax document.location.pathname,
+      data:
+        format: 'json'
+        last_updated: moment(@state.beatmapsetDiscussion.updated_at).unix()
+
+    .done (data) =>
+      if data.updated? && !data.updated
+        @nextTimeout *= 2
+        return
+
+      @nextTimeout = @checkNewTimeoutDefault
+
+      @setBeatmapsetDiscussion null, beatmapsetDiscussion: data.beatmapsetDiscussion
+
+    .always =>
+      @nextTimeout = Math.min @nextTimeout, @checkNewTimeoutMax
+
+      @checkNewTimeout = Timeout.set @nextTimeout, @checkNew
+
+
+  jumpByHash: ->
+    jumpId = document.location.hash.match(/\/(\d+)/)?[1]
+
+    if jumpId?
+      return $.publish 'beatmapDiscussion:jump', id: parseInt(jumpId, 10)
+
+    beatmapId = document.location.hash.match(/:(\d+)/)?[1]
+    beatmapId ?= @state.currentBeatmap.id
+    $.publish 'beatmap:select', id: parseInt(beatmapId, 10)
+
+
+  jumpTo: (_e, {id}) ->
+    discussion = @state.beatmapsetDiscussion.beatmap_discussions.find (d) => d.id == id
+
+    return if !discussion?
+
+    mode = if discussion.timestamp? then 'timeline' else 'general'
+
+    @setMode null, mode, =>
+      @setCurrentBeatmapId null,
+        id: discussion.beatmap_id
+        callback: =>
+          $.publish 'beatmapDiscussionEntry:highlight', id: discussion.id
+
+          target = $(".js-beatmap-discussion-jump[data-id='#{id}']")
+          $(window).stop().scrollTo target, 500,
+            offset: modeSwitcher[0].getBoundingClientRect().height * -1
+
+
+  markPostRead: (_e, {id}) ->
+    return if _.includes @state.readPostIds, id
+
+    @setState readPostIds: @state.readPostIds.concat(id)
+
 
   setBeatmapset: (_e, {beatmapset, callback}) ->
     @setState

    @setCurrentBeatmapId null, id: beatmap?.id


  users: ->
    @cache.users ?= _.keyBy @state.beatmapsetDiscussion.users, 'id'


  jumpTo: (_e, {id}) ->
    discussion = @state.beatmapsetDiscussion.beatmap_discussions.find (d) => d.id == id

    return if !discussion?

    mode = if discussion.timestamp? then 'timeline' else 'general'

    @setMode null, mode, =>
      @setCurrentBeatmapId null,
        id: discussion.beatmap_id
        callback: =>
          $.publish 'beatmapDiscussionEntry:highlight', id: discussion.id

          target = $(".js-beatmap-discussion-jump[data-id='#{id}']")
          $(window).stop().scrollTo target, 500,
            offset: modeSwitcher[0].getBoundingClientRect().height * -1


  setMode: (_e, mode, callback) ->
    return callback?() if mode == @state.mode

    @setState mode: mode, callback


  jumpByHash: ->
    jumpId = document.location.hash.match(/\/(\d+)/)?[1]

    if jumpId?
      return $.publish 'beatmapDiscussion:jump', id: parseInt(jumpId, 10)

    beatmapId = document.location.hash.match(/:(\d+)/)?[1]
    beatmapId ?= @state.currentBeatmap.id
    $.publish 'beatmap:select', id: parseInt(beatmapId, 10)


  checkNewTimeoutDefault: 10000
  checkNewTimeoutMax: 60000

  checkNew: ->
    @nextTimeout ?= @checkNewTimeoutDefault

    Timeout.clear @checkNewTimeout

    @checkNewAjax = $.ajax document.location.pathname,
      data:
        format: 'json'
        last_updated: moment(@state.beatmapsetDiscussion.updated_at).unix()

    .done (data) =>
      if data.updated? && !data.updated
        @nextTimeout *= 2
        return

      @nextTimeout = @checkNewTimeoutDefault

      @setBeatmapsetDiscussion null, beatmapsetDiscussion: data.beatmapsetDiscussion

    .always =>
      @nextTimeout = Math.min @nextTimeout, @checkNewTimeoutMax

      @checkNewTimeout = Timeout.set @nextTimeout, @checkNew


  markPostRead: (_e, {id}) ->
    return if _.includes @state.readPostIds, id

    @setState readPostIds: @state.readPostIds.concat(id)


  setFilter: (_e, {filter}) ->
    return if filter == @state.currentFilter

    @setState currentFilter: filter

    @checkNewTimeout = Timeout.set @checkNewTimeoutDefault, @checkNew


  componentWillUpdate: ->
    @cache = {}


  componentWillUnmount: ->
    $.unsubscribe '.beatmapDiscussions'

    Timeout.clear @checkNewTimeout
    @checkNewAjax?.abort?()


  render: ->
