diff --git a/app/controllers/france_connect/particulier_controller.rb b/app/controllers/france_connect/particulier_controller.rb
index 13f41b6f1..5817a3185 100644
--- a/app/controllers/france_connect/particulier_controller.rb
+++ b/app/controllers/france_connect/particulier_controller.rb
@@ -53,7 +53,7 @@ class FranceConnect::ParticulierController < ApplicationController
if !user.can_france_connect?
flash.alert = t('errors.messages.france_connect.forbidden_html', reset_link: new_user_password_path)
- render js: ajax_redirect(root_path)
+ redirect_to root_path
else
@fci.update(user: user)
@fci.delete_merge_token!
@@ -63,8 +63,6 @@ class FranceConnect::ParticulierController < ApplicationController
end
else
flash.alert = t('france_connect.particulier.flash.invalid_password')
-
- render js: helpers.render_flash
end
end
@@ -112,10 +110,7 @@ class FranceConnect::ParticulierController < ApplicationController
if @fci.nil? || !@fci.valid_for_merge?
flash.alert = t('france_connect.particulier.flash.merger_token_expired', application_name: APPLICATION_NAME)
- respond_to do |format|
- format.html { redirect_to root_path }
- format.js { render js: ajax_redirect(root_path) }
- end
+ redirect_to root_path
end
end
@@ -134,12 +129,7 @@ class FranceConnect::ParticulierController < ApplicationController
user.update_attribute('loged_in_with_france_connect', User.loged_in_with_france_connects.fetch(:particulier))
- redirection_location = stored_location_for(current_user) || root_path(current_user)
-
- respond_to do |format|
- format.html { redirect_to redirection_location }
- format.js { render js: ajax_redirect(root_path) }
- end
+ redirect_to stored_location_for(current_user) || root_path(current_user)
end
def redirect_france_connect_error_connection
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index b6f91df67..c45b81052 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -40,77 +40,6 @@ module ApplicationHelper
tag.div(**html.merge(data: { controller: 'react', react_component_value: name, react_props_value: props.to_json }))
end
- def render_to_element(selector, partial:, outer: false, locals: {})
- method = outer ? 'outerHTML' : 'innerHTML'
- html = escape_javascript(render partial: partial, locals: locals)
- # rubocop:disable Rails/OutputSafety
- raw("document.querySelector('#{selector}').#{method} = \"#{html}\";")
- # rubocop:enable Rails/OutputSafety
- end
-
- def append_to_element(selector, partial:, locals: {})
- html = escape_javascript(render partial: partial, locals: locals)
- # rubocop:disable Rails/OutputSafety
- raw("document.querySelector('#{selector}').insertAdjacentHTML('beforeend', \"#{html}\");")
- # rubocop:enable Rails/OutputSafety
- end
-
- def render_flash(timeout: false, sticky: false, fixed: false)
- if flash.any?
- html = render_to_element('#flash_messages', partial: 'layouts/flash_messages', locals: { sticky: sticky, fixed: fixed }, outer: true)
- flash.clear
- if timeout
- html += remove_element('#flash_messages', timeout: timeout, inner: true)
- end
- html
- end
- end
-
- def remove_element(selector, timeout: 0, inner: false)
- script = "(function() {";
- script << "var el = document.querySelector('#{selector}');"
- method = (inner ? "el.innerHTML = ''" : "el.parentNode.removeChild(el)")
- if timeout.present? && timeout > 0
- script << "if (el) { setTimeout(function() { #{method}; }, #{timeout}); }"
- else
- script << "if (el) { #{method} };"
- end
- script << "})();"
- # rubocop:disable Rails/OutputSafety
- raw(script);
- # rubocop:enable Rails/OutputSafety
- end
-
- def show_element(selector)
- # rubocop:disable Rails/OutputSafety
- raw("document.querySelector('#{selector}').classList.remove('hidden');")
- # rubocop:enable Rails/OutputSafety
- end
-
- def focus_element(selector)
- # rubocop:disable Rails/OutputSafety
- raw("document.querySelector('#{selector}').focus();")
- # rubocop:enable Rails/OutputSafety
- end
-
- def disable_element(selector)
- # rubocop:disable Rails/OutputSafety
- raw("document.querySelector('#{selector}').disabled = true;")
- # rubocop:enable Rails/OutputSafety
- end
-
- def enable_element(selector)
- # rubocop:disable Rails/OutputSafety
- raw("document.querySelector('#{selector}').disabled = false;")
- # rubocop:enable Rails/OutputSafety
- end
-
- def fire_event(event_name, data)
- # rubocop:disable Rails/OutputSafety
- raw("DS.fire('#{event_name}', #{raw(data)});")
- # rubocop:enable Rails/OutputSafety
- end
-
def current_email
current_user&.email ||
current_instructeur&.email ||
diff --git a/app/javascript/entrypoints/application.js b/app/javascript/entrypoints/application.js
index 6e3ec2bc6..3143e2dd9 100644
--- a/app/javascript/entrypoints/application.js
+++ b/app/javascript/entrypoints/application.js
@@ -5,17 +5,14 @@ import { Application } from '@hotwired/stimulus';
import '@gouvfr/dsfr/dist/dsfr.module.js';
import '../shared/activestorage/ujs';
-import '../shared/remote-poller';
-import '../shared/safari-11-file-xhr-workaround';
+import '../shared/safari-11-empty-file-workaround';
import '../shared/toggle-target';
-import '../shared/ujs-error-handling';
import { registerControllers } from '../shared/stimulus-loader';
import '../new_design/form-validation';
import '../new_design/procedure-context';
import '../new_design/procedure-form';
-import '../new_design/spinner';
import '../new_design/support';
import {
@@ -31,11 +28,7 @@ import {
acceptEmailSuggestion,
discardEmailSuggestionBox
} from '../new_design/user-sign_up';
-import {
- showFusion,
- showNewAccount,
- showNewAccountPasswordConfirmation
-} from '../new_design/fc-fusion';
+import { showFusion, showNewAccount } from '../new_design/fc-fusion';
const application = Application.start();
registerControllers(application);
@@ -49,7 +42,6 @@ const DS = {
showImportJustificatif,
showFusion,
showNewAccount,
- showNewAccountPasswordConfirmation,
replaceSemicolonByComma,
acceptEmailSuggestion,
discardEmailSuggestionBox
diff --git a/app/javascript/new_design/fc-fusion.js b/app/javascript/new_design/fc-fusion.js
index e9d4b7628..3ef0fce4e 100644
--- a/app/javascript/new_design/fc-fusion.js
+++ b/app/javascript/new_design/fc-fusion.js
@@ -3,17 +3,11 @@ import { show, hide } from '@utils';
export function showFusion() {
show(document.querySelector('.fusion'));
hide(document.querySelector('.new-account'));
- hide(document.querySelector('.new-account-password-confirmation'));
+ hide(document.querySelector('#new-account-password-confirmation'));
}
export function showNewAccount() {
hide(document.querySelector('.fusion'));
show(document.querySelector('.new-account'));
- hide(document.querySelector('.new-account-password-confirmation'));
-}
-
-export function showNewAccountPasswordConfirmation() {
- hide(document.querySelector('.fusion'));
- hide(document.querySelector('.new-account'));
- show(document.querySelector('.new-account-password-confirmation'));
+ hide(document.querySelector('#new-account-password-confirmation'));
}
diff --git a/app/javascript/new_design/spinner.js b/app/javascript/new_design/spinner.js
deleted file mode 100644
index 8b1fbac3e..000000000
--- a/app/javascript/new_design/spinner.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { show, hide, delegate } from '@utils';
-
-function showSpinner() {
- [...document.querySelectorAll('.spinner')].forEach(show);
-}
-
-function hideSpinner() {
- [...document.querySelectorAll('.spinner')].forEach(hide);
-}
-
-delegate('ajax:complete', '[data-spinner]', hideSpinner);
-delegate('ajax:stopped', '[data-spinner]', hideSpinner);
-delegate('ajax:send', '[data-spinner]', showSpinner);
diff --git a/app/javascript/shared/remote-poller.js b/app/javascript/shared/remote-poller.js
deleted file mode 100644
index 77984c900..000000000
--- a/app/javascript/shared/remote-poller.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import { httpRequest, delegate } from '@utils';
-
-addEventListener('DOMContentLoaded', () => {
- attachementPoller.deactivate();
- exportPoller.deactivate();
-
- const attachments = document.querySelectorAll('[data-attachment-poll-url]');
- const exports = document.querySelectorAll('[data-export-poll-url]');
-
- for (let { dataset } of attachments) {
- attachementPoller.add(dataset.attachmentPollUrl);
- }
-
- for (let { dataset } of exports) {
- exportPoller.add(dataset.exportPollUrl);
- }
-});
-
-addEventListener('attachment:update', ({ detail: { url } }) => {
- attachementPoller.add(url);
-});
-
-addEventListener('export:update', ({ detail: { url } }) => {
- exportPoller.add(url);
-});
-
-delegate('click', '[data-attachment-refresh]', (event) => {
- event.preventDefault();
- attachementPoller.check();
-});
-
-// Periodically check the state of a set of URLs.
-//
-// Each time the given URL is requested, the matching `show.js.erb` view is rendered,
-// causing the state to be refreshed.
-//
-// This is used mainly to refresh attachments during the anti-virus check,
-// but also to refresh the state of a pending spreadsheet export.
-class RemotePoller {
- urls = new Set();
- timeout;
- checks = 0;
-
- constructor(settings = {}) {
- this.interval = settings.interval;
- this.maxChecks = settings.maxChecks;
- }
-
- get isEnabled() {
- return this.checks <= this.maxChecks;
- }
-
- get isActive() {
- return this.timeout !== undefined;
- }
-
- add(url) {
- if (this.isEnabled) {
- if (!this.isActive) {
- this.activate();
- }
- this.urls.add(url);
- }
- }
-
- check() {
- let urls = this.urls;
- this.reset();
- for (let url of urls) {
- // Start the request. The JS payload in the response will update the page.
- // (Errors are ignored, because background tasks shouldn't report errors to the user.)
- httpRequest(url)
- .js()
- .catch(() => {});
- }
- }
-
- activate() {
- clearTimeout(this.timeout);
- this.timeout = setTimeout(() => {
- this.checks++;
- this.currentInterval = this.interval * 1.5;
- this.check();
- }, this.currentInterval);
- }
-
- deactivate() {
- this.checks = 0;
- this.currentInterval = this.interval;
- this.reset();
- }
-
- reset() {
- clearTimeout(this.timeout);
- this.urls = new Set();
- this.timeout = undefined;
- }
-}
-
-const attachementPoller = new RemotePoller({ interval: 3000, maxChecks: 5 });
-const exportPoller = new RemotePoller({ interval: 6000, maxChecks: 10 });
diff --git a/app/javascript/shared/safari-11-empty-file-workaround.ts b/app/javascript/shared/safari-11-empty-file-workaround.ts
new file mode 100644
index 000000000..19f689049
--- /dev/null
+++ b/app/javascript/shared/safari-11-empty-file-workaround.ts
@@ -0,0 +1,36 @@
+// iOS 11.3 Safari / macOS Safari 11.1 empty XHR bug workaround.
+// This should work with every modern browser which supports ES5 (including IE9).
+// https://stackoverflow.com/questions/49614091/safari-11-1-ajax-xhr-form-submission-fails-when-inputtype-file-is-empty
+// https://github.com/rails/rails/issues/32440
+
+document.documentElement.addEventListener(
+ 'turbo:before-fetch-request',
+ (event) => {
+ const target = event.target as Element;
+ const inputs = target.querySelectorAll(
+ 'input[type="file"]:not([disabled])'
+ );
+ for (const input of inputs) {
+ if (input.files?.length == 0) {
+ input.setAttribute('data-safari-temp-disabled', 'true');
+ input.setAttribute('disabled', '');
+ }
+ }
+ }
+);
+
+document.documentElement.addEventListener(
+ 'turbo:before-fetch-response',
+ (event) => {
+ const target = event.target as Element;
+ const inputs = target.querySelectorAll(
+ 'input[type="file"][data-safari-temp-disabled]'
+ );
+ for (const input of inputs) {
+ input.removeAttribute('data-safari-temp-disabled');
+ input.removeAttribute('disabled');
+ }
+ }
+);
+
+export {};
diff --git a/app/javascript/shared/safari-11-file-xhr-workaround.js b/app/javascript/shared/safari-11-file-xhr-workaround.js
deleted file mode 100644
index 2d7fa2707..000000000
--- a/app/javascript/shared/safari-11-file-xhr-workaround.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// iOS 11.3 Safari / macOS Safari 11.1 empty XHR bug workaround.
-// This should work with every modern browser which supports ES5 (including IE9).
-// https://stackoverflow.com/questions/49614091/safari-11-1-ajax-xhr-form-submission-fails-when-inputtype-file-is-empty
-// https://github.com/rails/rails/issues/32440
-
-document.addEventListener('ajax:before', function (e) {
- let inputs = e.target.querySelectorAll('input[type="file"]:not([disabled])');
- inputs.forEach(function (input) {
- if (input.files.length > 0) {
- return;
- }
- input.setAttribute('data-safari-temp-disabled', 'true');
- input.setAttribute('disabled', '');
- });
-});
-
-// You should call this by yourself when you aborted an ajax request by stopping a event in ajax:before hook.
-document.addEventListener('ajax:beforeSend', function (e) {
- let inputs = e.target.querySelectorAll(
- 'input[type="file"][data-safari-temp-disabled]'
- );
- inputs.forEach(function (input) {
- input.removeAttribute('data-safari-temp-disabled');
- input.removeAttribute('disabled');
- });
-});
diff --git a/app/javascript/shared/ujs-error-handling.js b/app/javascript/shared/ujs-error-handling.js
deleted file mode 100644
index 495dc0e06..000000000
--- a/app/javascript/shared/ujs-error-handling.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// For links and requests done through rails-ujs (mostly data-remote links),
-// redirect to the sign-in page when the server responds '401 Unauthorized'.
-document.addEventListener('ajax:error', (event) => {
- const [, , xhr] = event.detail;
- if (xhr && xhr.status == 401) {
- location.reload(); // reload whole page so Devise will redirect to sign-in
- }
-});
diff --git a/app/views/administrateurs/experts_procedures/index.html.haml b/app/views/administrateurs/experts_procedures/index.html.haml
index a4c658504..68ebef395 100644
--- a/app/views/administrateurs/experts_procedures/index.html.haml
+++ b/app/views/administrateurs/experts_procedures/index.html.haml
@@ -16,7 +16,7 @@
url: allow_expert_review_admin_procedure_path(@procedure),
html: { class: 'form procedure-form__column--form no-background' } do |f|
%label.toggle-switch
- = f.check_box :allow_expert_review, class: 'toggle-switch-checkbox', onchange: 'this.form.submit()'
+ = f.check_box :allow_expert_review, class: 'toggle-switch-checkbox', onchange: 'this.form.requestSubmit()'
%span.toggle-switch-control.round
%span.toggle-switch-label.on
%span.toggle-switch-label.off
@@ -30,7 +30,7 @@
url: experts_require_administrateur_invitation_admin_procedure_path(@procedure),
html: { class: 'form procedure-form__column--form no-background' } do |f|
%label.toggle-switch
- = f.check_box :experts_require_administrateur_invitation, class: 'toggle-switch-checkbox', onchange: 'this.form.submit()'
+ = f.check_box :experts_require_administrateur_invitation, class: 'toggle-switch-checkbox', onchange: 'this.form.requestSubmit()'
%span.toggle-switch-control.round
%span.toggle-switch-label.on
%span.toggle-switch-label.off
@@ -76,12 +76,11 @@
%td.text-center
= form_for expert_procedure,
url: admin_procedure_expert_path(id: expert_procedure),
- remote: true,
method: :put,
- authenticity_token: true,
+ data: { turbo: true },
html: { class: 'form procedure-form__column--form no-background' } do |f|
%label.toggle-switch
- = f.check_box :allow_decision_access, class: 'toggle-switch-checkbox', onchange: 'this.form.submit()'
+ = f.check_box :allow_decision_access, class: 'toggle-switch-checkbox', onchange: 'this.form.requestSubmit()'
%span.toggle-switch-control.round
%span.toggle-switch-label.on
%span.toggle-switch-label.off
diff --git a/app/views/france_connect/particulier/_password_confirmation.html.haml b/app/views/france_connect/particulier/_password_confirmation.html.haml
index eda3ea1bc..0fa778734 100644
--- a/app/views/france_connect/particulier/_password_confirmation.html.haml
+++ b/app/views/france_connect/particulier/_password_confirmation.html.haml
@@ -3,7 +3,7 @@
%br
= t('.fill_in_password')
-= form_tag france_connect_particulier_merge_with_existing_account_path, remote: true, class: 'mt-2 form fconnect-form' do
+= form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true }, class: 'mt-2 form fconnect-form' do
= hidden_field_tag :merge_token, merge_token
= hidden_field_tag :email, email
= label_tag :password, t('views.registrations.new.password_label', min_length: 8)
diff --git a/app/views/france_connect/particulier/merge.html.haml b/app/views/france_connect/particulier/merge.html.haml
index ce8fedec8..aeb4ec2bb 100644
--- a/app/views/france_connect/particulier/merge.html.haml
+++ b/app/views/france_connect/particulier/merge.html.haml
@@ -19,9 +19,9 @@
.fusion.hidden
%p= t('.title_fill_in_password')
- = form_tag france_connect_particulier_merge_with_existing_account_path, remote: true, class: 'mt-2 form fconnect-form' do
- = hidden_field_tag :merge_token, @fci.merge_token
- = hidden_field_tag :email, @fci.email_france_connect
+ = form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true }, class: 'mt-2 form fconnect-form' do
+ = hidden_field_tag :merge_token, @fci.merge_token, id: dom_id(@fci, :fusion_merge_token)
+ = hidden_field_tag :email, @fci.email_france_connect, id: dom_id(@fci, :fusion_email)
= label_tag :password, t('views.registrations.new.password_label', min_length: 8)
= password_field_tag :password, nil, autocomplete: 'current-password', class: 'mb-1'
@@ -36,11 +36,11 @@
.new-account.hidden
%p= t('.title_fill_in_email', application_name: APPLICATION_NAME)
- = form_tag france_connect_particulier_merge_with_new_account_path, remote: true, class: 'mt-2 form' do
- = hidden_field_tag :merge_token, @fci.merge_token
- = label_tag :email, t('views.registrations.new.email_label')
- = email_field_tag :email, "", required: true
+ = form_tag france_connect_particulier_merge_with_new_account_path, data: { turbo: true }, class: 'mt-2 form' do
+ = hidden_field_tag :merge_token, @fci.merge_token, id: dom_id(@fci, :new_account_merge_token)
+ = label_tag :email, t('views.registrations.new.email_label'), for: dom_id(@fci, :new_account_email)
+ = email_field_tag :email, "", required: true, id: dom_id(@fci, :new_account_email)
= submit_tag t('.button_use_this_email'), class: 'button primary'
- .new-account-password-confirmation.hidden
+ #new-account-password-confirmation.hidden
diff --git a/app/views/france_connect/particulier/merge_with_existing_account.turbo_stream.haml b/app/views/france_connect/particulier/merge_with_existing_account.turbo_stream.haml
new file mode 100644
index 000000000..e69de29bb
diff --git a/app/views/france_connect/particulier/merge_with_new_account.js.erb b/app/views/france_connect/particulier/merge_with_new_account.js.erb
deleted file mode 100644
index cea1ca67a..000000000
--- a/app/views/france_connect/particulier/merge_with_new_account.js.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= render_to_element('.new-account-password-confirmation', partial: 'password_confirmation', locals: { email: @email, merge_token: @merge_token }) %>
-
-DS.showNewAccountPasswordConfirmation();
diff --git a/app/views/france_connect/particulier/merge_with_new_account.turbo_stream.haml b/app/views/france_connect/particulier/merge_with_new_account.turbo_stream.haml
new file mode 100644
index 000000000..7d14ef01a
--- /dev/null
+++ b/app/views/france_connect/particulier/merge_with_new_account.turbo_stream.haml
@@ -0,0 +1,4 @@
+= turbo_stream.update 'new-account-password-confirmation', partial: 'password_confirmation', locals: { email: @email, merge_token: @merge_token }
+= turbo_stream.hide_all '.fusion'
+= turbo_stream.hide_all '.new-account'
+= turbo_stream.show 'new-account-password-confirmation'
diff --git a/app/views/instructeurs/archives/create.js.haml b/app/views/instructeurs/archives/create.js.haml
deleted file mode 100644
index 7fe9f7f0b..000000000
--- a/app/views/instructeurs/archives/create.js.haml
+++ /dev/null
@@ -1 +0,0 @@
-= render_flash(sticky: true)
diff --git a/spec/controllers/france_connect/particulier_controller_spec.rb b/spec/controllers/france_connect/particulier_controller_spec.rb
index fd6c2cd27..76bbae30d 100644
--- a/spec/controllers/france_connect/particulier_controller_spec.rb
+++ b/spec/controllers/france_connect/particulier_controller_spec.rb
@@ -198,7 +198,7 @@ describe FranceConnect::ParticulierController, type: :controller do
let(:merge_token) { fci.create_merge_token! }
let(:email) { 'EXISTING_account@a.com ' }
let(:password) { 'my-s3cure-p4ssword' }
- let(:format) { :js }
+ let(:format) { :turbo_stream }
subject { post :merge_with_existing_account, params: { merge_token: merge_token, email: email, password: password }, format: format }
@@ -309,7 +309,7 @@ describe FranceConnect::ParticulierController, type: :controller do
let(:fci) { FranceConnectInformation.create!(user_info) }
let(:merge_token) { fci.create_merge_token! }
let(:email) { ' Account@a.com ' }
- let(:format) { :js }
+ let(:format) { :turbo_stream }
subject { post :merge_with_new_account, params: { merge_token: merge_token, email: email }, format: format }
@@ -323,7 +323,7 @@ describe FranceConnect::ParticulierController, type: :controller do
expect(fci.user.email).to eq(email.downcase.strip)
expect(fci.merge_token).to be_nil
expect(controller.current_user).to eq(fci.user)
- expect(response.body).to include("window.location.href='/'")
+ expect(response).to redirect_to(root_path)
end
end
diff --git a/spec/system/france_connect/france_connect_particulier_spec.rb b/spec/system/france_connect/france_connect_particulier_spec.rb
index 33ad0aae1..f6cad87ee 100644
--- a/spec/system/france_connect/france_connect_particulier_spec.rb
+++ b/spec/system/france_connect/france_connect_particulier_spec.rb
@@ -83,7 +83,7 @@ describe 'France Connect Particulier Connexion' do
expect(page).to have_css('#password-for-another-account', visible: true)
- within '.new-account-password-confirmation' do
+ within '#new-account-password-confirmation' do
fill_in 'password', with: 'my-s3cure-p4ssword'
click_on 'Fusionner les comptes'
end