Skip to content

Commit

Permalink
Merge pull request #3105 from bitzesty/replace-poxa-with-action-cable…
Browse files Browse the repository at this point in the history
…-spike

Form Collaboration: Replace Poxa with Action Cable
  • Loading branch information
phil-l-brockwell authored Dec 17, 2024
2 parents 2760287 + 391a2ab commit aebd96f
Show file tree
Hide file tree
Showing 27 changed files with 400 additions and 286 deletions.
5 changes: 0 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ AWS_SECRET_ACCESS_KEY=xxx
AWS_REGION=xxx
AWS_S3_BUCKET_NAME=xxx
DISPLAY_SOCIAL_MOBILITY_AWARD=true
PUSHER_SOCKET_HOST=localhost
PUSHER_WS_PORT=8080
PUSHER_APP_ID=app_id
PUSHER_APP_KEY=app_key
PUSHER_SECRET=secret
GOV_UK_NOTIFY_API_KEY=key
GOV_UK_NOTIFY_API_TEMPLATE_ID=id
SESSION_TIMEOUT=1
Expand Down
4 changes: 1 addition & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,6 @@ gem "redis-store", "~> 1.4"
# We use it for communicating with api.debounce.io
gem "rest-client"

# We are using Pusher with Poxa server for realtime collaborator editing
gem "pusher", "0.15.2"

# Text Search
gem "pg_search", "~> 2.3.3"

Expand Down Expand Up @@ -177,6 +174,7 @@ group :development, :test do
end

group :test do
gem "action-cable-testing"
gem "factory_bot_rails"
gem "capybara", "~> 3.39.0"
gem "poltergeist"
Expand Down
9 changes: 3 additions & 6 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
action-cable-testing (0.6.1)
actioncable (>= 5.0)
actioncable (7.0.8.4)
actionpack (= 7.0.8.4)
activesupport (= 7.0.8.4)
Expand Down Expand Up @@ -429,11 +431,6 @@ GEM
nio4r (~> 2.0)
pundit (0.3.0)
activesupport (>= 3.0.0)
pusher (0.15.2)
httpclient (~> 2.5)
multi_json (~> 1.0)
pusher-signature (~> 0.1.8)
pusher-signature (0.1.8)
raabro (1.4.0)
racc (1.8.1)
rack (2.2.9)
Expand Down Expand Up @@ -732,6 +729,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
action-cable-testing
active_hash
amoeba (= 3.0.0)
binding_of_caller
Expand Down Expand Up @@ -788,7 +786,6 @@ DEPENDENCIES
pry-byebug
puma (~> 6.4.3)
pundit (~> 0.3)
pusher (= 0.15.2)
rack-cors (~> 1.0)
rack-mini-profiler (>= 0.10.1)
rack-protection (= 3.0.5)
Expand Down
6 changes: 3 additions & 3 deletions app/assets/javascripts/application.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#= require moment.min
#= require core
#= require libs/suchi/isOld.js
#= require libs/pusher.min.js
#= require mobile
#= require browser-check
#= require vendor/zxcvbn
Expand All @@ -23,6 +22,7 @@
#= require_tree ./frontend
#= require ./frontend/financial_summary_tables/fst_base.js
#= require_tree ./frontend/financial_summary_tables
#= require channels
#
#= require offline

Expand Down Expand Up @@ -740,7 +740,7 @@ jQuery ->

CollaboratorsLog.log("[COLLABORATOR MODE] ------------ redirect_url ----------- " + redirect_url)

if ApplicationCollaboratorsAccessManager.does_im_current_editor()
if ApplicationCollaboratorsAccessManager.i_am_current_editor()
CollaboratorsLog.log("[COLLABORATOR MODE] -------------I'm EDITOR---------- SAVE AND REDIRECT")
# If I'm current editor
# -> then save form data and once it saved redirect me to proper section in a callback
Expand All @@ -755,7 +755,7 @@ jQuery ->
window.location.href = redirect_url

else
CollaboratorsLog.log("[STANDART MODE] ----------------------- ")
CollaboratorsLog.log("[STANDARD MODE] ----------------------- ")

autosave()

Expand Down
6 changes: 6 additions & 0 deletions app/assets/javascripts/channels/index.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#= require actioncable
#= require_self
#= require_tree .

@App = {}
App.cable = ActionCable.createConsumer()
Original file line number Diff line number Diff line change
@@ -1,133 +1,53 @@
window.ApplicationCollaboratorsAccessManager =

register_member: (member) ->
member_info = ApplicationCollaboratorsAccessManager.get_member_info(member)

if member.id is String(window.pusher_current_channel.members.me.id)
CollaboratorsLog.log("[ME IS MEMBER] ------------------------- " + member_info)
else
CollaboratorsLog.log("[ANOTHER MEMBER] ------------------------- " + member_info)

set_access_mode: () ->
editor = ApplicationCollaboratorsAccessManager.current_editor()
CollaboratorsLog.log("[SET ACCESS MODE] ------------ CURRENT EDITOR IS -------- " + editor.id)
return if !window.current_channel_members

ApplicationCollaboratorsAccessManager.track_current_editor(editor)
editor_id = ApplicationCollaboratorsAccessManager.current_editor().id

if ApplicationCollaboratorsAccessManager.does_im_current_editor()
CollaboratorsLog.log("[EDITOR MODE] ----------------------")
CollaboratorsLog.log("[SET ACCESS MODE] ------------ CURRENT EDITOR IS -------- " + editor_id)
previous_editor_id = window.last_editor_id

else
CollaboratorsLog.log("[READ ONLY MODE] ----------------------")
ApplicationCollaboratorsAccessManager.track_current_editor(editor_id)

ApplicationCollaboratorsFormLocker.lock_current_form_section()
ApplicationCollaboratorsEditorBar.render_collaborators_bar()

login_to_the_room: (current_step) ->
# Unsubscribe client from current section room
room = window.pusher_current_channel.name;
window.pusher.unsubscribe(window.pusher_current_channel.name);

# Clean up last editor id as we switched to another section
window.pusher_last_editor_id = null;

# And hide collaborators info bar
ApplicationCollaboratorsEditorBar.hide_collaborators_bar()

CollaboratorsLog.log("[TAB SWITCH] ------------- Log out from (" + room + ") to " + current_step + " --------------------")

# Set new pusher section
window.pusher_section = current_step;
if ApplicationCollaboratorsAccessManager.i_am_current_editor()
CollaboratorsLog.log("[EDITOR MODE] ----------------------")

# Set new login timestamp for user
timestamp = new Date().getTime();
ApplicationCollaboratorsEditorBar.hide_collaborators_bar()

# Init new room based on selected section
ApplicationCollaboratorsConnectionManager.init_pusher(timestamp)
ApplicationCollaboratorsConnectionManager.init_room()
if previous_editor_id != undefined && previous_editor_id != ApplicationCollaboratorsAccessManager.current_editor().id
CollaboratorsLog.log("[NOW IM EDITOR] ---- REFRESHING PAGE")

my_position_in_members_queue: () ->
i = 0
my_index = 0
ApplicationCollaboratorsEditorBar.show_loading_bar()

members = window.pusher_current_channel.members
me = members.me
# Redirect user to same page in order to get the new changes
redirect_url = $(".js-step-link.step-current a").attr('href')
redirect_url += "&form_refresh=true"

members.each (member) ->
if member.id is String(me.id)
my_index = i
#
# In case it was an attempt to submit and validation errors are present
# then we are passing validate_on_form_load option
# in order to show validation errors to user after redirection
#
if window.location.href.search("validate_on_form_load") > 0
redirect_url += "&validate_on_form_load=true"

i += 1
window.location.href = redirect_url
else
CollaboratorsLog.log("[READ ONLY MODE] ----------------------")

my_index
ApplicationCollaboratorsFormLocker.lock_current_form_section()
ApplicationCollaboratorsEditorBar.render_collaborators_bar()

does_im_current_editor: () ->
ApplicationCollaboratorsAccessManager.my_position_in_members_queue() == 0
i_am_current_editor: () ->
ApplicationCollaboratorsAccessManager.current_editor().id == window.user_id &&
window.tab_ident == ApplicationCollaboratorsAccessManager.current_editor().tab_ident

im_in_viewer_mode: () ->
!ApplicationCollaboratorsAccessManager.does_im_current_editor()

normalized_members_array: () ->
list = []

members = window.pusher_current_channel.members
members.each (member) ->
list.push(member)

return list

get_member_info: (m) ->
return ("ID: " + m.id + ", NAME: " + m.info.name + ", JOINED AT: " + m.info.joined_at)
!ApplicationCollaboratorsAccessManager.i_am_current_editor()

current_editor: () ->
editor = ApplicationCollaboratorsAccessManager.normalized_members_array()[0]
member_info = ApplicationCollaboratorsAccessManager.get_member_info(editor)

CollaboratorsLog.log("[CURRENT EDITOR] ------------- " + member_info + " --------------------")
window.current_channel_members[0]

return editor

try_mark_as_editor: () ->
editor = ApplicationCollaboratorsAccessManager.current_editor()

if ApplicationCollaboratorsAccessManager.can_be_marked_as_editor(editor)
CollaboratorsLog.log("[NOW IM EDITOR] ----------------------")

ApplicationCollaboratorsEditorBar.show_loading_bar()

# Redirect user to same page in order to login him
redirect_url = $(".js-step-link.step-current a").attr('href')
redirect_url += "&form_refresh=true"

#
# In case if was attempt to submit and validation errors are present
# then we are passing validate_on_form_load option
# in order to show validation errors to user after redirection
#
if window.location.href.search("validate_on_form_load") > 0
redirect_url += "&validate_on_form_load=true"

window.location.href = redirect_url
else
#
# If I'm not next in queue to be marked as editor
# or I'm already editor
#
# Then do not need to refresh page
#

if window.pusher_last_editor_id == editor.id
CollaboratorsLog.log("[ACCESS CALC] ---------------------- I'M ALREADY EDITOR OF CURRENT TAB!")
else
CollaboratorsLog.log("[ACCESS CALC] ---------------------- NOPE - I STILL HAVE TO WAIT!")

track_current_editor: (editor) ->
window.pusher_last_editor_id = editor.id

can_be_marked_as_editor: (editor) ->
#
# If I'm next in queue to join room as editor
# and I'm not previous editor
#
ApplicationCollaboratorsAccessManager.does_im_current_editor() &&
window.pusher_last_editor_id != editor.id
track_current_editor: (editor_id) ->
window.last_editor_id = editor_id
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
window.ApplicationCollaboratorsConnectionManager =

init: (form_id, p_host, p_port, p_key, rails_env, timestamp) ->
init: (form_id, user_id, rails_env) ->

#
# Checking if browser supports WebSockets technology
Expand All @@ -9,79 +9,27 @@ window.ApplicationCollaboratorsConnectionManager =

window.form_id = form_id

window.pusher_host = p_host
window.pusher_port = p_port

window.pusher_key = p_key
window.rails_env = rails_env
window.user_id = user_id

if rails_env == "staging" || rails_env == "production"
window.pusher_encrypted = true
else
window.pusher_encrypted = false
window.tab_ident = ApplicationCollaboratorsConnectionManager.get_tab_ident()

window.pusher_section = $(".js-step-link.step-current").attr('data-step')
window.form_section = $(".js-step-link.step-current").attr('data-step')

ApplicationCollaboratorsConnectionManager.init_pusher(timestamp)
ApplicationCollaboratorsConnectionManager.init_room()
ApplicationCollaboratorsGeneralRoomTracking.login()

init_pusher: (timestamp) ->
CollaboratorsLog.log("[PUSHER INIT] form_id: " + window.form_id + ", host: " + window.pusher_host + ", port: " + window.pusher_port + ", key: " + window.pusher_key + ", enc: " + window.pusher_encrypted + ", section: " + window.pusher_section)

# Init Pusher to use own Poxa server
pusher_ops = {
wsHost: window.pusher_host,
wsPort: window.pusher_port,
authTransport: 'jsonp',
authEndpoint: "/users/form_answers/" + window.form_id + "/collaborator_access/auth/" + window.pusher_section + "/" + timestamp
}

# Use encryption on live and staging
# as they are using HTTPS
#
if window.pusher_encrypted == "true"
pusher_ops["encrypted"] = true
CollaboratorsLog.log("[PUSHER OPS] encryption turned on!")
else
CollaboratorsLog.log("[PUSHER OPS] encryption turned off!")

window.pusher = new Pusher(window.pusher_key, pusher_ops)

# Check connection status
connection_status = pusher.connection.state
CollaboratorsLog.log("PUSHER STATUS: " + connection_status)


init_room: () ->
# Introduce new channel
channel_name = 'presence-chat-' + window.rails_env + "-" + window.form_id + '-sep-' + window.pusher_section

CollaboratorsLog.log("[PUSHER INIT ROOM] ------------------------ channel_name: " + channel_name)

window.pusher_current_channel = window.pusher.subscribe(channel_name)

# Check if subscription was successful
window.pusher_current_channel.bind 'pusher:subscription_succeeded', (members) ->
CollaboratorsLog.log('[subscription_succeeded] Count ' + members.count)

ApplicationCollaboratorsAccessManager.set_access_mode()
members.each (member) =>
ApplicationCollaboratorsAccessManager.register_member(member)

return

# Handle member removed
window.pusher_current_channel.bind 'pusher:member_removed', (member) ->
CollaboratorsLog.log('[member_removed] Count ' + window.pusher_current_channel.members.count)
channel_name = 'presence-chat-' + window.rails_env + "-" + window.form_id + '-sep-' + window.form_section

ApplicationCollaboratorsAccessManager.try_mark_as_editor()
CollaboratorsLog.log("[INIT ROOM] ------------------------ channel_name: " + channel_name)

return
window.App.collaborators = App.cable.subscriptions.create { channel: "CollaboratorsChannel", channel_name: channel_name, user_id: window.user_id, current_tab: window.tab_ident },
received: (data) ->
window.current_channel_members = data.collaborators
ApplicationCollaboratorsAccessManager.set_access_mode()

# Handle member added
window.pusher_current_channel.bind 'pusher:member_added', (member) ->
CollaboratorsLog.log('[member_added] Count ' + window.pusher_current_channel.members.count)
ApplicationCollaboratorsAccessManager.register_member(member)
get_tab_ident: () ->
document.cookie.split('; ').find((c) => c.split("=")[0] == 'public_tab_ident').split('=')[1]

return
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ window.ApplicationCollaboratorsEditorBar =

render_collaborators_bar: () ->
editor = ApplicationCollaboratorsAccessManager.current_editor()
currentEditorName = editor.info.name + " (" + editor.info.email + ")"
currentEditorName = editor.name + " (" + editor.email + ")"

members = window.pusher_current_channel.members
me = members.me
me = window.user_id

if me.info.email == editor.info.email
if me == editor.id
header = "You cannot edit this section unless you close it elsewhere first."
message = "It looks like you have already opened this section in another tab or window. To avoid data-saving issues, you can only have it open in one tab or window at a time. Please close the other tabs or windows to continue editing."
else
Expand Down
Loading

0 comments on commit aebd96f

Please sign in to comment.