diff --git a/README.md b/README.md index 27eb1c6..c157691 100644 --- a/README.md +++ b/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 diff --git a/lib/postland/activities.ex b/lib/postland/activities.ex index 7079ec8..55790e1 100644 --- a/lib/postland/activities.ex +++ b/lib/postland/activities.ex @@ -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) diff --git a/lib/postland/follows.ex b/lib/postland/follows.ex index d8dacae..c02f5bd 100644 --- a/lib/postland/follows.ex +++ b/lib/postland/follows.ex @@ -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 diff --git a/lib/postland_web/components/core_components.ex b/lib/postland_web/components/core_components.ex index 692d786..ad43340 100644 --- a/lib/postland_web/components/core_components.ex +++ b/lib/postland_web/components/core_components.ex @@ -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""" +
+
+ +
+
+
+

+ + <%= @account["name"] %> + + + @<%= @account["preferredUsername"] %>@<%= @host %> + +

+

+ <%= {:safe, Earmark.as_html!(@account["summary"] || "")} %> +

+
+
+ <.button disabled>Pending +
+
+
+ """ + 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" /> diff --git a/lib/postland_web/components/layouts.ex b/lib/postland_web/components/layouts.ex index 2edc216..2346334 100644 --- a/lib/postland_web/components/layouts.ex +++ b/lib/postland_web/components/layouts.ex @@ -2,4 +2,24 @@ defmodule PostlandWeb.Layouts do use PostlandWeb, :html embed_templates "layouts/*" + + def nav_link(assigns) do + ~H""" +
  • + + + <%= render_slot(@inner_block) %> + +
  • + """ + end end diff --git a/lib/postland_web/components/layouts/app.html.heex b/lib/postland_web/components/layouts/app.html.heex index fec9a04..646aa60 100644 --- a/lib/postland_web/components/layouts/app.html.heex +++ b/lib/postland_web/components/layouts/app.html.heex @@ -1,6 +1,46 @@ -
    -
    - <.flash_group flash={@flash} /> - <%= @inner_content %> +
    +
    + +
    +
    +
    + <.flash_group flash={@flash} /> + <%= @inner_content %> +
    diff --git a/lib/postland_web/components/layouts/root.html.heex b/lib/postland_web/components/layouts/root.html.heex index 8db39ff..89a69ac 100644 --- a/lib/postland_web/components/layouts/root.html.heex +++ b/lib/postland_web/components/layouts/root.html.heex @@ -12,44 +12,6 @@ -
    -
    - Postland -
    -
      - <%= if @current_user do %> -
    • - Profile -
    • -
    • - <.link - href={~p"/users/settings"} - class="text-[0.8125rem] leading-6 font-semibold hover:text-violet-300" - > - Settings - -
    • -
    • - <.link - href={~p"/users/log_out"} - method="delete" - class="text-[0.8125rem] leading-6 font-semibold hover:text-violet-300" - > - Log out - -
    • - <% else %> -
    • - <.link - href={~p"/users/log_in"} - class="text-[0.8125rem] leading-6 font-semibold hover:text-violet-300" - > - Log in - -
    • - <% end %> -
    -
    <%= @inner_content %> diff --git a/lib/postland_web/live/followers_live.ex b/lib/postland_web/live/followers_live.ex new file mode 100644 index 0000000..e04ea0f --- /dev/null +++ b/lib/postland_web/live/followers_live.ex @@ -0,0 +1,27 @@ +defmodule PostlandWeb.FollowersLive do + use PostlandWeb, :live_view + + alias Postland.Actors + alias Postland.Follows + + def render(assigns) do + ~H""" +
    +

    Followers

    +

    + No one follows you. +

    +
    + <.profile_card account={acct} /> +
    +
    + """ + 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 diff --git a/lib/postland_web/live/following_live copy.ex b/lib/postland_web/live/following_live copy.ex new file mode 100644 index 0000000..5dfb173 --- /dev/null +++ b/lib/postland_web/live/following_live copy.ex @@ -0,0 +1,25 @@ +defmodule PostlandWeb.FollowingLive do + use PostlandWeb, :live_view + + alias Postland.Actors + alias Postland.Follows + + def render(assigns) do + ~H""" +
    +

    Following

    +

    + You aren't following anyone. +

    +
    + <.profile_card account={acct} /> +
    +
    + """ + 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 diff --git a/lib/postland_web/router.ex b/lib/postland_web/router.ex index d0246c7..2dc9bb4 100644 --- a/lib/postland_web/router.ex +++ b/lib/postland_web/router.ex @@ -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