API compatibility with fedibird, frontend config #163

Merged
floatingghost merged 9 commits from fedibird-compatibility into develop 2022-08-17 00:23:00 +00:00
13 changed files with 161 additions and 43 deletions
Showing only changes of commit 1b75aeead6 - Show all commits

View file

@ -734,6 +734,14 @@
"build_dir" => "distribution",
"ref" => "akkoma"
},
"fedibird-fe" => %{
"name" => "fedibird-fe",
"git" => "https://akkoma.dev/AkkomaGang/fedibird-fe",
"build_url" =>
"https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/fedibird-fe.zip",
"build_dir" => "distribution",
"ref" => "akkoma"
},
"admin-fe" => %{
"name" => "admin-fe",
"git" => "https://akkoma.dev/AkkomaGang/admin-fe",

View file

@ -27,9 +27,21 @@ defmodule Pleroma.Web.MastoFEController do
def index(conn, _params) do
with %{assigns: %{user: %User{} = user, token: %Token{app_id: token_app_id} = token}} <- conn,
{:ok, %{id: ^token_app_id}} <- AuthController.local_mastofe_app() do
flavour =
[:frontends, :mastodon]
|> Pleroma.Config.get()
|> Map.get("name", "mastodon-fe")
index =
if flavour == "fedibird-fe" do
"fedibird.index.html"
else
"glitchsoc.index.html"
end
conn
|> put_layout(false)
|> render("index.html",
|> render(index,
token: token.token,
user: user,
custom_emojis: Pleroma.Emoji.get_all()

View file

@ -32,8 +32,15 @@ def init(%{qs: qs} = req, state) do
req
end
{:cowboy_websocket, req, %{user: user, topic: topic, count: 0, timer: nil},
%{idle_timeout: @timeout}}
{:cowboy_websocket, req,
%{
user: user,
topic: topic,
count: 0,
timer: nil,
subscriptions: [],
oauth_token: oauth_token
}, %{idle_timeout: @timeout}}
else
{:error, :bad_topic} ->
Logger.debug("#{__MODULE__} bad topic #{inspect(req)}")
@ -70,16 +77,45 @@ def websocket_handle({:text, "ping"}, state) do
{:reply, {:text, "pong"}, %{state | timer: timer()}}
end
def websocket_handle({:text, text}, state) do
with {:ok, json} <- Jason.decode(text) do
websocket_handle({:json, json}, state)
else
_ ->
Logger.error("#{__MODULE__} received text frame: #{text}")
{:ok, state}
end
end
def websocket_handle(
{:json, %{"type" => "subscribe", "stream" => stream_name}},
%{user: user, oauth_token: token} = state
) do
with {:ok, topic} <- Streamer.get_topic(stream_name, user, token, %{}) do
new_subscriptions =
[topic | Map.get(state, :subscriptions, [])]
|> Enum.uniq()
{:ok, _topic} = Streamer.add_socket(topic, user)
{:ok, Map.put(state, :subscriptions, new_subscriptions)}
else
_ ->
Logger.error("#{__MODULE__} received invalid topic: #{stream_name}")
{:ok, state}
end
end
def websocket_handle(frame, state) do
Logger.error("#{__MODULE__} received frame: #{inspect(frame)}")
{:ok, state}
end
def websocket_info({:render_with_user, view, template, item}, state) do
def websocket_info({:render_with_user, view, template, item, topic}, state) do
user = %User{} = User.get_cached_by_ap_id(state.user.ap_id)
unless Streamer.filtered_by_user?(user, item) do
websocket_info({:text, view.render(template, item, user)}, %{state | user: user})
websocket_info({:text, view.render(template, item, user, topic)}, %{state | user: user})
else
{:ok, state}
end

View file

@ -75,6 +75,7 @@ defp filter(reactions, _), do: reactions
def create(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
emoji = Pleroma.Emoji.maybe_quote(emoji)
with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji) do
activity = Activity.get_by_id(activity_id)

View file

@ -114,6 +114,11 @@ def get_topic("list", _user, _oauth_token, _params) do
{:error, :unauthorized}
end
# mastodon multi-topic WS
def get_topic(nil, _user, _oauth_token, _params) do
{:ok, :multi}
end
def get_topic(_stream, _user, _oauth_token, _params) do
{:error, :bad_topic}
end
@ -186,8 +191,8 @@ defp do_stream("direct", item) do
end
defp do_stream("follow_relationship", item) do
text = StreamerView.render("follow_relationships_update.json", item)
user_topic = "user:#{item.follower.id}"
text = StreamerView.render("follow_relationships_update.json", item, user_topic)
Logger.debug("Trying to push follow relationship update to #{user_topic}\n\n")
@ -235,7 +240,7 @@ defp do_stream(topic, %Notification{} = item)
when topic in ["user", "user:notification"] do
Registry.dispatch(@registry, "#{topic}:#{item.user_id}", fn list ->
Enum.each(list, fn {pid, _auth} ->
send(pid, {:render_with_user, StreamerView, "notification.json", item})
send(pid, {:render_with_user, StreamerView, "notification.json", item, topic})
end)
end)
end
@ -259,7 +264,7 @@ defp do_stream(topic, item) do
end
defp push_to_socket(topic, %Participation{} = participation) do
rendered = StreamerView.render("conversation.json", participation)
rendered = StreamerView.render("conversation.json", participation, topic)
Registry.dispatch(@registry, topic, fn list ->
Enum.each(list, fn {pid, _} ->
@ -283,12 +288,12 @@ defp push_to_socket(topic, %Activity{
defp push_to_socket(_topic, %Activity{data: %{"type" => "Delete"}}), do: :noop
defp push_to_socket(topic, item) do
anon_render = StreamerView.render("update.json", item)
anon_render = StreamerView.render("update.json", item, topic)
Registry.dispatch(@registry, topic, fn list ->
Enum.each(list, fn {pid, auth?} ->
if auth? do
send(pid, {:render_with_user, StreamerView, "update.json", item})
send(pid, {:render_with_user, StreamerView, "update.json", item, topic})
else
send(pid, {:text, anon_render})
end

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta content='width=device-width, initial-scale=1' name='viewport'>
<title>
<%= Config.get([:instance, :name]) %>
</title>
<link rel="icon" type="image/png" href="/favicon.png"/>
<link rel="manifest" type="applicaton/manifest+json" href="<%= Routes.masto_fe_path(Pleroma.Web.Endpoint, :manifest) %>" />
<meta name="theme-color" content="<%= Config.get([:manifest, :theme_color]) %>" />
<script crossorigin='anonymous' src="/packs/js/locales.js"></script>
<script crossorigin='anonymous' src="/packs/js/locales/glitch/en.js"></script>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/getting_started.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/compose.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/home_timeline.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/notifications.js'>
<script id='initial-state' type='application/json'><%= initial_state(@token, @user, @custom_emojis) %></script>
<script src="/packs/js/core/common.js"></script>
<link rel="stylesheet" media="all" href="/packs/css/core/common.css" />
<script src="/packs/js/flavours/glitch/common.js"></script>
<link rel="stylesheet" media="all" href="/packs/css/flavours/glitch/common.css" />
<script src="/packs/js/flavours/glitch/home.js"></script>
</head>
<body class='app-body no-reduce-motion system-font'>
<div class='app-holder' data-props='{&quot;locale&quot;:&quot;en&quot;}' id='mastodon'>
</div>
</body>
</html>

View file

@ -30,7 +30,8 @@ def initial_state(token, user, custom_emojis) do
mascot: User.get_mascot(user)["url"],
show_quote_button: true,
enable_reaction: true,
compact_reaction: false
compact_reaction: false,
advanced_layout: true
},
poll_limits: Config.get([:instance, :poll_limits]),
rights: %{

View file

@ -11,8 +11,9 @@ defmodule Pleroma.Web.StreamerView do
alias Pleroma.User
alias Pleroma.Web.MastodonAPI.NotificationView
def render("update.json", %Activity{} = activity, %User{} = user) do
def render("update.json", %Activity{} = activity, %User{} = user, topic) do
%{
stream: [topic],
event: "update",
payload:
Pleroma.Web.MastodonAPI.StatusView.render(
@ -25,8 +26,9 @@ def render("update.json", %Activity{} = activity, %User{} = user) do
|> Jason.encode!()
end
def render("notification.json", %Notification{} = notify, %User{} = user) do
def render("notification.json", %Notification{} = notify, %User{} = user, topic) do
%{
stream: [topic],
event: "notification",
payload:
NotificationView.render(
@ -38,8 +40,9 @@ def render("notification.json", %Notification{} = notify, %User{} = user) do
|> Jason.encode!()
end
def render("update.json", %Activity{} = activity) do
def render("update.json", %Activity{} = activity, topic) do
%{
stream: [topic],
event: "update",
payload:
Pleroma.Web.MastodonAPI.StatusView.render(
@ -51,8 +54,9 @@ def render("update.json", %Activity{} = activity) do
|> Jason.encode!()
end
def render("follow_relationships_update.json", item) do
def render("follow_relationships_update.json", item, topic) do
%{
stream: [topic],
event: "pleroma:follow_relationships_update",
payload:
%{
@ -73,8 +77,9 @@ def render("follow_relationships_update.json", item) do
|> Jason.encode!()
end
def render("conversation.json", %Participation{} = participation) do
def render("conversation.json", %Participation{} = participation, topic) do
%{
stream: [topic],
event: "conversation",
payload:
Pleroma.Web.MastodonAPI.ConversationView.render("participation.json", %{

View file

@ -31,9 +31,9 @@ def start_socket(qs \\ nil, headers \\ []) do
WebsocketClient.start_link(self(), path, headers)
end
test "refuses invalid requests" do
test "allows multi-streams" do
capture_log(fn ->
assert {:error, {404, _}} = start_socket()
assert {:ok, _} = start_socket()
assert {:error, {404, _}} = start_socket("?stream=ncjdk")
Process.sleep(30)
end)

View file

@ -224,7 +224,7 @@ test "it creates a notification for user and send to the 'user' and the 'user:no
task =
Task.async(fn ->
{:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
assert_receive {:render_with_user, _, _, _}, 4_000
assert_receive {:render_with_user, _, _, _, "user"}, 4_000
end)
task_user_notification =
@ -232,7 +232,7 @@ test "it creates a notification for user and send to the 'user' and the 'user:no
{:ok, _topic} =
Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
assert_receive {:render_with_user, _, _, _}, 4_000
assert_receive {:render_with_user, _, _, _, "user:notification"}, 4_000
end)
activity = insert(:note_activity)

View file

@ -272,6 +272,7 @@ test "a note activity" do
spoiler_text: HTML.filter_tags(object_data["summary"]),
visibility: "public",
media_attachments: [],
emoji_reactions: [],
mentions: [],
tags: [
%{

View file

@ -157,7 +157,8 @@ test "it streams the user's post in the 'user' stream", %{user: user, token: oau
Streamer.get_topic_and_add_socket("user", user, oauth_token)
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
assert_receive {:render_with_user, _, _, ^activity}
stream_name = "user:#{user.id}"
assert_receive {:render_with_user, _, _, ^activity, ^stream_name}
refute Streamer.filtered_by_user?(user, activity)
end
@ -168,7 +169,11 @@ test "it streams boosts of the user in the 'user' stream", %{user: user, token:
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
{:ok, announce} = CommonAPI.repeat(activity.id, user)
assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
stream_name = "user:#{user.id}"
assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce,
^stream_name}
refute Streamer.filtered_by_user?(user, announce)
end
@ -221,7 +226,11 @@ test "it streams boosts of mastodon user in the 'user' stream", %{
{:ok, %Pleroma.Activity{data: _data, local: false} = announce} =
Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(data)
assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
stream_name = "user:#{user.id}"
assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce,
^stream_name}
refute Streamer.filtered_by_user?(user, announce)
end
@ -233,7 +242,7 @@ test "it sends notify to in the 'user' stream", %{
Streamer.get_topic_and_add_socket("user", user, oauth_token)
Streamer.stream("user", notify)
assert_receive {:render_with_user, _, _, ^notify}
assert_receive {:render_with_user, _, _, ^notify, "user"}
refute Streamer.filtered_by_user?(user, notify)
end
@ -245,7 +254,7 @@ test "it sends notify to in the 'user:notification' stream", %{
Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
Streamer.stream("user:notification", notify)
assert_receive {:render_with_user, _, _, ^notify}
assert_receive {:render_with_user, _, _, ^notify, "user:notification"}
refute Streamer.filtered_by_user?(user, notify)
end
@ -291,7 +300,7 @@ test "it sends favorite to 'user:notification' stream'", %{
Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
{:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
assert_receive {:render_with_user, _, "notification.json", notif}
assert_receive {:render_with_user, _, "notification.json", notif, "user:notification"}
assert notif.activity.id == favorite_activity.id
refute Streamer.filtered_by_user?(user, notif)
end
@ -320,7 +329,7 @@ test "it sends follow activities to the 'user:notification' stream", %{
Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
{:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user)
assert_receive {:render_with_user, _, "notification.json", notif}
assert_receive {:render_with_user, _, "notification.json", notif, "user:notification"}
assert notif.activity.id == follow_activity.id
refute Streamer.filtered_by_user?(user, notif)
end
@ -384,7 +393,7 @@ test "it sends to public (authenticated)" do
Streamer.get_topic_and_add_socket("public", user, oauth_token)
{:ok, activity} = CommonAPI.post(other_user, %{status: "Test"})
assert_receive {:render_with_user, _, _, ^activity}
assert_receive {:render_with_user, _, _, ^activity, "public"}
refute Streamer.filtered_by_user?(other_user, activity)
end
@ -436,7 +445,7 @@ test "it filters to user if recipients invalid and thread containment is enabled
Streamer.get_topic_and_add_socket("public", user, oauth_token)
Streamer.stream("public", activity)
assert_receive {:render_with_user, _, _, ^activity}
assert_receive {:render_with_user, _, _, ^activity, "public"}
assert Streamer.filtered_by_user?(user, activity)
end
@ -458,7 +467,7 @@ test "it sends message if recipients invalid and thread containment is disabled"
Streamer.get_topic_and_add_socket("public", user, oauth_token)
Streamer.stream("public", activity)
assert_receive {:render_with_user, _, _, ^activity}
assert_receive {:render_with_user, _, _, ^activity, "public"}
refute Streamer.filtered_by_user?(user, activity)
end
@ -481,7 +490,7 @@ test "it sends message if recipients invalid and thread containment is enabled b
Streamer.get_topic_and_add_socket("public", user, oauth_token)
Streamer.stream("public", activity)
assert_receive {:render_with_user, _, _, ^activity}
assert_receive {:render_with_user, _, _, ^activity, "public"}
refute Streamer.filtered_by_user?(user, activity)
end
end
@ -495,7 +504,7 @@ test "it filters messages involving blocked users", %{user: user, token: oauth_t
Streamer.get_topic_and_add_socket("public", user, oauth_token)
{:ok, activity} = CommonAPI.post(blocked_user, %{status: "Test"})
assert_receive {:render_with_user, _, _, ^activity}
assert_receive {:render_with_user, _, _, ^activity, "public"}
assert Streamer.filtered_by_user?(user, activity)
end
@ -512,17 +521,17 @@ test "it filters messages transitively involving blocked users", %{
{:ok, activity_one} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
assert_receive {:render_with_user, _, _, ^activity_one}
assert_receive {:render_with_user, _, _, ^activity_one, "public"}
assert Streamer.filtered_by_user?(blocker, activity_one)
{:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
assert_receive {:render_with_user, _, _, ^activity_two}
assert_receive {:render_with_user, _, _, ^activity_two, "public"}
assert Streamer.filtered_by_user?(blocker, activity_two)
{:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
assert_receive {:render_with_user, _, _, ^activity_three}
assert_receive {:render_with_user, _, _, ^activity_three, "public"}
assert Streamer.filtered_by_user?(blocker, activity_three)
end
end
@ -583,7 +592,8 @@ test "it sends wanted private posts to list", %{user: user_a, token: user_a_toke
visibility: "private"
})
assert_receive {:render_with_user, _, _, ^activity}
stream_name = "list:#{list.id}"
assert_receive {:render_with_user, _, _, ^activity, ^stream_name}
refute Streamer.filtered_by_user?(user_a, activity)
end
end
@ -601,7 +611,8 @@ test "it filters muted reblogs", %{user: user1, token: user1_token} do
Streamer.get_topic_and_add_socket("user", user1, user1_token)
{:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2)
assert_receive {:render_with_user, _, _, ^announce_activity}
stream_name = "user:#{user1.id}"
assert_receive {:render_with_user, _, _, ^announce_activity, ^stream_name}
assert Streamer.filtered_by_user?(user1, announce_activity)
end
@ -617,7 +628,7 @@ test "it filters reblog notification for reblog-muted actors", %{
Streamer.get_topic_and_add_socket("user", user1, user1_token)
{:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2)
assert_receive {:render_with_user, _, "notification.json", notif}
assert_receive {:render_with_user, _, "notification.json", notif, "user"}
assert Streamer.filtered_by_user?(user1, notif)
end
@ -633,7 +644,7 @@ test "it send non-reblog notification for reblog-muted actors", %{
Streamer.get_topic_and_add_socket("user", user1, user1_token)
{:ok, _favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
assert_receive {:render_with_user, _, "notification.json", notif}
assert_receive {:render_with_user, _, "notification.json", notif, "user"}
refute Streamer.filtered_by_user?(user1, notif)
end
end
@ -648,7 +659,8 @@ test "it filters posts from muted threads" do
{:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
{:ok, _} = CommonAPI.add_mute(user2, activity)
assert_receive {:render_with_user, _, _, ^activity}
stream_name = "user:#{user2.id}"
assert_receive {:render_with_user, _, _, ^activity, ^stream_name}
assert Streamer.filtered_by_user?(user2, activity)
end
end
@ -690,7 +702,8 @@ test "it doesn't send conversation update to the 'direct' stream when the last m
})
create_activity_id = create_activity.id
assert_receive {:render_with_user, _, _, ^create_activity}
stream_name = "direct:#{user.id}"
assert_receive {:render_with_user, _, _, ^create_activity, ^stream_name}
assert_receive {:text, received_conversation1}
assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1)
@ -725,8 +738,9 @@ test "it sends conversation update to the 'direct' stream when a message is dele
visibility: "direct"
})
assert_receive {:render_with_user, _, _, ^create_activity}
assert_receive {:render_with_user, _, _, ^create_activity2}
stream_name = "direct:#{user.id}"
assert_receive {:render_with_user, _, _, ^create_activity, ^stream_name}
assert_receive {:render_with_user, _, _, ^create_activity2, ^stream_name}
assert_receive {:text, received_conversation1}
assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1)
assert_receive {:text, received_conversation1}