Skip to content

Commit

Permalink
Fixes zammad#4476 - Show live users on both desktop and mobile.
Browse files Browse the repository at this point in the history
  • Loading branch information
mantas authored and dominikklein committed Jan 31, 2023
1 parent 3e04aa6 commit 4445d9e
Show file tree
Hide file tree
Showing 12 changed files with 438 additions and 167 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,79 @@ class App.TaskbarWatcher extends App.Controller
return if !preferences.tasks

currentUserId = App.Session.get('id')
for watcher in preferences.tasks
if watcher.user_id != currentUserId
if watcher.last_contact
watcher.idle = false
diff = new Date().getTime() - new Date(watcher.last_contact).getTime()
if diff > 300000
watcher.idle = true

return if !@diffrence(@lastTasks, preferences.tasks)
@markIdlePreferences(preferences, currentUserId)

return if _.isEqual(@lastTasks, preferences.tasks)
@lastTasks = clone(preferences.tasks)

watchers = []
filteredTasks = _.filter preferences.tasks, (watcher) -> watcher.user_id != currentUserId
@el.empty()

selfTask = _.find preferences.tasks, (watcher) -> watcher.user_id == currentUserId
filteredTasks = _.filter preferences.tasks, (watcher) -> watcher.user_id != currentUserId

if selfTask && selfTask.apps.mobile?.changed
@renderSelfWatcher(selfTask, filteredTasks.length)

for watcher, i in filteredTasks
cssClass = []
if watcher.idle
cssClass.push('avatar--idle')
if watcher.changed
cssClass.push('avatar--changed')
@renderOtherWatcher(watcher, i != filteredTasks.length - 1)

markIdlePreferences: (preferences, userId) ->
for watcher in preferences.tasks
if watcher.user_id != userId
for key in _.keys(watcher.apps)
if watcher.apps[key].last_contact
last_contact_date = new Date(watcher.apps[key].last_contact)
diff = new Date().getTime() - last_contact_date.getTime()
watcher.apps[key].idle = diff > 300000

preferences

renderSelfWatcher: (task, hasSpacer) =>
@renderWatcher(task, hasSpacer, 'mobile-edit', 'mobile')

renderOtherWatcher: (task, hasSpacer) =>
keys = _.keys(task.apps)

if keys.length > 1
if new Date(task.apps.desktop.last_contact) > new Date(task.apps.mobile.last_contact)
platform = 'desktop'
else
cssClass.push('avatar--not-changed')
@el.append('<div class="js-avatar"></div>')
platform = 'mobile'
else
platform = keys[0]

if i != filteredTasks.length - 1
@el.append('<div class="half-spacer"></div>')
if task.apps.desktop?.changed || task.apps.mobile?.changed
icon = 'pen'
else if platform == 'mobile'
icon = 'mobile'

avatar = new App.WidgetAvatar(
el: @el.find('.js-avatar').last()
object_id: watcher.user_id
size: 40
cssClass: cssClass.join(' ')
)
@renderWatcher(task, hasSpacer, icon, platform)

if watcher.changed
status = $('<div class="avatar-status"></div>')
status.append App.Utils.icon('pen')
avatar.el.find('.avatar').append status
renderWatcher: (watcher, needsSpacer, icon, platformKey) =>
cssClass = []
if watcher.apps[platformKey].idle
cssClass.push('avatar--idle')
if watcher.apps[platformKey].changed
cssClass.push('avatar--changed')
else
cssClass.push('avatar--not-changed')
@el.append('<div class="js-avatar"></div>')

if needsSpacer
@el.append('<div class="half-spacer"></div>')

avatar = new App.WidgetAvatar(
el: @el.find('.js-avatar').last()
object_id: watcher.user_id
size: 40
cssClass: cssClass.join(' ')
)

if icon
status = $('<div class="avatar-status"></div>')
status.append App.Utils.icon(icon)

avatar.el.find('.avatar').append status

start: =>
@intervalId = @interval(
Expand All @@ -63,13 +98,3 @@ class App.TaskbarWatcher extends App.Controller
stop: =>
return if !@intervalId
@clearInterval(@intervalId)

diffrence: (lastTasks, newTasks) ->
return true if !lastTasks
return true if lastTasks.length != newTasks.length
for taskPosition of lastTasks
return true if !lastTasks[taskPosition] || !newTasks[taskPosition]
return true if lastTasks[taskPosition].user_id != newTasks[taskPosition].user_id
return true if lastTasks[taskPosition].changed != newTasks[taskPosition].changed
return true if lastTasks[taskPosition].idle != newTasks[taskPosition].idle
false
2 changes: 2 additions & 0 deletions app/assets/stylesheets/svg-dimensions.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@
.icon-microsoft-button { width: 29px; height: 24px; }
.icon-minus-small { width: 16px; height: 16px; }
.icon-minus { width: 20px; height: 20px; }
.icon-mobile-edit { width: 9px; height: 14px; }
.icon-mobile { width: 9px; height: 14px; }
.icon-mood-bad { width: 60px; height: 59px; }
.icon-mood-good { width: 60px; height: 59px; }
.icon-mood-ok { width: 60px; height: 59px; }
Expand Down
10 changes: 6 additions & 4 deletions app/graphql/gql/subscriptions/ticket_live_user_updates.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,21 @@ def response(taskbar_item)
{ live_users: transform_tasks(taskbar_item) }
end

def transform_tasks(taskbar_item)
def transform_tasks(taskbar_item) # rubocop:disable Metrics/AbcSize
tasks = taskbar_item.preferences[:tasks]
return [] if tasks.blank?

tasks = tasks.reject { |task| task[:user_id].eql?(context.current_user.id) }
return [] if tasks.blank?

tasks.map do |task|
app_item = task[:apps].values.min_by { |app| app[:last_contact] }

{
user: ::User.find_by(id: task[:user_id]),
editing: task[:changed],
last_interaction: task[:last_contact],
apps: task[:apps],
editing: app_item[:changed],
last_interaction: app_item[:last_contact],
apps: task[:apps].keys,
}
end
end
Expand Down
16 changes: 8 additions & 8 deletions app/models/taskbar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def as_json(options = {})
end

def preferences_task_info
output = { user_id:, last_contact:, changed: state_changed?, apps: [app] }
output = { user_id:, apps: { app.to_sym => { last_contact: last_contact, changed: state_changed? } } }
output[:id] = id if persisted?
output
end
Expand Down Expand Up @@ -118,22 +118,22 @@ def update_preferences_infos
end

def collect_related_tasks
related_taskbars
.map(&:preferences_task_info)
.push(preferences_task_info)
related_taskbars.map(&:preferences_task_info)
.tap { |arr| arr.push(preferences_task_info) if !destroyed? }
.each_with_object({}) { |elem, memo| reduce_related_tasks(elem, memo) }
.values
.sort_by { |elem| elem[:id] || Float::MAX } # sort by IDs to pass old tests
end

def reduce_related_tasks(elem, memo)
if memo[elem[:user_id]]
memo[elem[:user_id]][:apps].concat elem[:apps]
memo[elem[:user_id]][:changed] = true if elem[:changed]
key = elem[:user_id]

if memo[key]
memo[key].deep_merge! elem
return
end

memo[elem[:user_id]] = elem
memo[key] = elem
end

def update_related_taskbars(preferences)
Expand Down
14 changes: 14 additions & 0 deletions db/migrate/20230129220648_taskbar_update_preference_tasks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/

class TaskbarUpdatePreferenceTasks < ActiveRecord::Migration[6.1]
def up
# return if it's a new setup
return if !Setting.exists?(name: 'system_init_done')

Taskbar.in_batches.each_record do |elem|
elem.preferences ||= {}
elem.preferences[:tasks] = elem.send(:collect_related_tasks)
elem.save!
end
end
end
4 changes: 4 additions & 0 deletions public/assets/images/icons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/assets/images/icons/mobile-edit.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/assets/images/icons/mobile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions spec/db/migrate/taskbar_update_preference_tasks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/

require 'rails_helper'

RSpec.describe TaskbarUpdatePreferenceTasks, type: :db_migration do
let(:taskbar) { create(:taskbar) }

it 'updates taskbar tasks' do
freeze_time

expect { migrate }
.to change { taskbar.reload.preferences }
.to({
tasks: [
{ user_id: 1, id: taskbar.id, apps: { desktop: { last_contact: taskbar.last_contact, changed: false } } }
]
})
end
end
Loading

0 comments on commit 4445d9e

Please sign in to comment.