feat: query and show objects liked by you
it shows it in quite a jank way: inside the "audience" collections you find your id as only item. it's weird af but technically valid ap i think? will probably be replaced with a local api extension as soon as i read about those
This commit is contained in:
parent
63ba54dad9
commit
45d16fa0a3
14 changed files with 78 additions and 26 deletions
|
@ -1,4 +1,4 @@
|
||||||
use apb::{ActivityMut, ObjectMut};
|
use apb::{ActivityMut, CollectionMut, ObjectMut};
|
||||||
use sea_orm::{entity::prelude::*, Condition, FromQueryResult, Iterable, Order, QueryOrder, QuerySelect, SelectColumns};
|
use sea_orm::{entity::prelude::*, Condition, FromQueryResult, Iterable, Order, QueryOrder, QuerySelect, SelectColumns};
|
||||||
|
|
||||||
use crate::routes::activitypub::jsonld::LD;
|
use crate::routes::activitypub::jsonld::LD;
|
||||||
|
@ -65,11 +65,15 @@ impl ActiveModelBehavior for ActiveModel {}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Tombstone,
|
Tombstone,
|
||||||
StrayObject(crate::model::object::Model),
|
|
||||||
Activity(crate::model::activity::Model),
|
Activity(crate::model::activity::Model),
|
||||||
|
StrayObject {
|
||||||
|
object: crate::model::object::Model,
|
||||||
|
liked: Option<String>,
|
||||||
|
},
|
||||||
DeepActivity {
|
DeepActivity {
|
||||||
activity: crate::model::activity::Model,
|
activity: crate::model::activity::Model,
|
||||||
object: crate::model::object::Model,
|
object: crate::model::object::Model,
|
||||||
|
liked: Option<String>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +82,9 @@ impl Event {
|
||||||
pub fn id(&self) -> &str {
|
pub fn id(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Event::Tombstone => "",
|
Event::Tombstone => "",
|
||||||
Event::StrayObject(x) => x.id.as_str(),
|
|
||||||
Event::Activity(x) => x.id.as_str(),
|
Event::Activity(x) => x.id.as_str(),
|
||||||
Event::DeepActivity { activity: _, object } => object.id.as_str(),
|
Event::StrayObject { object, liked: _ } => object.id.as_str(),
|
||||||
|
Event::DeepActivity { activity: _, liked: _, object } => object.id.as_str(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,11 +97,37 @@ impl Event {
|
||||||
};
|
};
|
||||||
match self {
|
match self {
|
||||||
Event::Activity(x) => x.ap(),
|
Event::Activity(x) => x.ap(),
|
||||||
Event::DeepActivity { activity, object } =>
|
Event::DeepActivity { activity, object, liked } =>
|
||||||
activity.ap().set_object(apb::Node::object(object.ap().set_attachment(attachment))),
|
activity.ap().set_object(apb::Node::object({
|
||||||
Event::StrayObject(x) => serde_json::Value::new_object()
|
let likes = object.likes;
|
||||||
|
let mut obj = object.ap()
|
||||||
|
.set_attachment(attachment);
|
||||||
|
if let Some(liked) = liked {
|
||||||
|
obj = obj.set_audience(apb::Node::object( // TODO setting this again ewww...
|
||||||
|
serde_json::Value::new_object()
|
||||||
|
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
|
||||||
|
.set_total_items(Some(likes as u64))
|
||||||
|
.set_ordered_items(apb::Node::links(vec![liked]))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
obj
|
||||||
|
})),
|
||||||
|
Event::StrayObject { object, liked } => serde_json::Value::new_object()
|
||||||
.set_activity_type(Some(apb::ActivityType::Activity))
|
.set_activity_type(Some(apb::ActivityType::Activity))
|
||||||
.set_object(apb::Node::object(x.ap().set_attachment(attachment))),
|
.set_object(apb::Node::object({
|
||||||
|
let likes = object.likes;
|
||||||
|
let mut obj = object.ap()
|
||||||
|
.set_attachment(attachment);
|
||||||
|
if let Some(liked) = liked {
|
||||||
|
obj = obj.set_audience(apb::Node::object( // TODO setting this again ewww...
|
||||||
|
serde_json::Value::new_object()
|
||||||
|
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
|
||||||
|
.set_total_items(Some(likes as u64))
|
||||||
|
.set_ordered_items(apb::Node::links(vec![liked]))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
obj
|
||||||
|
})),
|
||||||
Event::Tombstone => serde_json::Value::new_object()
|
Event::Tombstone => serde_json::Value::new_object()
|
||||||
.set_activity_type(Some(apb::ActivityType::Activity))
|
.set_activity_type(Some(apb::ActivityType::Activity))
|
||||||
.set_object(apb::Node::object(
|
.set_object(apb::Node::object(
|
||||||
|
@ -112,10 +142,11 @@ impl FromQueryResult for Event {
|
||||||
fn from_query_result(res: &sea_orm::QueryResult, _pre: &str) -> Result<Self, sea_orm::DbErr> {
|
fn from_query_result(res: &sea_orm::QueryResult, _pre: &str) -> Result<Self, sea_orm::DbErr> {
|
||||||
let activity = crate::model::activity::Model::from_query_result(res, crate::model::activity::Entity.table_name()).ok();
|
let activity = crate::model::activity::Model::from_query_result(res, crate::model::activity::Entity.table_name()).ok();
|
||||||
let object = crate::model::object::Model::from_query_result(res, crate::model::object::Entity.table_name()).ok();
|
let object = crate::model::object::Model::from_query_result(res, crate::model::object::Entity.table_name()).ok();
|
||||||
|
let liked = res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok();
|
||||||
match (activity, object) {
|
match (activity, object) {
|
||||||
(Some(activity), Some(object)) => Ok(Self::DeepActivity { activity, object }),
|
(Some(activity), Some(object)) => Ok(Self::DeepActivity { activity, object, liked }),
|
||||||
(Some(activity), None) => Ok(Self::Activity(activity)),
|
(Some(activity), None) => Ok(Self::Activity(activity)),
|
||||||
(None, Some(object)) => Ok(Self::StrayObject(object)),
|
(None, Some(object)) => Ok(Self::StrayObject { object, liked }),
|
||||||
(None, None) => Ok(Self::Tombstone),
|
(None, None) => Ok(Self::Tombstone),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +154,7 @@ impl FromQueryResult for Event {
|
||||||
|
|
||||||
|
|
||||||
impl Entity {
|
impl Entity {
|
||||||
pub fn find_addressed() -> Select<Entity> {
|
pub fn find_addressed(uid: Option<&str>) -> Select<Entity> {
|
||||||
let mut select = Entity::find()
|
let mut select = Entity::find()
|
||||||
.distinct()
|
.distinct()
|
||||||
.select_only()
|
.select_only()
|
||||||
|
@ -137,6 +168,13 @@ impl Entity {
|
||||||
)
|
)
|
||||||
.order_by(Column::Published, Order::Desc);
|
.order_by(Column::Published, Order::Desc);
|
||||||
|
|
||||||
|
if let Some(uid) = uid {
|
||||||
|
select = select
|
||||||
|
.filter(crate::model::like::Column::Actor.eq(uid))
|
||||||
|
.join(sea_orm::JoinType::LeftJoin, crate::model::object::Relation::Like.def())
|
||||||
|
.select_column_as(crate::model::like::Column::Actor, format!("{}{}", crate::model::like::Entity.table_name(), crate::model::like::Column::Actor.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
for col in crate::model::object::Column::iter() {
|
for col in crate::model::object::Column::iter() {
|
||||||
select = select.select_column_as(col, format!("{}{}", crate::model::object::Entity.table_name(), col.to_string()));
|
select = select.select_column_as(col, format!("{}{}", crate::model::object::Entity.table_name(), col.to_string()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,8 +76,8 @@ impl BatchFillable for &[Event] {
|
||||||
.filter_map(|x| match x {
|
.filter_map(|x| match x {
|
||||||
Event::Tombstone => None,
|
Event::Tombstone => None,
|
||||||
Event::Activity(_) => None,
|
Event::Activity(_) => None,
|
||||||
Event::StrayObject(x) => Some(x.clone()),
|
Event::StrayObject { object, liked: _ } => Some(object.clone()),
|
||||||
Event::DeepActivity { activity: _, object } => Some(object.clone()),
|
Event::DeepActivity { activity: _, liked: _, object } => Some(object.clone()),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub async fn view(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let row = model::addressing::Entity::find_addressed()
|
let row = model::addressing::Entity::find_addressed(auth.my_id())
|
||||||
.filter(model::activity::Column::Id.eq(&aid))
|
.filter(model::activity::Column::Id.eq(&aid))
|
||||||
.filter(auth.filter_condition())
|
.filter(auth.filter_condition())
|
||||||
.into_model::<Event>()
|
.into_model::<Event>()
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub async fn get(
|
||||||
let local_context_id = url!(ctx, "/context/{id}");
|
let local_context_id = url!(ctx, "/context/{id}");
|
||||||
let context = ctx.uri("context", id);
|
let context = ctx.uri("context", id);
|
||||||
|
|
||||||
let count = model::addressing::Entity::find_addressed()
|
let count = model::addressing::Entity::find_addressed(auth.my_id())
|
||||||
.filter(auth.filter_condition())
|
.filter(auth.filter_condition())
|
||||||
.filter(model::object::Column::Context.eq(context))
|
.filter(model::object::Column::Context.eq(context))
|
||||||
.count(ctx.db())
|
.count(ctx.db())
|
||||||
|
@ -40,7 +40,8 @@ pub async fn page(
|
||||||
.add(auth.filter_condition())
|
.add(auth.filter_condition())
|
||||||
.add(model::object::Column::Context.eq(context)),
|
.add(model::object::Column::Context.eq(context)),
|
||||||
ctx.db(),
|
ctx.db(),
|
||||||
page
|
page,
|
||||||
|
auth.my_id(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ pub async fn page(
|
||||||
url!(ctx, "/inbox/page"),
|
url!(ctx, "/inbox/page"),
|
||||||
auth.filter_condition(),
|
auth.filter_condition(),
|
||||||
ctx.db(),
|
ctx.db(),
|
||||||
page
|
page,
|
||||||
|
auth.my_id(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use apb::ObjectMut;
|
||||||
use axum::extract::{Path, Query, State};
|
use axum::extract::{Path, Query, State};
|
||||||
use sea_orm::{ColumnTrait, ModelTrait, QueryFilter};
|
use sea_orm::{ColumnTrait, ModelTrait, QueryFilter};
|
||||||
|
|
||||||
use crate::{errors::UpubError, model::{self, addressing::Event}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}};
|
use crate::{errors::UpubError, model::{self, addressing::Event}, server::{auth::{AuthIdentity, Identity}, fetcher::Fetcher, Context}};
|
||||||
|
|
||||||
use super::{jsonld::LD, JsonLD, TryFetch};
|
use super::{jsonld::LD, JsonLD, TryFetch};
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ pub async fn view(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = model::addressing::Entity::find_addressed()
|
let item = model::addressing::Entity::find_addressed(auth.my_id())
|
||||||
.filter(model::object::Column::Id.eq(&oid))
|
.filter(model::object::Column::Id.eq(&oid))
|
||||||
.filter(auth.filter_condition())
|
.filter(auth.filter_condition())
|
||||||
.into_model::<Event>()
|
.into_model::<Event>()
|
||||||
|
@ -38,8 +38,8 @@ pub async fn view(
|
||||||
let object = match item {
|
let object = match item {
|
||||||
Event::Tombstone => return Err(UpubError::not_found()),
|
Event::Tombstone => return Err(UpubError::not_found()),
|
||||||
Event::Activity(_) => return Err(UpubError::not_found()),
|
Event::Activity(_) => return Err(UpubError::not_found()),
|
||||||
Event::StrayObject(x) => x,
|
Event::StrayObject { object, liked: _ } => object,
|
||||||
Event::DeepActivity { activity: _, object } => object,
|
Event::DeepActivity { activity: _, liked: _, object } => object,
|
||||||
};
|
};
|
||||||
|
|
||||||
let attachments = object.find_related(model::attachment::Entity)
|
let attachments = object.find_related(model::attachment::Entity)
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub async fn get(
|
||||||
let replies_id = url!(ctx, "/objects/{id}/replies");
|
let replies_id = url!(ctx, "/objects/{id}/replies");
|
||||||
let oid = ctx.uri("objects", id);
|
let oid = ctx.uri("objects", id);
|
||||||
|
|
||||||
let count = model::addressing::Entity::find_addressed()
|
let count = model::addressing::Entity::find_addressed(auth.my_id())
|
||||||
.filter(auth.filter_condition())
|
.filter(auth.filter_condition())
|
||||||
.filter(model::object::Column::InReplyTo.eq(oid))
|
.filter(model::object::Column::InReplyTo.eq(oid))
|
||||||
.count(ctx.db())
|
.count(ctx.db())
|
||||||
|
@ -35,7 +35,8 @@ pub async fn page(
|
||||||
.add(auth.filter_condition())
|
.add(auth.filter_condition())
|
||||||
.add(model::object::Column::InReplyTo.eq(oid)),
|
.add(model::object::Column::InReplyTo.eq(oid)),
|
||||||
ctx.db(),
|
ctx.db(),
|
||||||
page
|
page,
|
||||||
|
auth.my_id(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub async fn page(
|
||||||
auth.filter_condition(), // TODO filter local only stuff
|
auth.filter_condition(), // TODO filter local only stuff
|
||||||
ctx.db(),
|
ctx.db(),
|
||||||
page,
|
page,
|
||||||
|
auth.my_id(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ pub async fn page(
|
||||||
.add(model::activity::Column::Actor.eq(uid)),
|
.add(model::activity::Column::Actor.eq(uid)),
|
||||||
ctx.db(),
|
ctx.db(),
|
||||||
page,
|
page,
|
||||||
|
auth.my_id(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub mod following;
|
||||||
use axum::extract::{Path, Query, State};
|
use axum::extract::{Path, Query, State};
|
||||||
use sea_orm::EntityTrait;
|
use sea_orm::EntityTrait;
|
||||||
|
|
||||||
use apb::{ActorMut, BaseMut, CollectionMut, Node, ObjectMut};
|
use apb::{ActorMut, Node, ObjectMut};
|
||||||
use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url};
|
use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url};
|
||||||
|
|
||||||
use super::{jsonld::LD, JsonLD, TryFetch};
|
use super::{jsonld::LD, JsonLD, TryFetch};
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub async fn page(
|
||||||
),
|
),
|
||||||
ctx.db(),
|
ctx.db(),
|
||||||
page,
|
page,
|
||||||
|
auth.my_id(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub async fn statuses(
|
||||||
Query(_query): Query<StatusesQuery>,
|
Query(_query): Query<StatusesQuery>,
|
||||||
) -> Result<Json<Vec<Status>>, StatusCode> {
|
) -> Result<Json<Vec<Status>>, StatusCode> {
|
||||||
let uid = ctx.uid(id);
|
let uid = ctx.uid(id);
|
||||||
model::addressing::Entity::find_addressed()
|
model::addressing::Entity::find_addressed(auth.my_id())
|
||||||
.filter(model::activity::Column::Actor.eq(uid))
|
.filter(model::activity::Column::Actor.eq(uid))
|
||||||
.filter(auth.filter_condition());
|
.filter(auth.filter_condition());
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,13 @@ impl Identity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn my_id(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
Identity::Local(x) => Some(x.as_str()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_anon(&self) -> bool {
|
pub fn is_anon(&self) -> bool {
|
||||||
matches!(self, Self::Anonymous)
|
matches!(self, Self::Anonymous)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,12 @@ pub async fn paginate(
|
||||||
filter: Condition,
|
filter: Condition,
|
||||||
db: &DatabaseConnection,
|
db: &DatabaseConnection,
|
||||||
page: Pagination,
|
page: Pagination,
|
||||||
|
my_id: Option<&str>,
|
||||||
) -> crate::Result<JsonLD<serde_json::Value>> {
|
) -> crate::Result<JsonLD<serde_json::Value>> {
|
||||||
let limit = page.batch.unwrap_or(20).min(50);
|
let limit = page.batch.unwrap_or(20).min(50);
|
||||||
let offset = page.offset.unwrap_or(0);
|
let offset = page.offset.unwrap_or(0);
|
||||||
|
|
||||||
let items = crate::model::addressing::Entity::find_addressed()
|
let items = crate::model::addressing::Entity::find_addressed(my_id)
|
||||||
.filter(filter)
|
.filter(filter)
|
||||||
// TODO also limit to only local activities
|
// TODO also limit to only local activities
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
|
|
Loading…
Reference in a new issue