Skip to content

Commit 7f09708

Browse files
authored
feat: send notification for joining/leaving an engagement (resolves #2578) (#2614)
* feat: notification for joining and leaving an engagement * feat: send notification for joining and leaving an engagement * fix: notifications not rendered * test: updating notified user in tests * test: remove only()
1 parent fd4bf38 commit 7f09708

File tree

12 files changed

+338
-1
lines changed

12 files changed

+338
-1
lines changed

app/Http/Controllers/EngagementController.php

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
use App\Models\Project;
3232
use App\Models\User;
3333
use App\Notifications\AccessNeedsFacilitationRequested;
34+
use App\Notifications\JoinedEngagement;
35+
use App\Notifications\LeftEngagement;
3436
use App\Notifications\OrganizationAddedToEngagement;
3537
use App\Notifications\OrganizationRemovedFromEngagement;
3638
use App\Notifications\ParticipantInvited;
@@ -625,6 +627,7 @@ public function join(Request $request, Engagement $engagement): RedirectResponse
625627
$engagement->participants()->save(Auth::user()->individual, ['status' => 'confirmed']);
626628

627629
$engagement->project->notify(new ParticipantJoined($engagement));
630+
Auth::user()->notify(new JoinedEngagement($engagement));
628631

629632
flash(__('You have successfully signed up for this engagement.'), 'success|'.__('You have successfully signed up for this engagement.', [], 'en'));
630633

@@ -708,6 +711,7 @@ public function leave(Request $request, Engagement $engagement): RedirectRespons
708711
Auth::user()->individual->engagements()->detach($engagement->id);
709712

710713
$engagement->project->notify(new ParticipantLeft($engagement));
714+
Auth::user()->notify(new LeftEngagement($engagement));
711715

712716
flash(__('You have successfully left this engagement.'), 'success|'.__('You have successfully left this engagement.', [], 'en'));
713717

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace App\Notifications;
4+
5+
use App\Models\Engagement;
6+
use Illuminate\Notifications\Messages\MailMessage;
7+
use Illuminate\Notifications\Messages\VonageMessage;
8+
9+
class JoinedEngagement extends PlatformNotification
10+
{
11+
public Engagement $engagement;
12+
13+
public mixed $projectable;
14+
15+
public function __construct(Engagement $engagement)
16+
{
17+
$this->engagement = $engagement;
18+
$this->projectable = $this->engagement->project->projectable;
19+
}
20+
21+
public function toMail(): MailMessage
22+
{
23+
return (new MailMessage)
24+
->subject(__('Signed up for :engagement', ['engagement' => $this->engagement->getTranslation('name', locale())]))
25+
->markdown(
26+
'mail.joined-engagement',
27+
[
28+
'engagement' => $this->engagement,
29+
'projectable' => $this->projectable,
30+
]
31+
);
32+
}
33+
34+
public function toVonage(): VonageMessage
35+
{
36+
return (new VonageMessage)
37+
->content(
38+
__('You have signed up for :engagement by :projectable', [
39+
'engagement' => $this->engagement->getTranslation('name', locale()),
40+
'projectable' => $this->projectable->getTranslation('name', locale()),
41+
]).'. '.__(
42+
'View this engagement at :url.',
43+
[
44+
'url' => localized_route('engagements.show', $this->engagement),
45+
]
46+
)
47+
)
48+
->unicode();
49+
}
50+
51+
public function toArray(): array
52+
{
53+
return [
54+
'engagement_id' => $this->engagement->id,
55+
];
56+
}
57+
}

app/Notifications/LeftEngagement.php

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace App\Notifications;
4+
5+
use App\Models\Engagement;
6+
use Illuminate\Notifications\Messages\MailMessage;
7+
use Illuminate\Notifications\Messages\VonageMessage;
8+
9+
class LeftEngagement extends PlatformNotification
10+
{
11+
public Engagement $engagement;
12+
13+
public mixed $projectable;
14+
15+
public function __construct(Engagement $engagement)
16+
{
17+
$this->engagement = $engagement;
18+
$this->projectable = $this->engagement->project->projectable;
19+
}
20+
21+
public function toMail(): MailMessage
22+
{
23+
return (new MailMessage)
24+
->subject(__('Left :engagement', ['engagement' => $this->engagement->getTranslation('name', locale())]))
25+
->markdown(
26+
'mail.left-engagement',
27+
[
28+
'engagement' => $this->engagement,
29+
'projectable' => $this->projectable,
30+
]
31+
);
32+
}
33+
34+
public function toVonage(): VonageMessage
35+
{
36+
return (new VonageMessage)
37+
->content(
38+
__('You have left :engagement by :projectable', [
39+
'engagement' => $this->engagement->getTranslation('name', locale()),
40+
'projectable' => $this->projectable->getTranslation('name', locale()),
41+
]).'. '.__(
42+
'View this engagement at :url.',
43+
[
44+
'url' => localized_route('engagements.show', $this->engagement),
45+
]
46+
)
47+
)
48+
->unicode();
49+
}
50+
51+
public function toArray(): array
52+
{
53+
return [
54+
'engagement_id' => $this->engagement->id,
55+
];
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace App\View\Components\Notification;
4+
5+
use App\Models\Engagement;
6+
use App\Models\Organization;
7+
use App\Models\RegulatedOrganization;
8+
use App\View\Components\Notification;
9+
use Illuminate\Contracts\View\View;
10+
use Illuminate\Notifications\DatabaseNotification;
11+
12+
class JoinedEngagement extends Notification
13+
{
14+
public Engagement $engagement;
15+
16+
public mixed $projectable;
17+
18+
public function __construct(DatabaseNotification $notification)
19+
{
20+
$this->engagement = Engagement::find($notification->data['engagement_id']);
21+
$this->projectable = $this->engagement->project->projectable;
22+
/** @var Organization|RegulatedOrganization */
23+
$projectable = $this->projectable;
24+
$this->title = __('Engagement joined');
25+
$this->body = safe_markdown('You have signed up for [:engagement](:engagement_url) by [:projectable](:projectable_url).', [
26+
'engagement' => $this->engagement->getTranslation('name', locale()),
27+
'engagement_url' => localized_route('engagements.show', $this->engagement),
28+
'projectable' => $projectable->getTranslation('name', locale()),
29+
'projectable_url' => localized_route($projectable->getRoutePrefix().'.show', $projectable),
30+
]);
31+
$this->interpretation = __('Engagement joined', [], 'en');
32+
33+
parent::__construct($notification);
34+
}
35+
36+
public function render(): View
37+
{
38+
return view('components.notification.joined-engagement', [
39+
'notification' => $this->notification,
40+
'read' => ! is_null($this->notification->read_at),
41+
'title' => $this->title,
42+
'body' => $this->body,
43+
'engagement' => $this->engagement,
44+
'interpretation' => $this->interpretation,
45+
]);
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace App\View\Components\Notification;
4+
5+
use App\Models\Engagement;
6+
use App\Models\Organization;
7+
use App\Models\RegulatedOrganization;
8+
use App\View\Components\Notification;
9+
use Illuminate\Contracts\View\View;
10+
use Illuminate\Notifications\DatabaseNotification;
11+
12+
class LeftEngagement extends Notification
13+
{
14+
public Engagement $engagement;
15+
16+
public mixed $projectable;
17+
18+
public function __construct(DatabaseNotification $notification)
19+
{
20+
$this->engagement = Engagement::find($notification->data['engagement_id']);
21+
$this->projectable = $this->engagement->project->projectable;
22+
/** @var Organization|RegulatedOrganization */
23+
$projectable = $this->projectable;
24+
$this->title = __('Left engagement');
25+
$this->body = safe_markdown('You left [:engagement](:engagement_url) by [:projectable](:projectable_url).', [
26+
'engagement' => $this->engagement->getTranslation('name', locale()),
27+
'engagement_url' => localized_route('engagements.show', $this->engagement),
28+
'projectable' => $projectable->getTranslation('name', locale()),
29+
'projectable_url' => localized_route($projectable->getRoutePrefix().'.show', $projectable),
30+
]);
31+
$this->interpretation = __('Left engagement', [], 'en');
32+
33+
parent::__construct($notification);
34+
}
35+
36+
public function render(): View
37+
{
38+
return view('components.notification.left-engagement', [
39+
'notification' => $this->notification,
40+
'read' => ! is_null($this->notification->read_at),
41+
'title' => $this->title,
42+
'body' => $this->body,
43+
'engagement' => $this->engagement,
44+
'interpretation' => $this->interpretation,
45+
]);
46+
}
47+
}

resources/lang/en.json

+12
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@
574574
"engagement description (French)": "engagement description (French)",
575575
"Engagement estimate has been submitted for your approval": "Engagement estimate has been submitted for your approval",
576576
"engagement format": "engagement format",
577+
"Engagement joined": "Engagement joined",
577578
"Engagement materials": "Engagement materials",
578579
"Engagement meetings": "Engagement meetings",
579580
"engagement name": "engagement name",
@@ -858,6 +859,8 @@
858859
"Leave engagement": "Leave engagement",
859860
"Leave organization": "Leave organization",
860861
"Leave this organization": "Leave this organization",
862+
"Left :engagement": "Left :engagement",
863+
"Left engagement": "Left engagement",
861864
"Light grey on grey": "Light grey on grey",
862865
"Light theme": "Light theme",
863866
"Link copied!": "Link copied!",
@@ -1480,6 +1483,7 @@
14801483
"show up on search results for them": "show up on search results for them",
14811484
"Signed language for interpretation": "Signed language for interpretation",
14821485
"signed language for translation": "signed language for translation",
1486+
"Signed up for :engagement": "Signed up for :engagement",
14831487
"Sign in": "Sign in",
14841488
"Sign language interpretation": "Sign language interpretation",
14851489
"Sign language interpretations": "Sign language interpretations",
@@ -1766,7 +1770,10 @@
17661770
"Video": "Video",
17671771
"Video recording": "Video recording",
17681772
"View": "View",
1773+
"View engagement": "View engagement",
1774+
"View engagement details": "View engagement details",
17691775
"View page": "View page",
1776+
"View this engagement at :url.": "View this engagement at :url.",
17701777
"Virtual or in-person": "Virtual or in-person",
17711778
"Virtual – phone call": "Virtual – phone call",
17721779
"Virtual – web conference": "Virtual – web conference",
@@ -1962,9 +1969,13 @@
19621969
"You have joined :invitationable as a :role": "You have joined :invitationable as a :role",
19631970
"You have joined as a :role": "You have joined as a :role",
19641971
"You have joined the team.": "You have joined the team.",
1972+
"You have left :engagement by :projectable": "You have left :engagement by :projectable",
1973+
"You have left [:engagement](:engagement_url) by [:projectable](:projectable_url).": "You have left [:engagement](:engagement_url) by [:projectable](:projectable_url).",
19651974
"You have not added any engagements yet.": "You have not added any engagements yet.",
19661975
"You have not passed the quiz.": "You have not passed the quiz.",
19671976
"You have now completed this course. Your certificate of completion has been sent to your email.": "You have now completed this course. Your certificate of completion has been sent to your email.",
1977+
"You have signed up for :engagement by :projectable": "You have signed up for :engagement by :projectable",
1978+
"You have signed up for [:engagement](:engagement_url) by [:projectable](:projectable_url).": "You have signed up for [:engagement](:engagement_url) by [:projectable](:projectable_url).",
19681979
"You have successfully added :notificationable to your list.": "You have successfully added :notificationable to your list.",
19691980
"You have successfully added :organization as the Community Organization you are consulting with for this engagement.": "You have successfully added :organization as the Community Organization you are consulting with for this engagement.",
19701981
"You have successfully added the Community Organization you are consulting with for this engagement.": "You have successfully added the Community Organization you are consulting with for this engagement.",
@@ -1987,6 +1998,7 @@
19871998
"You have successfully submitted an estimate request.": "You have successfully submitted an estimate request.",
19881999
"You have successfully unblocked.": "You have successfully unblocked.",
19892000
"You have successfully unblocked :blockable.": "You have successfully unblocked :blockable.",
2001+
"You left [:engagement](:engagement_url) by [:projectable](:projectable_url).": "You left [:engagement](:engagement_url) by [:projectable](:projectable_url).",
19902002
"You may accept this invitation by clicking the button below:": "You may accept this invitation by clicking the button below:",
19912003
"You must agree to the privacy policy.": "You must agree to the privacy policy.",
19922004
"You must agree to the terms of service.": "You must agree to the terms of service.",

resources/lang/fr.json

+13-1
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@
574574
"engagement description (French)": "description de la consultation (français)",
575575
"Engagement estimate has been submitted for your approval": "Le devis pour la consultation a été transmis pour votre approbation",
576576
"engagement format": "format de la consultation",
577+
"Engagement joined": "",
577578
"Engagement materials": "Documents de consultation",
578579
"Engagement meetings": "Réunions de la consultation",
579580
"engagement name": "nom de la consultation",
@@ -858,6 +859,8 @@
858859
"Leave engagement": "Quitter la consultation",
859860
"Leave organization": "Quitter l’organisation",
860861
"Leave this organization": "Quitter cette organisation",
862+
"Left :engagement": "",
863+
"Left engagement": "",
861864
"Light grey on grey": "Gris clair sur gris",
862865
"Light theme": "Thème clair",
863866
"Link copied!": "Lien copié !",
@@ -1480,6 +1483,7 @@
14801483
"show up on search results for them": "apparaître dans les résultats de recherche",
14811484
"Signed language for interpretation": "Langue signée pour l’interprétation",
14821485
"signed language for translation": "traduction en langue des signes",
1486+
"Signed up for :engagement": "",
14831487
"Sign in": "S’identifier",
14841488
"Sign language interpretation": "Interprétation en langue des signes",
14851489
"Sign language interpretations": "Interprétation en langue des signes",
@@ -1766,7 +1770,10 @@
17661770
"Video": "Vidéo",
17671771
"Video recording": "Enregistrement vidéo",
17681772
"View": "Afficher",
1773+
"View engagement": "",
1774+
"View engagement details": "",
17691775
"View page": "Voir la page",
1776+
"View this engagement at :url.": "",
17701777
"Virtual or in-person": "Virtuel ou en personne",
17711778
"Virtual – phone call": "Virtuel - appel téléphonique",
17721779
"Virtual – web conference": "Virtuel - conférence en ligne",
@@ -1962,9 +1969,13 @@
19621969
"You have joined :invitationable as a :role": "Vous avez rejoint :invitationable en tant que :role",
19631970
"You have joined as a :role": "Vous êtes maintenant :role",
19641971
"You have joined the team.": "Vous avez rejoint une équipe.",
1972+
"You have left :engagement by :projectable": "",
1973+
"You have left [:engagement](:engagement_url) by [:projectable](:projectable_url).": "",
19651974
"You have not added any engagements yet.": "Vous n’avez pas encore ajouté de consultations.",
19661975
"You have not passed the quiz.": "Vous n’avez pas réussi le jeu-questionnaire.",
19671976
"You have now completed this course. Your certificate of completion has been sent to your email.": "Vous avez complété avec succès ce sours. Votre certification de réussite vous a été envoyé par courriel.",
1977+
"You have signed up for :engagement by :projectable": "",
1978+
"You have signed up for [:engagement](:engagement_url) by [:projectable](:projectable_url).": "",
19681979
"You have successfully added :notificationable to your list.": "Vous avez ajouté avec succès :notificationable à votre liste.",
19691980
"You have successfully added :organization as the Community Organization you are consulting with for this engagement.": "Vous avez ajouté avec succès :organization comme l’organisation communautaire avec laquelle vous travaillez pour cette consultation.",
19701981
"You have successfully added the Community Organization you are consulting with for this engagement.": "Vous avez ajouté avec succès l’organisation communautaire avec laquelle vous travaillez pour cette consultation.",
@@ -1987,6 +1998,7 @@
19871998
"You have successfully submitted an estimate request.": "Votre demande de devis a été soumise avec succès.",
19881999
"You have successfully unblocked.": "Débloqué avec succès.",
19892000
"You have successfully unblocked :blockable.": "Vous avez débloqué :blockable.",
2001+
"You left [:engagement](:engagement_url) by [:projectable](:projectable_url).": "",
19902002
"You may accept this invitation by clicking the button below:": "Vous pouvez accepter cette invitation en cliquant sur le bouton ci-dessous :",
19912003
"You must agree to the privacy policy.": "Vous devez accepter la politique de confidentialité.",
19922004
"You must agree to the terms of service.": "Vous devez accepter les conditions de service.",
@@ -2167,4 +2179,4 @@
21672179
"“About your organization” (English)": "« À propos de votre organisation » (en anglais)",
21682180
"“About your organization” (French)": "À propos de votre organisation",
21692181
"“About your organization” must be provided in either English or French.": "À propos de votre organisation doit être fournis en anglais ou en français."
2170-
}
2182+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<x-notification :notification="$notification">
2+
<x-slot name="title">{{ $title }}</x-slot>
3+
<x-slot name="body">{{ $body }}</x-slot>
4+
<x-slot name="actions">
5+
<a class="cta secondary"
6+
href="{{ localized_route('engagements.show', $engagement) }}">{{ __('View engagement') }}</a>
7+
</x-slot>
8+
</x-notification>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<x-notification :notification="$notification">
2+
<x-slot name="title">{{ $title }}</x-slot>
3+
<x-slot name="body">{{ $body }}</x-slot>
4+
<x-slot name="actions">
5+
<a class="cta secondary"
6+
href="{{ localized_route('engagements.show', $engagement) }}">{{ __('View engagement') }}</a>
7+
</x-slot>
8+
</x-notification>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@component('mail::message')
2+
{{
3+
safe_markdown('You have signed up for [:engagement](:engagement_url) by [:projectable](:projectable_url).', [
4+
'engagement' => $engagement->getTranslation('name', locale()),
5+
'engagement_url' => localized_route('engagements.show', $engagement),
6+
'projectable' => $projectable->getTranslation('name', locale()),
7+
'projectable_url' => localized_route($projectable->getRoutePrefix().'.show', $projectable),
8+
])
9+
}}
10+
11+
@component('mail::button', ['url' => localized_route('engagements.show', $engagement)])
12+
{{ __('View engagement details') }}
13+
@endcomponent
14+
@endcomponent

0 commit comments

Comments
 (0)