From 87b37a4153381403289c5b2e6dc455b547088e73 Mon Sep 17 00:00:00 2001
From: Thomas Citharel <tcit@tcit.fr>
Date: Tue, 8 Feb 2022 15:27:17 +0100
Subject: [PATCH 1/2] Expose isOnline through AP

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
---
 lib/federation/activity_pub/utils.ex              | 4 ++++
 lib/federation/activity_stream/converter/event.ex | 6 ++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/lib/federation/activity_pub/utils.ex b/lib/federation/activity_pub/utils.ex
index aadb90ca8..c810b4d53 100644
--- a/lib/federation/activity_pub/utils.ex
+++ b/lib/federation/activity_pub/utils.ex
@@ -112,6 +112,10 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
             "@id" => "mz:participationMessage",
             "@type" => "sc:Text"
           },
+          "isOnline" => %{
+            "@type" => "sc:Boolean",
+            "@id" => "mz:isOnline"
+          },
           "PropertyValue" => "sc:PropertyValue",
           "value" => "sc:value",
           "propertyID" => "sc:propertyID",
diff --git a/lib/federation/activity_stream/converter/event.ex b/lib/federation/activity_stream/converter/event.ex
index 1ec250b16..9acaf1094 100644
--- a/lib/federation/activity_stream/converter/event.ex
+++ b/lib/federation/activity_stream/converter/event.ex
@@ -139,7 +139,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
       "url" => event.url,
       "inLanguage" => event.language,
       "timezone" => event.options.timezone,
-      "contacts" => Enum.map(event.contacts, & &1.url)
+      "contacts" => Enum.map(event.contacts, & &1.url),
+      "isOnline" => event.options.is_online
     }
     |> maybe_add_physical_address(event)
     |> maybe_add_event_picture(event)
@@ -168,7 +169,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
           "repliesModerationOption",
           if(Map.get(object, "commentsEnabled", true), do: :allow_all, else: :closed)
         ),
-      timezone: calculate_timezone(object, address)
+      timezone: calculate_timezone(object, address),
+      is_online: object["isOnline"] == true
     }
   end
 

From 11ac2dccebb112eb66387880839064d8b93b7b06 Mon Sep 17 00:00:00 2001
From: Thomas Citharel <tcit@tcit.fr>
Date: Tue, 8 Feb 2022 15:27:53 +0100
Subject: [PATCH 2/2] Expose remainingAttendeeCapacity and participantCount
 through AP

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
---
 lib/federation/activity_pub/utils.ex          |  5 +++++
 .../activity_stream/converter/event.ex        | 20 ++++++++++++++++++
 lib/web/views/json_ld/object_view.ex          | 21 +++++++++++++++++++
 3 files changed, 46 insertions(+)

diff --git a/lib/federation/activity_pub/utils.ex b/lib/federation/activity_pub/utils.ex
index c810b4d53..9743662a5 100644
--- a/lib/federation/activity_pub/utils.ex
+++ b/lib/federation/activity_pub/utils.ex
@@ -69,6 +69,7 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
           "category" => "sc:category",
           "uuid" => "sc:identifier",
           "maximumAttendeeCapacity" => "sc:maximumAttendeeCapacity",
+          "remainingAttendeeCapacity" => "sc:remainingAttendeeCapacity",
           "location" => %{
             "@id" => "sc:location",
             "@type" => "sc:Place"
@@ -112,6 +113,10 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
             "@id" => "mz:participationMessage",
             "@type" => "sc:Text"
           },
+          "participantCount" => %{
+            "@id" => "mz:participantCount",
+            "@type" => "sc:Integer"
+          },
           "isOnline" => %{
             "@type" => "sc:Boolean",
             "@id" => "mz:isOnline"
diff --git a/lib/federation/activity_stream/converter/event.ex b/lib/federation/activity_stream/converter/event.ex
index 9acaf1094..c89aced1c 100644
--- a/lib/federation/activity_stream/converter/event.ex
+++ b/lib/federation/activity_stream/converter/event.ex
@@ -110,6 +110,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
         do: {[@ap_public], [event.organizer_actor.followers_url]},
         else: {[attributed_to_or_default(event).followers_url], [@ap_public]}
 
+    participant_count = Mobilizon.Events.count_participant_participants(event.id)
+
     %{
       "type" => "Event",
       "to" => to,
@@ -129,6 +131,9 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
       "endTime" => event.ends_on |> shift_tz(event.options.timezone) |> date_to_string(),
       "tag" => event.tags |> build_tags(),
       "maximumAttendeeCapacity" => event.options.maximum_attendee_capacity,
+      "remainingAttendeeCapacity" =>
+        remaining_attendee_capacity(event.options, participant_count),
+      "participantCount" => participant_count,
       "repliesModerationOption" => event.options.comment_moderation,
       "commentsEnabled" => event.options.comment_moderation == :allow_all,
       "anonymousParticipationEnabled" => event.options.anonymous_participation,
@@ -310,4 +315,19 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
     |> get_url()
     |> fetch_actor()
   end
+
+  @spec remaining_attendee_capacity(map(), integer()) :: integer() | nil
+  defp remaining_attendee_capacity(
+         %{maximum_attendee_capacity: maximum_attendee_capacity},
+         participant_count
+       )
+       when is_integer(maximum_attendee_capacity) and maximum_attendee_capacity > 0 do
+    maximum_attendee_capacity - participant_count
+  end
+
+  defp remaining_attendee_capacity(
+         %{maximum_attendee_capacity: _},
+         _participant_count
+       ),
+       do: nil
 end
diff --git a/lib/web/views/json_ld/object_view.ex b/lib/web/views/json_ld/object_view.ex
index 256fc93aa..dadf7666e 100644
--- a/lib/web/views/json_ld/object_view.ex
+++ b/lib/web/views/json_ld/object_view.ex
@@ -53,6 +53,8 @@ defmodule Mobilizon.Web.JsonLD.ObjectView do
         organizer
       end
 
+    participant_count = Mobilizon.Events.count_participant_participants(event.id)
+
     json_ld = %{
       "@context" => "https://schema.org",
       "@type" => "Event",
@@ -63,6 +65,9 @@ defmodule Mobilizon.Web.JsonLD.ObjectView do
       "organizer" => organizer,
       "location" => render_all_locations(event),
       "eventAttendanceMode" => event |> attendance_mode() |> event_attendance_mode(),
+      "maximumAttendeeCapacity" => event.options.maximum_attendee_capacity,
+      "remainingAttendeeCapacity" =>
+        remaining_attendee_capacity(event.options, participant_count),
       "eventStatus" =>
         if(event.status == :cancelled,
           do: "https://schema.org/EventCancelled",
@@ -229,4 +234,20 @@ defmodule Mobilizon.Web.JsonLD.ObjectView do
   @spec virtual_location_links(list()) :: list()
   defp virtual_location_links(metadata),
     do: Enum.filter(metadata, &String.contains?(&1.key, @livestream_keys))
+
+  # TODO: Make this in common with Mobilizon.Federation.ActivityStream.Converter.Event
+  @spec remaining_attendee_capacity(map(), integer()) :: integer() | nil
+  defp remaining_attendee_capacity(
+         %{maximum_attendee_capacity: maximum_attendee_capacity},
+         participant_count
+       )
+       when is_integer(maximum_attendee_capacity) and maximum_attendee_capacity > 0 do
+    maximum_attendee_capacity - participant_count
+  end
+
+  defp remaining_attendee_capacity(
+         %{maximum_attendee_capacity: _},
+         _participant_count
+       ),
+       do: nil
 end