From 4445d9e76172fab1265b8d054b80a3d387b23492 Mon Sep 17 00:00:00 2001 From: Mantas Masalskis Date: Tue, 31 Jan 2023 21:33:13 +0100 Subject: [PATCH] Fixes #4476 - Show live users on both desktop and mobile. --- .../ticket_zoom/taskbar_watcher.coffee | 105 +++++---- app/assets/stylesheets/svg-dimensions.css | 2 + .../subscriptions/ticket_live_user_updates.rb | 10 +- app/models/taskbar.rb | 16 +- ...9220648_taskbar_update_preference_tasks.rb | 14 ++ public/assets/images/icons.svg | 4 + public/assets/images/icons/mobile-edit.svg | 4 + public/assets/images/icons/mobile.svg | 4 + .../taskbar_update_preference_tasks_spec.rb | 19 ++ spec/models/taskbar_spec.rb | 210 +++++++++-------- spec/system/popover_spec.rb | 3 +- .../simultaneously_with_two_user_spec.rb | 214 ++++++++++++++++-- 12 files changed, 438 insertions(+), 167 deletions(-) create mode 100644 db/migrate/20230129220648_taskbar_update_preference_tasks.rb create mode 100644 public/assets/images/icons/mobile-edit.svg create mode 100644 public/assets/images/icons/mobile.svg create mode 100644 spec/db/migrate/taskbar_update_preference_tasks_spec.rb diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/taskbar_watcher.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/taskbar_watcher.coffee index 114124e92c33..5653d069b902 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/taskbar_watcher.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/taskbar_watcher.coffee @@ -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('
') + platform = 'mobile' + else + platform = keys[0] - if i != filteredTasks.length - 1 - @el.append('
') + 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 = $('
') - 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('
') + + if needsSpacer + @el.append('
') + + avatar = new App.WidgetAvatar( + el: @el.find('.js-avatar').last() + object_id: watcher.user_id + size: 40 + cssClass: cssClass.join(' ') + ) + + if icon + status = $('
') + status.append App.Utils.icon(icon) + + avatar.el.find('.avatar').append status start: => @intervalId = @interval( @@ -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 diff --git a/app/assets/stylesheets/svg-dimensions.css b/app/assets/stylesheets/svg-dimensions.css index 311b4516133c..d721c60aa10f 100644 --- a/app/assets/stylesheets/svg-dimensions.css +++ b/app/assets/stylesheets/svg-dimensions.css @@ -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; } diff --git a/app/graphql/gql/subscriptions/ticket_live_user_updates.rb b/app/graphql/gql/subscriptions/ticket_live_user_updates.rb index a55bbbe982e3..62f12e3f4549 100644 --- a/app/graphql/gql/subscriptions/ticket_live_user_updates.rb +++ b/app/graphql/gql/subscriptions/ticket_live_user_updates.rb @@ -29,7 +29,7 @@ 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? @@ -37,11 +37,13 @@ def transform_tasks(taskbar_item) 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 diff --git a/app/models/taskbar.rb b/app/models/taskbar.rb index f18100e0b3ca..423ed9c1e7ad 100644 --- a/app/models/taskbar.rb +++ b/app/models/taskbar.rb @@ -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 @@ -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) diff --git a/db/migrate/20230129220648_taskbar_update_preference_tasks.rb b/db/migrate/20230129220648_taskbar_update_preference_tasks.rb new file mode 100644 index 000000000000..bd7baeb4060e --- /dev/null +++ b/db/migrate/20230129220648_taskbar_update_preference_tasks.rb @@ -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 diff --git a/public/assets/images/icons.svg b/public/assets/images/icons.svg index 7a724a1fc640..7de99668bde2 100644 --- a/public/assets/images/icons.svg +++ b/public/assets/images/icons.svg @@ -467,6 +467,10 @@ minus + + + + mood-bad diff --git a/public/assets/images/icons/mobile-edit.svg b/public/assets/images/icons/mobile-edit.svg new file mode 100644 index 000000000000..b0f778f3dd6f --- /dev/null +++ b/public/assets/images/icons/mobile-edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/images/icons/mobile.svg b/public/assets/images/icons/mobile.svg new file mode 100644 index 000000000000..b1ba6150ae3c --- /dev/null +++ b/public/assets/images/icons/mobile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/spec/db/migrate/taskbar_update_preference_tasks_spec.rb b/spec/db/migrate/taskbar_update_preference_tasks_spec.rb new file mode 100644 index 000000000000..5382de456870 --- /dev/null +++ b/spec/db/migrate/taskbar_update_preference_tasks_spec.rb @@ -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 diff --git a/spec/models/taskbar_spec.rb b/spec/models/taskbar_spec.rb index 16b44df90a86..0bb4260bb6ac 100644 --- a/spec/models/taskbar_spec.rb +++ b/spec/models/taskbar_spec.rb @@ -160,16 +160,16 @@ taskbar1.reload expect(taskbar1.preferences[:tasks].count).to eq(2) expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar1.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar1.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) taskbar2.reload expect(taskbar2.preferences[:tasks].count).to eq(2) expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar2.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar2.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) taskbar3 = described_class.create( key: 'Ticket-4444', @@ -186,21 +186,21 @@ taskbar1.reload expect(taskbar1.preferences[:tasks].count).to eq(2) expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar1.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar1.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) taskbar2.reload expect(taskbar2.preferences[:tasks].count).to eq(2) expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar2.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar2.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) taskbar3.reload expect(taskbar3.preferences[:tasks].count).to eq(1) expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2) - expect(taskbar3.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) agent_id = create(:agent).id UserInfo.current_user_id = agent_id @@ -220,34 +220,34 @@ taskbar1.reload expect(taskbar1.preferences[:tasks].count).to eq(3) expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar1.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar1.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar1.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar2.reload expect(taskbar2.preferences[:tasks].count).to eq(3) expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar2.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar2.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar2.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar3.reload expect(taskbar3.preferences[:tasks].count).to eq(1) expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2) - expect(taskbar3.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) taskbar4.reload expect(taskbar4.preferences[:tasks].count).to eq(3) expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar4.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar4.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar4.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) UserInfo.current_user_id = 2 taskbar2.state = { article: {}, ticket: {} } @@ -256,34 +256,34 @@ taskbar1.reload expect(taskbar1.preferences[:tasks].count).to eq(3) expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar1.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar1.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar1.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar2.reload expect(taskbar2.preferences[:tasks].count).to eq(3) expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar2.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar2.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar2.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar3.reload expect(taskbar3.preferences[:tasks].count).to eq(1) expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2) - expect(taskbar3.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) taskbar4.reload expect(taskbar4.preferences[:tasks].count).to eq(3) expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar4.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar4.preferences[:tasks][1][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][1][:apps][:desktop][:changed]).to be(false) expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar4.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) UserInfo.current_user_id = 2 taskbar2.state = { article: { body: 'some body' }, ticket: {} } @@ -292,34 +292,34 @@ taskbar1.reload expect(taskbar1.preferences[:tasks].count).to eq(3) expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar1.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar1.preferences[:tasks][1][:changed]).to be(true) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar1.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar2.reload expect(taskbar2.preferences[:tasks].count).to eq(3) expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar2.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar2.preferences[:tasks][1][:changed]).to be(true) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar2.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar3.reload expect(taskbar3.preferences[:tasks].count).to eq(1) expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2) - expect(taskbar3.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) taskbar4.reload expect(taskbar4.preferences[:tasks].count).to eq(3) expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar4.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar4.preferences[:tasks][1][:changed]).to be(true) + expect(taskbar4.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar4.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) UserInfo.current_user_id = 1 taskbar1.state = { article: { body: '' }, ticket: { state_id: 123 } } @@ -328,34 +328,34 @@ taskbar1.reload expect(taskbar1.preferences[:tasks].count).to eq(3) expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar1.preferences[:tasks][0][:changed]).to be(true) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar1.preferences[:tasks][1][:changed]).to be(true) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar1.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar2.reload expect(taskbar2.preferences[:tasks].count).to eq(3) expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar2.preferences[:tasks][0][:changed]).to be(true) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar2.preferences[:tasks][1][:changed]).to be(true) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar2.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar3.reload expect(taskbar3.preferences[:tasks].count).to eq(1) expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2) - expect(taskbar3.preferences[:tasks][0][:changed]).to be(false) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) taskbar4.reload expect(taskbar4.preferences[:tasks].count).to eq(3) expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar4.preferences[:tasks][0][:changed]).to be(true) + expect(taskbar4.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar4.preferences[:tasks][1][:changed]).to be(true) + expect(taskbar4.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar4.preferences[:tasks][2][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) taskbar1_last_contact = taskbar1.last_contact.to_s taskbar2_last_contact = taskbar2.last_contact.to_s @@ -371,44 +371,44 @@ taskbar1.reload expect(taskbar1.preferences[:tasks].count).to eq(3) expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar1.preferences[:tasks][0][:changed]).to be(true) - expect(taskbar1.preferences[:tasks][0][:last_contact].to_s).to eq(taskbar1_last_contact) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:last_contact].to_s).to eq(taskbar1_last_contact) expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar1.preferences[:tasks][1][:changed]).to be(true) - expect(taskbar1.preferences[:tasks][1][:last_contact].to_s).to eq(taskbar2_last_contact) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:last_contact].to_s).to eq(taskbar2_last_contact) expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar1.preferences[:tasks][2][:changed]).to be(false) - expect(taskbar1.preferences[:tasks][2][:last_contact].to_s).to eq(taskbar4_last_contact) + expect(taskbar1.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][2][:apps][:desktop][:last_contact].to_s).to eq(taskbar4_last_contact) taskbar2.reload expect(taskbar2.preferences[:tasks].count).to eq(3) expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar2.preferences[:tasks][0][:changed]).to be(true) - expect(taskbar2.preferences[:tasks][0][:last_contact].to_s).to eq(taskbar1_last_contact) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:last_contact].to_s).to eq(taskbar1_last_contact) expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar2.preferences[:tasks][1][:changed]).to be(true) - expect(taskbar2.preferences[:tasks][1][:last_contact].to_s).to eq(taskbar2_last_contact) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:last_contact].to_s).to eq(taskbar2_last_contact) expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar2.preferences[:tasks][2][:changed]).to be(false) - expect(taskbar2.preferences[:tasks][2][:last_contact].to_s).to eq(taskbar4_last_contact) + expect(taskbar2.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][2][:apps][:desktop][:last_contact].to_s).to eq(taskbar4_last_contact) taskbar3.reload expect(taskbar3.preferences[:tasks].count).to eq(1) expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2) - expect(taskbar3.preferences[:tasks][0][:changed]).to be(false) - expect(taskbar3.preferences[:tasks][0][:last_contact].to_s).to eq(taskbar3_last_contact) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:last_contact].to_s).to eq(taskbar3_last_contact) taskbar4.reload expect(taskbar4.preferences[:tasks].count).to eq(3) expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar4.preferences[:tasks][0][:changed]).to be(true) - expect(taskbar4.preferences[:tasks][0][:last_contact].to_s).to eq(taskbar1_last_contact) + expect(taskbar4.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) + expect(taskbar4.preferences[:tasks][0][:apps][:desktop][:last_contact].to_s).to eq(taskbar1_last_contact) expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar4.preferences[:tasks][1][:changed]).to be(true) - expect(taskbar4.preferences[:tasks][1][:last_contact].to_s).to eq(taskbar2_last_contact) + expect(taskbar4.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) + expect(taskbar4.preferences[:tasks][1][:apps][:desktop][:last_contact].to_s).to eq(taskbar2_last_contact) expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar4.preferences[:tasks][2][:changed]).to be(false) - expect(taskbar4.preferences[:tasks][2][:last_contact].to_s).to eq(taskbar4_last_contact) + expect(taskbar4.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][2][:apps][:desktop][:last_contact].to_s).to eq(taskbar4_last_contact) UserInfo.current_user_id = 2 taskbar2.state = { article: { body: 'some body 222' }, ticket: {} } @@ -418,44 +418,44 @@ taskbar1.reload expect(taskbar1.preferences[:tasks].count).to eq(3) expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar1.preferences[:tasks][0][:changed]).to be(true) - expect(taskbar1.preferences[:tasks][0][:last_contact].to_s).to eq(taskbar1_last_contact) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) + expect(taskbar1.preferences[:tasks][0][:apps][:desktop][:last_contact].to_s).to eq(taskbar1_last_contact) expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar1.preferences[:tasks][1][:changed]).to be(true) - expect(taskbar1.preferences[:tasks][1][:last_contact].to_s).not_to eq(taskbar2_last_contact) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) + expect(taskbar1.preferences[:tasks][1][:apps][:desktop][:last_contact].to_s).not_to eq(taskbar2_last_contact) expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar1.preferences[:tasks][2][:changed]).to be(false) - expect(taskbar1.preferences[:tasks][2][:last_contact].to_s).to eq(taskbar4_last_contact) + expect(taskbar1.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) + expect(taskbar1.preferences[:tasks][2][:apps][:desktop][:last_contact].to_s).to eq(taskbar4_last_contact) taskbar2.reload expect(taskbar2.preferences[:tasks].count).to eq(3) expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar2.preferences[:tasks][0][:changed]).to be(true) - expect(taskbar2.preferences[:tasks][0][:last_contact].to_s).to eq(taskbar1_last_contact) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) + expect(taskbar2.preferences[:tasks][0][:apps][:desktop][:last_contact].to_s).to eq(taskbar1_last_contact) expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar2.preferences[:tasks][1][:changed]).to be(true) - expect(taskbar2.preferences[:tasks][1][:last_contact].to_s).not_to eq(taskbar2_last_contact) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) + expect(taskbar2.preferences[:tasks][1][:apps][:desktop][:last_contact].to_s).not_to eq(taskbar2_last_contact) expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar2.preferences[:tasks][2][:changed]).to be(false) - expect(taskbar2.preferences[:tasks][2][:last_contact].to_s).to eq(taskbar4_last_contact) + expect(taskbar2.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) + expect(taskbar2.preferences[:tasks][2][:apps][:desktop][:last_contact].to_s).to eq(taskbar4_last_contact) taskbar3.reload expect(taskbar3.preferences[:tasks].count).to eq(1) expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2) - expect(taskbar3.preferences[:tasks][0][:changed]).to be(false) - expect(taskbar3.preferences[:tasks][0][:last_contact].to_s).to eq(taskbar3_last_contact) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:changed]).to be(false) + expect(taskbar3.preferences[:tasks][0][:apps][:desktop][:last_contact].to_s).to eq(taskbar3_last_contact) taskbar4.reload expect(taskbar4.preferences[:tasks].count).to eq(3) expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1) - expect(taskbar4.preferences[:tasks][0][:changed]).to be(true) - expect(taskbar4.preferences[:tasks][0][:last_contact].to_s).to eq(taskbar1_last_contact) + expect(taskbar4.preferences[:tasks][0][:apps][:desktop][:changed]).to be(true) + expect(taskbar4.preferences[:tasks][0][:apps][:desktop][:last_contact].to_s).to eq(taskbar1_last_contact) expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2) - expect(taskbar4.preferences[:tasks][1][:changed]).to be(true) - expect(taskbar4.preferences[:tasks][1][:last_contact].to_s).not_to eq(taskbar2_last_contact) + expect(taskbar4.preferences[:tasks][1][:apps][:desktop][:changed]).to be(true) + expect(taskbar4.preferences[:tasks][1][:apps][:desktop][:last_contact].to_s).not_to eq(taskbar2_last_contact) expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(agent_id) - expect(taskbar4.preferences[:tasks][2][:changed]).to be(false) - expect(taskbar4.preferences[:tasks][2][:last_contact].to_s).to eq(taskbar4_last_contact) + expect(taskbar4.preferences[:tasks][2][:apps][:desktop][:changed]).to be(false) + expect(taskbar4.preferences[:tasks][2][:apps][:desktop][:last_contact].to_s).to eq(taskbar4_last_contact) travel_back @@ -468,21 +468,27 @@ taskbar = create(:taskbar) expect(taskbar.preferences_task_info) - .to eq({ id: taskbar.id, user_id: 1, last_contact: taskbar.last_contact, changed: false, apps: %w[desktop] }) + .to eq({ + id: taskbar.id, user_id: 1, apps: { desktop: { last_contact: taskbar.last_contact, changed: false } } + }) end it 'returns task info for an existing taskbar with changes' do taskbar = create(:taskbar, state: { a: 123 }) expect(taskbar.preferences_task_info) - .to eq({ id: taskbar.id, user_id: 1, last_contact: taskbar.last_contact, changed: true, apps: %w[desktop] }) + .to eq({ + id: taskbar.id, user_id: 1, apps: { desktop: { last_contact: taskbar.last_contact, changed: true } } + }) end it 'returns task info for a new taskbar' do taskbar = build(:taskbar) expect(taskbar.preferences_task_info) - .to eq({ user_id: 1, last_contact: taskbar.last_contact, changed: false, apps: %w[desktop] }) + .to eq({ + user_id: 1, apps: { desktop: { last_contact: taskbar.last_contact, changed: false } } + }) end end @@ -539,7 +545,8 @@ taskbar.update! state: { a: :b } - expect(taskbar.preferences[:tasks]).to include(include(user_id: other_user.id, apps: include('desktop', 'mobile'))) + expect(taskbar.preferences[:tasks]) + .to include(include(user_id: other_user.id, apps: have_key(:desktop).and(have_key(:mobile)))) end it 'updates related items when updating a taskbar' do @@ -579,10 +586,19 @@ expect(new_taskbar.send(:collect_related_tasks)) .to eq([taskbar_2.preferences_task_info, new_taskbar.preferences_task_info]) end + + it 'do not include task of the destroyed taskbar' do + taskbar_1 + + taskbar_2.destroy! + + expect(taskbar_2.send(:collect_related_tasks)) + .to eq([taskbar_1.preferences_task_info]) + end end describe '#reduce_related_tasks' do - let(:elem) { { user_id: 123, apps: ['desktop'], changed: false } } + let(:elem) { { user_id: 123, changed: { desktop: false } } } let(:memo) { {} } it 'adds new task details' do @@ -590,18 +606,18 @@ taskbar.send(:reduce_related_tasks, elem, memo) - expect(memo).to include(elem[:user_id] => include(apps: include('desktop'), changed: false)) + expect(memo).to include(elem[:user_id] => include(changed: include(desktop: false))) end it 'extends existing task details with additional apps' do taskbar = create(:taskbar) - another_elem = { user_id: 123, apps: ['mobile'], changed: true } + another_elem = { user_id: 123, changed: { mobile: true } } taskbar.send(:reduce_related_tasks, elem, memo) taskbar.send(:reduce_related_tasks, another_elem, memo) - expect(memo).to include(elem[:user_id] => include(apps: include('desktop', 'mobile'), changed: true)) + expect(memo).to include(elem[:user_id] => include(changed: include(desktop: false, mobile: true))) end end diff --git a/spec/system/popover_spec.rb b/spec/system/popover_spec.rb index be50397fa1fd..eb4a769a5bc1 100644 --- a/spec/system/popover_spec.rb +++ b/spec/system/popover_spec.rb @@ -68,7 +68,8 @@ within :active_content do taskbar.update! preferences: { 'tasks' => [{ - 'user_id' => create(:admin).id + 'user_id' => create(:admin).id, + 'apps' => taskbar.preferences['tasks'].first['apps'], }] } TransactionDispatcher.commit diff --git a/spec/system/ticket/update/simultaneously_with_two_user_spec.rb b/spec/system/ticket/update/simultaneously_with_two_user_spec.rb index fd8feebd130e..7c09ffa373db 100644 --- a/spec/system/ticket/update/simultaneously_with_two_user_spec.rb +++ b/spec/system/ticket/update/simultaneously_with_two_user_spec.rb @@ -7,13 +7,63 @@ let(:ticket) { create(:ticket, group: group) } let(:agent) { User.find_by(login: 'agent1@example.com') } - def check_avatar(text, changed: true) - changed_class = changed ? 'changed' : 'not-changed' + # rubocop:disable RSpec/InstanceVariable + define :have_avatar do |expected| + chain(:changed, :text) - within(:active_content) do - expect(page).to have_css(".js-attributeBar .js-avatar .avatar--#{changed_class}", text: text) + match do + elem = find_element + + return false if elem.nil? + + return true if !@icon && !@no_icon + + return elem.has_no_css? '.icon' if @no_icon + + elem.has_css? ".icon-#{@icon}" + end + + def find_element + if expected.is_a? User + actual.find "#{base_selector}#{select_by_user}" + else + actual.find base_selector, text: expected + end + rescue + nil + end + + match_when_negated do + if expected.is_a? User + return actual.has_no_css? "#{base_selector}#{select_by_user}" + end + + actual.has_no_css? base_selector, text: expected + end + + chain :changed! do + @changed = true + end + + chain :with_icon do |icon| + @icon = icon + end + + chain :with_no_icon! do + @no_icon = true + end + + def select_by_user + "[data-id='#{expected.id}']" + end + + def base_selector + changed_class = @changed ? 'changed' : 'not-changed' + + ".js-attributeBar .js-avatar .avatar--#{changed_class}" end end + # rubocop:enable RSpec/InstanceVariable def check_taskbar_tab(ticket_id, title: nil, modified: false) tab_data_key = "Ticket-#{ticket_id}" @@ -45,10 +95,10 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) end it 'avatar from other user should be visible in ticket zoom' do - check_avatar('AT', changed: false) + expect(page).to have_avatar('AT') using_session(:second_browser) do - check_avatar('TA', changed: false) + expect(page).to have_avatar('TA') end end @@ -59,10 +109,10 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) expect(page).to have_css('.js-reset') end - check_avatar('AT', changed: false) + expect(page).to have_avatar('AT') using_session(:second_browser) do - check_avatar('TA', changed: true) + expect(page).to have_avatar('TA').changed! within(:active_content) do find('.js-textarea').send_keys('some other note') @@ -71,7 +121,7 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) end end - check_avatar('AT', changed: true) + expect(page).to have_avatar('AT').changed! using_session(:second_browser) do within(:active_content) do @@ -81,10 +131,10 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) expect(page).to have_css('.article-content', text: 'some other note') end - check_avatar('TA', changed: true) + expect(page).to have_avatar('TA').changed! end - check_avatar('AT', changed: false) + expect(page).to have_avatar('AT') check_taskbar_tab(ticket.id, title: ticket.title, modified: true) within(:active_content) do @@ -97,7 +147,7 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) end using_session(:second_browser) do - check_avatar('TA', changed: false) + expect(page).to have_avatar('TA') expect(page).to have_css('.article-content', text: 'some note') check_taskbar_tab(ticket.id, title: ticket.title, modified: true) @@ -109,11 +159,11 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) using_session(:second_browser) do refresh - check_avatar('TA', changed: false) + expect(page).to have_avatar('TA') expect(page).to have_no_css('.js-reset') end - check_avatar('AT', changed: false) + expect(page).to have_avatar('AT') expect(page).to have_no_css('.js-reset') end @@ -125,7 +175,7 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) expect(page).to have_css('.js-reset') end - check_avatar('TA', changed: false) + expect(page).to have_avatar('TA') # We need to wait for the auto save feature. wait.until do @@ -135,7 +185,7 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) refresh end - check_avatar('AT', changed: true) + expect(page).to have_avatar('AT').changed! using_session(:second_browser) do refresh @@ -146,7 +196,7 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) end end - check_avatar('AT', changed: false) + expect(page).to have_avatar('AT') end it 'change title with second user' do @@ -176,4 +226,134 @@ def check_taskbar_tab(ticket_id, title: nil, modified: false) check_taskbar_tab(ticket.id, title: 'TTTsome level 2 subject 123äöü', modified: false) end end + + context 'when working on multiple platforms', authenticated_as: :user do + let(:ticket) { create(:ticket) } + let(:user) { create(:agent, groups: [ticket.group]) } + let(:another_user) { create(:agent, groups: [ticket.group]) } + let(:key) { "Ticket-#{ticket.id}" } + let(:path) { "ticket/zoom/#{ticket.id}" } + + let(:taskbar_mobile) { create(:taskbar, user: user, app: :mobile, key: key) } + let(:taskbar_desktop) { create(:taskbar, user: user, app: :desktop, key: key) } + + let(:another_taskbar_mobile) { create(:taskbar, user: another_user, app: :mobile, key: key) } + let(:another_taskbar_desktop) { create(:taskbar, user: another_user, app: :desktop, key: key) } + + context 'when looking on a ticket' do + before do + taskbar_desktop + + visit path + end + + it 'does not show current user' do + expect(page).not_to have_avatar(user) + end + end + + context 'when another user is looking on desktop' do + before do + another_taskbar_desktop + taskbar_desktop + + visit path + end + + it 'shows another user' do + expect(page).to have_avatar(another_user).with_no_icon! + end + end + + context 'when another user is looking on mobile' do + before do + another_taskbar_mobile + taskbar_desktop + + visit path + end + + it 'shows another user' do + expect(page).to have_avatar(another_user).with_icon(:mobile) + end + end + + context 'when another user is looking on mobile and desktop' do + before do + another_taskbar_mobile + another_taskbar_desktop + taskbar_desktop + + visit path + end + + it 'shows another user' do + expect(page).to have_avatar(another_user).with_no_icon! + end + end + + context 'when another user is editing on desktop' do + before do + another_taskbar_desktop.update!(state: { a: 1 }) + taskbar_desktop + + visit path + end + + it 'shows another user' do + expect(page).to have_avatar(another_user).with_icon(:pen).changed! + end + end + + context 'when another user is editing on mobile' do + before do + another_taskbar_mobile.update!(state: { a: 1 }) + taskbar_desktop + + visit path + end + + it 'shows another user' do + expect(page).to have_avatar(another_user).with_icon(:pen).changed! + end + end + + context 'when same user is looking on mobile too' do + before do + taskbar_mobile + taskbar_desktop + + visit path + end + + it 'shows same user' do + expect(page).not_to have_avatar(user) + end + end + + context 'when same user is editing' do + before do + taskbar_desktop.update!(state: { a: 1 }) + + visit path + end + + it 'do not show same user' do + expect(page).not_to have_avatar(user) + end + end + + context 'when same user is editing on mobile' do + before do + taskbar_mobile.update!(state: { a: 1 }) + taskbar_desktop + + visit path + end + + it 'shows same user' do + expect(page).to have_avatar(user).with_icon(:'mobile-edit').changed! + end + end + end end