feat(message): replace message partial with MessageComponent

This commit is contained in:
Paul Chavard 2022-04-25 12:41:01 +02:00
parent 91879b89ce
commit d2ab8b5593
22 changed files with 182 additions and 131 deletions

View file

@ -0,0 +1,58 @@
class Dossiers::MessageComponent < ApplicationComponent
def initialize(commentaire:, connected_user:, messagerie_seen_at: nil, show_reply_button: false)
@commentaire = commentaire
@connected_user = connected_user
@messagerie_seen_at = messagerie_seen_at
@show_reply_button = show_reply_button
end
attr_reader :commentaire, :connected_user, :messagerie_seen_at
private
def show_reply_button?
@show_reply_button
end
def highlight_if_unseen_class
helpers.highlight_if_unseen_class(@messagerie_seen_at, commentaire.created_at)
end
def icon_path
if commentaire.sent_by_system?
'icons/mail.svg'
elsif commentaire.sent_by?(connected_user)
'icons/account-circle.svg'
else
'icons/blue-person.svg'
end
end
def commentaire_issuer
if commentaire.sent_by_system?
t('.automatic_email')
elsif commentaire.sent_by?(connected_user)
t('.you')
else
commentaire.redacted_email
end
end
def commentaire_from_guest?
commentaire.dossier.invites.map(&:email).include?(commentaire.email)
end
def commentaire_date
is_current_year = (commentaire.created_at.year == Time.zone.today.year)
l(commentaire.created_at, format: is_current_year ? :message_date : :message_date_with_year)
end
def commentaire_body
if commentaire.discarded?
t('.deleted_body')
else
body_formatted = commentaire.sent_by_system? ? commentaire.body : simple_format(commentaire.body)
sanitize(body_formatted)
end
end
end

View file

@ -0,0 +1,9 @@
---
en:
reply: Reply
guest: Guest
delete_button: Delete this message
confirm: Are you sure you want to delete this message ?
automatic_email: Automatic email
you: You
deleted_body: Message deleted

View file

@ -0,0 +1,9 @@
---
fr:
reply: Répondre
guest: Invité
delete_button: Supprimer le message
confirm: Êtes-vous sûr de vouloir supprimer ce message ?
automatic_email: Email automatique
you: Vous
deleted_body: Message supprimé

View file

@ -1,14 +1,14 @@
= render partial: 'shared/dossiers/messages/message_icon', locals: { commentaire: commentaire, connected_user: connected_user } = image_tag(icon_path, class: 'person-icon', alt: '')
.width-100 .width-100
%h2 %h2
%span.mail %span.mail
= render partial: 'shared/dossiers/messages/message_issuer', locals: { commentaire: commentaire, connected_user: connected_user } = commentaire_issuer
- if commentaire_is_from_guest(commentaire) - if commentaire_from_guest?
%span.guest= t('.guest') %span.guest= t('.guest')
%span.date{ class: highlight_if_unseen_class(messagerie_seen_at, commentaire.created_at) } %span.date{ class: highlight_if_unseen_class }
= commentaire_date(commentaire) = commentaire_date
.rich-text= pretty_commentaire(commentaire) .rich-text= commentaire_body
.message-extras.flex.justify-start .message-extras.flex.justify-start
- if commentaire.soft_deletable?(connected_user) - if commentaire.soft_deletable?(connected_user)
@ -20,7 +20,7 @@
.attachment-link .attachment-link
= render partial: "shared/attachment/show", locals: { attachment: commentaire.piece_jointe.attachment } = render partial: "shared/attachment/show", locals: { attachment: commentaire.piece_jointe.attachment }
- if show_reply_button - if show_reply_button?
= button_tag type: 'button', class: 'button small message-answer-button', onclick: 'document.querySelector("#commentaire_body").focus()' do = button_tag type: 'button', class: 'button small message-answer-button', onclick: 'document.querySelector("#commentaire_body").focus()' do
%span.icon.reply %span.icon.reply
= t('.reply') = t('.reply')

View file

@ -12,20 +12,4 @@ module CommentaireHelper
I18n.t('helpers.commentaire.reply_in_mailbox') I18n.t('helpers.commentaire.reply_in_mailbox')
end end
end end
def commentaire_is_from_guest(commentaire)
commentaire.dossier.invites.map(&:email).include?(commentaire.email)
end
def commentaire_date(commentaire)
is_current_year = (commentaire.created_at.year == Time.zone.today.year)
template = is_current_year ? :message_date : :message_date_with_year
I18n.l(commentaire.created_at, format: template)
end
def pretty_commentaire(commentaire)
return t('views.shared.commentaires.destroy.deleted_body') if commentaire.discarded?
body_formatted = commentaire.sent_by_system? ? commentaire.body : simple_format(commentaire.body)
sanitize(body_formatted)
end
end end

View file

@ -1,2 +1,3 @@
- if @commentaire.discarded? - if @commentaire.discarded?
= turbo_stream.update @commentaire, partial: "shared/dossiers/messages/message", locals: { commentaire: @commentaire, connected_user: @commentaire.instructeur || @commentaire.expert, messagerie_seen_at: nil, show_reply_button: false } = turbo_stream.update @commentaire do
= render Dossiers::MessageComponent.new(commentaire: @commentaire, connected_user: @commentaire.instructeur || @commentaire.expert)

View file

@ -49,7 +49,7 @@
%ul.messages-list %ul.messages-list
- @dossier.commentaires.with_attached_piece_jointe.each do |commentaire| - @dossier.commentaires.with_attached_piece_jointe.each do |commentaire|
%li %li
= render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: current_instructeur, messagerie_seen_at: nil, show_reply_button: false } = render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: current_instructeur)
%script{ type: "text/javascript" } %script{ type: "text/javascript" }
window.print(); window.print();

View file

@ -2,7 +2,7 @@
%ul.messages-list %ul.messages-list
- dossier.commentaires.with_attached_piece_jointe.each do |commentaire| - dossier.commentaires.with_attached_piece_jointe.each do |commentaire|
%li.message{ class: commentaire_is_from_me_class(commentaire, connected_user), id: dom_id(commentaire) } %li.message{ class: commentaire_is_from_me_class(commentaire, connected_user), id: dom_id(commentaire) }
= render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at, show_reply_button: show_reply_button(commentaire, connected_user) } = render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at, show_reply_button: show_reply_button(commentaire, connected_user))
- if dossier.messagerie_available? - if dossier.messagerie_available?
= render partial: "shared/dossiers/messages/form", locals: { commentaire: new_commentaire, form_url: form_url, dossier: dossier } = render partial: "shared/dossiers/messages/form", locals: { commentaire: new_commentaire, form_url: form_url, dossier: dossier }

View file

@ -1,7 +0,0 @@
- if commentaire.sent_by_system?
= image_tag('icons/mail.svg', class: 'person-icon', alt: '')
- elsif commentaire.sent_by?(connected_user)
= image_tag('icons/account-circle.svg', class: 'person-icon', alt: '')
- else
= image_tag('icons/blue-person.svg', class: 'person-icon', alt: '')

View file

@ -1,6 +0,0 @@
- if commentaire.sent_by_system?
= t('views.shared.dossiers.messages.message_issuer.automatic_email')
- elsif commentaire.sent_by?(connected_user)
= t('views.shared.dossiers.messages.message_issuer.you')
- else
= commentaire.redacted_email

View file

@ -4,7 +4,7 @@
%h3.tab-title= t('views.users.dossiers.show.latest_message.latest_message') %h3.tab-title= t('views.users.dossiers.show.latest_message.latest_message')
.message.inverted-background .message.inverted-background
= render partial: "shared/dossiers/messages/message", locals: { commentaire: latest_message, connected_user: current_user, messagerie_seen_at: nil, show_reply_button: false } = render Dossiers::MessageComponent.new(commentaire: latest_message, connected_user: current_user)
= link_to messagerie_dossier_url(dossier, anchor: 'new_commentaire'), class: 'button send' do = link_to messagerie_dossier_url(dossier, anchor: 'new_commentaire'), class: 'button send' do
%span.icon.reply %span.icon.reply

View file

@ -127,9 +127,6 @@ en:
submit_dossier: Submit the file submit_dossier: Submit the file
save_changes: Save the changes of the file save_changes: Save the changes of the file
messages: messages:
message_issuer:
automatic_email: "Automatic email"
you: "You"
form: form:
send_message: "Send message" send_message: "Send message"
attachment_size: "(attachment size max : 20 Mo)" attachment_size: "(attachment size max : 20 Mo)"

View file

@ -122,9 +122,6 @@ fr:
submit_dossier: Déposer le dossier submit_dossier: Déposer le dossier
save_changes: Enregistrer les modifications du dossier save_changes: Enregistrer les modifications du dossier
messages: messages:
message_issuer:
automatic_email: "Email automatique"
you: "Vous"
form: form:
send_message: "Envoyer le message" send_message: "Envoyer le message"
attachment_size: "(taille max : 20 Mo)" attachment_size: "(taille max : 20 Mo)"

View file

@ -1,7 +1,5 @@
en: en:
instructeurs: instructeurs:
commentaires_controller:
alert_already_discarded: "Can not destroy message: it was already destroyed"
procedure: procedure:
archive_pending_html: Archive creation pending<br>(requested %{created_period} ago) archive_pending_html: Archive creation pending<br>(requested %{created_period} ago)
archive_ready_html: Download archive<br>(requested %{generated_period} ago) archive_ready_html: Download archive<br>(requested %{generated_period} ago)

View file

@ -1,7 +1,5 @@
fr: fr:
instructeurs: instructeurs:
commentaires_controller:
alert_already_discarded: Ce message a déjà été supprimé
procedure: procedure:
archive_pending_html: Archive en cours de création<br>(demandée il y a %{created_period}) archive_pending_html: Archive en cours de création<br>(demandée il y a %{created_period})
archive_ready_html: Télécharger larchive<br>(demandée il y a %{generated_period}) archive_ready_html: Télécharger larchive<br>(demandée il y a %{generated_period})

View file

@ -1,9 +0,0 @@
en:
shared:
dossiers:
messages:
message:
reply: Reply
guest: Guest
delete_button: Delete this message
confirm: Are you sure you want to delete this message ?

View file

@ -1,9 +0,0 @@
fr:
shared:
dossiers:
messages:
message:
reply: Répondre
guest: Invité
delete_button: Supprimer le message
confirm: Êtes-vous sûr de vouloir supprimer ce message ?

View file

@ -20,6 +20,3 @@ en:
already_user: "I already have an account" already_user: "I already have an account"
create: 'Create an account' create: 'Create an account'
signin: 'Sign in' signin: 'Sign in'
commentaires:
destroy:
deleted_body: Message deleted

View file

@ -20,6 +20,3 @@ fr:
already_user: 'Jai déjà un compte' already_user: 'Jai déjà un compte'
create: 'Créer un compte' create: 'Créer un compte'
signin: 'Connexion' signin: 'Connexion'
commentaires:
destroy:
deleted_body: Message supprimé

View file

@ -1,21 +1,28 @@
describe 'shared/dossiers/messages/message.html.haml', type: :view do RSpec.describe Dossiers::MessageComponent, type: :component do
before { view.extend DossierHelper } let(:component) do
described_class.new(
subject { render 'shared/dossiers/messages/message.html.haml', commentaire: commentaire, messagerie_seen_at: seen_at, connected_user: dossier.user, show_reply_button: true } commentaire: commentaire,
connected_user: connected_user,
messagerie_seen_at: seen_at,
show_reply_button: true
)
end
let(:dossier) { create(:dossier, :en_construction) } let(:dossier) { create(:dossier, :en_construction) }
let(:commentaire) { create(:commentaire, dossier: dossier) } let(:commentaire) { create(:commentaire, dossier: dossier) }
let(:connected_user) { dossier.user }
let(:seen_at) { commentaire.created_at + 1.hour } let(:seen_at) { commentaire.created_at + 1.hour }
subject { render_inline(component).to_html }
it { is_expected.to have_button("Répondre") } it { is_expected.to have_button("Répondre") }
context "with a seen_at after commentaire created_at" do context 'with a seen_at after commentaire created_at' do
let(:seen_at) { commentaire.created_at + 1.hour } let(:seen_at) { commentaire.created_at + 1.hour }
it { is_expected.not_to have_css(".highlighted") } it { is_expected.not_to have_css(".highlighted") }
end end
context "with a seen_at after commentaire created_at" do context 'with a seen_at after commentaire created_at' do
let(:seen_at) { commentaire.created_at - 1.hour } let(:seen_at) { commentaire.created_at - 1.hour }
it { is_expected.to have_css(".highlighted") } it { is_expected.to have_css(".highlighted") }
@ -51,8 +58,8 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
let(:instructeur) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure) }
let(:dossier) { create(:dossier, :en_construction, commentaires: [commentaire], procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, commentaires: [commentaire], procedure: procedure) }
subject { render 'shared/dossiers/messages/message.html.haml', commentaire: commentaire, messagerie_seen_at: seen_at, connected_user: instructeur, show_reply_button: true } let(:connected_user) { instructeur }
let(:form_url) { instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire) } let(:form_url) { component.helpers.instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire) }
context 'on a procedure where commentaire had been written by connected instructeur' do context 'on a procedure where commentaire had been written by connected instructeur' do
let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message') } let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message') }
@ -64,7 +71,7 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message', discarded_at: 2.days.ago) } let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message', discarded_at: 2.days.ago) }
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") } it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
it { is_expected.not_to have_selector(".rich-text", text: I18n.t(t('views.shared.commentaires.destroy.deleted_body'))) } it { is_expected.to have_selector(".rich-text", text: component.t('.deleted_body')) }
end end
context 'on a procedure where commentaire had been written by connected an user' do context 'on a procedure where commentaire had been written by connected an user' do
@ -86,4 +93,50 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
end end
end end
end end
describe '#commentaire_from_guest?' do
let!(:guest) { create(:invite, dossier: dossier) }
subject { component.send(:commentaire_from_guest?) }
context 'when the commentaire sender is not a guest' do
let(:commentaire) { create(:commentaire, dossier: dossier, email: "michel@pref.fr") }
it { is_expected.to be false }
end
context 'when the commentaire sender is a guest on this dossier' do
let(:commentaire) { create(:commentaire, dossier: dossier, email: guest.email) }
it { is_expected.to be true }
end
end
describe '#commentaire_date' do
let(:present_date) { Time.zone.local(2018, 9, 2, 10, 5, 0) }
let(:creation_date) { present_date }
let(:commentaire) do
Timecop.freeze(creation_date) { create(:commentaire, email: "michel@pref.fr") }
end
subject do
Timecop.freeze(present_date) { component.send(:commentaire_date) }
end
it 'doesnt include the creation year' do
expect(subject).to eq 'le 2 septembre à 10 h 05'
end
context 'when displaying a commentaire created on a previous year' do
let(:creation_date) { present_date.prev_year }
it 'includes the creation year' do
expect(subject).to eq 'le 2 septembre 2017 à 10 h 05'
end
end
context 'when formatting the first day of the month' do
let(:present_date) { Time.zone.local(2018, 9, 1, 10, 5, 0) }
it 'includes the ordinal' do
expect(subject).to eq 'le 1er septembre à 10 h 05'
end
end
end
end end

View file

@ -0,0 +1,31 @@
class Dossiers::MessageComponentPreview < ViewComponent::Preview
def with_default_commentaire
render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: user)
end
def with_discarded_commentaire
render Dossiers::MessageComponent.new(commentaire: discarded_commentaire, connected_user: user)
end
private
def user
User.new email: "usager@example.com", locale: I18n.locale
end
def commentaire
Commentaire.new body: 'Hello world!', email: user.email, dossier: dossier, created_at: 2.days.ago
end
def discarded_commentaire
Commentaire.new body: 'Hello world!', email: user.email, dossier: dossier, created_at: 2.days.ago, discarded_at: 1.day.ago
end
def dossier
Dossier.new(id: 47882, state: :en_instruction, procedure: procedure, user: user)
end
def procedure
Procedure.new id: 1234, libelle: 'Dotation dÉquipement des Territoires Ruraux - Exercice 2019'
end
end

View file

@ -28,51 +28,4 @@ RSpec.describe CommentaireHelper, type: :helper do
it { is_expected.to include('Répondre') } it { is_expected.to include('Répondre') }
end end
end end
describe '.commentaire_is_from_guest' do
let(:dossier) { create(:dossier, :en_instruction) }
let!(:guest) { create(:invite, dossier: dossier) }
subject { commentaire_is_from_guest(commentaire) }
context 'when the commentaire sender is not a guest' do
let(:commentaire) { create(:commentaire, dossier: dossier, email: "michel@pref.fr") }
it { is_expected.to be false }
end
context 'when the commentaire sender is a guest on this dossier' do
let(:commentaire) { create(:commentaire, dossier: dossier, email: guest.email) }
it { is_expected.to be true }
end
end
describe '.commentaire_date' do
let(:present_date) { Time.zone.local(2018, 9, 2, 10, 5, 0) }
let(:creation_date) { present_date }
let(:commentaire) do
Timecop.freeze(creation_date) { create(:commentaire, email: "michel@pref.fr") }
end
subject do
Timecop.freeze(present_date) { commentaire_date(commentaire) }
end
it 'doesnt include the creation year' do
expect(subject).to eq 'le 2 septembre à 10 h 05'
end
context 'when displaying a commentaire created on a previous year' do
let(:creation_date) { present_date.prev_year }
it 'includes the creation year' do
expect(subject).to eq 'le 2 septembre 2017 à 10 h 05'
end
end
context 'when formatting the first day of the month' do
let(:present_date) { Time.zone.local(2018, 9, 1, 10, 5, 0) }
it 'includes the ordinal' do
expect(subject).to eq 'le 1er septembre à 10 h 05'
end
end
end
end end