Browse Source

Fixes #4476 - Show live users on both desktop and mobile.

Mantas Masalskis 2 years ago
parent
commit
4445d9e761

+ 65 - 40
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('<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(
@@ -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 - 0
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; }

+ 6 - 4
app/graphql/gql/subscriptions/ticket_live_user_updates.rb

@@ -29,7 +29,7 @@ module Gql::Subscriptions
       { 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 @@ module Gql::Subscriptions
       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

+ 8 - 8
app/models/taskbar.rb

@@ -69,7 +69,7 @@ class Taskbar < ApplicationModel
   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 @@ class Taskbar < ApplicationModel
   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)

+ 14 - 0
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

+ 4 - 0
public/assets/images/icons.svg

@@ -467,6 +467,10 @@
 </symbol><symbol id="icon-minus" viewBox="0 0 20 20">
     <title>minus</title>
     <path d="M2 8v3h17V8z" fill-rule="evenodd"/>
+</symbol><symbol id="icon-mobile-edit" viewBox="0 0 9 14">
+    <path d="M8 4.7v8.1c0 .7-.4 1.1-1 1.1H1.2c-.6.1-1-.4-1-1V1.8c0-.6.4-1 1-1h4.5L4.3 2.9H1.1v9H7V6.4l1-1.7zM8.7.5c.2.1.3.4.1.6l-.3.6L7 .7l.3-.5c.2-.2.4-.3.6-.1l.8.4zM4.4 5l2.4-3.9 1.5 1-2.4 3.8L4.4 5zm-.3 1.1v-.8l1.4.9-.6.4-.8.4v-.9z"/>
+</symbol><symbol id="icon-mobile" viewBox="0 0 9 14">
+    <path d="M.3 12.9V1.1C.3.5.8 0 1.4 0h6.1c.7 0 1.2.5 1.2 1.1v11.8c0 .6-.5 1.1-1.1 1.1H1.4c-.6.1-1.1-.4-1.1-1.1zM7.6 2.3H1.3v9.4h6.4V2.3z"/>
 </symbol><symbol id="icon-mood-bad" viewBox="0 0 60 59">
     <title>mood-bad</title>
     <path d="M60 5.108c0-.82-.062-1.577-.187-2.396-.25-1.577-1.06-2.397-2.679-2.586C56.574.063 55.95 0 55.389 0H4.673c-.81 0-1.558.126-2.368.252C1.121.442.436 1.262.187 2.46.062 3.216 0 3.91 0 4.666V39.54c.125 2.522 1.06 3.468 3.551 3.468 3.303.063 6.792.063 10.156.063.686 0 1.807.063 1.807.063v1.072c0 4.162.062 8.324.062 12.486 0 .82 0 1.766.935 2.207.934.379 1.495-.378 2.056-.946 5.171-4.792 10.28-9.522 15.452-14.251.498-.442.934-.568 1.557-.568h18.941c.81 0 1.558-.063 2.368-.126 1.869-.19 2.679-.883 2.928-2.775.062-.567.125-1.198.125-1.765C60 27.368 60 16.27 60 5.107ZM35 34.5c0 .825-.563 1.5-1.25 1.5h-7.5c-.688 0-1.25-.675-1.25-1.5s.563-1.5 1.25-1.5h7.5c.688 0 1.25.675 1.25 1.5ZM37 19h9c0 4-2.043 6-4.468 6C39.106 25 37 23 37 19Zm-23 0h9c0 4-2.043 6-4.468 6C16.106 25 14 23 14 19Z" fill="#F6820B" fill-rule="evenodd"/>

+ 4 - 0
public/assets/images/icons/mobile-edit.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="9px" height="14px" viewBox="0 0 9 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <path fill="#50E3C2" d="M8,4.7v8.1c0,0.7-0.4,1.1-1,1.1H1.2c-0.6,0.1-1-0.4-1-1V1.8c0-0.6,0.4-1,1-1h4.5L4.3,2.9H1.1v9H7V6.4L8,4.7z M8.7,0.5C8.9,0.6,9,0.9,8.8,1.1L8.5,1.7L7,0.7l0.3-0.5C7.5,0,7.7-0.1,7.9,0.1L8.7,0.5L8.7,0.5z M4.4,5l2.4-3.9l1.5,1L5.9,5.9L4.4,5 L4.4,5z M4.1,6.1V5.3l1.4,0.9L4.9,6.6L4.1,7V6.1L4.1,6.1z"/>
+</svg>

+ 4 - 0
public/assets/images/icons/mobile.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="9px" height="14px" viewBox="0 0 9 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <path fill="#50E3C2" d="M0.3,12.9V1.1C0.3,0.5,0.8,0,1.4,0h6.1c0.7,0,1.2,0.5,1.2,1.1v11.8c0,0.6-0.5,1.1-1.1,1.1H1.4 C0.8,14.1,0.3,13.6,0.3,12.9z M7.6,2.3H1.3v9.4h6.4V2.3z"/>
+</svg>

+ 19 - 0
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

+ 113 - 97
spec/models/taskbar_spec.rb

@@ -160,16 +160,16 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
 
         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 @@ RSpec.describe Taskbar, type: :model do
       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 @@ RSpec.describe Taskbar, type: :model do
 
       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
 

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