Moved to generic App.CollectionController for Tasks and OnlineNotifications controller.

This commit is contained in:
Martin Edenhofer 2016-04-04 15:54:37 +02:00
parent ada5538e1b
commit 0af243514b
14 changed files with 1057 additions and 279 deletions

View file

@ -487,25 +487,28 @@ class App.Controller extends Spine.Controller
prepareForObjectList: (items) ->
for item in items
item.link = ''
item.title = '???'
# convert backend name space to local name space
item.object = item.object.replace('::', '')
# lookup real data
if App[item.object] && App[item.object].exists(item.o_id)
object = App[item.object].find(item.o_id)
item.objectNative = object
item.link = object.uiUrl()
item.title = object.displayName()
item.object_name = object.objectDisplayName()
item.cssIcon = object.iconActivity(@Session.get())
item.created_by = App.User.find(item.created_by_id)
item = @prepareForObjectListItem(item)
items
prepareForObjectListItem: (item) ->
item.link = ''
item.title = '???'
# convert backend name space to local name space
item.object = item.object.replace('::', '')
# lookup real data
if App[item.object] && App[item.object].exists(item.o_id)
object = App[item.object].find(item.o_id)
item.objectNative = object
item.link = object.uiUrl()
item.title = object.displayName()
item.object_name = object.objectDisplayName()
item.cssIcon = object.iconActivity(@Session.get())
item.created_by = App.User.find(item.created_by_id)
item
# central method, is getting called on every ticket form change
ticketFormChanges: (params, attribute, attributes, classname, form, ui) =>
if @formMeta.dependencies && @formMeta.dependencies[attribute.name]

View file

@ -740,3 +740,311 @@ class App.WizardFullScreen extends App.WizardModal
super
$('.content').addClass('hide')
$('#content').removeClass('hide')
class App.CollectionController extends App.Controller
events:
'click .js-remove': 'remove'
'click .js-item': 'click'
'click .js-locationVerify': 'location'
observe:
field1: true
field2: false
#currentItems: {}
#1:
# a: 123
# b: 'some string'
#2:
# a: 123
# b: 'some string'
#renderList: {}
#1: ..dom..ref..
#2: ..dom..ref..
template: '_need_to_be_defined_'
uniqKey: 'id'
model: '_need_to_be_defined_'
sortBy: 'name'
order: 'ASC',
constructor: ->
@events = @constructor.events unless @events
@observe = @constructor.observe unless @observe
@currentItems = {}
@renderList = {}
@queue = []
@queueRunning = false
@lastOrder = []
super
@queue.push ['renderAll']
@uIRunner()
# bind to changes
if @model
@subscribeId = App[@model].subscribe(@collectionSync)
# render on generic ui call
@bind('ui:rerender', =>
@queue.push ['renderAll']
@uIRunner()
)
# render on login
@bind('auth:login', =>
@queue.push ['renderAll']
@uIRunner()
)
# reset current tasks on logout
@bind('auth:logout', =>
@queue.push ['renderAll']
@uIRunner()
)
@log 'debug', 'Init @uniqKey', @uniqKey
@log 'debug', 'Init @observe', @observe
@log 'debug', 'Init @model', @model
release: =>
if @subscribeId
App[@model].unsubscribe(@subscribeId)
uIRunner: ->
return if !@queue[0]
return if @queueRunning
@queueRunning = true
loop
param = @queue.shift()
if param[0] is 'domChange'
@domChange(param[1])
else if param[0] is 'domRemove'
@domRemove(param[1])
else if param[0] is 'renderAll'
@renderAll()
if !@queue[0]
@onRenderEnd()
@queueRunning = false
break
collectionOrderGet: =>
newOrder = []
all = @itemsAll()
for item in all
newOrder.push item[@uniqKey]
newOrder
collectionOrderSet: (newOrder = false) =>
if !newOrder
newOrder = @collectionOrderGet()
@lastOrder = newOrder
collectionSync: (items, type) =>
console.log('collectionSync', items, type)
# remove items
if type is 'destroy'
ids = []
for item in items
ids.push item.id
@queue.push ['domRemove', ids]
@uIRunner()
return
# inital render
if _.isEmpty(@renderList)
@queue.push ['renderAll']
@uIRunner()
return
# check if item order is the same
newOrder = @collectionOrderGet()
removedIds = _.difference(@lastOrder, newOrder)
addedIds = _.difference(newOrder, @lastOrder)
@log 'debug', 'collectionSync removedIds', removedIds
@log 'debug', 'collectionSync addedIds', addedIds
@log 'debug', 'collectionSync @lastOrder', @lastOrder
@log 'debug', 'collectionSync newOrder', newOrder
# add items
alreadyRemoved = false
if !_.isEmpty(addedIds)
lastOrderNew = []
for id in @lastOrder
if !_.contains(removedIds, id)
lastOrderNew.push id
# try to find positions of new items
applyOrder = App.Utils.diffPositionAdd(lastOrderNew, newOrder)
if !applyOrder
@queue.push ['renderAll']
@uIRunner()
return
if !_.isEmpty(removedIds)
alreadyRemoved = true
@queue.push ['domRemove', removedIds]
@uIRunner()
newItems = []
for apply in applyOrder
item = App[@model].find(apply.id)
item.meta_position = apply.position
newItems.push item
@queue.push ['domChange', newItems]
@uIRunner()
# remove items
if !alreadyRemoved && !_.isEmpty(removedIds)
@queue.push ['domRemove', removedIds]
@uIRunner()
# update items
newItems = []
for item in items
if !_.contains(removedIds, item.id) && !_.contains(addedIds, item.id)
newItems.push item
return if _.isEmpty(newItems)
@queue.push ['domChange', newItems]
@uIRunner()
#return
# rerender all items
#@queue.push ['renderAll']
#@uIRunner()
domRemove: (ids) =>
@log 'debug', 'domRemove', ids
for id in ids
@itemAttributesDelete(id)
if @renderList[id]
@renderList[id].remove()
delete @renderList[id]
@onRemoved(id)
@collectionOrderSet()
domChange: (items) =>
@log 'debug', 'domChange items', items
@log 'debug', 'domChange @currentItems', @currentItems
changedItems = []
for item in items
@log 'debug', 'domChange|item', item
attributes = @itemAttributes(item)
currentItem = @itemAttributesGet(item[@uniqKey])
if !currentItem
@log 'debug', 'domChange|add', item
changedItems.push item
@itemAttributesSet(item[@uniqKey], attributes)
else
@log 'debug', 'domChange|change', item
@log 'debug', 'domChange|change|observe attributes', @observe
@log 'debug', 'domChange|change|current', currentItem
@log 'debug', 'domChange|change|new', attributes
for field of @observe
@log 'debug', 'domChange|change|compare', field, currentItem[field], attributes[field]
diff = !_.isEqual(currentItem[field], attributes[field])
@log 'debug', 'domChange|diff', diff, item
if diff
changedItems.push item
@itemAttributesSet(item[@uniqKey], attributes)
break
return if _.isEmpty(changedItems)
@renderParts(changedItems)
renderAll: =>
items = @itemsAll()
@log 'debug', 'renderAll', items
localeEls = []
for item in items
attributes = @itemAttributes(item)
@itemAttributesSet(item[@uniqKey], attributes)
localeEls.push @renderItem(item, false)
@html localeEls
@collectionOrderSet()
@onRenderEnd()
itemsAll: =>
#App[@model].all()
App[@model].search(sortBy: @sortBy, order: @order)
itemAttributesDiff: (item) =>
attributes = @itemAttributes(item)
currentItem = @itemAttributesGet(item[@uniqKey])
for field of @observe
@log 'debug', 'itemAttributesDiff|compare', field, currentItem[field], attributes[field]
diff = !_.isEqual(currentItem[field], attributes[field])
if diff
@log 'debug', 'itemAttributesDiff|diff', diff
return true
false
itemAttributesDelete: (id) =>
delete @currentItems[id]
itemAttributesGet: (id) =>
@currentItems[id]
itemAttributesSet: (id, attributes) =>
@currentItems[id] = attributes
itemAttributes: (item) =>
attributes = {}
for field of @observe
attributes[field] = item[field]
attributes
renderParts: (items) =>
@log 'debug', 'renderParts', items
for item in items
if !@renderList[item[@uniqKey]]
@renderItem(item)
else
@renderItem(item, @renderList[item[@uniqKey]])
@collectionOrderSet()
renderItem: (item, el) =>
if @prepareForObjectListItemSupport
item = @prepareForObjectListItem(item)
@log 'debug', 'renderItem', item, @template, el, @renderList[item[@uniqKey]]
html = $(App.view(@template)(
item: item
))
@renderList[item[@uniqKey]] = html
if el is false
return html
else if !el
position = item.meta_position + 1
console.log('!el', item, position, item.meta_position)
#if item.meta_position
@el.find(".js-item:nth-child(#{position})").before(html)
else
el.replaceWith(html)
onRenderEnd: ->
# nothing
location: (e) ->
@locationVerify(e)
click: (e) =>
row = $(e.target).closest('.js-item')
id = row.data('id')
console.log('click id', id)
@onClick(id, e)
onClick: (id, e) ->
# nothing
remove: (e) =>
e.preventDefault()
e.stopPropagation()
row = $(e.target).closest('.js-item')
id = row.data('id')
@onRemove(id,e)
App[@model].destroy(id)
onRemove: (id, e) ->
# nothing
onRemoved: (id) ->
# nothing

View file

@ -1,37 +1,19 @@
class App.TaskbarWidget extends App.Controller
class App.TaskbarWidget extends App.CollectionController
events:
'click .js-close': 'remove'
'click .js-locationVerify': 'location'
fields:
observe:
field1: true
field2: false
meta: true
active: true
notify: true
observeNot:
field1: true
field2: false
currentItems: {}
#1:
# a: 123
# b: 'some string'
#2:
# a: 123
# b: 'some string'
renderList: {}
#1: ..dom..ref..
#2: ..dom..ref..
model: false
template: 'widget/task_item'
uniqKey: 'key'
observe:
meta: true
active: true
notify: true
constructor: ->
super
@queue = []
@queueRunning = false
@renderAll()
dndOptions =
tolerance: 'pointer'
distance: 15
@ -51,108 +33,24 @@ class App.TaskbarWidget extends App.Controller
@el.sortable(dndOptions)
# bind to changes
@bind('taskInit', => @renderAll())
@bind('taskInit', =>
console.log('renderAll aaa')
@renderAll()
console.log('renderAll bbb')
#@queue.push ['renderAll']
#@uIRunner()
)
@bind('taskUpdate', (tasks) =>
@queue.push ['taskUpdate', tasks]
@queue.push ['domChange', tasks]
@uIRunner()
)
@bind('taskRemove', (task_ids) =>
@queue.push ['checkRemoves', task_ids]
@queue.push ['domRemove', task_ids]
@uIRunner()
)
# render on generic ui call
@bind('ui:rerender', =>
@queue.push ['renderAll']
@uIRunner()
)
# render on login
@bind('auth:login', =>
@queue.push ['renderAll']
@uIRunner()
)
# reset current tasks on logout
@bind('auth:logout', =>
@queue.push ['renderAll']
@uIRunner()
)
uIRunner: ->
return if !@queue[0]
return if @queueRunning
@queueRunning = true
loop
param = @queue.shift()
if param[0] is 'taskUpdate'
@checkChanges(param[1])
else if param[0] is 'checkRemoves'
@checkRemoves(param[1])
else if param[0] is 'renderAll'
@renderAll()
if !@queue[0]
@queueRunning = false
break
checkRemoves: (keys) ->
for key in keys
delete @currentItems[key]
if @renderList[key]
@renderList[key].remove()
delete @renderList[key]
checkChanges: (items) ->
changedItems = []
for item in items
attributes = {}
for field of @fields.observe
attributes[field] = item[field]
#console.log('item', item)
#attributes = item.attributes()
#console.log('item', @fields.observe, item, attributes)
if !@currentItems[item.key]
changedItems.push item
@currentItems[item.key] = attributes
else
currentItem = @currentItems[item.key]
hit = false
for field of @fields.observe
diff = _.isEqual(currentItem[field], attributes[field])
#console.log('diff', field, diff, currentItem[field], attributes[field])
if !hit && diff
changedItems.push item
@currentItems[item.key] = attributes
hit = true
return if _.isEmpty(changedItems)
@renderParts(changedItems)
renderAll: ->
#@html ''
items = App.TaskManager.allWithMeta()
localeEls = []
for item in items
localeEls.push @renderItem(item, false)
@html localeEls
renderParts: (items) ->
for item in items
if !@renderList[item.key]
@renderItem(item)
else
@renderItem(item, @renderList[item.key])
renderItem: (item, el) ->
html = $(App.view(@template)(
item: item
))
@renderList[item.key] = html
if el is false
return html
else if !el
@el.append(html)
else
el.replaceWith(html)
itemsAll: ->
App.TaskManager.allWithMeta()
location: (e) =>
return if !$(e.currentTarget).hasClass('is-modified')

View file

@ -7,13 +7,11 @@ class App.OnlineNotificationWidget extends App.Controller
events:
'click .js-mark': 'markAllAsRead'
'click .js-item': 'hide'
'click .js-remove': 'removeItem'
'click .js-locationVerify': 'onItemClick'
'click': 'stopPropagation'
elements:
'.js-mark': 'mark'
'.js-noNotifications': 'noNotifications'
'.js-item': 'item'
'.js-content': 'content'
'.js-header': 'header'
@ -25,7 +23,7 @@ class App.OnlineNotificationWidget extends App.Controller
@bind 'OnlineNotification::changed', =>
@delay(
=> @fetch()
2600
2200
'online-notification-changed'
)
@ -48,20 +46,10 @@ class App.OnlineNotificationWidget extends App.Controller
if !@access()
@counterUpdate(0)
return
if !@subscribeId
@subscribeId = App.OnlineNotification.subscribe(@updateContent)
if @access()
@subscribeId = App.OnlineNotification.subscribe(@updateContent)
@bind('ui:reshow', =>
@show()
'popover'
)
$(window).on 'click.notifications', @hide
@updateContent()
@createContainer()
release: ->
$(window).off 'click.notifications'
@ -84,14 +72,15 @@ class App.OnlineNotificationWidget extends App.Controller
@nudge(e, 1)
return
else if e.keyCode is 13 # enter
@item.filter('.is-hover').find('.js-locationVerify').click()
@$('.js-item').filter('.is-hover').find('.js-locationVerify').click()
nudge: (e, position) ->
# get current
current = @item.filter('.is-hover')
items = @$('.js-item')
current = items.filter('.is-hover')
if !current.size()
@item.first().addClass('is-hover')
items.first().addClass('is-hover')
return
if position is 1
@ -110,27 +99,41 @@ class App.OnlineNotificationWidget extends App.Controller
if prev
@scrollToIfNeeded(prev, true)
counterUpdate: (count) =>
counterUpdate: (count, force = false) =>
count = '' if count is 0
return if !force && @count is count
@count = count
$('.js-notificationsCounter').text(count)
App.Event.trigger('online_notification_counter', count.toString())
@count = count
# show mark all as read if needed
if !count
@mark.addClass('hidden')
@mark.addClass('hide')
else
@mark.removeClass('hidden')
@mark.removeClass('hide')
markAllAsRead: (event) ->
event.preventDefault()
counterGen: (force = false) =>
items = App.OnlineNotification.all()
count = 0
for item in items
if !item.seen
count++
@counterUpdate(count, force)
if _.isEmpty(items)
@noNotifications.removeClass('hide')
else
@noNotifications.addClass('hide')
markAllAsRead: (e) ->
e.preventDefault()
@counterUpdate(0)
@ajax(
id: 'markAllAsRead'
type: 'POST'
url: @apiPath + '/online_notifications/mark_all_as_read'
url: "#{@apiPath}/online_notifications/mark_all_as_read"
data: JSON.stringify('')
processData: true
)
@ -143,13 +146,14 @@ class App.OnlineNotificationWidget extends App.Controller
heightPopoverHeader = @header.outerHeight(true)
isOverflowing = false
heightPopoverContent = 0
@item.each (i, el) =>
@$('.js-item').each (i, el) =>
# accumulate height of items
heightPopoverContent += el.clientHeight
if (heightPopoverHeader + heightPopoverContent + heightPopoverSpacer) > heightApp
@content.css 'height', heightApp - heightPopoverHeader - heightPopoverSpacer
containerHeight = heightApp - heightPopoverHeader - heightPopoverSpacer
@content.css 'height', containerHeight
isOverflowing = true
return false # exit .each loop
@ -160,7 +164,6 @@ class App.OnlineNotificationWidget extends App.Controller
load = (data) =>
@fetchedData = true
App.OnlineNotification.refresh(data.stream, clear: true)
@updateContent()
App.OnlineNotification.fetchFull(load)
toggle: =>
@ -169,43 +172,23 @@ class App.OnlineNotificationWidget extends App.Controller
else
@show()
updateContent: =>
createContainer: =>
if !@Session.get()
@content.html('')
return
items = App.OnlineNotification.search(sortBy: 'created_at', order: 'DESC')
@count = 0
for item in items
if !item.seen
@count++
count = ''
localeEl = $( App.view('widget/online_notification')(
count: count
))
@counterUpdate(@count)
# update content
items = @prepareForObjectList(items)
# generate desktop notifications
for item in items
if !@alreadyShown[item.id]
@alreadyShown[item.id] = true
if @fetchedData
if item.objectNative && item.objectNative.activityMessage
title = item.objectNative.activityMessage(item)
else
title = "Need objectNative in item #{item.object}.find(#{item.o_id})"
title = App.Utils.html2text(title.replace(/<.+?>/g, '"'))
@notifyDesktop(
url: item.link
title: title
)
App.OnlineNotification.play()
@html App.view('widget/online_notification')(
items: items
count: @count
new App.OnlineNotificationContentWidget(
el: localeEl.find('.js-items')
container: @
)
@html localeEl
@counterGen(true)
return if !@shown
@show()
@ -221,20 +204,43 @@ class App.OnlineNotificationWidget extends App.Controller
@shown = false
@el.hide()
onItemClick: (e) ->
@locationVerify(e)
@hide()
stopPropagation: (e) ->
e.stopPropagation()
removeItem: (e) =>
e.preventDefault()
e.stopPropagation()
row = $(e.target).closest('.js-item')
id = row.data('id')
App.OnlineNotification.destroy(id)
@updateHeight()
remove: =>
@el.remove()
@el.remove()
class App.OnlineNotificationContentWidget extends App.CollectionController
model: 'OnlineNotification'
template: 'widget/online_notification_item'
prepareForObjectListItemSupport: true
observe:
seen: true
sortBy: 'created_at'
order: 'DESC'
alreadyShown: {}
onRenderEnd: ->
@container.counterGen()
@container.updateHeight()
# generate desktop notifications
items = App.OnlineNotification.search(sortBy: 'created_at', order: 'DESC')
for item in items
if !@alreadyShown[item.id]
@alreadyShown[item.id] = true
if @container.fetchedData
item = @prepareForObjectListItem(item)
if item.objectNative && item.objectNative.activityMessage
title = item.objectNative.activityMessage(item)
else
title = "Need objectNative in item #{item.object}.find(#{item.o_id})"
title = App.Utils.html2text(title.replace(/<.+?>/g, '"'))
@notifyDesktop(
url: item.link
title: title
)
App.OnlineNotification.play()
onClick: ->
@container.hide()

View file

@ -61,7 +61,7 @@ class _collectionSingleton extends Spine.Module
return
# reset in-memory
appObject.refresh(params.data, { clear: true })
appObject.refresh(params.data, clear: true)
loadAssets: (assets) ->
@log 'debug', 'loadAssets', assets

View file

@ -626,3 +626,26 @@ class App.Utils
$outer.remove()
return 100 - widthWithScroll
@diffPositionAdd: (a, b) ->
applyOrder = []
newOrderMethod = (a, b, applyOrder) ->
for position of b
if a[position] isnt b[position]
# changes to complex, whole rerender
if _.contains(a, b[position])
return false
# insert new item and try next
a.splice(position, 0, b[position])
apply =
position: parseInt(position)
id: b[position]
applyOrder.push apply
newOrderMethod(a, b, applyOrder)
true
result = newOrderMethod(a, b, applyOrder)
return false if !result
applyOrder

View file

@ -309,11 +309,33 @@ class App.Model extends Spine.Model
# subscribe and render data / fetch new data if triggered
@bind(
'refresh change remove'
(items) =>
App.Log.debug('Model', "local collection refresh/change #{@className}", items)
'refresh'
(items, options) =>
if !_.isArray(items)
items = [items]
console.log('refresh', items, options)
App.Log.debug('Model', "local collection refresh #{@className}", items)
for key, callback of @SUBSCRIPTION_COLLECTION
callback(items)
callback(items, 'refresh')
)
@bind(
'change'
(items, subEvent) =>
return if subEvent is 'destroy'
if !_.isArray(items)
items = [items]
App.Log.debug('Model', "local collection change #{@className}", items)
for key, callback of @SUBSCRIPTION_COLLECTION
callback(items, 'change')
)
@bind(
'destroy'
(items) =>
if !_.isArray(items)
items = [items]
App.Log.debug('Model', "local collection destroy #{@className}", items)
for key, callback of @SUBSCRIPTION_COLLECTION
callback(items, 'destroy')
)
# fetch() all on network notify

View file

@ -1,37 +1,11 @@
<div class="arrow js-arrow"></div>
<div class="popover-notificationsHeader js-header">
<div class="popover-title"><%- @T('Notifications') %> <span class='popover-notificationsCounter js-notificationsCounter'><%- @count %></span></div>
<a class="btn btn--text btn--subtle js-mark<%- ' hidden' if !@count %>"><%- @T( 'Mark all as read' ) %></a>
<a class="btn btn--text btn--subtle js-mark<%- ' hide' if !@count %>"><%- @T( 'Mark all as read' ) %></a>
</div>
<div class="popover-content js-content">
<% if @items.length: %>
<% for item in @items: %>
<div class="js-item activity-entry activity-entry--removeable<% if item.seen: %> is-inactive<% end %>" data-id="<%- item.id %>">
<a class="activity-avatar user-popover" data-id="<%= item.created_by_id %>" <% if item.created_by_id isnt 1: %>href="<%- item.created_by.uiUrl() %>"<% end %>>
<%- item.created_by.avatar() %>
</a>
<div class="activity-body">
<a class="activity-message js-locationVerify" href="<%- item.link %>">
<span class="activity-text">
<% if item.objectNative && item.objectNative.activityMessage: %>
<%- item.objectNative.activityMessage(item) %>
<% else: %>
Need objectNative in item <%= item.object %>.find(<%= item.o_id %>)
<% end %>
</span>
<%- @humanTime(item.created_at, false, 'activity-time') %>
</a>
<div class="activity-remove js-remove">
<div class="activity-remove-icon-holder">
<%- @Icon('diagonal-cross') %>
</div>
</div>
</div>
</div>
<% end %>
<% else: %>
<div class="activity-placeholder">
<%- @T("No unread Notifications for you. :) ") %>
</div>
<% end %>
<div class="activity-placeholder js-noNotifications">
<%- @T("No unread Notifications for you. :) ") %>
</div>
<div class="js-items"></div>
</div>

View file

@ -1,30 +0,0 @@
<% if @items.length: %>
<% for item in @items: %>
<div class="activity-entry activity-entry--removeable<% if item.seen: %> is-inactive<% end %>" data-id="<%- item.id %>">
<a class="activity-avatar user-popover" data-id="<%= item.created_by_id %>" <% if item.created_by_id isnt 1: %>href="<%- item.created_by.uiUrl() %>"<% end %>>
<%- item.created_by.avatar() %>
</a>
<div class="activity-body">
<a class="activity-message js-locationVerify" href="<%- item.link %>">
<span class="activity-text">
<% if item.objectNative && item.objectNative.activityMessage: %>
<%- item.objectNative.activityMessage(item) %>
<% else: %>
Need objectNative in item <%= item.object %>.find(<%= item.o_id %>)
<% end %>
</span>
<%- @humanTime(item.created_at, false, 'activity-time') %>
</a>
<div class="activity-remove js-remove">
<div class="activity-remove-icon-holder">
<%- @Icon('diagonal-cross') %>
</div>
</div>
</div>
</div>
<% end %>
<% else: %>
<div class="activity-placeholder">
<%- @T("No unread Notifications for you. :) ") %>
</div>
<% end %>

View file

@ -0,0 +1,22 @@
<div class="js-item activity-entry activity-entry--removeable<% if @item.seen: %> is-inactive<% end %>" data-id="<%- @item.id %>">
<a class="activity-avatar user-popover" data-id="<%= @item.created_by_id %>" <% if @item.created_by_id isnt 1: %>href="<%- @item.created_by.uiUrl() %>"<% end %>>
<%- @item.created_by.avatar() %>
</a>
<div class="activity-body">
<a class="activity-message js-locationVerify" href="<%- @item.link %>">
<span class="activity-text">
<% if @item.objectNative && @item.objectNative.activityMessage: %>
<%- @item.objectNative.activityMessage(@item) %>
<% else: %>
Need objectNative in item <%= @item.object %>.find(<%= @item.o_id %>)
<% end %>
</span>
<%- @humanTime(@item.created_at, false, 'activity-time') %>
</a>
<div class="activity-remove js-remove">
<div class="activity-remove-icon-holder">
<%- @Icon('diagonal-cross') %>
</div>
</div>
</div>
</div>

View file

@ -3730,6 +3730,7 @@ footer {
padding-left: 0;
padding-right: 0;
overflow-y: auto;
margin-bottom: 10px;
}
&.is-overflowing .popover-notificationsHeader {

View file

@ -1400,4 +1400,74 @@ test("check formatTime format", function() {
equal(verify, result, string)
});
// check diffPosition
test("check diffPosition format", function() {
var a = [1,2,3,4]
var b = [1,2,3,4,5]
var result = [
{
position: 4,
id: 5,
},
]
var verify = App.Utils.diffPositionAdd(a, b)
deepEqual(verify, result)
a = [2,3,4]
b = [1,2,3,4]
result = [
{
position: 0,
id: 1,
},
]
verify = App.Utils.diffPositionAdd(a, b)
deepEqual(verify, result)
a = [2,3,4]
b = [1,2,3,4,5]
result = [
{
position: 0,
id: 1,
},
{
position: 4,
id: 5,
},
]
verify = App.Utils.diffPositionAdd(a, b)
deepEqual(verify, result)
a = [2,3,4]
b = [1,99,12,2,3,4,5]
result = [
{
position: 0,
id: 1,
},
{
position: 1,
id: 99,
},
{
position: 2,
id: 12,
},
{
position: 6,
id: 5,
},
]
verify = App.Utils.diffPositionAdd(a, b)
deepEqual(verify, result)
a = [4,3,1]
b = [1,2,3,4,5]
result = false
verify = App.Utils.diffPositionAdd(a, b)
deepEqual(verify, result)
});
}

View file

@ -0,0 +1,393 @@
# encoding: utf-8
require 'browser_test_helper'
class AgentTicketActionLevel9Test < TestCase
def test_online_notifications
@browser = browser_instance
login(
username: 'master@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all()
# create new ticket
ticket1 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'online notification #1',
body: 'online notification #1',
},
)
browser2 = browser_instance
login(
browser: browser2,
username: 'agent1@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all(browser: browser2)
click(
browser: browser2,
css: '.js-toggleNotifications',
)
click(
browser: browser2,
css: '.js-mark',
)
sleep 2
# remove all notificatons
online_notitifcation_close_all(
browser: browser2,
)
exists_not(
browser: browser2,
css: '.js-noNotifications.hide',
)
match(
browser: browser2,
css: '.js-noNotifications',
value: 'No unread Notifications',
)
exists(
browser: browser2,
css: '.js-mark.hide',
)
match_not(
browser: browser2,
css: '.js-notificationsCounter',
value: '\d',
no_quote: true,
)
ticket2 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'online notification #2',
body: 'online notification #2',
},
)
watch_for(
browser: browser2,
css: '.js-notificationsContainer .js-item',
value: 'online notification #2',
timeout: 10,
)
match(
browser: browser2,
css: '.js-notificationsCounter',
value: '1',
)
exists_not(
browser: browser2,
css: '.js-mark.hide',
)
ticket3 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'online notification #3',
body: 'online notification #3',
},
)
watch_for(
browser: browser2,
css: '.js-notificationsContainer .js-item',
value: 'online notification #3',
timeout: 6,
)
watch_for(
browser: browser2,
css: '.js-notificationsCounter',
value: '2',
)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item')
count = items.count
assert_equal(2, count)
items[1].click
click(
browser: browser2,
css: '.js-toggleNotifications',
)
watch_for(
browser: browser2,
css: '.js-notificationsCounter',
value: '1',
)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item')
assert_equal(2, items.count)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item.is-inactive')
assert_equal(1, items.count)
ticket4 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'online notification #4',
body: 'online notification #4',
},
)
watch_for(
browser: browser2,
css: '.js-notificationsCounter',
value: '2',
)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item')
assert_equal(3, items.count)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item.is-inactive')
assert_equal(1, items.count)
click(
browser: browser2,
css: '.js-mark',
)
sleep 3
items = browser2.find_elements(css: '.js-notificationsContainer .js-item')
assert_equal(3, items.count)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item.is-inactive')
assert_equal(3, items.count)
match_not(
browser: browser2,
css: '.js-notificationsCounter',
value: '\d',
no_quote: true,
)
ticket5 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'online notification #5',
body: 'online notification #5',
},
)
watch_for(
browser: browser2,
css: '.js-notificationsCounter',
value: '1',
)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item')
assert_equal(4, items.count)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item.is-inactive')
assert_equal(3, items.count)
# Note: title update will generate extra notification - so we will have 5
ticket_update(
data: {
title: 'online notification #5/5',
state: 'closed',
},
)
watch_for(
browser: browser2,
css: '.js-notificationsContainer .js-item',
value: 'online notification #5/5',
timeout: 20,
)
watch_for(
browser: browser2,
css: '.js-notificationsContainer .js-item.is-inactive',
value: 'online notification #5/5',
timeout: 20,
)
match_not(
browser: browser2,
css: '.js-notificationsCounter',
value: '\d',
no_quote: true,
)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item')
assert_equal(6, items.count)
items = browser2.find_elements(css: '.js-notificationsContainer .js-item.is-inactive')
assert_equal(6, items.count)
end
def test_online_notifications_render
@browser = browser_instance
login(
username: 'master@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all()
browser2 = browser_instance
login(
browser: browser2,
username: 'agent1@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all(browser: browser2)
click(
browser: browser2,
css: '.js-toggleNotifications',
)
online_notitifcation_close_all(browser: browser2)
ticket1 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'online notification render #1',
body: 'online notification render #1',
},
)
ticket2 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'online notification render #2',
body: 'online notification render #2',
},
)
watch_for(
browser: browser2,
css: '.js-notificationsCounter',
value: '2',
)
execute(
browser: browser2,
js: '$(".js-notificationsContainer .js-items .js-item:nth-child(1) .activity-text").text("render test 2")',
)
execute(
browser: browser2,
js: '$(".js-notificationsContainer .js-items .js-item:nth-child(2) .activity-text").text("render test 1")',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(1) .activity-text',
value: 'render test 2',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(2) .activity-text',
value: 'render test 1',
)
ticket3 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'online notification render #3',
body: 'online notification render #3',
},
)
watch_for(
browser: browser2,
css: '.js-notificationsCounter',
value: '3',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(1) .activity-text',
value: 'online notification render #3',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(2) .activity-text',
value: 'render test 2',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(3) .activity-text',
value: 'render test 1',
)
ticket_update(
data: {
state: 'closed',
},
)
watch_for(
browser: browser2,
css: '.js-notificationsCounter',
value: '2',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .is-inactive.js-item:nth-child(1) .activity-text',
value: 'online notification render #3',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .is-inactive.js-item:nth-child(2) .activity-text',
value: 'online notification render #3',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(3) .activity-text',
value: 'render test 2',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(4) .activity-text',
value: 'render test 1',
)
execute(
browser: browser2,
js: '$(".js-notificationsContainer .js-items .js-item:nth-child(2) .activity-text").text("render test 3")',
)
close_online_notitifcation(
browser: browser2,
data: {
position: 3,
},
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .is-inactive.js-item:nth-child(1) .activity-text',
value: 'online notification render #3',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(2) .activity-text',
value: 'render test 3',
)
match(
browser: browser2,
css: '.js-notificationsContainer .js-items .js-item:nth-child(3) .activity-text',
value: 'render test 1',
)
end
end

View file

@ -186,7 +186,7 @@ class TestCase < Test::Unit::TestCase
end
instance.find_elements(css: '#login button')[0].click
sleep 5
sleep 4
login = instance.find_elements(css: '.user-menu .user a')[0].attribute('title')
if login != params[:username]
screenshot(browser: instance, comment: 'login_failed')
@ -264,7 +264,7 @@ class TestCase < Test::Unit::TestCase
return if !clues
instance.execute_script("$('.js-modal--clue .js-close').click()")
assert(true, 'clues closed')
sleep 2
sleep 1
end
=begin
@ -1222,7 +1222,7 @@ wait untill text in selector disabppears
instance = params[:browser] || @browser
(1..100).each do
sleep 0.1
#sleep 0.5
begin
if instance.find_elements(css: '.navigation .tasks .task:first-child')[0]
instance.mouse.move_to(instance.find_elements(css: '.navigation .tasks .task:first-child')[0])
@ -1247,6 +1247,85 @@ wait untill text in selector disabppears
assert(true, 'all tasks closed')
end
=begin
close_online_notitifcation(
browser: browser1,
data: {
#title: 'some title',
position: 3,
},
)
=end
def close_online_notitifcation(params = {})
switch_window_focus(params)
log('close_online_notitifcation', params)
instance = params[:browser] || @browser
data = params[:data]
if data[:title]
element = instance.find_elements(partial_link_text: data[:title])[0]
if !element
screenshot(browser: instance, comment: 'close_online_notitifcation')
raise "no online notification with title '#{data[:title]}' found"
end
instance.mouse.move_to(element)
sleep 0.1
instance.execute_script("$('.js-notificationsContainer .js-items .js-item .activity-text:contains(\"#{data[:title]}\") .js-remove').first().click()")
else
css = ".js-notificationsContainer .js-items .js-item:nth-child(#{data[:position]})"
element = instance.find_elements(css: css)[0]
if !element
screenshot(browser: instance, comment: 'close_online_notitifcation')
raise "no online notification with postion '#{css}' found"
end
instance.mouse.move_to(element)
sleep 0.1
instance.find_elements(css: "#{css} .js-remove")[0].click
end
true
end
=begin
online_notitifcation_close_all(
browser: browser1,
)
=end
def online_notitifcation_close_all(params = {})
switch_window_focus(params)
log('online_notitifcation_close_all', params)
instance = params[:browser] || @browser
(1..100).each do
sleep 0.5
begin
if instance.find_elements(css: '.js-notificationsContainer .js-item:first-child')[0]
instance.mouse.move_to(instance.find_elements(css: '.js-notificationsContainer .js-item:first-child')[0])
sleep 0.1
click_element = instance.find_elements(css: '.js-notificationsContainer .js-item:first-child .js-remove')[0]
if click_element
click_element.click
end
else
break
end
rescue
# try again
end
end
assert(true, 'all online notification closed')
end
=begin
empty_search(
@ -1525,6 +1604,7 @@ wait untill text in selector disabppears
customer: 'nico',
group: 'Users', # optional / '-NONE-' # if group selection should not be shown
priority: '2 normal',
state: 'open',
title: 'overview #1',
body: 'overview #1',
},
@ -1598,6 +1678,14 @@ wait untill text in selector disabppears
mute_log: true,
)
end
if data[:state]
select(
browser: instance,
css: '.active .newTicket select[name="state_id"]',
value: data[:state],
mute_log: true,
)
end
if data[:title]
set(
browser: instance,