 
       return callback err  if err
 
-  getTimeDiffWithServer: ->
-    @pubnub.time (serverTime) =>
-
-      return  unless serverTime
-
-      diff = new Date() * 10000 - serverTime
-      # when time difference between pubnub server and client is less than 500ms
-      # ignore the difference
-      @timeDiff = if Math.abs(diff) < 5000000 then 0 else diff
+
+  isDisconnected: (err) ->
+    # I know this is so error prone, but they are not sending any error code; just message. :(
+    return err?.message is 'Offline. Please check your network settings.'
+
+
+  reconnect: (channel, pbInstance) ->
+    KDTimeAgoView.emit 'OneMinutePassed'
+
+    @once 'Ping', (serverTimestamp) =>
+
+      return  unless @lastSeenOnline
+
+      # if user is not online for more than a day, then reload the page
+      if @lastSeenOnline < serverTimestamp - 864000000000
+        return window.location.reload()
+
+      @fetchHistory {channel, timestamp: @lastSeenOnline, pbInstance}
+
+
+  fetchHistory: (options) ->
+
+    {channel, timestamp, pbInstance} = options
+
+    return  unless timestamp
+
+    pb = pbInstance or @pubnub
+
+    limit = 100
+    historyOptions =
+      channel : channel
+      start   : "#{timestamp}"
+      count   : limit
+      reverse : true
+      callback: (response) =>
+
+        return  unless response?.length and Array.isArray(response)
+
+        [messages, start] = response
+
+        return  unless messages?.length and Array.isArray(messages)
+
+        @handleMessage message, channel  for message in messages
+
+        # since the maximum message limit is 100, we are making a recursive call here
+        @fetchHistory channel, start  if messages.length is limit
+      err: (err) ->
+        # instead of getting into a stale state, just reload the page
+        window.location.reload()  if err
+
+    pb.history historyOptions
+
+
+  handleMessage: (message, channel) ->
+
+    return  unless message
+
+    {eventName, body, eventId} = message
+
+    return  unless eventName and body
+
+    if eventId?
+      return  if @eventCache[eventId]
+
+      @eventCache[eventId] = yes
+
+
+    # when a user is connected in two browsers, and leaves a channel, in second one
+    # they receive RemovedFromChannel event for their own. Therefore we must unsubscribe
+    # user from all connected devices.
+    if eventName is 'RemovedFromChannel' and body.accountId is whoami().socialApiId
+      return @unsubscribePubnub message.channel
+
+    # no need to emit any events when not subscribed
+    return  unless @channels[channel]
+
+    # instance events are received via public channel. For this reason
+    # if an event name includes "instance-", update the related message channel
+    # An instance event format is like "instance-5dc4ce55-b159-11e4-8329-c485b673ee34.ReplyAdded" - ctf
+    if eventName.indexOf("instance-") < 0
+      return @channels[channel].emit eventName, body
+
+    events = eventName.split "."
+    if events.length < 2
+      warn 'could not parse event name', eventName
+      return
+
+    [instanceChannel, eventName] = events
+
+    return  unless @channels[instanceChannel]
+
+    @channels[instanceChannel].emit eventName, body
+
 
   handleError: (err) ->
     {message, payload} = err
+
+    if @isDisconnected err
+      @lastSeenOnline = @serverTimestamp
 
     return kd.warn err  unless payload?.channels
 


      return callback { message : 'Could not find realtime token'}  unless realtimeToken

      @pubnub.auth realtimeToken

      callback null


        endPoint : "/api/gatekeeper/subscribe/channel"
        data     : { name: channelName, typeConstant, groupName: group }

    options.pbInstance = @pubnub

    return @subscribePubnub options, callback


