Add replying to statuses

This commit is contained in:
Shadowfacts 2020-04-22 22:54:46 -04:00
parent 6c0fc06c21
commit 7d071d971c
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
6 changed files with 116 additions and 10 deletions

View File

@ -32,6 +32,7 @@ defmodule Clacks.ActivityPub do
actor :: String.t(), actor :: String.t(),
html :: String.t(), html :: String.t(),
context :: String.t() | nil, context :: String.t() | nil,
in_reply_to :: String.t() | nil,
id :: String.t() | nil, id :: String.t() | nil,
published :: DateTime.t(), published :: DateTime.t(),
to :: [String.t()], to :: [String.t()],
@ -41,6 +42,7 @@ defmodule Clacks.ActivityPub do
actor, actor,
html, html,
context \\ nil, context \\ nil,
in_reply_to \\ nil,
id \\ nil, id \\ nil,
published \\ DateTime.utc_now(), published \\ DateTime.utc_now(),
to \\ [@public], to \\ [@public],
@ -61,6 +63,7 @@ defmodule Clacks.ActivityPub do
"content" => html, "content" => html,
"conversation" => context, "conversation" => context,
"context" => context, "context" => context,
"inReplyTo" => in_reply_to,
"published" => published |> DateTime.to_iso8601() "published" => published |> DateTime.to_iso8601()
} }
end end

View File

@ -18,7 +18,27 @@ defmodule ClacksWeb.ActivitiesController do
end end
_ -> _ ->
resp(conn, 404, "Not Found") case conn.assigns[:format] do
"activity+json" ->
conn
|> put_status(404)
|> json(%{error: "Not Found"})
"html" ->
resp(conn, 404, "Not Found")
end
end
end
def get_status(conn, %{"id" => status_id}) do
case Activity.get(status_id) do
%Activity{local: true, data: data} ->
json(conn, data)
_ ->
conn
|> put_status(404)
|> json(%{error: "Not Found"})
end end
end end
end end

View File

@ -4,6 +4,8 @@ defmodule ClacksWeb.FrontendController do
alias ClacksWeb.Router.Helpers, as: Routes alias ClacksWeb.Router.Helpers, as: Routes
alias ClacksWeb.Endpoint alias ClacksWeb.Endpoint
@public "https://www.w3.org/ns/activitystreams#Public"
def index(%Plug.Conn{assigns: %{user: user}} = conn, params) do def index(%Plug.Conn{assigns: %{user: user}} = conn, params) do
user = Repo.preload(user, :actor) user = Repo.preload(user, :actor)
@ -48,23 +50,67 @@ defmodule ClacksWeb.FrontendController do
with %Activity{ with %Activity{
local: true, local: true,
data:
%{
"type" => "Create",
"object" => %{"type" => "Note", "attributedTo" => author_id} = note
} = data
} <- Activity.get(id),
%Actor{} = author <- Actor.get_by_ap_id(author_id) do
case conn.assigns[:format] do
"activity+json" ->
json(conn, data)
"html" ->
render(conn, "status.html", %{
current_user: current_user,
note: note,
author: author
})
end
else
nil ->
case conn.assigns[:format] do
"activity+json" ->
conn
|> put_status(404)
|> json(%{error: "Not Found"})
"html" ->
resp(conn, 404, "Not Found")
end
%Activity{local: false, data: %{"id" => ap_id}} ->
case conn.assigns[:format] do
"activity+json" ->
conn
|> put_status(404)
|> json(%{error: "Not Found"})
"html" ->
redirect(conn, external: ap_id)
end
end
end
def reply(conn, %{"id" => id}) do
current_user = conn.assigns[:user]
with %Activity{
data: %{ data: %{
"type" => "Create", "type" => "Create",
"object" => %{"type" => "Note", "attributedTo" => author_id} = note "object" => %{"type" => "Note", "attributedTo" => author_id} = note
} }
} <- Activity.get(id), } <- Activity.get(id),
%Actor{} = author <- Actor.get_by_ap_id(author_id) do %Actor{} = author <- Actor.get_by_ap_id(author_id) do
render(conn, "status.html", %{ render(conn, "reply.html", %{
current_user: current_user, current_user: current_user,
note: note, note: note,
author: author author: author
}) })
else else
nil -> _ ->
put_status(conn, 404) resp(conn, 404, "Not Found")
%Activity{local: false, data: %{"id" => ap_id}} ->
redirect(conn, external: ap_id)
end end
end end
@ -89,7 +135,7 @@ defmodule ClacksWeb.FrontendController do
def post_status(conn, %{"content" => content} = params) do def post_status(conn, %{"content" => content} = params) do
current_user = conn.assigns[:user] |> Repo.preload(:actor) current_user = conn.assigns[:user] |> Repo.preload(:actor)
note = ActivityPub.note(current_user.actor.ap_id, content) note = note_for_posting(current_user, params)
note_changeset = Object.changeset_for_creating(note) note_changeset = Object.changeset_for_creating(note)
{:ok, object} = Repo.insert(note_changeset) {:ok, object} = Repo.insert(note_changeset)
@ -104,4 +150,31 @@ defmodule ClacksWeb.FrontendController do
path = Map.get(params, "continue", Routes.frontend_path(Endpoint, :status, activity.id)) path = Map.get(params, "continue", Routes.frontend_path(Endpoint, :status, activity.id))
redirect(conn, to: path) redirect(conn, to: path)
end end
defp note_for_posting(current_user, %{"content" => content, "in_reply_to" => in_reply_to_ap_id}) do
with %Activity{data: %{"context" => context, "actor" => in_reply_to_actor}} <-
Activity.get_by_ap_id(in_reply_to_ap_id) do
to = [in_reply_to_actor, @public]
# todo: followers
cc = []
ActivityPub.note(
current_user.actor.ap_id,
content,
context,
in_reply_to_ap_id,
nil,
DateTime.utc_now(),
to,
cc
)
else
_ ->
ActivityPub.note(current_user.actor.ap_id, content)
end
end
defp note_for_posting(current_user, %{"content" => content}) do
ActivityPub.note(current_user.actor.ap_id, content)
end
end end

View File

@ -51,13 +51,13 @@ defmodule ClacksWeb.Router do
pipe_through :browser_maybe_authenticated pipe_through :browser_maybe_authenticated
get "/", FrontendController, :index get "/", FrontendController, :index
get "/status/:id", FrontendController, :status
post "/post", FrontendController, :post_status
end end
scope "/", ClacksWeb do scope "/", ClacksWeb do
pipe_through :browser pipe_through :browser
pipe_through :browser_authenticated pipe_through :browser_authenticated
post "/post", FrontendController, :post_status
end end
scope "/", ClacksWeb do scope "/", ClacksWeb do
@ -80,6 +80,7 @@ defmodule ClacksWeb.Router do
get "/users/:username", ActorController, :get get "/users/:username", ActorController, :get
get "/activities/:id", ActivitiesController, :get get "/activities/:id", ActivitiesController, :get
get "/objects/:id", ObjectsController, :get get "/objects/:id", ObjectsController, :get
get "/status/:id", FrontendController, :status
end end
# Other scopes may use custom stacks. # Other scopes may use custom stacks.

View File

@ -9,6 +9,7 @@
<%= @author.data["name"] %> <%= @author.data["name"] %>
</a> </a>
</h3> </h3>
<a href="<%= @note["url"] %>">Permalink</a>
<div class="status-content"> <div class="status-content">
<%= @note["content"] %> <%= @note["content"] %>
</div> </div>

View File

@ -1 +1,9 @@
<%= render "_status.html", author: @author, note: @note %> <%= render "_status.html", author: @author, note: @note %>
<hr>
<%= form_tag Routes.frontend_path(@conn, :post_status), method: :post do %>
<input type="hidden" name="in_reply_to" value="<%= @note["id"] %>">
<textarea id="content" name="content" cols="30" rows="10" placeholder="Reply"></textarea>
<%= submit "Post" %>
<% end %>