Merge branch 'feld/pleroma-feld-mastodon-usersearch' into develop
This commit is contained in:
commit
f23edd2d6b
|
@ -24,7 +24,7 @@ def change(struct, params \\ %{}) do
|
||||||
def get_by_ap_id(nil), do: nil
|
def get_by_ap_id(nil), do: nil
|
||||||
def get_by_ap_id(ap_id) do
|
def get_by_ap_id(ap_id) do
|
||||||
Repo.one(from object in Object,
|
Repo.one(from object in Object,
|
||||||
where: fragment("? @> ?", object.data, ^%{id: ap_id}))
|
where: fragment("(?)->>'id' = ?", object.data, ^ap_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_cached_by_ap_id(ap_id) do
|
def get_cached_by_ap_id(ap_id) do
|
||||||
|
|
|
@ -241,7 +241,7 @@ def get_friends(%User{id: id, following: following}) do
|
||||||
|
|
||||||
def update_note_count(%User{} = user) do
|
def update_note_count(%User{} = user) do
|
||||||
note_count_query = from a in Object,
|
note_count_query = from a in Object,
|
||||||
where: fragment("? @> ?", a.data, ^%{actor: user.ap_id, type: "Note"}),
|
where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
|
||||||
select: count(a.id)
|
select: count(a.id)
|
||||||
|
|
||||||
note_count = Repo.one(note_count_query)
|
note_count = Repo.one(note_count_query)
|
||||||
|
@ -274,4 +274,14 @@ def get_notified_from_activity(%Activity{data: %{"to" => to}} = activity) do
|
||||||
|
|
||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def search(query, resolve) do
|
||||||
|
if resolve do
|
||||||
|
User.get_or_fetch_by_nickname(query)
|
||||||
|
end
|
||||||
|
q = from u in User,
|
||||||
|
where: fragment("(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)", u.nickname, u.name, ^query),
|
||||||
|
limit: 20
|
||||||
|
Repo.all(q)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -90,7 +90,11 @@ def update_object_in_activities(%{data: %{"id" => id}} = object) do
|
||||||
"""
|
"""
|
||||||
def get_existing_like(actor, %{data: %{"id" => id}} = object) do
|
def get_existing_like(actor, %{data: %{"id" => id}} = object) do
|
||||||
query = from activity in Activity,
|
query = from activity in Activity,
|
||||||
where: fragment("? @> ?", activity.data, ^%{actor: actor, object: id, type: "Like"})
|
where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
|
||||||
|
# this is to use the index
|
||||||
|
where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, activity.data, ^id),
|
||||||
|
where: fragment("(?)->>'type' = 'Like'", activity.data)
|
||||||
|
|
||||||
Repo.one(query)
|
Repo.one(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -271,9 +271,27 @@ def following(conn, %{"id" => id}) do
|
||||||
|
|
||||||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
with %User{} = followed <- Repo.get(User, id),
|
with %User{} = followed <- Repo.get(User, id),
|
||||||
{:ok, follower} <- User.follow(follower, followed),
|
{:ok, follower} <- User.follow(follower, followed),
|
||||||
{:ok, activity} <- ActivityPub.follow(follower, followed) do
|
{:ok, activity} <- ActivityPub.follow(follower, followed) do
|
||||||
render conn, AccountView, "relationship.json", %{user: follower, target: followed}
|
render conn, AccountView, "relationship.json", %{user: follower, target: followed}
|
||||||
|
else
|
||||||
|
{:error, message} = err ->
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/json")
|
||||||
|
|> send_resp(403, Poison.encode!(%{"error" => message}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||||
|
with %User{} = followed <- Repo.get_by(User, nickname: uri),
|
||||||
|
{:ok, follower} <- User.follow(follower, followed),
|
||||||
|
{:ok, activity} <- ActivityPub.follow(follower, followed) do
|
||||||
|
render conn, AccountView, "account.json", %{user: followed}
|
||||||
|
else
|
||||||
|
{:error, message} = err ->
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/json")
|
||||||
|
|> send_resp(403, Poison.encode!(%{"error" => message}))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -291,14 +309,7 @@ def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
if params["resolve"] == "true" do
|
accounts = User.search(query, params["resolve"] == "true")
|
||||||
User.get_or_fetch_by_nickname(query)
|
|
||||||
end
|
|
||||||
|
|
||||||
q = from u in User,
|
|
||||||
where: fragment("(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)", u.nickname, u.name, ^query),
|
|
||||||
limit: 20
|
|
||||||
accounts = Repo.all(q)
|
|
||||||
|
|
||||||
q = from a in Activity,
|
q = from a in Activity,
|
||||||
where: fragment("?->>'type' = 'Create'", a.data),
|
where: fragment("?->>'type' = 'Create'", a.data),
|
||||||
|
@ -315,6 +326,14 @@ def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
json(conn, res)
|
json(conn, res)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
|
accounts = User.search(query, params["resolve"] == "true")
|
||||||
|
|
||||||
|
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
|
||||||
|
|
||||||
|
json(conn, res)
|
||||||
|
end
|
||||||
|
|
||||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||||
params = conn
|
params = conn
|
||||||
|> Map.put("type", "Create")
|
|> Map.put("type", "Create")
|
||||||
|
|
|
@ -55,6 +55,7 @@ def user_fetcher(username) do
|
||||||
|
|
||||||
get "/accounts/verify_credentials", MastodonAPIController, :verify_credentials
|
get "/accounts/verify_credentials", MastodonAPIController, :verify_credentials
|
||||||
get "/accounts/relationships", MastodonAPIController, :relationships
|
get "/accounts/relationships", MastodonAPIController, :relationships
|
||||||
|
get "/accounts/search", MastodonAPIController, :account_search
|
||||||
post "/accounts/:id/follow", MastodonAPIController, :follow
|
post "/accounts/:id/follow", MastodonAPIController, :follow
|
||||||
post "/accounts/:id/unfollow", MastodonAPIController, :unfollow
|
post "/accounts/:id/unfollow", MastodonAPIController, :unfollow
|
||||||
post "/accounts/:id/block", MastodonAPIController, :relationship_noop
|
post "/accounts/:id/block", MastodonAPIController, :relationship_noop
|
||||||
|
@ -62,6 +63,8 @@ def user_fetcher(username) do
|
||||||
post "/accounts/:id/mute", MastodonAPIController, :relationship_noop
|
post "/accounts/:id/mute", MastodonAPIController, :relationship_noop
|
||||||
post "/accounts/:id/unmute", MastodonAPIController, :relationship_noop
|
post "/accounts/:id/unmute", MastodonAPIController, :relationship_noop
|
||||||
|
|
||||||
|
post "/follows", MastodonAPIController, :follow
|
||||||
|
|
||||||
get "/blocks", MastodonAPIController, :empty_array
|
get "/blocks", MastodonAPIController, :empty_array
|
||||||
get "/domain_blocks", MastodonAPIController, :empty_array
|
get "/domain_blocks", MastodonAPIController, :empty_array
|
||||||
get "/follow_requests", MastodonAPIController, :empty_array
|
get "/follow_requests", MastodonAPIController, :empty_array
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.DropObjectIndex do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
drop_if_exists index(:objects, [:data], using: :gin)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddObjectActorIndex do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
@disable_ddl_transaction true
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create index(:objects, ["(data->>'actor')", "(data->>'type')"], concurrently: true, name: :objects_actor_type)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.9cfa0ec1ee2d157fc360f5a3f1ee687c.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.13905a962b52e27fb039.js></script><script type=text/javascript src=/static/js/vendor.5ae36edd0f238a1334af.js></script><script type=text/javascript src=/static/js/app.437f43ecaf6ade101b9a.js></script></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.9cfa0ec1ee2d157fc360f5a3f1ee687c.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.5e2e6cbffed3dea6be7c.js></script><script type=text/javascript src=/static/js/vendor.a940853cbf3c748efda4.js></script><script type=text/javascript src=/static/js/app.37dc091547e5c8fb0a0e.js></script></body></html>
|
5
priv/static/static/js/app.37dc091547e5c8fb0a0e.js
Normal file
5
priv/static/static/js/app.37dc091547e5c8fb0a0e.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/app.37dc091547e5c8fb0a0e.js.map
Normal file
1
priv/static/static/js/app.37dc091547e5c8fb0a0e.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,2 +0,0 @@
|
||||||
!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var a=window.webpackJsonp;window.webpackJsonp=function(o,c){for(var p,s,l=0,i=[];l<o.length;l++)s=o[l],r[s]&&i.push.apply(i,r[s]),r[s]=0;for(p in c)e[p]=c[p];for(a&&a(o,c);i.length;)i.shift().call(null,t);if(c[0])return n[0]=0,t(0)};var n={},r={0:0};t.e=function(e,a){if(0===r[e])return a.call(null,t);if(void 0!==r[e])r[e].push(a);else{r[e]=[a];var n=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=t.p+"static/js/"+e+"."+{1:"5ae36edd0f238a1334af",2:"437f43ecaf6ade101b9a"}[e]+".js",n.appendChild(o)}},t.m=e,t.c=n,t.p="/"}([]);
|
|
||||||
//# sourceMappingURL=manifest.13905a962b52e27fb039.js.map
|
|
2
priv/static/static/js/manifest.5e2e6cbffed3dea6be7c.js
Normal file
2
priv/static/static/js/manifest.5e2e6cbffed3dea6be7c.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
!function(e){function t(n){if(a[n])return a[n].exports;var r=a[n]={exports:{},id:n,loaded:!1};return e[n].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,s,l=0,i=[];l<c.length;l++)s=c[l],r[s]&&i.push.apply(i,r[s]),r[s]=0;for(p in o)e[p]=o[p];for(n&&n(c,o);i.length;)i.shift().call(null,t);if(o[0])return a[0]=0,t(0)};var a={},r={0:0};t.e=function(e,n){if(0===r[e])return n.call(null,t);if(void 0!==r[e])r[e].push(n);else{r[e]=[n];var a=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"a940853cbf3c748efda4",2:"37dc091547e5c8fb0a0e"}[e]+".js",a.appendChild(c)}},t.m=e,t.c=a,t.p="/"}([]);
|
||||||
|
//# sourceMappingURL=manifest.5e2e6cbffed3dea6be7c.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
priv/static/static/js/vendor.a940853cbf3c748efda4.js.map
Normal file
1
priv/static/static/js/vendor.a940853cbf3c748efda4.js.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -281,6 +281,14 @@ test "following / unfollowing a user", %{conn: conn} do
|
||||||
|> post("/api/v1/accounts/#{other_user.id}/unfollow")
|
|> post("/api/v1/accounts/#{other_user.id}/unfollow")
|
||||||
|
|
||||||
assert %{"id" => id, "following" => false} = json_response(conn, 200)
|
assert %{"id" => id, "following" => false} = json_response(conn, 200)
|
||||||
|
|
||||||
|
user = Repo.get(User, user.id)
|
||||||
|
conn = build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/follows", %{"uri" => other_user.nickname})
|
||||||
|
|
||||||
|
assert %{"id" => id} = json_response(conn, 200)
|
||||||
|
assert id == other_user.id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "unimplemented block/mute endpoints" do
|
test "unimplemented block/mute endpoints" do
|
||||||
|
@ -311,6 +319,19 @@ test "unimplemented mutes, follow_requests, blocks, domain blocks" do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "account seach", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
||||||
|
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
|
||||||
|
|
||||||
|
conn = conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/search", %{"q" => "2hu"})
|
||||||
|
|
||||||
|
assert [account] = json_response(conn, 200)
|
||||||
|
assert account["id"] == user_three.id
|
||||||
|
end
|
||||||
|
|
||||||
test "search", %{conn: conn} do
|
test "search", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
||||||
|
|
Loading…
Reference in a new issue