diff --git a/lib/federation/activity_pub/types/conversation.ex b/lib/federation/activity_pub/types/conversation.ex
index b72cef022..6bb8c7918 100644
--- a/lib/federation/activity_pub/types/conversation.ex
+++ b/lib/federation/activity_pub/types/conversation.ex
@@ -147,6 +147,12 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Conversations do
       (args |> Map.get(:mentions, []) |> prepare_mentions()) ++
         ConverterUtils.fetch_mentions(mentions)
 
+    # Can't create a conversation with just ourselves
+    mentions =
+      Enum.filter(mentions, fn %{actor_id: actor_id} ->
+        to_string(actor_id) != to_string(args.actor_id)
+      end)
+
     if Enum.empty?(mentions) do
       {:error, :empty_participants}
     else
diff --git a/lib/graphql/resolvers/conversation.ex b/lib/graphql/resolvers/conversation.ex
index 00f95de53..33c76c333 100644
--- a/lib/graphql/resolvers/conversation.ex
+++ b/lib/graphql/resolvers/conversation.ex
@@ -11,8 +11,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Conversation do
   alias Mobilizon.Storage.Page
   alias Mobilizon.Users.User
   alias Mobilizon.Web.Endpoint
-  # alias Mobilizon.Users.User
   import Mobilizon.Web.Gettext, only: [dgettext: 2]
+  require Logger
 
   @spec find_conversations_for_event(Event.t(), map(), Absinthe.Resolution.t()) ::
           {:ok, Page.t(ConversationView.t())} | {:error, :unauthenticated}
@@ -157,9 +157,17 @@ defmodule Mobilizon.GraphQL.Resolvers.Conversation do
           {:ok, conversation_to_view(conversation, conversation_participant_actor)}
 
         {:error, :empty_participants} ->
-          {:error, dgettext("errors", "Conversation needs to mention at least one participant")}
+          {:error,
+           dgettext(
+             "errors",
+             "Conversation needs to mention at least one participant that's not yourself"
+           )}
       end
     else
+      Logger.debug(
+        "Actor #{current_actor.id} is not authorized to reply to conversation #{inspect(Map.get(args, :conversation_id))}"
+      )
+
       {:error, :unauthorized}
     end
   end
@@ -259,7 +267,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Conversation do
       %Conversation{participants: participants} ->
         participant_ids = Enum.map(participants, fn participant -> to_string(participant.id) end)
 
-        current_actor_id in participant_ids or
+        to_string(current_actor_id) in participant_ids or
           Enum.any?(participant_ids, fn participant_id ->
             Actors.is_member?(current_actor_id, participant_id) and
               attributed_to_id == participant_id
diff --git a/lib/graphql/resolvers/participant.ex b/lib/graphql/resolvers/participant.ex
index 520ecb149..23e739873 100644
--- a/lib/graphql/resolvers/participant.ex
+++ b/lib/graphql/resolvers/participant.ex
@@ -2,8 +2,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
   @moduledoc """
   Handles the participation-related GraphQL calls.
   """
-  # alias Mobilizon.Conversations.ConversationParticipant
-  alias Mobilizon.{Actors, Config, Crypto, Events}
+  alias Mobilizon.{Actors, Config, Conversations, Crypto, Events}
   alias Mobilizon.Actors.Actor
   alias Mobilizon.Conversations.{Conversation, ConversationView}
   alias Mobilizon.Events.{Event, Participant}
@@ -386,6 +385,13 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
       {:member, false} ->
         {:error, :unauthorized}
 
+      {:error, :empty_participants} ->
+        {:error,
+         dgettext(
+           "errors",
+           "There are no participants matching the audience you've selected."
+         )}
+
       {:error, err} ->
         {:error, err}
     end
@@ -394,11 +400,19 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
   def send_private_messages_to_participants(_parent, _args, _resolution),
     do: {:error, :unauthorized}
 
-  defp conversation_to_view(%Conversation{} = conversation, %Actor{} = actor) do
+  defp conversation_to_view(
+         %Conversation{id: conversation_id} = conversation,
+         %Actor{id: actor_id} = actor
+       ) do
     value =
       conversation
       |> Map.from_struct()
       |> Map.put(:actor, actor)
+      |> Map.put(:unread, false)
+      |> Map.put(
+        :conversation_participant_id,
+        Conversations.get_participant_by_conversation_and_actor(conversation_id, actor_id).id
+      )
 
     struct(ConversationView, value)
   end
diff --git a/lib/mobilizon/discussions/comment.ex b/lib/mobilizon/discussions/comment.ex
index 0bd20f245..151f45218 100644
--- a/lib/mobilizon/discussions/comment.ex
+++ b/lib/mobilizon/discussions/comment.ex
@@ -77,7 +77,7 @@ defmodule Mobilizon.Discussions.Comment do
     belongs_to(:conversation, Conversation)
     has_many(:replies, Comment, foreign_key: :origin_comment_id)
     many_to_many(:tags, Tag, join_through: "comments_tags", on_replace: :delete)
-    has_many(:mentions, Mention)
+    has_many(:mentions, Mention, on_replace: :delete)
     many_to_many(:media, Media, join_through: "comments_medias", on_replace: :delete)
 
     timestamps(type: :utc_datetime)
diff --git a/lib/service/activity/conversation.ex b/lib/service/activity/conversation.ex
index f9dfe9738..3f31f1db0 100644
--- a/lib/service/activity/conversation.ex
+++ b/lib/service/activity/conversation.ex
@@ -79,11 +79,16 @@ defmodule Mobilizon.Service.Activity.Conversation do
   defp send_participant_notifications(_, _, _, _), do: {:ok, :skipped}
 
   defp event_subject_params(%Conversation{
-         event: %Event{id: conversation_event_id, title: conversation_event_title}
+         event: %Event{
+           id: conversation_event_id,
+           title: conversation_event_title,
+           uuid: conversation_event_uuid
+         }
        }),
        do: %{
          conversation_event_id: conversation_event_id,
-         conversation_event_title: conversation_event_title
+         conversation_event_title: conversation_event_title,
+         conversation_event_uuid: conversation_event_uuid
        }
 
   defp event_subject_params(_), do: %{}
diff --git a/lib/service/formatter/html.ex b/lib/service/formatter/html.ex
index 5544eeded..67948d030 100644
--- a/lib/service/formatter/html.ex
+++ b/lib/service/formatter/html.ex
@@ -14,6 +14,8 @@ defmodule Mobilizon.Service.Formatter.HTML do
 
   def filter_tags(html), do: Sanitizer.scrub(html, DefaultScrubbler)
 
+  defdelegate basic_html(html), to: FastSanitize
+
   @spec strip_tags(String.t()) :: String.t() | no_return()
   def strip_tags(html) do
     case FastSanitize.strip_tags(html) do
@@ -39,5 +41,17 @@ defmodule Mobilizon.Service.Formatter.HTML do
 
   def strip_tags_and_insert_spaces(html), do: html
 
+  @spec html_to_text(String.t()) :: String.t()
+  def html_to_text(html) do
+    html
+    |> String.replace(~r/<li>/, "\\g{1}- ", global: true)
+    |> String.replace(
+      ~r/<\/?\s?br>|<\/\s?p>|<\/\s?li>|<\/\s?div>|<\/\s?h.>/,
+      "\\g{1}\n\r",
+      global: true
+    )
+    |> strip_tags()
+  end
+
   def filter_tags_for_oembed(html), do: Sanitizer.scrub(html, OEmbed)
 end
diff --git a/lib/service/formatter/text.ex b/lib/service/formatter/text.ex
new file mode 100644
index 000000000..140502f16
--- /dev/null
+++ b/lib/service/formatter/text.ex
@@ -0,0 +1,37 @@
+defmodule Mobilizon.Service.Formatter.Text do
+  @moduledoc """
+  Helps to format text blocks
+
+  Inspired from https://elixirforum.com/t/is-there-are-text-wrapping-library-for-elixir/21733/4
+  Using the Knuth-Plass Line Wrapping Algorithm https://www.students.cs.ubc.ca/~cs-490/2015W2/lectures/Knuth.pdf
+  """
+
+  def quote_paragraph(string, max_line_length) do
+    paragraph(string, max_line_length, "> ")
+  end
+
+  def paragraph(string, max_line_length, prefix \\ "") do
+    string
+    |> String.split("\n\n", trim: true)
+    |> Enum.map(&subparagraph(&1, max_line_length, prefix))
+    |> Enum.join("\n#{prefix}\n")
+  end
+
+  defp subparagraph(string, max_line_length, prefix) do
+    [word | rest] = String.split(string, ~r/\s+/, trim: true)
+
+    lines_assemble(rest, max_line_length - String.length(prefix), String.length(word), word, [])
+    |> Enum.map(&"#{prefix}#{&1}")
+    |> Enum.join("\n")
+  end
+
+  defp lines_assemble([], _, _, line, acc), do: [line | acc] |> Enum.reverse()
+
+  defp lines_assemble([word | rest], max, line_length, line, acc) do
+    if line_length + 1 + String.length(word) > max do
+      lines_assemble(rest, max, String.length(word), word, [line | acc])
+    else
+      lines_assemble(rest, max, line_length + 1 + String.length(word), line <> " " <> word, acc)
+    end
+  end
+end
diff --git a/lib/service/workers/legacy_notifier_builder.ex b/lib/service/workers/legacy_notifier_builder.ex
index 4c0f776c7..a00e19260 100644
--- a/lib/service/workers/legacy_notifier_builder.ex
+++ b/lib/service/workers/legacy_notifier_builder.ex
@@ -22,6 +22,13 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilder do
       notify_anonymous_participants(get_in(args, ["subject_params", "event_id"]), activity)
     end
 
+    if args["subject"] == "conversation_created" do
+      notify_anonymous_participants(
+        get_in(args, ["subject_params", "conversation_event_id"]),
+        activity
+      )
+    end
+
     args
     |> users_to_notify(author_id: args["author_id"], group_id: Map.get(args, "group_id"))
     |> Enum.each(&notify_user(&1, activity))
diff --git a/lib/web/email/activity.ex b/lib/web/email/activity.ex
index 4f3f1a9f1..686360e0f 100644
--- a/lib/web/email/activity.ex
+++ b/lib/web/email/activity.ex
@@ -10,6 +10,7 @@ defmodule Mobilizon.Web.Email.Activity do
   alias Mobilizon.Actors.Actor
   alias Mobilizon.Config
   alias Mobilizon.Web.Email
+  require Logger
 
   @spec direct_activity(String.t(), list(), Keyword.t()) :: Swoosh.Email.t()
   def direct_activity(
@@ -39,6 +40,36 @@ defmodule Mobilizon.Web.Email.Activity do
   end
 
   @spec anonymous_activity(String.t(), Activity.t(), Keyword.t()) :: Swoosh.Email.t()
+  def anonymous_activity(
+        email,
+        %Activity{subject_params: subject_params, type: :conversation} = activity,
+        options
+      ) do
+    locale = Keyword.get(options, :locale, "en")
+
+    subject =
+      dgettext(
+        "activity",
+        "Informations about your event %{event}",
+        event: subject_params["conversation_event_title"]
+      )
+
+    conversation = Mobilizon.Conversations.get_conversation(activity.object_id)
+
+    Logger.debug("Going to send anonymous activity of type #{activity.type} to #{email}")
+
+    [to: email, subject: subject]
+    |> Email.base_email()
+    |> render_body(:email_anonymous_activity, %{
+      subject: subject,
+      activity: activity,
+      locale: locale,
+      extra: %{
+        "conversation" => conversation
+      }
+    })
+  end
+
   def anonymous_activity(email, %Activity{subject_params: subject_params} = activity, options) do
     locale = Keyword.get(options, :locale, "en")
 
@@ -49,6 +80,8 @@ defmodule Mobilizon.Web.Email.Activity do
         event: subject_params["event_title"]
       )
 
+    Logger.debug("Going to send anonymous activity of type #{activity.type} to #{email}")
+
     [to: email, subject: subject]
     |> Email.base_email()
     |> render_body(:email_anonymous_activity, %{
diff --git a/lib/web/templates/email/email_anonymous_activity.html.heex b/lib/web/templates/email/email_anonymous_activity.html.heex
index 4e0d9421e..b08a0652f 100644
--- a/lib/web/templates/email/email_anonymous_activity.html.heex
+++ b/lib/web/templates/email/email_anonymous_activity.html.heex
@@ -35,61 +35,164 @@
         <tr>
           <td align="center" valign="top" width="600">
     <![endif]-->
-    <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
-      <!-- COPY -->
-      <tr>
-        <td bgcolor="#ffffff" align="left">
-          <table width="100%" border="0" cellspacing="0" cellpadding="0">
-            <tr>
-              <td
-                align="center"
-                style="border-radius: 3px; text-align: left; padding: 10px 5% 0px 30px; color: #474467; font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; font-weight: 400;line-height: 25px;"
-              >
-                <%= dgettext(
-                  "activity",
-                  "%{profile} has posted an announcement under event %{event}.",
-                  %{
-                    profile: "<b>#{escape_html(display_name_and_username(@activity.author))}</b>",
-                    event:
-                      "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
-                      :event,
-                      @activity.subject_params["event_uuid"]) |> URI.decode()}\">
+    <%= case @activity.type do %>
+      <% :comment -> %>
+        <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
+          <!-- COPY -->
+          <tr>
+            <td bgcolor="#ffffff" align="left">
+              <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                <tr>
+                  <td
+                    align="center"
+                    style="border-radius: 3px; text-align: left; padding: 10px 5% 0px 30px; color: #474467; font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; font-weight: 400;line-height: 25px;"
+                  >
+                    <%= dgettext(
+                      "activity",
+                      "%{profile} has posted a public announcement under event %{event}.",
+                      %{
+                        profile:
+                          "<b>#{escape_html(display_name_and_username(@activity.author))}</b>",
+                        event:
+                          "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
+                          :event,
+                          @activity.subject_params["event_uuid"]) |> URI.decode()}\">
                                 #{escape_html(@activity.subject_params["event_title"])}
                             </a>"
-                  }
-                )
-                |> raw %>
-              </td>
-            </tr>
-          </table>
-        </td>
-      </tr>
-      <tr>
-        <td bgcolor="#ffffff" align="left">
-          <table width="100%" border="0" cellspacing="0" cellpadding="0">
-            <tr>
-              <td bgcolor="#ffffff" align="center" style="padding: 20px 30px 60px 30px;">
-                <table border="0" cellspacing="0" cellpadding="0">
-                  <tr>
-                    <td align="center" style="border-radius: 3px;" bgcolor="#3C376E">
-                      <a
-                        href={
+                      }
+                    )
+                    |> raw %>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+          <tr>
+            <td bgcolor="#ffffff" align="left">
+              <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                <tr>
+                  <td bgcolor="#ffffff" align="center" style="padding: 20px 30px 60px 30px;">
+                    <table border="0" cellspacing="0" cellpadding="0">
+                      <tr>
+                        <td align="center" style="border-radius: 3px;" bgcolor="#3C376E">
+                          <a
+                            href={
                           "#{Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"])}"
                         }
-                        target="_blank"
-                        style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #3C376E; display: inline-block;"
-                      >
-                        <%= gettext("Visit event page") %>
-                      </a>
-                    </td>
-                  </tr>
-                </table>
-              </td>
-            </tr>
-          </table>
-        </td>
-      </tr>
-    </table>
+                            target="_blank"
+                            style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #3C376E; display: inline-block;"
+                          >
+                            <%= gettext("Visit event page") %>
+                          </a>
+                        </td>
+                      </tr>
+                    </table>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+        </table>
+      <% :conversation -> %>
+        <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
+          <!-- COPY -->
+          <tr>
+            <td bgcolor="#ffffff" align="left">
+              <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                <tr>
+                  <td
+                    align="center"
+                    style="border-radius: 3px; text-align: left; padding: 10px 5% 0px 30px; color: #474467; font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; font-weight: 400;line-height: 25px;"
+                  >
+                    <%= dgettext(
+                      "activity",
+                      "%{profile} has posted a private announcement about event %{event}.",
+                      %{
+                        profile:
+                          "<b>#{escape_html(display_name_and_username(@activity.author))}</b>",
+                        event:
+                          "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
+                          :event,
+                          @activity.subject_params["conversation_event_uuid"]) |> URI.decode()}\">
+                                #{escape_html(@activity.subject_params["conversation_event_title"])}
+                            </a>"
+                      }
+                    )
+                    |> raw %>
+                    <%= dgettext(
+                      "activity",
+                      "It might give details on how to join the event, so make sure to read it appropriately."
+                    ) %>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+          <tr>
+            <td bgcolor="#ffffff" align="left">
+              <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                <tr>
+                  <td bgcolor="#ffffff" align="center" style="padding: 20px 30px 60px 30px;">
+                    <table border="0" cellspacing="0" cellpadding="0">
+                      <tr>
+                        <td align="center">
+                          <blockquote style="border-left-width: 0.25rem;border-left-color: #e2e8f0;border-left-style: solid;padding-left: 1em;margin: 0;text-align: start;color: #474467;font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; font-weight: 400;line-height: 25px;">
+                            <%= @extra["conversation"].last_comment.text
+                            |> sanitize_to_basic_html()
+                            |> raw() %>
+                          </blockquote>
+                        </td>
+                      </tr>
+                    </table>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+          <tr>
+            <td bgcolor="#ffffff" align="left">
+              <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                <tr>
+                  <td
+                    align="center"
+                    style="border-radius: 3px; text-align: left; padding: 10px 5% 0px 30px; color: #474467; font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; font-weight: 400;line-height: 25px;"
+                  >
+                    <%= dgettext(
+                      "activity",
+                      "This information is sent privately to you as a person who registered for this event. Share the informations above with other people with caution."
+                    ) %>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+          <tr>
+            <td bgcolor="#ffffff" align="left">
+              <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                <tr>
+                  <td bgcolor="#ffffff" align="center" style="padding: 20px 30px 60px 30px;">
+                    <table border="0" cellspacing="0" cellpadding="0">
+                      <tr>
+                        <td align="center" style="border-radius: 3px;" bgcolor="#3C376E">
+                          <a
+                            href={
+                          "#{Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["conversation_event_uuid"])}"
+                        }
+                            target="_blank"
+                            style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #3C376E; display: inline-block;"
+                          >
+                            <%= gettext("Visit event page") %>
+                          </a>
+                        </td>
+                      </tr>
+                    </table>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+        </table>
+    <% end %>
     <!--[if (gte mso 9)|(IE)]>
     </td>
   </tr>
diff --git a/lib/web/templates/email/email_anonymous_activity.text.eex b/lib/web/templates/email/email_anonymous_activity.text.eex
index c19b43834..106d81bd4 100644
--- a/lib/web/templates/email/email_anonymous_activity.text.eex
+++ b/lib/web/templates/email/email_anonymous_activity.text.eex
@@ -1,11 +1,30 @@
 <%= @subject %>
 
 ==
-
-<%= dgettext("activity", "%{profile} has posted an announcement under event %{event}.",
+<%= case @activity.type do %>
+<% :comment -> %>
+<%= dgettext("activity", "%{profile} has posted a public announcement under event %{event}.",
     %{
         profile: Mobilizon.Actors.Actor.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() %>
\ No newline at end of file
+<%= Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %>
+<% :conversation -> %>
+<%= dgettext("activity", "%{profile} has posted a private announcement about event %{event}.",
+    %{
+        profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
+        event: @activity.subject_params["conversation_event_title"]
+    }
+) %>
+<%= dgettext("activity", "It might give details on how to join the event, so make sure to read it appropriately.") %>
+
+--
+
+<%= @extra["conversation"].last_comment.text |> html_to_text() |> mail_quote() %>
+
+--
+
+<%= dgettext("activity", "This information is sent privately to you as a person who registered for this event. Share the informations above with other people with caution.") %>
+<%= Routes.page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["conversation_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 82b0e74d9..c24aa87f7 100644
--- a/lib/web/views/email_view.ex
+++ b/lib/web/views/email_view.ex
@@ -7,6 +7,7 @@ defmodule Mobilizon.Web.EmailView do
   alias Mobilizon.Actors.Actor
   alias Mobilizon.Service.Address
   alias Mobilizon.Service.DateTime, as: DateTimeRenderer
+  alias Mobilizon.Service.Formatter.{HTML, Text}
   alias Mobilizon.Web.Router.Helpers, as: Routes
   import Mobilizon.Web.Gettext
   import Mobilizon.Service.Metadata.Utils, only: [process_description: 1]
@@ -35,6 +36,21 @@ defmodule Mobilizon.Web.EmailView do
     |> safe_to_string()
   end
 
+  @spec sanitize_to_basic_html(String.t()) :: String.t()
+  def sanitize_to_basic_html(html) do
+    case HTML.basic_html(html) do
+      {:ok, html} -> html
+      _ -> ""
+    end
+  end
+
+  defdelegate html_to_text(html), to: HTML
+
+  def mail_quote(text) do
+    # https://www.emailonacid.com/blog/article/email-development/line-length-in-html-email/
+    Text.quote_paragraph(text, 78)
+  end
+
   def escaped_display_name_and_username(actor) do
     actor
     |> display_name_and_username()
diff --git a/src/assets/oruga-tailwindcss.css b/src/assets/oruga-tailwindcss.css
index 2583e89fc..619c5af46 100644
--- a/src/assets/oruga-tailwindcss.css
+++ b/src/assets/oruga-tailwindcss.css
@@ -42,13 +42,13 @@ body {
   @apply bg-transparent text-black dark:text-white font-semibold py-2 px-4 border border-mbz-bluegreen dark:border-violet-3;
 }
 .btn-outlined-success {
-  @apply border-mbz-success hover:bg-mbz-success;
+  @apply border-2 border-mbz-success bg-transparent text-mbz-success hover:bg-mbz-success;
 }
 .btn-outlined-warning {
   @apply bg-transparent border dark:text-white hover:dark:text-slate-900 hover:bg-mbz-warning border-mbz-warning;
 }
 .btn-outlined-danger {
-  @apply border-mbz-danger hover:bg-mbz-danger;
+  @apply border-2 bg-transparent border-mbz-danger text-mbz-danger hover:bg-mbz-danger;
 }
 .btn-outlined-text {
   @apply bg-transparent hover:text-slate-900;
@@ -161,15 +161,18 @@ body {
 }
 
 .dropdown-item-active {
-  @apply bg-white dark:bg-zinc-700 dark:text-zinc-100 text-black;
+  @apply bg-mbz-yellow-500 dark:bg-mbz-yellow-900 dark:text-zinc-100 text-black;
 }
 .dropdown-button {
   @apply inline-flex gap-1;
 }
 
 /* Checkbox */
-
 .checkbox {
+  margin-inline-end: 1rem;
+}
+
+.checkbox-check {
   @apply appearance-none bg-primary border-primary;
 }
 
diff --git a/src/components/Account/ActorAutoComplete.vue b/src/components/Account/ActorAutoComplete.vue
index b95c37055..47da387aa 100644
--- a/src/components/Account/ActorAutoComplete.vue
+++ b/src/components/Account/ActorAutoComplete.vue
@@ -1,6 +1,6 @@
 <template>
   <o-inputitems
-    :modelValue="modelValue"
+    :modelValue="modelValueWithDisplayName"
     @update:modelValue="(val: IActor[]) => $emit('update:modelValue', val)"
     :data="availableActors"
     :allow-autocomplete="true"
@@ -21,10 +21,10 @@ import { SEARCH_PERSON_AND_GROUPS } from "@/graphql/search";
 import { IActor, IGroup, IPerson, displayName } from "@/types/actor";
 import { Paginate } from "@/types/paginate";
 import { useLazyQuery } from "@vue/apollo-composable";
-import { ref } from "vue";
+import { computed, ref } from "vue";
 import ActorInline from "./ActorInline.vue";
 
-defineProps<{
+const props = defineProps<{
   modelValue: IActor[];
 }>();
 
@@ -32,6 +32,15 @@ defineEmits<{
   "update:modelValue": [value: IActor[]];
 }>();
 
+const modelValue = computed(() => props.modelValue);
+
+const modelValueWithDisplayName = computed(() =>
+  modelValue.value.map((actor) => ({
+    ...actor,
+    displayName: displayName(actor),
+  }))
+);
+
 const {
   load: loadSearchPersonsAndGroupsQuery,
   refetch: refetchSearchPersonsAndGroupsQuery,
diff --git a/src/components/Account/ActorCard.vue b/src/components/Account/ActorCard.vue
index c4d49710d..2124b746d 100644
--- a/src/components/Account/ActorCard.vue
+++ b/src/components/Account/ActorCard.vue
@@ -39,8 +39,18 @@
         v-html="actor.summary"
       />
     </div>
-    <div class="flex pr-2">
-      <Email />
+    <div class="flex pr-2" v-if="actor.type === ActorType.PERSON">
+      <router-link
+        :to="{
+          name: RouteName.CONVERSATION_LIST,
+          query: {
+            newMessage: 'true',
+            personMentions: usernameWithDomain(actor),
+          },
+        }"
+      >
+        <Email />
+      </router-link>
     </div>
   </div>
   <!-- <div
@@ -85,6 +95,8 @@
 import { displayName, IActor, usernameWithDomain } from "../../types/actor";
 import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
 import Email from "vue-material-design-icons/Email.vue";
+import RouteName from "@/router/name";
+import { ActorType } from "@/types/enums";
 
 withDefaults(
   defineProps<{
diff --git a/src/components/Account/ActorInline.vue b/src/components/Account/ActorInline.vue
index b99a7d381..54e518b87 100644
--- a/src/components/Account/ActorInline.vue
+++ b/src/components/Account/ActorInline.vue
@@ -24,15 +24,11 @@
         @{{ usernameWithDomain(actor) }}
       </p>
     </div>
-    <div class="flex pr-2 self-center">
-      <Email />
-    </div>
   </div>
 </template>
 <script lang="ts" setup>
 import { displayName, IActor, usernameWithDomain } from "../../types/actor";
 import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
-import Email from "vue-material-design-icons/Email.vue";
 
 defineProps<{
   actor: IActor;
diff --git a/src/components/Conversations/ConversationListItem.vue b/src/components/Conversations/ConversationListItem.vue
index cdc4a8110..8ac1913b1 100644
--- a/src/components/Conversations/ConversationListItem.vue
+++ b/src/components/Conversations/ConversationListItem.vue
@@ -1,6 +1,6 @@
 <template>
   <router-link
-    class="flex gap-2 w-full items-center px-2 py-4 border-b-stone-200 border-b bg-white dark:bg-transparent"
+    class="flex gap-4 w-full items-center px-2 py-4 border-b-stone-200 border-b bg-white dark:bg-transparent"
     dir="auto"
     :to="{
       name: RouteName.CONVERSATION,
diff --git a/src/components/Conversations/NewConversation.vue b/src/components/Conversations/NewConversation.vue
index 4895dea28..9e800cd94 100644
--- a/src/components/Conversations/NewConversation.vue
+++ b/src/components/Conversations/NewConversation.vue
@@ -9,6 +9,15 @@
       :currentActor="currentActor"
       :placeholder="t('Write a new message')"
     />
+    <o-notification
+      class="my-2"
+      variant="danger"
+      :closable="false"
+      v-for="error in errors"
+      :key="error"
+    >
+      {{ error }}
+    </o-notification>
     <footer class="flex gap-2 py-3 mx-2 justify-end">
       <o-button :disabled="!canSend" nativeType="submit">{{
         t("Send")
@@ -18,13 +27,14 @@
 </template>
 
 <script lang="ts" setup>
-import { IActor, IPerson, usernameWithDomain } from "@/types/actor";
+import { IActor, IGroup, IPerson, usernameWithDomain } from "@/types/actor";
 import { computed, defineAsyncComponent, provide, ref } from "vue";
 import { useI18n } from "vue-i18n";
 import ActorAutoComplete from "../../components/Account/ActorAutoComplete.vue";
 import {
   DefaultApolloClient,
   provideApolloClient,
+  useLazyQuery,
   useMutation,
 } from "@vue/apollo-composable";
 import { apolloClient } from "@/vue-apollo";
@@ -34,12 +44,15 @@ import { IConversation } from "@/types/conversation";
 import { useCurrentActorClient } from "@/composition/apollo/actor";
 import { useRouter } from "vue-router";
 import RouteName from "@/router/name";
+import { FETCH_PERSON } from "@/graphql/actor";
+import { FETCH_GROUP_PUBLIC } from "@/graphql/group";
 
 const props = withDefaults(
   defineProps<{
-    mentions?: IActor[];
+    personMentions?: string[];
+    groupMentions?: string[];
   }>(),
-  { mentions: () => [] }
+  { personMentions: () => [], groupMentions: () => [] }
 );
 
 provide(DefaultApolloClient, apolloClient);
@@ -48,15 +61,36 @@ const router = useRouter();
 
 const emit = defineEmits(["close"]);
 
-const actorMentions = ref(props.mentions);
+const errors = ref<string[]>([]);
 
-const textMentions = computed(() =>
-  (props.mentions ?? []).map((actor) => usernameWithDomain(actor)).join(" ")
+const textPersonMentions = computed(() => props.personMentions);
+const textGroupMentions = computed(() => props.groupMentions);
+const actorMentions = ref<IActor[]>([]);
+
+const { load: fetchPerson } = provideApolloClient(apolloClient)(() =>
+  useLazyQuery<{ fetchPerson: IPerson }, { username: string }>(FETCH_PERSON)
 );
+const { load: fetchGroup } = provideApolloClient(apolloClient)(() =>
+  useLazyQuery<{ group: IGroup }, { name: string }>(FETCH_GROUP_PUBLIC)
+);
+textPersonMentions.value.forEach(async (textPersonMention) => {
+  const result = await fetchPerson(FETCH_PERSON, {
+    username: textPersonMention,
+  });
+  if (!result) return;
+  actorMentions.value.push(result.fetchPerson);
+});
+textGroupMentions.value.forEach(async (textGroupMention) => {
+  const result = await fetchGroup(FETCH_GROUP_PUBLIC, {
+    name: textGroupMention,
+  });
+  if (!result) return;
+  actorMentions.value.push(result.group);
+});
 
 const { t } = useI18n({ useScope: "global" });
 
-const text = ref(textMentions.value);
+const text = ref("");
 
 const Editor = defineAsyncComponent(
   () => import("../../components/TextEditor.vue")
@@ -70,8 +104,8 @@ const canSend = computed(() => {
   return actorMentions.value.length > 0 || /@.+/.test(text.value);
 });
 
-const { mutate: postPrivateMessageMutate } = provideApolloClient(apolloClient)(
-  () =>
+const { mutate: postPrivateMessageMutate, onError: onPrivateMessageError } =
+  provideApolloClient(apolloClient)(() =>
     useMutation<
       {
         postPrivateMessage: IConversation;
@@ -116,7 +150,13 @@ const { mutate: postPrivateMessageMutate } = provideApolloClient(apolloClient)(
         });
       },
     })
-);
+  );
+
+onPrivateMessageError((err) => {
+  err.graphQLErrors.forEach((error) => {
+    errors.value.push(error.message);
+  });
+});
 
 const sendForm = async (e: Event) => {
   e.preventDefault();
diff --git a/src/components/Participation/NewPrivateMessage.vue b/src/components/Participation/NewPrivateMessage.vue
index 9bfb7e001..ccc3225bc 100644
--- a/src/components/Participation/NewPrivateMessage.vue
+++ b/src/components/Participation/NewPrivateMessage.vue
@@ -1,5 +1,30 @@
 <template>
   <form @submit="sendForm">
+    <h2>{{ t("New announcement") }}</h2>
+    <p>
+      {{
+        t(
+          "This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you."
+        )
+      }}
+    </p>
+    <o-field class="mt-2 mb-4">
+      <o-checkbox
+        v-model="selectedRoles"
+        :native-value="ParticipantRole.PARTICIPANT"
+        :label="t('Participant')"
+      />
+      <o-checkbox
+        v-model="selectedRoles"
+        :native-value="ParticipantRole.NOT_APPROVED"
+        :label="t('Not approved')"
+      />
+      <o-checkbox
+        v-model="selectedRoles"
+        :native-value="ParticipantRole.REJECTED"
+        :label="t('Rejected')"
+      />
+    </o-field>
     <Editor
       v-model="text"
       mode="basic"
@@ -8,6 +33,15 @@
       :currentActor="currentActor"
       :placeholder="t('Write a new message')"
     />
+    <o-notification
+      class="my-2"
+      variant="danger"
+      :closable="true"
+      v-for="error in errors"
+      :key="error"
+    >
+      {{ error }}
+    </o-notification>
     <o-button class="mt-3" nativeType="submit">{{ t("Send") }}</o-button>
   </form>
 </template>
@@ -32,9 +66,15 @@ const props = defineProps<{
 const event = computed(() => props.event);
 
 const text = ref("");
+
+const errors = ref<string[]>([]);
+
+const selectedRoles = ref<ParticipantRole[]>([ParticipantRole.PARTICIPANT]);
+
 const {
   mutate: eventPrivateMessageMutate,
   onDone: onEventPrivateMessageMutated,
+  onError: onEventPrivateMessageError,
 } = useMutation<
   {
     sendEventPrivateMessage: IConversation;
@@ -43,8 +83,7 @@ const {
     text: string;
     actorId: string;
     eventId: string;
-    roles?: string;
-    inReplyToActorId?: ParticipantRole[];
+    roles?: ParticipantRole[];
     language?: string;
   }
 >(SEND_EVENT_PRIVATE_MESSAGE_MUTATION, {
@@ -96,6 +135,7 @@ const sendForm = (e: Event) => {
       event.value.organizerActor?.id ??
       currentActor.value?.id,
     eventId: event.value.id,
+    roles: selectedRoles.value,
   });
 };
 
@@ -103,6 +143,12 @@ onEventPrivateMessageMutated(() => {
   text.value = "";
 });
 
+onEventPrivateMessageError((err) => {
+  err.graphQLErrors.forEach((error) => {
+    errors.value.push(error.message);
+  });
+});
+
 const Editor = defineAsyncComponent(
   () => import("../../components/TextEditor.vue")
 );
diff --git a/src/components/Participation/UnloggedParticipation.vue b/src/components/Participation/UnloggedParticipation.vue
index b73cf14ee..8be6313a6 100644
--- a/src/components/Participation/UnloggedParticipation.vue
+++ b/src/components/Participation/UnloggedParticipation.vue
@@ -9,7 +9,7 @@
         <router-link :to="{ name: RouteName.EVENT_PARTICIPATE_WITH_ACCOUNT }">
           <figure class="flex justify-center my-2">
             <img
-              src="../../../public/img/undraw_profile.svg"
+              src="/img/undraw_profile.svg"
               alt="Profile illustration"
               width="128"
               height="128"
@@ -55,7 +55,7 @@
             <img
               width="128"
               height="128"
-              src="../../../public/img/undraw_mail_2.svg"
+              src="/img/undraw_mail_2.svg"
               alt="Privacy illustration"
             />
           </figure>
@@ -66,7 +66,7 @@
         <a :href="`${event.url}/participate/without-account`" v-else>
           <figure class="flex justify-center my-2">
             <img
-              src="../../../public/img/undraw_mail_2.svg"
+              src="/img/undraw_mail_2.svg"
               width="128"
               height="128"
               alt="Privacy illustration"
diff --git a/src/graphql/conversations.ts b/src/graphql/conversations.ts
index 92069d81d..32af7f823 100644
--- a/src/graphql/conversations.ts
+++ b/src/graphql/conversations.ts
@@ -54,7 +54,6 @@ export const SEND_EVENT_PRIVATE_MESSAGE_MUTATION = gql`
     $actorId: ID!
     $eventId: ID!
     $roles: [ParticipantRoleEnum]
-    $attributedToId: ID
     $language: String
   ) {
     sendEventPrivateMessage(
@@ -62,7 +61,6 @@ export const SEND_EVENT_PRIVATE_MESSAGE_MUTATION = gql`
       actorId: $actorId
       eventId: $eventId
       roles: $roles
-      attributedToId: $attributedToId
       language: $language
     ) {
       ...ConversationQuery
diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json
index b9d5ae6f8..781aacae4 100644
--- a/src/i18n/en_US.json
+++ b/src/i18n/en_US.json
@@ -1625,5 +1625,8 @@
   "You have access to this conversation as a member of the {group} group": "You have access to this conversation as a member of the {group} group",
   "Comment from an event announcement": "Comment from an event announcement",
   "Comment from a private conversation": "Comment from a private conversation",
-  "I've been mentionned in a conversation": "I've been mentionned in a conversation"
+  "I've been mentionned in a conversation": "I've been mentionned in a conversation",
+  "New announcement": "New announcement",
+  "This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.": "This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.",
+  "The following participants are groups, which means group members are able to reply to this conversation:": "The following participants are groups, which means group members are able to reply to this conversation:"
 }
\ No newline at end of file
diff --git a/src/i18n/fr_FR.json b/src/i18n/fr_FR.json
index 897d13370..028e70b0d 100644
--- a/src/i18n/fr_FR.json
+++ b/src/i18n/fr_FR.json
@@ -1621,5 +1621,8 @@
   "You have access to this conversation as a member of the {group} group": "Vous avez accès à cette conversation en tant que membre du groupe {group}",
   "Comment from an event announcement": "Commentaire d'une annonce d'événement",
   "Comment from a private conversation": "Commentaire d'une conversation privée",
-  "I've been mentionned in a conversation": "J'ai été mentionnée dans une conversation"
+  "I've been mentionned in a conversation": "J'ai été mentionnée dans une conversation",
+  "New announcement": "Nouvelle annonce",
+  "This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.": "Cette annonce sera envoyée à tous les participant·es ayant le statut sélectionné ci-dessous. Iels ne pourront pas répondre à votre annonce, mais iels peuvent créer une nouvelle conversation avec vous.",
+  "The following participants are groups, which means group members are able to reply to this conversation:": "Les participants suivants sont des groupes, ce qui signifie que les membres du groupes peuvent répondre dans cette conversation:"
 }
diff --git a/src/oruga-config.ts b/src/oruga-config.ts
index 165c37a39..38db635ec 100644
--- a/src/oruga-config.ts
+++ b/src/oruga-config.ts
@@ -35,7 +35,8 @@ export const orugaConfig = {
     variantClass: "icon-",
   },
   checkbox: {
-    checkClass: "checkbox",
+    rootClass: "checkbox",
+    checkClass: "checkbox-check",
     checkCheckedClass: "checkbox-checked",
     labelClass: "checkbox-label",
   },
diff --git a/src/utils/route.ts b/src/utils/route.ts
new file mode 100644
index 000000000..2c3f332b2
--- /dev/null
+++ b/src/utils/route.ts
@@ -0,0 +1,10 @@
+import { RouteQueryTransformer } from "vue-use-route-query";
+
+export const arrayTransformer: RouteQueryTransformer<string[]> = {
+  fromQuery(query: string) {
+    return query.split(",");
+  },
+  toQuery(value: string[]) {
+    return value.join(",");
+  },
+};
diff --git a/src/views/Admin/InstancesView.vue b/src/views/Admin/InstancesView.vue
index a62f09cea..3dad71a07 100644
--- a/src/views/Admin/InstancesView.vue
+++ b/src/views/Admin/InstancesView.vue
@@ -80,7 +80,7 @@
             <img
               class="w-12"
               v-if="instance.hasRelay"
-              src="../../../public/img/logo.svg"
+              src="/img/logo.svg"
               alt=""
             />
             <CloudQuestion v-else :size="36" />
diff --git a/src/views/Conversations/ConversationListView.vue b/src/views/Conversations/ConversationListView.vue
index 14c702dad..f903d147f 100644
--- a/src/views/Conversations/ConversationListView.vue
+++ b/src/views/Conversations/ConversationListView.vue
@@ -44,19 +44,28 @@
 <script lang="ts" setup>
 import RouteName from "../../router/name";
 import { useQuery } from "@vue/apollo-composable";
-import { computed, defineAsyncComponent, ref } from "vue";
+import { computed, defineAsyncComponent, ref, watchEffect } from "vue";
 import { useI18n } from "vue-i18n";
-import { integerTransformer, useRouteQuery } from "vue-use-route-query";
+import {
+  booleanTransformer,
+  integerTransformer,
+  useRouteQuery,
+} from "vue-use-route-query";
 import { PROFILE_CONVERSATIONS } from "@/graphql/event";
 import ConversationListItem from "../../components/Conversations/ConversationListItem.vue";
 import EmptyContent from "../../components/Utils/EmptyContent.vue";
 import { useHead } from "@vueuse/head";
 import { IPerson } from "@/types/actor";
 import { useProgrammatic } from "@oruga-ui/oruga-next";
+import { arrayTransformer } from "@/utils/route";
 
 const page = useRouteQuery("page", 1, integerTransformer);
 const CONVERSATIONS_PER_PAGE = 10;
 
+const showModal = useRouteQuery("newMessage", false, booleanTransformer);
+const personMentions = useRouteQuery("personMentions", [], arrayTransformer);
+const groupMentions = useRouteQuery("groupMentions", [], arrayTransformer);
+
 const { t } = useI18n({ useScope: "global" });
 
 useHead({
@@ -69,6 +78,7 @@ const { result: conversationsResult } = useQuery<{
   loggedPerson: Pick<IPerson, "conversations">;
 }>(PROFILE_CONVERSATIONS, () => ({
   page: page.value,
+  limit: CONVERSATIONS_PER_PAGE,
 }));
 
 const conversations = computed(
@@ -88,7 +98,17 @@ const NewConversation = defineAsyncComponent(
 const openNewMessageModal = () => {
   oruga.modal.open({
     component: NewConversation,
+    props: {
+      personMentions: personMentions.value,
+      groupMentions: groupMentions.value,
+    },
     trapFocus: true,
   });
 };
+
+watchEffect(() => {
+  if (showModal.value) {
+    openNewMessageModal();
+  }
+});
 </script>
diff --git a/src/views/Conversations/ConversationView.vue b/src/views/Conversations/ConversationView.vue
index d568f0b82..f92d53640 100644
--- a/src/views/Conversations/ConversationView.vue
+++ b/src/views/Conversations/ConversationView.vue
@@ -14,13 +14,13 @@
       ]"
     />
     <div
-      v-if="conversation.event"
-      class="bg-mbz-yellow p-6 mb-6 rounded flex gap-2 items-center"
+      v-if="conversation.event && !isCurrentActorAuthor"
+      class="bg-mbz-yellow p-6 mb-3 rounded flex gap-2 items-center"
     >
       <Calendar :size="36" />
       <i18n-t
         tag="p"
-        keypath="This is a announcement from the organizers of event {event}"
+        keypath="This is a announcement from the organizers of event {event}. You can't reply to it, but you can send a private message to event organizers."
       >
         <template #event>
           <b>
@@ -35,10 +35,7 @@
         </template>
       </i18n-t>
     </div>
-    <div
-      v-if="currentActor && currentActor.id !== conversation.actor?.id"
-      class="bg-mbz-info p-6 rounded flex gap-2 items-center my-3"
-    >
+    <o-notification v-if="isCurrentActorAuthor" variant="info" closable>
       <i18n-t
         keypath="You have access to this conversation as a member of the {group} group"
         tag="p"
@@ -55,7 +52,36 @@
           >
         </template>
       </i18n-t>
-    </div>
+    </o-notification>
+    <o-notification
+      v-else-if="groupParticipants.length > 0 && !conversation.event"
+      variant="info"
+      closable
+    >
+      <p>
+        {{
+          t(
+            "The following participants are groups, which means group members are able to reply to this conversation:"
+          )
+        }}
+      </p>
+      <ul class="list-disc">
+        <li
+          v-for="groupParticipant in groupParticipants"
+          :key="groupParticipant.id"
+        >
+          <router-link
+            :to="{
+              name: RouteName.GROUP,
+              params: {
+                preferredUsername: usernameWithDomain(groupParticipant),
+              },
+            }"
+            ><b>{{ displayName(groupParticipant) }}</b></router-link
+          >
+        </li>
+      </ul>
+    </o-notification>
     <o-notification v-if="error" variant="danger">
       {{ error }}
     </o-notification>
@@ -107,7 +133,7 @@
       </form>
       <div
         v-else-if="conversation.event"
-        class="bg-mbz-yellow p-6 rounded flex gap-2 items-center mt-6"
+        class="bg-mbz-yellow p-6 rounded flex gap-2 items-center mt-3"
       >
         <Calendar :size="36" />
         <i18n-t
@@ -239,6 +265,12 @@ const otherParticipants = computed(
     ) ?? []
 );
 
+const groupParticipants = computed(() => {
+  return otherParticipants.value.filter(
+    (participant) => participant.type === ActorType.GROUP
+  );
+});
+
 const Editor = defineAsyncComponent(
   () => import("../../components/TextEditor.vue")
 );
@@ -253,8 +285,15 @@ const title = computed(() =>
   })
 );
 
+const isCurrentActorAuthor = computed(
+  () =>
+    currentActor.value &&
+    conversation.value &&
+    currentActor.value.id !== conversation.value?.actor?.id
+);
+
 useHead({
-  title: title.value,
+  title: () => title.value,
 });
 
 const newComment = ref("");
diff --git a/src/views/Event/EditView.vue b/src/views/Event/EditView.vue
index a83fb61b3..c18eba759 100644
--- a/src/views/Event/EditView.vue
+++ b/src/views/Event/EditView.vue
@@ -650,12 +650,6 @@ const FullAddressAutoComplete = defineAsyncComponent(
 
 const { t } = useI18n({ useScope: "global" });
 
-useHead({
-  title: computed(() =>
-    props.isUpdate ? t("Event edition") : t("Event creation")
-  ),
-});
-
 const props = withDefaults(
   defineProps<{
     eventId?: undefined | string;
@@ -667,6 +661,12 @@ const props = withDefaults(
 
 const eventId = computed(() => props.eventId);
 
+useHead({
+  title: computed(() =>
+    props.isUpdate ? t("Event edition") : t("Event creation")
+  ),
+});
+
 const event = ref<IEditableEvent>(new EventModel());
 const unmodifiedEvent = ref<IEditableEvent>(new EventModel());
 
diff --git a/src/views/Event/ParticipantsView.vue b/src/views/Event/ParticipantsView.vue
index 50447c2cb..4630b581f 100644
--- a/src/views/Event/ParticipantsView.vue
+++ b/src/views/Event/ParticipantsView.vue
@@ -225,6 +225,7 @@
             @click="acceptParticipants(checkedRows)"
             variant="success"
             :disabled="!canAcceptParticipants"
+            outlined
           >
             {{
               t(
@@ -238,6 +239,7 @@
             @click="refuseParticipants(checkedRows)"
             variant="danger"
             :disabled="!canRefuseParticipants"
+            outlined
           >
             {{
               t(
diff --git a/src/views/Group/GroupView.vue b/src/views/Group/GroupView.vue
index e9532c49f..1ccc52e5e 100644
--- a/src/views/Group/GroupView.vue
+++ b/src/views/Group/GroupView.vue
@@ -266,6 +266,21 @@
                     : t("Deactivate notifications")
                 }}</span>
               </o-button>
+              <o-button
+                outlined
+                tag="router-link"
+                :to="{
+                  name: RouteName.CONVERSATION_LIST,
+                  query: {
+                    newMessage: 'true',
+                    groupMentions: usernameWithDomain(group),
+                  },
+                }"
+                icon-left="email"
+                v-if="!isCurrentActorAGroupMember || previewPublic"
+              >
+                {{ t("Contact") }}
+              </o-button>
               <o-button
                 outlined
                 icon-left="share"
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index e1f435275..15cacd6f3 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -5,19 +5,19 @@
     <div class="-z-10 overflow-hidden">
       <img
         alt=""
-        src="../../public/img/shape-1.svg"
+        src="/img/shape-1.svg"
         class="-z-10 absolute left-[2%] top-36"
         width="300"
       />
       <img
         alt=""
-        src="../../public/img/shape-2.svg"
+        src="/img/shape-2.svg"
         class="-z-10 absolute left-[50%] top-[5%] -translate-x-2/4 opacity-60"
         width="800"
       />
       <img
         alt=""
-        src="../../public/img/shape-3.svg"
+        src="/img/shape-3.svg"
         class="-z-10 absolute top-0 right-36"
         width="200"
       />
diff --git a/src/views/SearchView.vue b/src/views/SearchView.vue
index 46faf5bf3..d1d2cc385 100644
--- a/src/views/SearchView.vue
+++ b/src/views/SearchView.vue
@@ -749,7 +749,6 @@ import {
   useRouteQuery,
   enumTransformer,
   booleanTransformer,
-  RouteQueryTransformer,
 } from "vue-use-route-query";
 import Calendar from "vue-material-design-icons/Calendar.vue";
 import AccountMultiple from "vue-material-design-icons/AccountMultiple.vue";
@@ -776,6 +775,7 @@ import lodashSortBy from "lodash/sortBy";
 import EmptyContent from "@/components/Utils/EmptyContent.vue";
 import SkeletonGroupResultList from "@/components/Group/SkeletonGroupResultList.vue";
 import SkeletonEventResultList from "@/components/Event/SkeletonEventResultList.vue";
+import { arrayTransformer } from "@/utils/route";
 
 const EventMarkerMap = defineAsyncComponent(
   () => import("@/components/Search/EventMarkerMap.vue")
@@ -840,15 +840,6 @@ enum SortValues {
   MEMBER_COUNT_DESC = "MEMBER_COUNT_DESC",
 }
 
-const arrayTransformer: RouteQueryTransformer<string[]> = {
-  fromQuery(query: string) {
-    return query.split(",");
-  },
-  toQuery(value: string[]) {
-    return value.join(",");
-  },
-};
-
 const props = defineProps<{
   tag?: string;
 }>();