feat: Add content warnings
This commit is contained in:
parent
2685a8dfb8
commit
0bca082286
5 changed files with 109 additions and 42 deletions
|
|
@ -5,7 +5,7 @@
|
|||
- [x] Post formatting
|
||||
- [x] Deleting posts
|
||||
- [x] Sending posts w/ images
|
||||
- [ ] Making posts with CWs
|
||||
- [x] Making posts with CWs
|
||||
- [ ] Making polls
|
||||
- [ ] Followers-only posts (or maybe this is handled because we only send posts to followers? but we also include public in the TO field?)
|
||||
- [] Sending posts with videos
|
||||
|
|
@ -39,11 +39,11 @@
|
|||
- [x] Your posts show up in timeline
|
||||
- [x] Posts from accounts you follow show up in timeline
|
||||
- [x] Show the actor avatar and display name
|
||||
- [ ] Receiving posts w/ images
|
||||
- [x] Receiving posts w/ images
|
||||
- [ ] Receiving posts w/ videos
|
||||
- [ ] Liking posts
|
||||
- [ ] Unliking posts
|
||||
- [ ] Displaying CW posts behind CW
|
||||
- [x] Displaying CW posts behind CW
|
||||
- [ ] Displaying polls
|
||||
- [ ] Voting in polls
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Postland.Activities do
|
|||
alias Postland.Repo
|
||||
alias Postland.Follows
|
||||
|
||||
def record_markdown_post(markdown, attachments) do
|
||||
def record_markdown_post(markdown, cw, attachments) do
|
||||
id = Ecto.UUID.autogenerate()
|
||||
html = Earmark.as_html!(markdown)
|
||||
|
||||
|
|
@ -21,6 +21,22 @@ defmodule Postland.Activities do
|
|||
# CW posts have a summary field which is the warning itself, a content
|
||||
# field (like a non-CW post) that is the body, and a "sensitive" => true field
|
||||
|
||||
note =
|
||||
%{
|
||||
"id" => url(~p"/activities/#{id}"),
|
||||
"type" => "Note",
|
||||
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
||||
"attributedTo" => Postland.my_actor_id(),
|
||||
"mediaType" => "text/html",
|
||||
"content" => html,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [
|
||||
"#{Postland.my_actor_id()}/followers"
|
||||
],
|
||||
"attachment" => attachments
|
||||
}
|
||||
|> maybe_add_cw(cw)
|
||||
|
||||
activity =
|
||||
%{
|
||||
"@context" => [
|
||||
|
|
@ -38,19 +54,7 @@ defmodule Postland.Activities do
|
|||
"id" => url(~p"/activities/#{id}"),
|
||||
"type" => "Create",
|
||||
"actor" => Postland.my_actor_id(),
|
||||
"object" => %{
|
||||
"id" => url(~p"/activities/#{id}"),
|
||||
"type" => "Note",
|
||||
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
||||
"attributedTo" => Postland.my_actor_id(),
|
||||
"mediaType" => "text/html",
|
||||
"content" => html,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [
|
||||
"#{Postland.my_actor_id()}/followers"
|
||||
],
|
||||
"attachment" => attachments
|
||||
}
|
||||
"object" => note
|
||||
}
|
||||
|
||||
activity
|
||||
|
|
@ -59,6 +63,10 @@ defmodule Postland.Activities do
|
|||
|> broadcast_to_followers(activity)
|
||||
end
|
||||
|
||||
defp maybe_add_cw(note, nil), do: note
|
||||
defp maybe_add_cw(note, ""), do: note
|
||||
defp maybe_add_cw(note, cw), do: Map.merge(note, %{"sensitive" => true, "summary" => cw})
|
||||
|
||||
def delete_post(post) do
|
||||
activity =
|
||||
%{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ defmodule Postland.Timeline do
|
|||
# TODO: Filter down to just image/* and video/* MIME types
|
||||
end
|
||||
|
||||
def cw(activity) do
|
||||
activity.data
|
||||
|> Map.get("items")
|
||||
|> Enum.find_value(fn item -> item["sensitive"] && item["summary"] end)
|
||||
end
|
||||
|
||||
def attribution(activity) do
|
||||
Postland.Actors.actor(get_from_html_item(activity, "attributedTo"))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -87,6 +87,16 @@ defmodule PostlandWeb.CoreComponents do
|
|||
<div class="min-w-0 flex-1 relative">
|
||||
<div class="overflow-hidden bg-white relative rounded-lg shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-violet-600">
|
||||
<.form phx-submit="create_post" phx-change="change_post" id="create-post-form">
|
||||
<div :if={@cw} class="px-3 pt-2 text-orange-700">
|
||||
<.icon name="hero-exclamation-triangle" />
|
||||
<input
|
||||
id="cw-input"
|
||||
name="cw"
|
||||
value={@cw}
|
||||
placeholder="add content warning"
|
||||
class="border-0 bg-transparent py-1.5 text-orange-700 placeholder:text-gray-400 focus:ring-0 outline-none sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
<label for="post-body" class="sr-only">post body</label>
|
||||
<textarea
|
||||
rows="3"
|
||||
|
|
@ -94,8 +104,7 @@ defmodule PostlandWeb.CoreComponents do
|
|||
id="post-body"
|
||||
class="block w-full resize-none border-0 bg-transparent py-1.5 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
|
||||
placeholder="post body"
|
||||
value={@post_content}
|
||||
></textarea>
|
||||
><%= @post_content %></textarea>
|
||||
</.form>
|
||||
<div class="flex">
|
||||
<div :for={attachment <- @attachments} class="m-2 w-1/2">
|
||||
|
|
@ -117,6 +126,12 @@ defmodule PostlandWeb.CoreComponents do
|
|||
<.icon name="hero-photo" />
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="h-9 flex items-center text-gray-500"
|
||||
phx-click={if @cw, do: "remove_cw", else: "add_cw"}
|
||||
>
|
||||
<.icon name="hero-exclamation-triangle" class="relative top-0.5" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<button
|
||||
|
|
@ -176,11 +191,14 @@ defmodule PostlandWeb.CoreComponents do
|
|||
attachments =
|
||||
Postland.Timeline.attachments(assigns.post)
|
||||
|
||||
cw = Postland.Timeline.cw(assigns.post)
|
||||
|
||||
assigns =
|
||||
assigns
|
||||
|> Map.put(:author, author)
|
||||
|> Map.put(:author_host, host)
|
||||
|> Map.put(:attachments, attachments)
|
||||
|> Map.put(:cw, cw)
|
||||
|
||||
~H"""
|
||||
<div class="flex items-start space-x-4 w-full">
|
||||
|
|
@ -200,26 +218,30 @@ defmodule PostlandWeb.CoreComponents do
|
|||
@<%= @author["preferredUsername"] %>@<%= @author_host %>
|
||||
</span>
|
||||
</div>
|
||||
<div class="px-4 py-5 sm:p-6 prose">
|
||||
<%= {:safe, Postland.Timeline.html_content(@post)} %>
|
||||
</div>
|
||||
<div :if={@attachments != []} class="px-4 py-5 sm:p-6">
|
||||
<div :for={attachment <- @attachments} class="flex">
|
||||
<div
|
||||
class="w-1/2 aspect-square bg-black bg-center bg-contain bg-no-repeat rounded-lg shadow"
|
||||
style={"background-image: url('#{attachment["url"]}')"}
|
||||
phx-click={show_modal("modal-#{Base.url_encode64(attachment["url"], padding: false)}")}
|
||||
phx-value-attachment={attachment["url"]}
|
||||
alt={attachment["name"]}
|
||||
title={attachment["name"]}
|
||||
/>
|
||||
<.modal id={"modal-#{Base.url_encode64(attachment["url"], padding: false)}"}>
|
||||
<div class="w-full flex justify-center" title={attachment["name"]}>
|
||||
<img src={attachment["url"]} alt={attachment["name"]} />
|
||||
</div>
|
||||
</.modal>
|
||||
<.cw_wrapper cw={@cw}>
|
||||
<div class="px-4 py-5 sm:p-6 prose">
|
||||
<%= {:safe, Postland.Timeline.html_content(@post)} %>
|
||||
</div>
|
||||
</div>
|
||||
<div :if={@attachments != []} class="px-4 py-5 sm:p-6">
|
||||
<div :for={attachment <- @attachments} class="flex">
|
||||
<div
|
||||
class="w-1/2 aspect-square bg-black bg-center bg-contain bg-no-repeat rounded-lg shadow"
|
||||
style={"background-image: url('#{attachment["url"]}')"}
|
||||
phx-click={
|
||||
show_modal("modal-#{Base.url_encode64(attachment["url"], padding: false)}")
|
||||
}
|
||||
phx-value-attachment={attachment["url"]}
|
||||
alt={attachment["name"]}
|
||||
title={attachment["name"]}
|
||||
/>
|
||||
<.modal id={"modal-#{Base.url_encode64(attachment["url"], padding: false)}"}>
|
||||
<div class="w-full flex justify-center" title={attachment["name"]}>
|
||||
<img src={attachment["url"]} alt={attachment["name"]} />
|
||||
</div>
|
||||
</.modal>
|
||||
</div>
|
||||
</div>
|
||||
</.cw_wrapper>
|
||||
<div class="px-4 py-4 sm:px-6">
|
||||
<a
|
||||
:if={@author["id"] == Postland.my_actor_id()}
|
||||
|
|
@ -239,6 +261,21 @@ defmodule PostlandWeb.CoreComponents do
|
|||
"""
|
||||
end
|
||||
|
||||
def cw_wrapper(assigns) do
|
||||
~H"""
|
||||
<%= if @cw do %>
|
||||
<details>
|
||||
<summary class="ml-6 text-orange-700">
|
||||
<.icon name="hero-exclamation-triangle" /> <%= @cw %>
|
||||
</summary>
|
||||
<%= render_slot(@inner_block) %>
|
||||
</details>
|
||||
<% else %>
|
||||
<%= render_slot(@inner_block) %>
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a modal.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule PostlandWeb.TimelineLive do
|
|||
def render(assigns) do
|
||||
~H"""
|
||||
<div id="timeline">
|
||||
<.post_form post_content={@post} attachments={@attachments} upload={@uploads.files} />
|
||||
<.post_form post_content={@post} attachments={@attachments} upload={@uploads.files} cw={@cw} />
|
||||
<div id="timeline-posts" phx-update="stream">
|
||||
<div :for={{id, post} <- @streams.posts} id={id} class="mt-10">
|
||||
<.post_card post={post} post_dom_id={id} />
|
||||
|
|
@ -22,6 +22,7 @@ defmodule PostlandWeb.TimelineLive do
|
|||
socket
|
||||
|> assign(:post, "")
|
||||
|> assign(:attachments, [])
|
||||
|> assign(:cw, nil)
|
||||
|> stream(:posts, Postland.Timeline.timeline())
|
||||
|> allow_upload(:files,
|
||||
accept: ["image/*"],
|
||||
|
|
@ -33,7 +34,12 @@ defmodule PostlandWeb.TimelineLive do
|
|||
end
|
||||
|
||||
def handle_event("create_post", %{"post" => post}, socket) do
|
||||
{:ok, results} = Postland.Activities.record_markdown_post(post, socket.assigns.attachments)
|
||||
{:ok, results} =
|
||||
Postland.Activities.record_markdown_post(
|
||||
post,
|
||||
socket.assigns.cw,
|
||||
socket.assigns.attachments
|
||||
)
|
||||
|
||||
new_posts =
|
||||
results
|
||||
|
|
@ -45,6 +51,7 @@ defmodule PostlandWeb.TimelineLive do
|
|||
socket
|
||||
|> assign(:post, "")
|
||||
|> assign(:attachments, [])
|
||||
|> assign(:cw, nil)
|
||||
|> then(fn socket ->
|
||||
Enum.reduce(new_posts, socket, fn {_id, post}, socket ->
|
||||
stream_insert(socket, :posts, post, at: 0)
|
||||
|
|
@ -66,8 +73,9 @@ defmodule PostlandWeb.TimelineLive do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_event("change_post", %{"post" => post}, socket) do
|
||||
{:noreply, socket |> assign(:post, post)}
|
||||
def handle_event("change_post", %{"post" => post} = params, socket) do
|
||||
cw = Map.get(params, "cw")
|
||||
{:noreply, socket |> assign(post: post, cw: cw)}
|
||||
end
|
||||
|
||||
def handle_event("remove_attachment", %{"url" => url}, socket) do
|
||||
|
|
@ -92,6 +100,14 @@ defmodule PostlandWeb.TimelineLive do
|
|||
{:noreply, assign(socket, attachments: attachments)}
|
||||
end
|
||||
|
||||
def handle_event("add_cw", _, socket) do
|
||||
{:noreply, assign(socket, cw: "")}
|
||||
end
|
||||
|
||||
def handle_event("remove_cw", _, socket) do
|
||||
{:noreply, assign(socket, cw: nil)}
|
||||
end
|
||||
|
||||
def handle_progress(:files, entry, socket) do
|
||||
if entry.done? do
|
||||
uploaded_file =
|
||||
|
|
|
|||
Loading…
Reference in a new issue