feat: Add following and followers list
This commit is contained in:
parent
b49925b830
commit
f27dd6a9a7
10 changed files with 219 additions and 88 deletions
99
README.md
99
README.md
|
|
@ -1,56 +1,67 @@
|
|||
## Backend
|
||||
## Posting
|
||||
|
||||
- [x] Check that signature header (digest) matches digest of body contents
|
||||
- [ ] Posting
|
||||
- [x] Making posts locally
|
||||
- [x] Figuring out follower list
|
||||
- [x] Sending to followers
|
||||
- [x] Post formatting
|
||||
- [ ] Sending posts w/ images / videos
|
||||
- [ ] Receiving posts w/ images / videos
|
||||
- [x] Timeline
|
||||
- [x] My posts
|
||||
- [x] Posts from accounts you follow
|
||||
- [x] Show the actor avatar and display name
|
||||
- [x] Making posts
|
||||
- [x] Broadcasting them to followers
|
||||
- [x] Post formatting
|
||||
- [x] Deleting posts
|
||||
- [ ] Sending posts w/ images / videos
|
||||
- [ ] 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?)
|
||||
|
||||
## Profile
|
||||
|
||||
- [x] Profile
|
||||
- [x] Name field (for display name)
|
||||
- [x] Bust actor cache when you update your profile
|
||||
- [x] Following
|
||||
- [ ] Scrape public posts from the outbox when you follow
|
||||
|
||||
## Following
|
||||
|
||||
- [x] Sending follow request
|
||||
- [x] View following list
|
||||
- [ ] Withdrawing follow request
|
||||
- [ ] Unfollowing
|
||||
- [x] Being followed
|
||||
- [x] Accepting follows
|
||||
- [ ] Proactively check the outbox of newly-accepted follows
|
||||
|
||||
## Being Followed
|
||||
|
||||
- [x] Receiving follower requests
|
||||
- [ ] Viewing follower requests
|
||||
- [ ] Accepting follower requests
|
||||
- [ ] Rejecting follower requests
|
||||
- [ ] Ignoring follower requests
|
||||
- [ ] Unaccepting follower request ("soft block")
|
||||
- [ ] Blocking
|
||||
- [ ] Approving / declining follows
|
||||
- [ ] Manage authorized instance list
|
||||
- [ ] Liking
|
||||
- [ ] Unliking
|
||||
- [ ] CW posts
|
||||
- [ ] Polls
|
||||
- [ ] DMs
|
||||
- [ ] Support authenticated fetch of outbox (by allowed domains / servers)
|
||||
- [ ] Followers-only posts (or maybe this is handled because we only send posts to followers? but we also include public in the TO field?)
|
||||
|
||||
## Timeline
|
||||
|
||||
- [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 / videos
|
||||
- [ ] Liking posts
|
||||
- [ ] Unliking posts
|
||||
- [ ] Displaying CW posts behind CW
|
||||
- [ ] Displaying polls
|
||||
- [ ] Voting in polls
|
||||
|
||||
## DMs
|
||||
|
||||
- [ ] Receiving DMs
|
||||
- [ ] Replying to DMs
|
||||
- [ ] Sending new DMs
|
||||
|
||||
## Allowlist
|
||||
|
||||
- [ ] Manage approved instance list
|
||||
- [ ] Only accept activities from approved instances
|
||||
- [ ] Allow approved instances to see posts in outbox
|
||||
|
||||
## Protocol Support
|
||||
|
||||
- [x] Check that signature header (digest) matches digest of body contents
|
||||
- [ ] Check the domain of the public key against the domain of the object being CRUDed
|
||||
|
||||
## UX
|
||||
|
||||
- [x] Posting
|
||||
- [x] Timeline
|
||||
- [x] My posts
|
||||
- [x] Posts from accounts you follow
|
||||
- [ ] Deleting posts
|
||||
- [~] Following
|
||||
- [ ] Unfollowing
|
||||
- [ ] Being followed
|
||||
- [ ] Accepting follows
|
||||
- [ ] Approving / declining follows and authorized instance list
|
||||
- [ ] Liking
|
||||
- [ ] Unliking
|
||||
- [ ] CW posts
|
||||
- [ ] Polls
|
||||
- [ ] DMs
|
||||
|
||||
## Testing
|
||||
|
||||
- [ ] Measure test coverage
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ defmodule Postland.Activities do
|
|||
my_actor_id = Postland.my_actor_id()
|
||||
private_key = Postland.Accounts.solo_user().private_key
|
||||
|
||||
Postland.Follows.all_followers()
|
||||
Postland.Follows.confirmed_followers()
|
||||
|> Enum.map(fn %{follower: follower} ->
|
||||
inbox = Postland.Actors.inbox(follower)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,13 +14,20 @@ defmodule Postland.Follows do
|
|||
def all_following() do
|
||||
my_actor_id = Postland.my_actor_id()
|
||||
|
||||
from(f in Follow, where: f.follower == ^my_actor_id, where: not is_nil(f.confirmed_at))
|
||||
from(f in Follow, where: f.follower == ^my_actor_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def all_followers() do
|
||||
my_actor_id = Postland.my_actor_id()
|
||||
|
||||
from(f in Follow, where: f.followee == ^my_actor_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def confirmed_followers() do
|
||||
my_actor_id = Postland.my_actor_id()
|
||||
|
||||
from(f in Follow, where: f.followee == ^my_actor_id, where: not is_nil(f.confirmed_at))
|
||||
|> Repo.all()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,6 +20,42 @@ defmodule PostlandWeb.CoreComponents do
|
|||
alias Phoenix.LiveView.JS
|
||||
use Gettext, backend: PostlandWeb.Gettext
|
||||
|
||||
def profile_card(assigns) do
|
||||
%{host: host} = URI.parse(assigns.account["id"])
|
||||
|
||||
assigns = Map.put(assigns, :host, host)
|
||||
|
||||
~H"""
|
||||
<div class="flex items-start space-x-4">
|
||||
<div class="flex-shrink-0 relative">
|
||||
<img
|
||||
class="h-16 w-16 rounded-full drop-shadow-lg"
|
||||
src={@account["icon"] || url(~p"/images/avatar.png")}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="flex bg-white rounded-lg shadow container p-4">
|
||||
<div class="py-2">
|
||||
<p>
|
||||
<span class="font-bold">
|
||||
<%= @account["name"] %>
|
||||
</span>
|
||||
<span class="ml-2 text-gray-500">
|
||||
@<%= @account["preferredUsername"] %>@<%= @host %>
|
||||
</span>
|
||||
</p>
|
||||
<p class="text-gray-500 text-sm">
|
||||
<%= {:safe, Earmark.as_html!(@account["summary"] || "")} %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-grow text-right">
|
||||
<.button disabled>Pending</.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def post_form(assigns) do
|
||||
user = Postland.Accounts.solo_user()
|
||||
|
||||
|
|
@ -121,6 +157,7 @@ defmodule PostlandWeb.CoreComponents do
|
|||
phx-click="delete_post"
|
||||
phx-value-post-dom-id={@post_dom_id}
|
||||
phx-value-post-id={@post.id}
|
||||
class="text-gray-500"
|
||||
>
|
||||
<.icon name="hero-trash" />
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -2,4 +2,24 @@ defmodule PostlandWeb.Layouts do
|
|||
use PostlandWeb, :html
|
||||
|
||||
embed_templates "layouts/*"
|
||||
|
||||
def nav_link(assigns) do
|
||||
~H"""
|
||||
<li>
|
||||
<!-- Current: "bg-gray-50 text-violet-600", Default: "text-gray-700 hover:text-violet-600 hover:bg-gray-50" -->
|
||||
<a
|
||||
href={@href}
|
||||
class={[
|
||||
"group flex gap-x-3 rounded-lg p-2 pl-3 text-sm/6 font-semibold",
|
||||
if(assigns[:active],
|
||||
do: "bg-gray-50 text-violet-600",
|
||||
else: "text-gray-700 hover:text-violet-600 hover:bg-gray-50"
|
||||
)
|
||||
]}
|
||||
>
|
||||
<%= render_slot(@inner_block) %>
|
||||
</a>
|
||||
</li>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,46 @@
|
|||
<main class="px-4 py-20 sm:px-6 lg:px-8">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<.flash_group flash={@flash} />
|
||||
<%= @inner_content %>
|
||||
<main class="flex items-start px-4 py-20 sm:px-6 lg:px-8">
|
||||
<div :if={@current_user} class="flex-1 bg-white rounded-lg py-2 px-3 shadow">
|
||||
<nav class="flex flex-col" aria-label="Sidebar">
|
||||
<ul role="list" class="-mx-2 space-y-1">
|
||||
<li>
|
||||
<div class="group flex gap-x-3 rounded-lg p-2 pl-3 text-sm/6 uppercase font-bold">
|
||||
Postland
|
||||
</div>
|
||||
</li>
|
||||
<.nav_link href={~p"/"} active={@socket.view == PostlandWeb.TimelineLive}>
|
||||
Timeline
|
||||
</.nav_link>
|
||||
<.nav_link href={~p"/about"} active={@socket.view == PostlandWeb.ProfileLive}>
|
||||
Profile
|
||||
</.nav_link>
|
||||
<.nav_link href={~p"/following"} active={@socket.view == PostlandWeb.FollowingLive}>
|
||||
Following
|
||||
</.nav_link>
|
||||
<.nav_link href={~p"/followers"} active={@socket.view == PostlandWeb.FollowersLive}>
|
||||
Followers
|
||||
</.nav_link>
|
||||
<.nav_link
|
||||
href={~p"/users/settings"}
|
||||
active={@socket.view == PostlandWeb.UserSettingsLive}
|
||||
>
|
||||
Settings
|
||||
</.nav_link>
|
||||
<li>
|
||||
<.link
|
||||
href={~p"/users/log_out"}
|
||||
method="delete"
|
||||
class="group flex gap-x-3 rounded-lg p-2 pl-3 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-violet-600"
|
||||
>
|
||||
Log Out
|
||||
</.link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="flex-[5]">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<.flash_group flash={@flash} />
|
||||
<%= @inner_content %>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -12,44 +12,6 @@
|
|||
</script>
|
||||
</head>
|
||||
<body class="bg-violet-50 antialiased">
|
||||
<header class="flex w-full bg-violet-700 text-violet-100 py-4">
|
||||
<div class="flex-1 px-4 uppercase font-bold ">
|
||||
<a href={~p"/"}>Postland</a>
|
||||
</div>
|
||||
<ul class="relative z-10 flex flex-1 items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
|
||||
<%= if @current_user do %>
|
||||
<li class="text-[0.8125rem] leading-6 font-semibold hover:text-violet-300">
|
||||
<a href={~p"/about"}>Profile</a>
|
||||
</li>
|
||||
<li>
|
||||
<.link
|
||||
href={~p"/users/settings"}
|
||||
class="text-[0.8125rem] leading-6 font-semibold hover:text-violet-300"
|
||||
>
|
||||
Settings
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link
|
||||
href={~p"/users/log_out"}
|
||||
method="delete"
|
||||
class="text-[0.8125rem] leading-6 font-semibold hover:text-violet-300"
|
||||
>
|
||||
Log out
|
||||
</.link>
|
||||
</li>
|
||||
<% else %>
|
||||
<li>
|
||||
<.link
|
||||
href={~p"/users/log_in"}
|
||||
class="text-[0.8125rem] leading-6 font-semibold hover:text-violet-300"
|
||||
>
|
||||
Log in
|
||||
</.link>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</header>
|
||||
<%= @inner_content %>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
27
lib/postland_web/live/followers_live.ex
Normal file
27
lib/postland_web/live/followers_live.ex
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
defmodule PostlandWeb.FollowersLive do
|
||||
use PostlandWeb, :live_view
|
||||
|
||||
alias Postland.Actors
|
||||
alias Postland.Follows
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="py-4">
|
||||
<h3 class="text-base font-semibold">Followers</h3>
|
||||
<p :if={@accounts == []} class="text-gray-400">
|
||||
No one follows you.
|
||||
</p>
|
||||
<div :for={acct <- @accounts} class="mt-2">
|
||||
<.profile_card account={acct} />
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def mount(_params, _session, socket) do
|
||||
followers =
|
||||
Follows.all_followers() |> Enum.map(fn follow -> Actors.actor(follow.follower) end)
|
||||
|
||||
{:ok, assign(socket, :accounts, followers)}
|
||||
end
|
||||
end
|
||||
25
lib/postland_web/live/following_live copy.ex
Normal file
25
lib/postland_web/live/following_live copy.ex
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
defmodule PostlandWeb.FollowingLive do
|
||||
use PostlandWeb, :live_view
|
||||
|
||||
alias Postland.Actors
|
||||
alias Postland.Follows
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="py-4">
|
||||
<h3 class="text-base font-semibold">Following</h3>
|
||||
<p :if={@accounts == []} class="text-gray-400">
|
||||
You aren't following anyone.
|
||||
</p>
|
||||
<div :for={acct <- @accounts} class="mt-2">
|
||||
<.profile_card account={acct} />
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def mount(_params, _session, socket) do
|
||||
follows = Follows.all_following() |> Enum.map(fn follow -> Actors.actor(follow.followee) end)
|
||||
{:ok, assign(socket, :accounts, follows)}
|
||||
end
|
||||
end
|
||||
|
|
@ -82,6 +82,8 @@ defmodule PostlandWeb.Router do
|
|||
live_session :require_authenticated_user,
|
||||
on_mount: [{PostlandWeb.UserAuth, :ensure_authenticated}] do
|
||||
live "/", TimelineLive, :show
|
||||
live "/following", FollowingLive, :show
|
||||
live "/followers", FollowersLive, :show
|
||||
live "/users/settings", UserSettingsLive, :edit
|
||||
live "/@:acct", OtherProfileLive, :show
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue