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:
əlemi 2024-04-30 01:50:25 +02:00
parent 63ba54dad9
commit 45d16fa0a3
Signed by: alemi
GPG key ID: A4895B84D311642C
14 changed files with 78 additions and 26 deletions

View file

@ -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()));
} }

View file

@ -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();

View file

@ -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>()

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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)

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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};

View file

@ -33,6 +33,7 @@ pub async fn page(
), ),
ctx.db(), ctx.db(),
page, page,
auth.my_id(),
) )
.await .await
} }

View file

@ -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());

View file

@ -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)
} }

View file

@ -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)