Browse Source

Added ajax long polling.

Martin Edenhofer 12 years ago
parent
commit
5e69e1af4e

+ 14 - 13
app/assets/javascripts/app/controllers/_dashboard/activity_stream.js.coffee

@@ -8,8 +8,6 @@ class App.DashboardActivityStream extends App.Controller
     super
     @items = []
 
-    # refresh list ever 140 sec.
-#    @interval( @fetch, 1400000, 'dashboard_activity_stream' )
     @fetch()
 
     # bind to rebuild view event
@@ -22,17 +20,20 @@ class App.DashboardActivityStream extends App.Controller
     if cache
       @load( cache )
 
-#    # get data
-#    App.Com.ajax(
-#      id:    'dashoard_activity_stream',
-#      type:  'GET',
-#      url:   '/api/activity_stream',
-#      data:  {
-#        limit: @limit,
-#      }
-#      processData: true,
-#      success: @load
-#    )
+    # init fetch via ajax, all other updates on time via websockets
+    else
+      App.Com.ajax(
+        id:    'dashoard_activity_stream'
+        type:  'GET'
+        url:   '/api/activity_stream'
+        data:  {
+          limit: 8
+        }
+        processData: true
+        success: (data) =>
+          App.Store.write( 'activity_stream', data )
+          @load(data)
+      )
 
   load: (data) =>
     items = data.activity_stream

+ 34 - 11
app/assets/javascripts/app/controllers/_dashboard/rss.js.coffee

@@ -11,19 +11,42 @@ class App.DashboardRss extends App.Controller
     @fetch()
 
   fetch: =>
+
+    # get data from cache
     cache = App.Store.get( 'dashboard_rss' )
     if cache
-      @load( cache )
+      @render( cache )
 
-  load: (data) =>
-    items = data.items || []
-    @head = data.head || '?'
-    @render(items)
+    # init fetch via ajax, all other updates on time via websockets
+    else
+      App.Com.ajax(
+        id:    'dashboard_rss'
+        type:  'GET'
+        url:   '/api/rss_fetch'
+        data:  {
+          limit: 8
+          url:   'http://www.heise.de/newsticker/heise-atom.xml'
+        }
+        processData: true
+        success: (data) =>
+          if data.message
+            @render(
+              head:    'Heise ATOM'
+              message: data.message
+            )
+          else
+            App.Store.write( 'dashboard_rss', data )
+            @render(data)
+        error: =>
+          @render(
+            head:    'Heise ATOM'
+            message: 'Unable to fetch rss!'
+          )
+      )
 
-  render: (items) ->
-    html = App.view('dashboard/rss')(
-      head:  @head,
-      items: items
+  render: (data) ->
+    @html App.view('dashboard/rss')(
+      head:  data.head,
+      items: data.item || []
+      message: data.message
     )
-    html = $(html)
-    @html html

+ 30 - 17
app/assets/javascripts/app/controllers/_dashboard/ticket.js.coffee

@@ -8,7 +8,7 @@ class App.DashboardTicket extends App.Controller
 
   constructor: ->
     super
-    @start_page    = 1
+    @start_page = 1
     @navupdate '#'
 
     # set new key
@@ -25,25 +25,37 @@ class App.DashboardTicket extends App.Controller
     # use cache of first page
     cache = App.Store.get( @key )
     if cache
-#      @render( cache )
       @load( cache )
 
-    # get data
-#    App.Com.ajax(
-#      id:    'dashboard_ticket_' + @key,
-#      type:  'GET',
-#      url:   '/api/ticket_overviews',
-#      data:  {
-#        view:       @view,
-#        view_mode:  'd',
-#        start_page: @start_page,
-#      }
-#      processData: true,
-#      success: @load
-#    )
+    # init fetch via ajax, all other updates on time via websockets
+    else
+      App.Com.ajax(
+        id:    'dashboard_ticket_' + @key,
+        type:  'GET',
+        url:   '/api/ticket_overviews',
+        data:  {
+          view:       @view,
+          view_mode:  'd',
+          start_page: @start_page,
+        }
+        processData: true,
+        success: (data) =>
+          data.ajax = true
+          @load(data)
+      )
 
   load: (data) =>
 
+    if data.ajax
+      data.ajax = false
+      App.Store.write( @key, data )
+
+      # load user collection
+      App.Collection.load( type: 'User', data: data.collections.users )
+  
+      # load ticket collection
+      App.Collection.load( type: 'Ticket', data: data.collections.tickets )
+
     # get meta data
     App.Overview.refresh( data.overview, options: { clear: true } )
 
@@ -57,11 +69,12 @@ class App.DashboardTicket extends App.Controller
       @log 'refetch...', record
       @fetch()
 
-#    App.Store.write( @key, data )
-
     @render( data )
 
   render: (data) ->
+    return if !data
+    return if !data.ticket_list
+    return if !data.overview
 
     @overview      = data.overview
     @tickets_count = data.tickets_count

+ 33 - 3
app/assets/javascripts/app/controllers/agent_ticket_view.js.coffee

@@ -37,12 +37,42 @@ class Index extends App.Controller
     # use cache of first page
     cache = App.Store.get( @key )
     if cache
-      @overview      = cache.overview
-      @tickets_count = cache.tickets_count
-      @ticket_list   = cache.ticket_list
       @load(cache)
 
+    # init fetch via ajax, all other updates on time via websockets
+    else
+      App.Com.ajax(
+        id:    'ticket_overview_' + @key,
+        type:  'GET',
+        url:   '/api/ticket_overviews',
+        data:  {
+          view:       @view,
+          view_mode:  @view_mode,
+        }
+        processData: true,
+        success: (data) =>
+          data.ajax = true
+          @load(data)
+        )
+
   load: (data) =>
+    return if !data
+    return if !data.ticket_list
+    return if !data.overview
+
+    @overview      = data.overview
+    @tickets_count = data.tickets_count
+    @ticket_list   = data.ticket_list
+
+    if data.ajax
+      data.ajax = false
+      App.Store.write( @key, data )
+
+      # load user collection
+      App.Collection.load( type: 'User', data: data.collections.users )
+  
+      # load ticket collection
+      App.Collection.load( type: 'Ticket', data: data.collections.tickets )
 
     # get meta data
     @overview = data.overview

+ 0 - 2
app/assets/javascripts/app/lib/app_post/ajax.js.coffee

@@ -5,9 +5,7 @@ class App.Com
   @ajax: (args) -> # Must be a static method
     if _instance == undefined
       _instance ?= new _Singleton
-
     _instance.ajax(args)
-    _instance
 
 # The actual Singleton class
 class _Singleton

+ 158 - 54
app/assets/javascripts/app/lib/app_post/websocket.js.coffee

@@ -9,7 +9,7 @@ class App.WebSocket
 
   @close: (args) -> # Must be a static method
     if _instance isnt undefined
-      _instance.close()
+      _instance.close(args)
 
   @send: (args) -> # Must be a static method
     @connect()
@@ -20,6 +20,7 @@ class App.WebSocket
     _instance.auth(args)
 
   @_spool: ->
+    @connect()
     _instance.spool()
 
 # The actual Singleton class
@@ -27,9 +28,12 @@ class _Singleton extends App.Controller
   @include App.Log
 
   queue: []
-  supported:             true
-  lastSpoolMessage:      undefined
-  connectionEstablished: false
+  supported:                true
+  lastSpoolMessage:         undefined
+  connectionEstablished:    false
+  connectionWasEstablished: false
+  backend:                  'websocket'
+  client_id:                undefined
 
   constructor: (@args) ->
 
@@ -46,19 +50,22 @@ class _Singleton extends App.Controller
     @connect()
 
   send: (data) =>
-    return if !@supported
-#    console.log 'ws:send trying', data, @ws, @ws.readyState
-
-    # A value of 0 indicates that the connection has not yet been established.
-    # A value of 1 indicates that the connection is established and communication is possible.
-    # A value of 2 indicates that the connection is going through the closing handshake.
-    # A value of 3 indicates that the connection has been closed or could not be opened.
-    if @ws.readyState is 0
-      @queue.push data
+    if @backend is 'ajax'
+      @_ajaxSend(data)
     else
-#      console.log( 'ws:send', data )
-      string = JSON.stringify( data )
-      @ws.send(string)
+
+#      console.log 'ws:send trying', data, @ws, @ws.readyState
+  
+      # A value of 0 indicates that the connection has not yet been established.
+      # A value of 1 indicates that the connection is established and communication is possible.
+      # A value of 2 indicates that the connection is going through the closing handshake.
+      # A value of 3 indicates that the connection has been closed or could not be opened.
+      if @ws.readyState is 0
+        @queue.push data
+      else
+#        console.log( 'ws:send', data )
+        string = JSON.stringify( data )
+        @ws.send(string)
 
   auth: (data) =>
     return if !@supported
@@ -77,7 +84,7 @@ class _Singleton extends App.Controller
     if @lastSpoolMessage
       data['timestamp'] = @lastSpoolMessage
 
-    @log 'Event', 'debug', 'spool', data
+    @log 'Websocket', 'debug', 'spool', data
 
     # ask for spool messages
     App.Event.trigger(
@@ -89,37 +96,38 @@ class _Singleton extends App.Controller
     @lastSpoolMessage = Math.round( +new Date()/1000 )
 
   close: =>
-    return if !@supported
+    return if @backend is 'ajax'
 
     @ws.close()
 
   ping: =>
-    return if !@supported
+    return if @backend is 'ajax'
 
-    @log 'Event', 'debug', 'send websockend ping'
+    @log 'Websocket', 'debug', 'send websockend ping'
     @send( { action: 'ping' } )
 
     # check if ping is back within 2 min
     @clearDelay('websocket-ping-check')
     check = =>
-      @log 'Event', 'notice', 'no websockend ping response, reconnect...'
+      @log 'Websocket', 'notice', 'no websockend ping response, reconnect...'
       @close()
     @delay check, 120000, 'websocket-ping-check'
 
   pong: ->
-    return if !@supported
-    @log 'Event', 'debug', 'received websockend ping'
+    return if @backend is 'ajax'
+
+    @log 'Websocket', 'debug', 'received websockend ping'
 
     # test again after 1 min
     @delay @ping, 60000
 
   connect: =>
+    return if @backend is 'ajax'
 
     if !window.WebSocket
-      @error = new App.ErrorModal(
-        message: 'Sorry, no websocket support!'
-      )
-      @supported = false
+      @backend = 'ajax'
+      @log 'WebSocket', 'notice', 'no support of websocket, use ajax long polling'
+      @_ajaxInit()
       return
 
     protocol = 'ws://'
@@ -130,9 +138,10 @@ class _Singleton extends App.Controller
 
     # Set event handlers.
     @ws.onopen = =>
-      @log 'Event', 'notice', 'new websocked connection open'
+      @log 'Websocket', 'notice', 'new websocket connection open'
 
       @connectionEstablished = true
+      @connectionWasEstablished = true
 
       # close error message show up (because try so connect again) if exists
       @clearDelay('websocket-no-connection-try-reconnect')
@@ -144,7 +153,7 @@ class _Singleton extends App.Controller
 
       # empty queue
       for item in @queue
-        @log 'Event', 'debug', 'empty ws queue', item
+        @log 'Websocket', 'debug', 'empty ws queue', item
         @send(item)
       @queue = []
 
@@ -153,41 +162,31 @@ class _Singleton extends App.Controller
 
     @ws.onmessage = (e) =>
       pipe = JSON.parse( e.data )
-      @log 'Event', 'debug', 'ws:onmessage', pipe
-
-      # go through all blocks
-      for item in pipe
-
-        # reset reconnect loop
-        if item['action'] is 'pong'
-          @pong()
-
-        # fill collection
-        if item['collection']
-          @log 'Event', 'debug', "ws:onmessage collection:" + item['collection']
-          App.Store.write( item['collection'], item['data'] )
-
-        # fire event
-        if item['event']
-          if typeof item['event'] is 'object'
-            for event in item['event']
-              @log 'Event', 'debug', "ws:onmessage event:" + event
-              App.Event.trigger( event, item['data'] )
-          else
-            @log 'Event', 'debug', "ws:onmessage event:" + item['event']
-            App.Event.trigger( item['event'], item['data'] )
+      @log 'Websocket', 'debug', 'ws:onmessage', pipe
+      @_receiveMessage(pipe)
 
     @ws.onclose = (e) =>
-      @log 'Event', 'debug', "ws:onclose", e
+      @log 'Websocket', 'debug', "ws:onclose", e
 
       # set timestamp to get spool messages later
       if @connectionEstablished
         @lastSpoolMessage = Math.round( +new Date()/1000 )
         @connectionEstablished = false
 
+      return if @backend is 'ajax'
+
       # show error message, first try to reconnect
       if !@error
         message = =>
+
+          # use fallback if no connection was possible
+          if !@connectionWasEstablished
+            @backend = 'ajax'
+            @log 'WebSocket', 'notice', 'No connection to websocket, use use ajax long polling as fallback'
+            @_ajaxInit()
+            return
+
+          # show reconnect message
           @error = new App.ErrorModal(
             message: 'No connection to websocket, trying to reconnect...'
           )
@@ -197,5 +196,110 @@ class _Singleton extends App.Controller
       @delay @connect, 4500
 
     @ws.onerror = (e) =>
-      @log 'Event', 'debug', "ws:onerror", e
+      @log 'Websocket', 'debug', "ws:onerror", e
+
+  _receiveMessage: (data = []) =>
+
+      # go through all blocks
+      for item in data
 
+        # reset reconnect loop
+        if item['action'] is 'pong'
+          @pong()
+
+        # fill collection
+        if item['collection']
+          @log 'Websocket', 'debug', "onmessage collection:" + item['collection']
+          App.Store.write( item['collection'], item['data'] )
+
+        # fire event
+        if item['event']
+          if typeof item['event'] is 'object'
+            for event in item['event']
+              @log 'Websocket', 'debug', "onmessage event:" + event
+              App.Event.trigger( event, item['data'] )
+          else
+            @log 'Websocket', 'debug', "onmessage event:" + item['event']
+            App.Event.trigger( item['event'], item['data'] )
+
+  _ajaxInit: (data = {}) =>
+
+    # return if init is already done and not forced
+    return if @_ajaxInitDone && !data.force
+
+    # stop init request if new one is started
+    if @_ajaxInitWorking
+      console.log '@_ajaxInitWorking', @_ajaxInitWorking
+      @_ajaxInitWorking.abort()
+
+    # call init request
+    @_ajaxInitWorking = App.Com.ajax(
+      type:  'POST'
+      url:   '/api/message_send'
+      data:  JSON.stringify({ data: { action: 'login' }  })
+      processData: false
+      queue: false
+      success: (data) =>
+        if data.client_id
+          @log 'Websocket', 'notice', 'ajax:new client_id', data.client_id
+          @client_id = data.client_id
+          @_ajaxReceive()
+          @_ajaxSendQueue()
+        @_ajaxInitDone = true
+        @_ajaxInitWorking = false
+      error: =>
+        @_ajaxInitDone = true
+        @_ajaxInitWorking = false
+    )
+
+  _ajaxSend: (data) =>
+    @log 'Websocket', 'debug', 'ajax:sendmessage', data
+    if !@client_id || @client_id is undefined || !@_ajaxInitDone
+      @_ajaxInit()
+      @queue.push data
+    else
+      @queue.push data
+      @_ajaxSendQueue()
+
+  _ajaxSendQueue: =>
+    while !_.isEmpty(@queue)
+      data = @queue.shift()
+      App.Com.ajax(
+        type:  'POST'
+        url:   '/api/message_send'
+        data:  JSON.stringify({ client_id: @client_id, data: data })
+        processData: false
+        queue: true
+        success: (data) =>
+          if data && data.error
+            @client_id = undefined
+            @_ajaxInit( force: true )
+        error: =>
+          @client_id = undefined
+          @_ajaxInit( force: true )
+      )
+
+  _ajaxReceive: =>
+    return if !@client_id
+    return if @_ajaxReceiveWorking is true
+    @_ajaxReceiveWorking = true
+    App.Com.ajax(
+      id:    'message_receive',
+      type:  'POST'
+      url:   '/api/message_receive'
+      data:  JSON.stringify({ client_id: @client_id })
+      processData: false
+      success: (data) =>
+        @log 'Websocket', 'notice', 'ajax:onmessage', data
+        @_receiveMessage(data)
+        if data && data.error
+          @client_id = undefined
+          @_ajaxInit( force: true )
+        @_ajaxReceiveWorking = false
+        @_ajaxReceive()
+      error: (data) =>
+        @client_id = undefined
+        @_ajaxInit( force: true )
+        @_ajaxReceiveWorking = false
+        @delay @_ajaxReceive, 5000
+    )

+ 3 - 0
app/assets/javascripts/app/views/dashboard/rss.jst.eco

@@ -1,6 +1,9 @@
 <div class="row">
   <div class="span3 can-move">
     <h2><%- @T( @head ) %></h2>
+    <% if @message: %>
+      <%- @T(@message) %>
+    <% end %>
     <ul>
     <% for item in @items: %>
       <li><a href="<%= item.link %>" title="<%= item.summary %>" target="_blank"><%= item.title %>"</a></li>

+ 1 - 1
app/controllers/activity_controller.rb

@@ -3,7 +3,7 @@ class ActivityController < ApplicationController
 
   # GET /api/activity_stream
   def activity_stream
-    activity_stream = History.activity_stream_fulldata(current_user, params[:limit])
+    activity_stream = History.activity_stream_fulldata( current_user, params[:limit] )
 
     # return result
     render :json => activity_stream

+ 1 - 0
app/controllers/rss_controller.rb

@@ -20,6 +20,7 @@ curl http://localhost/api/rss_fetch.json -v -u #{login}:#{password} -H "Content-
     items = RSS.fetch(params[:url], params[:limit])
     if items == nil
       render :json => { :message => "failed to fetch #{ params[:url] }", :status => :unprocessable_entity }
+      return
     end
     render :json => { :items => items }
   end

+ 19 - 13
app/controllers/ticket_overviews_controller.rb

@@ -22,8 +22,8 @@ class TicketOverviewsController < ApplicationController
         :array        => true,
       ) 
       tickets = []
-      overview[:tickets].each {|ticket|
-        data = { :id => ticket.id }
+      overview[:tickets].each {|ticket_id|
+        data = { :id => ticket_id }
         tickets.push data
       }
 
@@ -33,21 +33,24 @@ class TicketOverviewsController < ApplicationController
         :tickets       => tickets,
         :tickets_count => overview[:tickets_count],
       }
-      return      
+      return
     end
     overview = Ticket.overview(
-      :view            => params[:view],
-      :view_mode       => params[:view_mode],
-      :current_user_id => current_user.id,
-      :start_page      => params[:start_page],
-      :array           => true,
+      :view         => params[:view],
+#      :view_mode    => params[:view_mode],
+      :current_user => User.find( current_user.id ),
+      :array        => true,
     )
- 
+    if !overview
+      render :json => { :error => "No such view #{ params[:view] }!" }, :status => :unprocessable_entity
+      return
+    end
+
     # get related users
     users = {}
     tickets = []
-    overview[:tickets].each {|ticket|
-      data = Ticket.full_data(ticket.id)
+    overview[:ticket_list].each {|ticket_id|
+      data = Ticket.full_data(ticket_id)
       tickets.push data
       if !users[ data['owner_id'] ]
         users[ data['owner_id'] ] = User.user_data_full( data['owner_id'] )
@@ -84,12 +87,15 @@ class TicketOverviewsController < ApplicationController
     # return result
     render :json => {
       :overview      => overview[:overview],
-      :tickets       => tickets,
+      :ticket_list   => overview[:ticket_list],
       :tickets_count => overview[:tickets_count],
-      :users         => users,
       :bulk          => {
         :group_id__owner_id => groups_users,
       },
+      :collections    => {
+        :users   => users,
+        :tickets => tickets,
+      },
     }
   end
 

Some files were not shown because too many files changed in this diff