Add following and followers
This commit is contained in:
parent
f8bedff913
commit
0b88881934
8 changed files with 261 additions and 4 deletions
|
@ -139,4 +139,90 @@ defmodule Nulla.ActivityPub do
|
||||||
to: activity.to
|
to: activity.to
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec following(String.t(), Nulla.Models.User.t(), Integer.t()) :: Jason.OrderedObject.t()
|
||||||
|
def following(domain, user, total) do
|
||||||
|
Jason.OrderedObject.new(
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
id: "https://#{domain}/@#{user.username}/following",
|
||||||
|
type: "OrderedCollection",
|
||||||
|
totalItems: total,
|
||||||
|
first: "https://#{domain}/@#{user.username}/following?page=1"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec following(String.t(), Nulla.Models.User.t(), Integer.t(), List.t(), Integer.t(), Integer.t()) :: Jason.OrderedObject.t()
|
||||||
|
def following(domain, user, total, following_list, page, offset) when is_integer(page) and page > 0 do
|
||||||
|
data = [
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
id: "https://#{domain}/@#{user.username}/following?page=#{page}",
|
||||||
|
type: "OrderedCollectionPage",
|
||||||
|
totalItems: total,
|
||||||
|
next: "https://#{domain}/@#{user.username}/following?page=#{page + 1}",
|
||||||
|
prev: "https://#{domain}/@#{user.username}/following?page=#{page - 1}",
|
||||||
|
partOf: "https://#{domain}/@#{user.username}/following",
|
||||||
|
orderedItems: following_list
|
||||||
|
]
|
||||||
|
|
||||||
|
data =
|
||||||
|
if page <= 1 do
|
||||||
|
Keyword.delete(data, :prev)
|
||||||
|
else
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
data =
|
||||||
|
if page * offset > total do
|
||||||
|
data
|
||||||
|
|> Keyword.delete(:next)
|
||||||
|
|> Keyword.delete(:prev)
|
||||||
|
else
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
Jason.OrderedObject.new(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec followers(String.t(), Nulla.Models.User.t(), Integer.t()) :: Jason.OrderedObject.t()
|
||||||
|
def followers(domain, user, total) do
|
||||||
|
Jason.OrderedObject.new(
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
id: "https://#{domain}/@#{user.username}/followers",
|
||||||
|
type: "OrderedCollection",
|
||||||
|
totalItems: total,
|
||||||
|
first: "https://#{domain}/@#{user.username}/followers?page=1"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec followers(String.t(), Nulla.Models.User.t(), Integer.t(), List.t(), Integer.t(), Integer.t()) :: Jason.OrderedObject.t()
|
||||||
|
def followers(domain, user, total, followers_list, page, offset) when is_integer(page) and page > 0 do
|
||||||
|
data = [
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
id: "https://#{domain}/@#{user.username}/followers?page=#{page}",
|
||||||
|
type: "OrderedCollectionPage",
|
||||||
|
totalItems: total,
|
||||||
|
next: "https://#{domain}/@#{user.username}/followers?page=#{page + 1}",
|
||||||
|
prev: "https://#{domain}/@#{user.username}/followers?page=#{page - 1}",
|
||||||
|
partOf: "https://#{domain}/@#{user.username}/followers",
|
||||||
|
orderedItems: followers_list
|
||||||
|
]
|
||||||
|
|
||||||
|
data =
|
||||||
|
if page <= 1 do
|
||||||
|
Keyword.delete(data, :prev)
|
||||||
|
else
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
data =
|
||||||
|
if page * offset > total do
|
||||||
|
data
|
||||||
|
|> Keyword.delete(:next)
|
||||||
|
|> Keyword.delete(:prev)
|
||||||
|
else
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
Jason.OrderedObject.new(data)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Nulla.Models.InstanceSettings do
|
||||||
field :registration, :boolean, default: false
|
field :registration, :boolean, default: false
|
||||||
field :max_characters, :integer, default: 5000
|
field :max_characters, :integer, default: 5000
|
||||||
field :max_upload_size, :integer, default: 50
|
field :max_upload_size, :integer, default: 50
|
||||||
|
field :offset, :integer, default: 100
|
||||||
field :public_key, :string
|
field :public_key, :string
|
||||||
field :private_key, :string
|
field :private_key, :string
|
||||||
end
|
end
|
||||||
|
@ -25,6 +26,7 @@ defmodule Nulla.Models.InstanceSettings do
|
||||||
:registration,
|
:registration,
|
||||||
:max_characters,
|
:max_characters,
|
||||||
:max_upload_size,
|
:max_upload_size,
|
||||||
|
:offset,
|
||||||
:public_key,
|
:public_key,
|
||||||
:private_key
|
:private_key
|
||||||
])
|
])
|
||||||
|
@ -35,6 +37,7 @@ defmodule Nulla.Models.InstanceSettings do
|
||||||
:registration,
|
:registration,
|
||||||
:max_characters,
|
:max_characters,
|
||||||
:max_upload_size,
|
:max_upload_size,
|
||||||
|
:offset,
|
||||||
:public_key,
|
:public_key,
|
||||||
:private_key
|
:private_key
|
||||||
])
|
])
|
||||||
|
|
87
lib/nulla/utils.ex
Normal file
87
lib/nulla/utils.ex
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
defmodule Nulla.Utils do
|
||||||
|
import Ecto.Query
|
||||||
|
alias Nulla.Repo
|
||||||
|
alias Nulla.Models.User
|
||||||
|
alias Nulla.Models.Follow
|
||||||
|
alias Nulla.Models.InstanceSettings
|
||||||
|
|
||||||
|
def count_following_by_username!(username) do
|
||||||
|
case Repo.get_by(User, username: username) do
|
||||||
|
nil ->
|
||||||
|
{:error, :user_not_found}
|
||||||
|
|
||||||
|
%User{id: user_id} ->
|
||||||
|
count =
|
||||||
|
Follow
|
||||||
|
|> where([f], f.user_id == ^user_id)
|
||||||
|
|> select([f], count(f.id))
|
||||||
|
|> Repo.one()
|
||||||
|
|
||||||
|
count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_following_users_by_username!(username, page) when is_integer(page) and page > 0 do
|
||||||
|
case Repo.get_by(User, username: username) do
|
||||||
|
nil ->
|
||||||
|
{:error, :user_not_found}
|
||||||
|
|
||||||
|
%User{id: user_id} ->
|
||||||
|
instance_settings = InstanceSettings.get_instance_settings!()
|
||||||
|
per_page = instance_settings.offset
|
||||||
|
offset = (page - 1) * per_page
|
||||||
|
|
||||||
|
query =
|
||||||
|
from [f, u] in
|
||||||
|
from(f in Follow,
|
||||||
|
join: u in User, on: u.id == f.target_id,
|
||||||
|
where: f.user_id == ^user_id,
|
||||||
|
order_by: [asc: u.inserted_at],
|
||||||
|
offset: ^offset,
|
||||||
|
limit: ^per_page,
|
||||||
|
select: u
|
||||||
|
)
|
||||||
|
|
||||||
|
users = Repo.all(query)
|
||||||
|
|
||||||
|
users
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def count_followers_by_username!(username) do
|
||||||
|
case Repo.get_by(User, username: username) do
|
||||||
|
nil ->
|
||||||
|
0
|
||||||
|
|
||||||
|
%User{id: user_id} ->
|
||||||
|
from(f in Follow, where: f.target_id == ^user_id)
|
||||||
|
|> select([f], count(f.id))
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_followers_by_username!(username, page) when is_integer(page) and page > 0 do
|
||||||
|
case Repo.get_by(User, username: username) do
|
||||||
|
nil ->
|
||||||
|
{:error, :user_not_found}
|
||||||
|
|
||||||
|
%User{id: user_id} ->
|
||||||
|
instance_settings = InstanceSettings.get_instance_settings!()
|
||||||
|
per_page = instance_settings.offset
|
||||||
|
offset = (page - 1) * per_page
|
||||||
|
|
||||||
|
query =
|
||||||
|
from f in Follow,
|
||||||
|
where: f.target_id == ^user_id,
|
||||||
|
join: u in User, on: u.id == f.user_id,
|
||||||
|
order_by: [asc: u.inserted_at],
|
||||||
|
offset: ^offset,
|
||||||
|
limit: ^per_page,
|
||||||
|
select: u
|
||||||
|
|
||||||
|
users = Repo.all(query)
|
||||||
|
|
||||||
|
users
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -66,9 +66,9 @@
|
||||||
</dl>
|
</dl>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="flex mt-5 gap-3">
|
<div class="flex mt-5 gap-3">
|
||||||
<a href={~p"/@#{@user.username}"}>1.7K Posts</a>
|
<a href={~p"/@#{@user.username}"}>{length(@notes)} Posts</a>
|
||||||
<a href={~p"/@#{@user.username}/following"}>33 Following</a>
|
<a href={~p"/@#{@user.username}/following"}>{@following} Following</a>
|
||||||
<a href={~p"/@#{@user.username}/followers"}>31 Followers</a>
|
<a href={~p"/@#{@user.username}/followers"}>{@followers} Followers</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between px-20 py-2 mt-5 border-y border-gray-300">
|
<div class="flex justify-between px-20 py-2 mt-5 border-y border-gray-300">
|
||||||
|
|
65
lib/nulla_web/controllers/follow_controller.ex
Normal file
65
lib/nulla_web/controllers/follow_controller.ex
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
defmodule NullaWeb.FollowController do
|
||||||
|
use NullaWeb, :controller
|
||||||
|
alias Nulla.Models.User
|
||||||
|
alias Nulla.Models.InstanceSettings
|
||||||
|
alias Nulla.ActivityPub
|
||||||
|
alias Nulla.Utils
|
||||||
|
|
||||||
|
def following(conn, %{"username" => username, "page" => page_param}) do
|
||||||
|
instance_settings = InstanceSettings.get_instance_settings!()
|
||||||
|
domain = instance_settings.domain
|
||||||
|
offset = instance_settings.offset
|
||||||
|
user = User.get_user_by_username!(username)
|
||||||
|
total = Utils.count_following_by_username!(user.username)
|
||||||
|
page =
|
||||||
|
case Integer.parse(page_param) do
|
||||||
|
{int, _} when int > 0 -> int
|
||||||
|
_ -> 1
|
||||||
|
end
|
||||||
|
following_list = Utils.get_following_users_by_username!(user.username, page)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/activity+json")
|
||||||
|
|> json(ActivityPub.following(domain, user, total, following_list, page, offset))
|
||||||
|
end
|
||||||
|
|
||||||
|
def following(conn, %{"username" => username}) do
|
||||||
|
instance_settings = InstanceSettings.get_instance_settings!()
|
||||||
|
domain = instance_settings.domain
|
||||||
|
user = User.get_user_by_username!(username)
|
||||||
|
total = Utils.count_following_by_username!(user.username)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/activity+json")
|
||||||
|
|> json(ActivityPub.following(domain, user, total))
|
||||||
|
end
|
||||||
|
|
||||||
|
def followers(conn, %{"username" => username, "page" => page_param}) do
|
||||||
|
instance_settings = InstanceSettings.get_instance_settings!()
|
||||||
|
domain = instance_settings.domain
|
||||||
|
offset = instance_settings.offset
|
||||||
|
user = User.get_user_by_username!(username)
|
||||||
|
total = Utils.count_followers_by_username!(user.username)
|
||||||
|
page =
|
||||||
|
case Integer.parse(page_param) do
|
||||||
|
{int, _} when int > 0 -> int
|
||||||
|
_ -> 1
|
||||||
|
end
|
||||||
|
followers_list = Utils.get_followers_by_username!(user.username, page)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/activity+json")
|
||||||
|
|> json(ActivityPub.followers(domain, user, total, followers_list, page, offset))
|
||||||
|
end
|
||||||
|
|
||||||
|
def followers(conn, %{"username" => username}) do
|
||||||
|
instance_settings = InstanceSettings.get_instance_settings!()
|
||||||
|
domain = instance_settings.domain
|
||||||
|
user = User.get_user_by_username!(username)
|
||||||
|
total = Utils.count_followers_by_username!(user.username)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/activity+json")
|
||||||
|
|> json(ActivityPub.followers(domain, user, total))
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@ defmodule NullaWeb.UserController do
|
||||||
alias Nulla.Models.Note
|
alias Nulla.Models.Note
|
||||||
alias Nulla.Models.InstanceSettings
|
alias Nulla.Models.InstanceSettings
|
||||||
alias Nulla.ActivityPub
|
alias Nulla.ActivityPub
|
||||||
|
alias Nulla.Utils
|
||||||
|
|
||||||
def show(conn, %{"username" => username}) do
|
def show(conn, %{"username" => username}) do
|
||||||
accept = List.first(get_req_header(conn, "accept"))
|
accept = List.first(get_req_header(conn, "accept"))
|
||||||
|
@ -17,7 +18,19 @@ defmodule NullaWeb.UserController do
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> json(ActivityPub.user(domain, user))
|
|> json(ActivityPub.user(domain, user))
|
||||||
else
|
else
|
||||||
render(conn, :show, domain: domain, user: user, notes: notes, layout: false)
|
following = Utils.count_following_by_username!(user.username)
|
||||||
|
followers = Utils.count_followers_by_username!(user.username)
|
||||||
|
|
||||||
|
render(
|
||||||
|
conn,
|
||||||
|
:show,
|
||||||
|
domain: domain,
|
||||||
|
user: user,
|
||||||
|
notes: notes,
|
||||||
|
following: following,
|
||||||
|
followers: followers,
|
||||||
|
layout: false
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,6 +22,8 @@ defmodule NullaWeb.Router do
|
||||||
pipe_through :browser
|
pipe_through :browser
|
||||||
|
|
||||||
get "/@:username", UserController, :show
|
get "/@:username", UserController, :show
|
||||||
|
get "/@:username/following", FollowController, :following
|
||||||
|
get "/@:username/followers", FollowController, :followers
|
||||||
get "/@:username/:note_id", NoteController, :show
|
get "/@:username/:note_id", NoteController, :show
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Nulla.Repo.Migrations.CreateInstanceSettings do
|
||||||
add :registration, :boolean, default: false, null: false
|
add :registration, :boolean, default: false, null: false
|
||||||
add :max_characters, :integer, default: 5000, null: false
|
add :max_characters, :integer, default: 5000, null: false
|
||||||
add :max_upload_size, :integer, default: 50, null: false
|
add :max_upload_size, :integer, default: 50, null: false
|
||||||
|
add :offset, :integer, default: 100, null: false
|
||||||
add :public_key, :string
|
add :public_key, :string
|
||||||
add :private_key, :string
|
add :private_key, :string
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue