diff options
Diffstat (limited to 'app/channels/awareness_channel.rb')
-rw-r--r-- | app/channels/awareness_channel.rb | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/app/channels/awareness_channel.rb b/app/channels/awareness_channel.rb new file mode 100644 index 00000000000..554e057ca83 --- /dev/null +++ b/app/channels/awareness_channel.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +class AwarenessChannel < ApplicationCable::Channel # rubocop:disable Gitlab/NamespacedClass + REFRESH_INTERVAL = ENV.fetch("GITLAB_AWARENESS_REFRESH_INTERVAL_SEC", 60) + private_constant :REFRESH_INTERVAL + + # Produces a refresh interval value, based of the + # GITLAB_AWARENESS_REFRESH_INTERVAL_SEC environment variable or the given + # default. Makes sure, that the interval after a jitter is applied, is never + # less than half the predefined interval. + def self.refresh_interval(range: -10..10) + min = REFRESH_INTERVAL / 2.to_f + [min.to_i, REFRESH_INTERVAL.to_i + rand(range)].max.seconds + end + private_class_method :refresh_interval + + # keep clients updated about session membership + periodically every: self.refresh_interval do + transmit payload + end + + def subscribed + reject unless valid_subscription? + return if subscription_rejected? + + stream_for session, coder: ActiveSupport::JSON + + session.join(current_user) + AwarenessChannel.broadcast_to(session, payload) + end + + def unsubscribed + return if subscription_rejected? + + session.leave(current_user) + AwarenessChannel.broadcast_to(session, payload) + end + + # Allows a client to let the server know they are still around. This is not + # like a heartbeat mechanism. This can be triggered by any action that results + # in a meaningful "presence" update. Like scrolling the screen (debounce), + # window becoming active, user starting to type in a text field, etc. + def touch + session.touch!(current_user) + + transmit payload + end + + private + + def valid_subscription? + current_user.present? && path.present? + end + + def payload + { collaborators: collaborators } + end + + def collaborators + session.online_users_with_last_activity.map do |user, last_activity| + collaborator(user, last_activity) + end + end + + def collaborator(user, last_activity) + { + id: user.id, + name: user.name, + avatar_url: user.avatar_url(size: 36), + last_activity: last_activity, + last_activity_humanized: ActionController::Base.helpers.distance_of_time_in_words( + Time.zone.now, last_activity + ) + } + end + + def session + @session ||= AwarenessSession.for(path) + end + + def path + params[:path] + end +end |