From 8a5dc7abc042b7c1b8f3f50dad0a8324c668dcbf Mon Sep 17 00:00:00 2001 From: Kira Grammel <49536988+kiragrammel@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:14:33 +0200 Subject: [PATCH] Forward person when a programming group is created with them Further, we remove the "check invitation" button and extract some methods to our new ProgrammingGroups object in JavaScript. Co-authored-by: Sebastian Serth --- .../channels/pg_matching_channel.js | 30 +++++++++++++++++++ .../channels/synchronized_editor_channel.js | 29 ++++++------------ app/assets/javascripts/programming_groups.js | 15 ++++++++-- app/channels/pg_matching_channel.rb | 24 +++++++++++++++ .../programming_groups_controller.rb | 14 +++++++++ app/views/programming_groups/_form.html.slim | 1 - app/views/programming_groups/new.html.slim | 2 +- config/locales/de.yml | 1 - config/locales/en.yml | 1 - 9 files changed, 90 insertions(+), 27 deletions(-) create mode 100644 app/assets/javascripts/channels/pg_matching_channel.js create mode 100644 app/channels/pg_matching_channel.rb diff --git a/app/assets/javascripts/channels/pg_matching_channel.js b/app/assets/javascripts/channels/pg_matching_channel.js new file mode 100644 index 00000000..cc393058 --- /dev/null +++ b/app/assets/javascripts/channels/pg_matching_channel.js @@ -0,0 +1,30 @@ +$(document).on('turbolinks:load', function () { + + if ($.isController('programming_groups') && window.location.pathname.includes('programming_groups/new')) { + const matching_page = $('#matching'); + const exercise_id = matching_page.data('exercise-id'); + + App.pg_matching = App.cable.subscriptions.create({ + channel: "PgMatchingChannel", exercise_id: exercise_id + }, { + connected() { + // Called when the subscription is ready for use on the server + }, + + disconnected() { + // Called when the subscription has been terminated by the server + }, + + received(data) { + // Called when there's incoming data on the websocket for this channel + switch (data.action) { + case 'invited': + if (!ProgrammingGroups.is_other_user(data.user)) { + window.location.reload(); + } + break; + } + }, + }); + } +}); diff --git a/app/assets/javascripts/channels/synchronized_editor_channel.js b/app/assets/javascripts/channels/synchronized_editor_channel.js index ffe72d44..e6351621 100644 --- a/app/assets/javascripts/channels/synchronized_editor_channel.js +++ b/app/assets/javascripts/channels/synchronized_editor_channel.js @@ -1,25 +1,14 @@ $(document).on('turbolinks:load', function () { if (window.location.pathname.includes('/implement')) { - function is_other_user(user) { - return !_.isEqual(current_user, user); - } - - function is_other_session(other_session_id) { - return session_id !== other_session_id; - } - const editor = $('#editor'); const exercise_id = editor.data('exercise-id'); - let session_id; - if ($.isController('exercises') && is_other_user(current_contributor)) { + if ($.isController('exercises') && ProgrammingGroups.is_other_user(current_contributor)) { App.synchronized_editor = App.cable.subscriptions.create({ channel: "SynchronizedEditorChannel", exercise_id: exercise_id }, { - - connected() { // Called when the subscription is ready for use on the server }, @@ -33,19 +22,19 @@ $(document).on('turbolinks:load', function () { // Called when there's incoming data on the websocket for this channel switch (data.action) { case 'session_id': - session_id = data.session_id; + ProgrammingGroups.session_id = data.session_id; break; case 'editor_change': - if (is_other_session(data.session_id)) { + if (ProgrammingGroups.is_other_session(data.session_id)) { CodeOceanEditor.applyChanges(data.delta, data.active_file); } break; case 'connection_change': - if (is_other_session(data.session_id) && data.status === 'connected') { - const message = {files: CodeOceanEditor.collectFiles(), session_id: session_id}; + if (ProgrammingGroups.is_other_session(data.session_id) && data.status === 'connected') { + const message = {files: CodeOceanEditor.collectFiles(), session_id: ProgrammingGroups.session_id}; this.perform('current_content', message); } - if (is_other_user(data.user)) { + if (ProgrammingGroups.is_other_user(data.user)) { CodeOceanEditor.showPartnersConnectionStatus(data.status, data.user.displayname); this.perform('connection_status'); } @@ -58,13 +47,13 @@ $(document).on('turbolinks:load', function () { } break; case 'connection_status': - if (is_other_user(data.user)) { + if (ProgrammingGroups.is_other_user(data.user)) { CodeOceanEditor.showPartnersConnectionStatus(data.status, data.user.displayname); } break; case 'current_content': case 'reset_content': - if (is_other_session(data.session_id)) { + if (ProgrammingGroups.is_other_session(data.session_id)) { CodeOceanEditor.setEditorContent(data); } break; @@ -76,7 +65,7 @@ $(document).on('turbolinks:load', function () { }, editor_change(delta, active_file) { - const message = {session_id: session_id, active_file: active_file, delta: delta} + const message = {session_id: ProgrammingGroups.session_id, active_file: active_file, delta: delta} this.perform('editor_change', message); }, diff --git a/app/assets/javascripts/programming_groups.js b/app/assets/javascripts/programming_groups.js index fc336e15..8b9c5a52 100644 --- a/app/assets/javascripts/programming_groups.js +++ b/app/assets/javascripts/programming_groups.js @@ -1,4 +1,6 @@ -var ProgrammingGroups = { +const ProgrammingGroups = { + session_id: null, + getStoredViewedPPInfo: function () { return localStorage.getItem('viewed_pp_info') }, @@ -7,10 +9,17 @@ var ProgrammingGroups = { localStorage.setItem('viewed_pp_info', 'true') }, - initializeEventHandler: function () { $('#dont_show_info_pp_modal_again').on('click', this.setStoredViewedPPInfo.bind(this)); - } + }, + + is_other_user: function (user) { + return !_.isEqual(current_user, user); + }, + + is_other_session: function (other_session_id) { + return this.session_id !== other_session_id; + }, }; $(document).on('turbolinks:load', function () { diff --git a/app/channels/pg_matching_channel.rb b/app/channels/pg_matching_channel.rb new file mode 100644 index 00000000..cca4308a --- /dev/null +++ b/app/channels/pg_matching_channel.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class PgMatchingChannel < ApplicationCable::Channel + def subscribed + set_and_authorize_exercise + stream_from specific_channel + end + + def unsubscribed + # Any cleanup needed when channel is unsubscribed + stop_all_streams + end + + def specific_channel + "pg_matching_channel_exercise_#{@exercise.id}" + end + + private + + def set_and_authorize_exercise + @exercise = Exercise.find(params[:exercise_id]) + reject unless ExercisePolicy.new(current_user, @exercise).implement? + end +end diff --git a/app/controllers/programming_groups_controller.rb b/app/controllers/programming_groups_controller.rb index 7d5de258..968ba4d9 100644 --- a/app/controllers/programming_groups_controller.rb +++ b/app/controllers/programming_groups_controller.rb @@ -42,7 +42,21 @@ class ProgrammingGroupsController < ApplicationController end create_and_respond(object: @programming_group, path: proc { implement_exercise_path(@exercise) }) do + # Inform all other users in the programming group that they have been invited. + @programming_group.users.each do |user| + next if user == current_user + + message = { + action: 'invited', + user: user.to_page_context, + } + ActionCable.server.broadcast("pg_matching_channel_exercise_#{@exercise.id}", message) + end + + # Just set the programming group id in the session for the creator of the group, so that the user can be redirected. session[:pg_id] = @programming_group.id + + # Don't return a specific value from this block, so that the default is used. nil end end diff --git a/app/views/programming_groups/_form.html.slim b/app/views/programming_groups/_form.html.slim index 63da350d..03732a07 100644 --- a/app/views/programming_groups/_form.html.slim +++ b/app/views/programming_groups/_form.html.slim @@ -6,4 +6,3 @@ /.help-block.form-text = t('.hints.programming_partner_ids') .actions.mb-0 = render('shared/submit_button', f: f, object: @programming_group) - a.btn.btn-secondary.float-end href=new_exercise_programming_group_path(@exercise) == t('programming_groups.new.check_invitation') diff --git a/app/views/programming_groups/new.html.slim b/app/views/programming_groups/new.html.slim index b2fdb91e..e3aaee62 100644 --- a/app/views/programming_groups/new.html.slim +++ b/app/views/programming_groups/new.html.slim @@ -1,5 +1,5 @@ h1 = t('programming_groups.new.create_programming_pair') -.row +#matching.row data-exercise-id=@exercise.id .col-md-6 p => t('programming_groups.new.own_user_id') diff --git a/config/locales/de.yml b/config/locales/de.yml index fe10c40f..c3651a71 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -597,7 +597,6 @@ de: new: close: Schließen create_programming_pair: Programmierpaar erstellen - check_invitation: "Einladung prüfen" dont_show_modal_again: "Auf diesem Gerät nicht mehr anzeigen" enter_partner_id: "Bitte gib hier die Nutzer-ID der Person ein, mit der du zusammen die Aufgabe '%{exercise_title}' lösen möchtest. Beachte jedoch, dass anschließend keiner die Zusammenarbeit beenden kann. Dein:e Teampartner:in kann sehen, was du in dieser Aufgabe schreibst und umgekehrt. Für die nächste Aufgabe kannst du dich erneuert entscheiden, ob und mit wem du zusammen arbeiten möchtest." find_partner_title: "Finde eine:n Programmierpartner:in für die Aufgabe" diff --git a/config/locales/en.yml b/config/locales/en.yml index 80947451..c55593e3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -597,7 +597,6 @@ en: new: close: Close create_programming_pair: Create Programming Pair - check_invitation: "Check invitation" dont_show_modal_again: "Don't display on this device anymore" enter_partner_id: "Please enter the user ID from the practice partner with whom you want to solve the exercise '%{exercise_title}'. However, note that no one can leave the pair afterward. Hence, your team partner can see what you write in this exercise and vice versa. For the next exercise, you can decide again whether and with whom you want to work together." find_partner_title: "Find a programming partner for the exercise"