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 crate::routes::activitypub::jsonld::LD;
@ -65,11 +65,15 @@ impl ActiveModelBehavior for ActiveModel {}
#[derive(Debug, Clone)]
pub enum Event {
Tombstone,
StrayObject(crate::model::object::Model),
Activity(crate::model::activity::Model),
StrayObject {
object: crate::model::object::Model,
liked: Option<String>,
},
DeepActivity {
activity: crate::model::activity::Model,
object: crate::model::object::Model,
liked: Option<String>,
}
}
@ -78,9 +82,9 @@ impl Event {
pub fn id(&self) -> &str {
match self {
Event::Tombstone => "",
Event::StrayObject(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 {
Event::Activity(x) => x.ap(),
Event::DeepActivity { activity, object } =>
activity.ap().set_object(apb::Node::object(object.ap().set_attachment(attachment))),
Event::StrayObject(x) => serde_json::Value::new_object()
Event::DeepActivity { activity, object, liked } =>
activity.ap().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::StrayObject { object, liked } => serde_json::Value::new_object()
.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()
.set_activity_type(Some(apb::ActivityType::Activity))
.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> {
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 liked = res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok();
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)),
(None, Some(object)) => Ok(Self::StrayObject(object)),
(None, Some(object)) => Ok(Self::StrayObject { object, liked }),
(None, None) => Ok(Self::Tombstone),
}
}
@ -123,7 +154,7 @@ impl FromQueryResult for Event {
impl Entity {
pub fn find_addressed() -> Select<Entity> {
pub fn find_addressed(uid: Option<&str>) -> Select<Entity> {
let mut select = Entity::find()
.distinct()
.select_only()
@ -137,6 +168,13 @@ impl Entity {
)
.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() {
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 {
Event::Tombstone => None,
Event::Activity(_) => None,
Event::StrayObject(x) => Some(x.clone()),
Event::DeepActivity { activity: _, object } => Some(object.clone()),
Event::StrayObject { object, liked: _ } => Some(object.clone()),
Event::DeepActivity { activity: _, liked: _, object } => Some(object.clone()),
})
.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(auth.filter_condition())
.into_model::<Event>()

View file

@ -11,7 +11,7 @@ pub async fn get(
let local_context_id = url!(ctx, "/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(model::object::Column::Context.eq(context))
.count(ctx.db())
@ -40,7 +40,8 @@ pub async fn page(
.add(auth.filter_condition())
.add(model::object::Column::Context.eq(context)),
ctx.db(),
page
page,
auth.my_id(),
)
.await
}

View file

@ -21,7 +21,8 @@ pub async fn page(
url!(ctx, "/inbox/page"),
auth.filter_condition(),
ctx.db(),
page
page,
auth.my_id(),
)
.await
}

View file

@ -4,7 +4,7 @@ use apb::ObjectMut;
use axum::extract::{Path, Query, State};
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};
@ -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(auth.filter_condition())
.into_model::<Event>()
@ -38,8 +38,8 @@ pub async fn view(
let object = match item {
Event::Tombstone => return Err(UpubError::not_found()),
Event::Activity(_) => return Err(UpubError::not_found()),
Event::StrayObject(x) => x,
Event::DeepActivity { activity: _, object } => object,
Event::StrayObject { object, liked: _ } => object,
Event::DeepActivity { activity: _, liked: _, object } => object,
};
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 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(model::object::Column::InReplyTo.eq(oid))
.count(ctx.db())
@ -35,7 +35,8 @@ pub async fn page(
.add(auth.filter_condition())
.add(model::object::Column::InReplyTo.eq(oid)),
ctx.db(),
page
page,
auth.my_id(),
)
.await
}

View file

@ -16,6 +16,7 @@ pub async fn page(
auth.filter_condition(), // TODO filter local only stuff
ctx.db(),
page,
auth.my_id(),
)
.await
}

View file

@ -41,6 +41,7 @@ pub async fn page(
.add(model::activity::Column::Actor.eq(uid)),
ctx.db(),
page,
auth.my_id(),
)
.await
}

View file

@ -7,7 +7,7 @@ pub mod following;
use axum::extract::{Path, Query, State};
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 super::{jsonld::LD, JsonLD, TryFetch};

View file

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

View file

@ -70,7 +70,7 @@ pub async fn statuses(
Query(_query): Query<StatusesQuery>,
) -> Result<Json<Vec<Status>>, StatusCode> {
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(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 {
matches!(self, Self::Anonymous)
}

View file

@ -8,11 +8,12 @@ pub async fn paginate(
filter: Condition,
db: &DatabaseConnection,
page: Pagination,
my_id: Option<&str>,
) -> crate::Result<JsonLD<serde_json::Value>> {
let limit = page.batch.unwrap_or(20).min(50);
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)
// TODO also limit to only local activities
.limit(limit)