From da532c7059bea5fcd47e2f42210e8ba842a11d63 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 24 Aug 2023 11:42:03 +0200 Subject: [PATCH] feat(notifications): add missing notifications when an user registers to an event Closes #1344 Signed-off-by: Thomas Citharel --- .../components/Activity/EventActivityItem.vue | 9 ++++ js/src/i18n/en_US.json | 5 +- js/src/i18n/fr_FR.json | 5 +- js/src/types/activity.model.ts | 4 +- js/src/types/enums.ts | 4 ++ lib/federation/activity_pub/actions/accept.ex | 5 ++ lib/federation/activity_pub/types/events.ex | 4 ++ lib/mobilizon/activities/activities.ex | 14 +++++- lib/service/activity/activity.ex | 16 ++++++- lib/service/activity/participant.ex | 48 +++++++++++++++++++ lib/service/activity/renderer/event.ex | 12 ++++- lib/service/notifier/email.ex | 2 +- .../activity/_event_activity_item.html.heex | 9 ++++ .../activity/_event_activity_item.text.eex | 18 ++++--- lib/web/views/email_view.ex | 9 +++- 15 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 lib/service/activity/participant.ex diff --git a/js/src/components/Activity/EventActivityItem.vue b/js/src/components/Activity/EventActivityItem.vue index 4c9f1a359..cdb13264e 100644 --- a/js/src/components/Activity/EventActivityItem.vue +++ b/js/src/components/Activity/EventActivityItem.vue @@ -42,6 +42,7 @@ import { usernameWithDomain } from "@/types/actor"; import { formatTimeString } from "@/filters/datetime"; import { ActivityEventCommentSubject, + ActivityEventParticipantSubject, ActivityEventSubject, } from "@/types/enums"; import { computed } from "vue"; @@ -90,6 +91,14 @@ const translation = computed((): string | undefined => { return "You posted a comment on the event {event}."; } return "{profile} posted a comment on the event {event}."; + case ActivityEventParticipantSubject.EVENT_NEW_PARTICIPATION: + if (isAuthorCurrentActor.value) { + return "You joined the event {event}."; + } + if (props.activity.author.preferredUsername === "anonymous") { + return "An anonymous profile joined the event {event}."; + } + return "{profile} joined the the event {event}."; default: return undefined; } diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index dc52d2ec0..58d487f1f 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -1579,5 +1579,8 @@ "Access drafts events": "Access drafts events", "This application will be allowed to list and view your draft events": "This application will be allowed to list and view your draft events", "Access group suggested events": "Access group suggested events", - "This application will be allowed to list your suggested group events": "This application will be allowed to list your suggested group events" + "This application will be allowed to list your suggested group events": "This application will be allowed to list your suggested group events", + "{profile} joined the the event {event}.": "{profile} joined the the event {event}.", + "You joined the event {event}.": "You joined the event {event}.", + "An anonymous profile joined the event {event}.": "An anonymous profile joined the event {event}." } \ No newline at end of file diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index bc91b3b8e..dba74ff49 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -1575,5 +1575,8 @@ "Access drafts events": "Accéder aux événements brouillons", "This application will be allowed to list and view your draft events": "Cetta application sera autorisée à lister et accéder à vos événements brouillons", "Access group suggested events": "Accéder aux événements des groupes suggérés", - "This application will be allowed to list your suggested group events": "Cetta application sera autorisée à lister les événements de vos groupes qui vous sont suggérés" + "This application will be allowed to list your suggested group events": "Cetta application sera autorisée à lister les événements de vos groupes qui vous sont suggérés", + "{profile} joined the the event {event}.": "{profile} a rejoint l'événement {event}.", + "You joined the event {event}.": "Vous avez rejoint l'événement {event}.", + "An anonymous profile joined the event {event}.": "Un profil anonyme a rejoint l'événement {event}." } diff --git a/js/src/types/activity.model.ts b/js/src/types/activity.model.ts index c5c218757..f3ecb913e 100644 --- a/js/src/types/activity.model.ts +++ b/js/src/types/activity.model.ts @@ -3,6 +3,7 @@ import { IMember } from "./actor/member.model"; import { ActivityDiscussionSubject, ActivityEventCommentSubject, + ActivityEventParticipantSubject, ActivityEventSubject, ActivityGroupSubject, ActivityMemberSubject, @@ -21,7 +22,8 @@ export type ActivitySubject = | ActivityResourceSubject | ActivityDiscussionSubject | ActivityGroupSubject - | ActivityEventCommentSubject; + | ActivityEventCommentSubject + | ActivityEventParticipantSubject; export interface IActivity { id: string; diff --git a/js/src/types/enums.ts b/js/src/types/enums.ts index 0eb3841bb..dc551f3f9 100644 --- a/js/src/types/enums.ts +++ b/js/src/types/enums.ts @@ -200,6 +200,10 @@ export enum ActivityEventCommentSubject { COMMENT_POSTED = "comment_posted", } +export enum ActivityEventParticipantSubject { + EVENT_NEW_PARTICIPATION = "event_new_participation", +} + export enum ActivityPostSubject { POST_CREATED = "post_created", POST_UPDATED = "post_updated", diff --git a/lib/federation/activity_pub/actions/accept.ex b/lib/federation/activity_pub/actions/accept.ex index 87912de97..1480a262f 100644 --- a/lib/federation/activity_pub/actions/accept.ex +++ b/lib/federation/activity_pub/actions/accept.ex @@ -82,6 +82,11 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Accept do ) Scheduler.trigger_notifications_for_participant(participant) + + Mobilizon.Service.Activity.Participant.insert_activity(participant, + subject: "event_new_participation" + ) + participant_as_data = Convertible.model_to_as(participant) audience = Audience.get_audience(participant) diff --git a/lib/federation/activity_pub/types/events.ex b/lib/federation/activity_pub/types/events.ex index 13b3e2f12..94cd9d665 100644 --- a/lib/federation/activity_pub/types/events.ex +++ b/lib/federation/activity_pub/types/events.ex @@ -224,6 +224,10 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do cond do Mobilizon.Events.get_default_participant_role(event) == :participant && role == :participant -> + Mobilizon.Service.Activity.Participant.insert_activity(participant, + subject: "event_new_participation" + ) + {:accept, Actions.Accept.accept( :join, diff --git a/lib/mobilizon/activities/activities.ex b/lib/mobilizon/activities/activities.ex index 52b7683ed..d851f19fb 100644 --- a/lib/mobilizon/activities/activities.ex +++ b/lib/mobilizon/activities/activities.ex @@ -19,6 +19,7 @@ defmodule Mobilizon.Activities do @activity_types ["event", "post", "discussion", "resource", "group", "member", "comment"] @event_activity_subjects ["event_created", "event_updated", "event_deleted", "comment_posted"] + @participant_activity_subjects ["event_new_participation"] @post_activity_subjects ["post_created", "post_updated", "post_deleted"] @discussion_activity_subjects [ "discussion_created", @@ -48,12 +49,23 @@ defmodule Mobilizon.Activities do @settings_activity_subjects ["group_created", "group_updated"] @subjects @event_activity_subjects ++ + @participant_activity_subjects ++ @post_activity_subjects ++ @discussion_activity_subjects ++ @resource_activity_subjects ++ @member_activity_subjects ++ @settings_activity_subjects - @object_type ["event", "actor", "post", "discussion", "resource", "member", "group", "comment"] + @object_type [ + "event", + "participant", + "actor", + "post", + "discussion", + "resource", + "member", + "group", + "comment" + ] defenum(Type, @activity_types) defenum(Subject, @subjects) diff --git a/lib/service/activity/activity.ex b/lib/service/activity/activity.ex index 9c436fecf..1262102c6 100644 --- a/lib/service/activity/activity.ex +++ b/lib/service/activity/activity.ex @@ -4,7 +4,17 @@ defmodule Mobilizon.Service.Activity do """ alias Mobilizon.Activities.Activity - alias Mobilizon.Service.Activity.{Comment, Discussion, Event, Group, Member, Post, Resource} + + alias Mobilizon.Service.Activity.{ + Comment, + Discussion, + Event, + Group, + Member, + Participant, + Post, + Resource + } @callback insert_activity(entity :: struct(), options :: Keyword.t()) :: {:ok, Oban.Job.t()} | {:ok, any()} | {:error, Ecto.Changeset.t()} @@ -45,4 +55,8 @@ defmodule Mobilizon.Service.Activity do defp do_get_object(:comment, comment_id) do Comment.get_object(comment_id) end + + defp do_get_object(:participant, participant_id) do + Participant.get_object(participant_id) + end end diff --git a/lib/service/activity/participant.ex b/lib/service/activity/participant.ex new file mode 100644 index 000000000..791e67fff --- /dev/null +++ b/lib/service/activity/participant.ex @@ -0,0 +1,48 @@ +defmodule Mobilizon.Service.Activity.Participant do + @moduledoc """ + Insert an event activity + """ + alias Mobilizon.{Actors, Events} + alias Mobilizon.Actors.Actor + alias Mobilizon.Events.Participant + alias Mobilizon.Service.Activity + alias Mobilizon.Service.Workers.ActivityBuilder + + @behaviour Activity + + @impl Activity + def insert_activity(event, options \\ []) + + def insert_activity( + %Participant{event_id: event_id, actor_id: actor_id, id: participant_id} = + _participant, + options + ) do + actor = Actors.get_actor(actor_id) + event = Events.get_event!(event_id) + subject = Keyword.fetch!(options, :subject) + + ActivityBuilder.enqueue(:build_activity, %{ + "type" => "event", + "subject" => subject, + "subject_params" => %{ + actor_name: Actor.display_name(actor), + event_title: event.title, + event_uuid: event.uuid + }, + "group_id" => event.attributed_to_id, + "author_id" => actor.id, + "object_type" => "participant", + "object_id" => participant_id, + "inserted_at" => DateTime.utc_now() + }) + end + + @impl Activity + def insert_activity(_, _), do: {:ok, nil} + + @impl Activity + def get_object(participant_id) do + Events.get_participant(participant_id) + end +end diff --git a/lib/service/activity/renderer/event.ex b/lib/service/activity/renderer/event.ex index 809583bb0..3bf07232f 100644 --- a/lib/service/activity/renderer/event.ex +++ b/lib/service/activity/renderer/event.ex @@ -1,6 +1,6 @@ defmodule Mobilizon.Service.Activity.Renderer.Event do @moduledoc """ - Insert a comment activity + Insert an event activity """ alias Mobilizon.Activities.Activity alias Mobilizon.Actors.Actor @@ -67,6 +67,16 @@ defmodule Mobilizon.Service.Activity.Renderer.Event do url: event_url(activity) } end + + :event_new_participation -> + %{ + body: + dgettext("activity", "%{profile} joined your event %{event}.", %{ + profile: profile(activity), + event: title(activity) + }), + url: event_url(activity) + } end end diff --git a/lib/service/notifier/email.ex b/lib/service/notifier/email.ex index acd213d2e..750c70706 100644 --- a/lib/service/notifier/email.ex +++ b/lib/service/notifier/email.ex @@ -96,7 +96,7 @@ defmodule Mobilizon.Service.Notifier.Email do defp can_send_activity?(activity, user, options) do Logger.warning( - "Can't check if user #{inspect(user)} can be sent an activity (#{inspect(activity)}) (#{inspect(options)})" + "Can't check if user #{inspect(user.email)} can be sent an activity (#{inspect(activity)}) (#{inspect(options)})" ) false diff --git a/lib/web/templates/email/activity/_event_activity_item.html.heex b/lib/web/templates/email/activity/_event_activity_item.html.heex index 9af392741..0497d658d 100644 --- a/lib/web/templates/email/activity/_event_activity_item.html.heex +++ b/lib/web/templates/email/activity/_event_activity_item.html.heex @@ -51,4 +51,13 @@ }) |> raw %> <% end %> + <% :event_new_participation -> %> + <%= dgettext("activity", "%{profile} joined your event %{event}.", %{ + profile: "#{escaped_display_name_and_username(@activity.author)}", + event: + " URI.decode()}\">#{escape_html(@activity.subject_params["event_title"])}" + }) + |> raw %> <% end %> diff --git a/lib/web/templates/email/activity/_event_activity_item.text.eex b/lib/web/templates/email/activity/_event_activity_item.text.eex index 390bd2611..30b05abc0 100644 --- a/lib/web/templates/email/activity/_event_activity_item.text.eex +++ b/lib/web/templates/email/activity/_event_activity_item.text.eex @@ -1,31 +1,37 @@ <%= case @activity.subject do %><% :event_created -> %><%= dgettext("activity", "The event %{event} was created by %{profile}.", %{ - profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author), + profile: display_name_and_username(@activity.author), event: @activity.subject_params["event_title"] } ) %> <%= Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %><% :event_updated -> %><%= dgettext("activity", "The event %{event} was updated by %{profile}.", %{ - profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author), + profile: display_name_and_username(@activity.author), event: @activity.subject_params["event_title"] } ) %> <%= Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %><% :event_deleted -> %><%= dgettext("activity", "The event %{event} was deleted by %{profile}.", %{ - profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author), + profile: display_name_and_username(@activity.author), event: @activity.subject_params["event_title"] } ) %> <% :comment_posted -> %><%= if @activity.subject_params["comment_reply_to"] do %><%= dgettext("activity", "%{profile} replied to a comment on the event %{event}.", %{ - profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author), + profile: display_name_and_username(@activity.author), event: @activity.subject_params["event_title"] } ) %> <%= Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %><% else %><%= dgettext("activity", "%{profile} posted a comment on the event %{event}.", %{ - profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author), + profile: display_name_and_username(@activity.author), event: @activity.subject_params["event_title"] } ) %> -<%= Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %><% end %><% end %> \ No newline at end of file +<%= Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %><% end %><% :event_new_participation -> %><%= dgettext("activity", "%{profile} joined your event %{event}.", + %{ + profile: display_name_and_username(@activity.author), + event: @activity.subject_params["event_title"] + } +) %> +<%= Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %><% end %> \ No newline at end of file diff --git a/lib/web/views/email_view.ex b/lib/web/views/email_view.ex index c103e4fcf..82b0e74d9 100644 --- a/lib/web/views/email_view.ex +++ b/lib/web/views/email_view.ex @@ -25,7 +25,6 @@ defmodule Mobilizon.Web.EmailView do defdelegate datetime_relative(datetime, locale \\ "en"), to: DateTimeRenderer defdelegate render_address(address), to: Address defdelegate is_same_day?(one, two), to: DateTimeRenderer - defdelegate display_name_and_username(actor), to: Actor defdelegate display_name(actor), to: Actor defdelegate preferred_username_and_domain(actor), to: Actor @@ -38,7 +37,13 @@ defmodule Mobilizon.Web.EmailView do def escaped_display_name_and_username(actor) do actor - |> Actor.display_name_and_username() + |> display_name_and_username() |> escape_html() end + + def display_name_and_username(%Actor{preferred_username: "anonymous"}) do + dgettext("activity", "An anonymous profile") + end + + def display_name_and_username(actor), do: Actor.display_name_and_username(actor) end