From 4c7d5c25b1e9ecbcb7f4dbf3c4fd02c8d54937b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 29 Jul 2024 16:18:54 +0200 Subject: [PATCH] Calculate value to be compared instead of using updated_at updated_at breaks when uploading attachments, as the same page will be updated without us having a chance to update the header. Instead, calculate an "etag" value from the meeting sections and agenda items timestamps as well as the meeting's own lock_version. --- .../poll-for-changes.controller.ts | 4 ++-- .../meetings/header_component.html.erb | 2 +- .../app/controllers/meetings_controller.rb | 6 +++--- modules/meeting/app/models/meeting.rb | 19 +++++++++++++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/frontend/src/stimulus/controllers/poll-for-changes.controller.ts b/frontend/src/stimulus/controllers/poll-for-changes.controller.ts index 9be894e96238..fac59aa36734 100644 --- a/frontend/src/stimulus/controllers/poll-for-changes.controller.ts +++ b/frontend/src/stimulus/controllers/poll-for-changes.controller.ts @@ -38,7 +38,7 @@ export default class PollForChangesController extends ApplicationController { updatedAt: String, }; - declare updatedAtValue:string; + declare referenceValue:string; declare urlValue:string; declare intervalValue:number; @@ -58,7 +58,7 @@ export default class PollForChangesController extends ApplicationController { } async triggerTurboStream():Promise { - await fetch(`${this.urlValue}?updatedAt=${this.updatedAtValue}`, { + await fetch(`${this.urlValue}?reference=${this.referenceValue}`, { headers: { Accept: 'text/vnd.turbo-stream.html', }, diff --git a/modules/meeting/app/components/meetings/header_component.html.erb b/modules/meeting/app/components/meetings/header_component.html.erb index 8219e286ac8c..487d164bbf54 100644 --- a/modules/meeting/app/components/meetings/header_component.html.erb +++ b/modules/meeting/app/components/meetings/header_component.html.erb @@ -3,7 +3,7 @@ render(Primer::OpenProject::PageHeader.new( data: { controller: "poll-for-changes", - poll_for_changes_updated_at_value: @meeting.updated_at.iso8601, + poll_for_changes_reference_value: @meeting.changed_hash, poll_for_changes_url_value: check_for_updates_meeting_path(@meeting), poll_for_changes_interval_value: check_for_updates_interval, } diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index ab5fc91a73c5..dcd4751c1373 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -76,10 +76,10 @@ def show end def check_for_updates - if Time.zone.parse(params[:updatedAt]) < @meeting.updated_at.change(usec: 0) - respond_with_flash(Meetings::UpdateFlashComponent.new(meeting: @meeting)) - else + if params[:updatedAt] == @meeting.changed_hash head :no_content + else + respond_with_flash(Meetings::UpdateFlashComponent.new(meeting: @meeting)) end end diff --git a/modules/meeting/app/models/meeting.rb b/modules/meeting/app/models/meeting.rb index 9116281ae333..5c663a9b369f 100644 --- a/modules/meeting/app/models/meeting.rb +++ b/modules/meeting/app/models/meeting.rb @@ -110,6 +110,25 @@ class Meeting < ApplicationRecord closed: 5 } + ## + # Cache key for detecting changes to be shown to the user + def changed_hash + sql = <<~SQL + SELECT MAX(meeting_agenda_items.updated_at), MAX(meeting_sections.updated_at), MAX(meetings.lock_version) FROM meetings + LEFT JOIN meeting_agenda_items ON meeting_agenda_items.meeting_id = meetings.id + LEFT JOIN meeting_sections ON meeting_sections.meeting_id = meetings.id + WHERE meetings.id = :id + SQL + + parts = ActiveRecord::Base + .connection + .exec_query(OpenProject::SqlSanitization.sanitize(sql, id:)) + .rows + .flatten + + OpenProject::Cache::CacheKey.expand(parts) + end + ## # Return the computed start_time when changed def start_time