feat: track activity of likes, track like content
aka emoji reactions 👀👀👀👀👀👀👀👀
This commit is contained in:
parent
d6756ef44d
commit
22149f7bc7
7 changed files with 202 additions and 22 deletions
|
@ -33,6 +33,10 @@ pub enum Relation {
|
||||||
Actors,
|
Actors,
|
||||||
#[sea_orm(has_many = "super::addressing::Entity")]
|
#[sea_orm(has_many = "super::addressing::Entity")]
|
||||||
Addressing,
|
Addressing,
|
||||||
|
#[sea_orm(has_many = "super::announce::Entity")]
|
||||||
|
Announces,
|
||||||
|
#[sea_orm(has_many = "super::like::Entity")]
|
||||||
|
Likes,
|
||||||
#[sea_orm(has_many = "super::notification::Entity")]
|
#[sea_orm(has_many = "super::notification::Entity")]
|
||||||
Notifications,
|
Notifications,
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
|
@ -57,6 +61,18 @@ impl Related<super::addressing::Entity> for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::announce::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Announces.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::like::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Likes.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::notification::Entity> for Entity {
|
impl Related<super::notification::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Notifications.def()
|
Relation::Notifications.def()
|
||||||
|
|
|
@ -8,10 +8,19 @@ pub struct Model {
|
||||||
pub actor: i64,
|
pub actor: i64,
|
||||||
pub object: i64,
|
pub object: i64,
|
||||||
pub published: ChronoDateTimeUtc,
|
pub published: ChronoDateTimeUtc,
|
||||||
|
pub activity: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::activity::Entity",
|
||||||
|
from = "Column::Activity",
|
||||||
|
to = "super::activity::Column::Internal",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
Activities,
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
belongs_to = "super::actor::Entity",
|
belongs_to = "super::actor::Entity",
|
||||||
from = "Column::Actor",
|
from = "Column::Actor",
|
||||||
|
@ -30,6 +39,12 @@ pub enum Relation {
|
||||||
Objects,
|
Objects,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::activity::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Activities.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::actor::Entity> for Entity {
|
impl Related<super::actor::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Actors.def()
|
Relation::Actors.def()
|
||||||
|
|
|
@ -8,10 +8,20 @@ pub struct Model {
|
||||||
pub actor: i64,
|
pub actor: i64,
|
||||||
pub object: i64,
|
pub object: i64,
|
||||||
pub published: ChronoDateTimeUtc,
|
pub published: ChronoDateTimeUtc,
|
||||||
|
pub activity: Option<i64>,
|
||||||
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::activity::Entity",
|
||||||
|
from = "Column::Activity",
|
||||||
|
to = "super::activity::Column::Internal",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
Activities,
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
belongs_to = "super::actor::Entity",
|
belongs_to = "super::actor::Entity",
|
||||||
from = "Column::Actor",
|
from = "Column::Actor",
|
||||||
|
@ -30,6 +40,12 @@ pub enum Relation {
|
||||||
Objects,
|
Objects,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::activity::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Activities.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::actor::Entity> for Entity {
|
impl Related<super::actor::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Actors.def()
|
Relation::Actors.def()
|
||||||
|
|
|
@ -106,11 +106,26 @@ pub async fn like(ctx: &crate::Context, activity: impl apb::Activity, tx: &Datab
|
||||||
return Err(ProcessorError::AlreadyProcessed);
|
return Err(ProcessorError::AlreadyProcessed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let published = activity.published().unwrap_or_else(|_|chrono::Utc::now());
|
||||||
|
let content = activity.content().unwrap_or_default();
|
||||||
|
|
||||||
|
// likes without addressing are "silent likes", process them but dont store activity or notify
|
||||||
|
let aid = if likes_local_object || !activity.addressed().is_empty() {
|
||||||
|
let mut activity_model = ctx.insert_activity(activity, tx).await?;
|
||||||
|
if likes_local_object {
|
||||||
|
activity_model.to.0.push(obj.attributed_to.clone().unwrap_or_default());
|
||||||
|
}
|
||||||
|
ctx.address(Some(&activity_model), None, tx).await?;
|
||||||
|
Some(activity_model.internal)
|
||||||
|
} else { None };
|
||||||
|
|
||||||
let like = crate::model::like::ActiveModel {
|
let like = crate::model::like::ActiveModel {
|
||||||
internal: NotSet,
|
internal: NotSet,
|
||||||
actor: Set(actor.internal),
|
actor: Set(actor.internal),
|
||||||
object: Set(obj.internal),
|
object: Set(obj.internal),
|
||||||
published: Set(activity.published().unwrap_or_else(|_|chrono::Utc::now())),
|
published: Set(published),
|
||||||
|
activity: Set(aid),
|
||||||
|
content: Set(content),
|
||||||
};
|
};
|
||||||
|
|
||||||
crate::model::like::Entity::insert(like).exec(tx).await?;
|
crate::model::like::Entity::insert(like).exec(tx).await?;
|
||||||
|
@ -120,19 +135,12 @@ pub async fn like(ctx: &crate::Context, activity: impl apb::Activity, tx: &Datab
|
||||||
.exec(tx)
|
.exec(tx)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// likes without addressing are "silent likes", process them but dont store activity or notify
|
if let Some(aid) = aid {
|
||||||
if likes_local_object || !activity.addressed().is_empty() {
|
|
||||||
let mut activity_model = ctx.insert_activity(activity, tx).await?;
|
|
||||||
if likes_local_object {
|
|
||||||
activity_model.to.0.push(obj.attributed_to.clone().unwrap_or_default());
|
|
||||||
}
|
|
||||||
ctx.address(Some(&activity_model), None, tx).await?;
|
|
||||||
|
|
||||||
// TODO check that object author is in this like addressing!!! otherwise skip notification
|
// TODO check that object author is in this like addressing!!! otherwise skip notification
|
||||||
if let Some(ref attributed_to) = obj.attributed_to {
|
if let Some(ref attributed_to) = obj.attributed_to {
|
||||||
if ctx.is_local(attributed_to) {
|
if ctx.is_local(attributed_to) {
|
||||||
if let Some(actor_internal) = crate::model::actor::Entity::ap_to_internal(attributed_to, tx).await? {
|
if let Some(actor_internal) = crate::model::actor::Entity::ap_to_internal(attributed_to, tx).await? {
|
||||||
crate::Query::notify(activity_model.internal, actor_internal)
|
crate::Query::notify(aid, actor_internal)
|
||||||
.exec(tx)
|
.exec(tx)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
@ -534,6 +542,19 @@ pub async fn announce(ctx: &crate::Context, activity: impl apb::Activity, tx: &D
|
||||||
|
|
||||||
let actor = ctx.fetch_user(&activity.actor().id()?, tx).await?;
|
let actor = ctx.fetch_user(&activity.actor().id()?, tx).await?;
|
||||||
|
|
||||||
|
let published = activity.published().unwrap_or(chrono::Utc::now());
|
||||||
|
|
||||||
|
// TODO we should probably insert an activity, otherwise this won't appear on timelines!!
|
||||||
|
// or maybe go update all addressing records for this object, pushing them up
|
||||||
|
// or maybe create new addressing rows with more recent dates
|
||||||
|
// or maybe create fake objects that reference the original one
|
||||||
|
// idk!!!!
|
||||||
|
let aid = if actor.actor_type == apb::ActorType::Person || ctx.is_local(&actor.id) {
|
||||||
|
let activity_model = ctx.insert_activity(activity, tx).await?;
|
||||||
|
ctx.address(Some(&activity_model), None, tx).await?;
|
||||||
|
Some(activity_model.internal)
|
||||||
|
} else { None };
|
||||||
|
|
||||||
// we only care about announces produced by "Person" actors, because there's intention
|
// we only care about announces produced by "Person" actors, because there's intention
|
||||||
// anything shared by groups, services or applications is automated: fetch it and be done
|
// anything shared by groups, services or applications is automated: fetch it and be done
|
||||||
if actor.actor_type == apb::ActorType::Person {
|
if actor.actor_type == apb::ActorType::Person {
|
||||||
|
@ -553,26 +574,19 @@ pub async fn announce(ctx: &crate::Context, activity: impl apb::Activity, tx: &D
|
||||||
internal: NotSet,
|
internal: NotSet,
|
||||||
actor: Set(actor.internal),
|
actor: Set(actor.internal),
|
||||||
object: Set(object.internal),
|
object: Set(object.internal),
|
||||||
published: Set(activity.published().unwrap_or(chrono::Utc::now())),
|
published: Set(published),
|
||||||
|
activity: Set(aid),
|
||||||
};
|
};
|
||||||
|
|
||||||
crate::model::announce::Entity::insert(share)
|
crate::model::announce::Entity::insert(share)
|
||||||
.exec(tx).await?;
|
.exec(tx).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO we should probably insert an activity, otherwise this won't appear on timelines!!
|
if let Some(aid) = aid {
|
||||||
// or maybe go update all addressing records for this object, pushing them up
|
|
||||||
// or maybe create new addressing rows with more recent dates
|
|
||||||
// or maybe create fake objects that reference the original one
|
|
||||||
// idk!!!!
|
|
||||||
if actor.actor_type == apb::ActorType::Person || ctx.is_local(&actor.id) {
|
|
||||||
let activity_model = ctx.insert_activity(activity, tx).await?;
|
|
||||||
ctx.address(Some(&activity_model), None, tx).await?;
|
|
||||||
|
|
||||||
if let Some(ref attributed_to) = object.attributed_to {
|
if let Some(ref attributed_to) = object.attributed_to {
|
||||||
if ctx.is_local(attributed_to) {
|
if ctx.is_local(attributed_to) {
|
||||||
if let Some(actor_internal) = crate::model::actor::Entity::ap_to_internal(attributed_to, tx).await? {
|
if let Some(actor_internal) = crate::model::actor::Entity::ap_to_internal(attributed_to, tx).await? {
|
||||||
crate::Query::notify(activity_model.internal, actor_internal)
|
crate::Query::notify(aid, actor_internal)
|
||||||
.exec(tx)
|
.exec(tx)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ mod m20240715_000001_add_quote_uri_to_objects;
|
||||||
mod m20240715_000002_add_actors_fields_and_aliases;
|
mod m20240715_000002_add_actors_fields_and_aliases;
|
||||||
mod m20240811_000001_add_full_text_index;
|
mod m20240811_000001_add_full_text_index;
|
||||||
mod m20241226_000001_add_show_likes_collection;
|
mod m20241226_000001_add_show_likes_collection;
|
||||||
|
mod m20241226_000002_add_like_activities;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@ impl MigratorTrait for Migrator {
|
||||||
Box::new(m20240715_000002_add_actors_fields_and_aliases::Migration),
|
Box::new(m20240715_000002_add_actors_fields_and_aliases::Migration),
|
||||||
Box::new(m20240811_000001_add_full_text_index::Migration),
|
Box::new(m20240811_000001_add_full_text_index::Migration),
|
||||||
Box::new(m20241226_000001_add_show_likes_collection::Migration),
|
Box::new(m20241226_000001_add_show_likes_collection::Migration),
|
||||||
|
Box::new(m20241226_000002_add_like_activities::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ pub enum Likes {
|
||||||
Internal,
|
Internal,
|
||||||
Actor,
|
Actor,
|
||||||
Object,
|
Object,
|
||||||
Activity, // DROPPED
|
Activity, // DROPPED and ADDED BACK with migration m20241226_000002
|
||||||
|
Content, // ADDED with migration m20241226_000002
|
||||||
Published,
|
Published,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ pub enum Announces {
|
||||||
Internal,
|
Internal,
|
||||||
Actor,
|
Actor,
|
||||||
Object,
|
Object,
|
||||||
|
Activity, // ADDED with migration m20241226_000002
|
||||||
Published,
|
Published,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
115
upub/migrations/src/m20241226_000002_add_like_activities.rs
Normal file
115
upub/migrations/src/m20241226_000002_add_like_activities.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
use crate::{m20240524_000001_create_actor_activity_object_tables::Activities, m20240524_000002_create_relations_likes_shares::{Announces, Likes}};
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Likes::Table)
|
||||||
|
.add_column(ColumnDef::new(Likes::Content).string().not_null().default(""))
|
||||||
|
.add_column(ColumnDef::new(Likes::Activity).big_integer().null())
|
||||||
|
.add_foreign_key(
|
||||||
|
TableForeignKey::new()
|
||||||
|
.name("fkey-likes-activity")
|
||||||
|
.from_tbl(Likes::Table)
|
||||||
|
.from_col(Likes::Activity)
|
||||||
|
.to_tbl(Activities::Table)
|
||||||
|
.to_col(Activities::Internal)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
)
|
||||||
|
.to_owned()
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.drop_index(
|
||||||
|
Index::drop()
|
||||||
|
.name("index-likes-actor-object")
|
||||||
|
.table(Likes::Table)
|
||||||
|
.to_owned()
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_index(
|
||||||
|
Index::create()
|
||||||
|
.unique()
|
||||||
|
.name("index-likes-actor-object-content")
|
||||||
|
.table(Likes::Table)
|
||||||
|
.col(Likes::Actor)
|
||||||
|
.col(Likes::Object)
|
||||||
|
.col(Likes::Content)
|
||||||
|
.to_owned()
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Announces::Table)
|
||||||
|
.add_column(ColumnDef::new(Announces::Activity).big_integer().null())
|
||||||
|
.add_foreign_key(
|
||||||
|
TableForeignKey::new()
|
||||||
|
.name("fkey-announces-activity")
|
||||||
|
.from_tbl(Announces::Table)
|
||||||
|
.from_col(Announces::Activity)
|
||||||
|
.to_tbl(Activities::Table)
|
||||||
|
.to_col(Activities::Internal)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
)
|
||||||
|
.to_owned()
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Likes::Table)
|
||||||
|
.drop_column(Likes::Activity)
|
||||||
|
.to_owned()
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.drop_index(
|
||||||
|
Index::drop()
|
||||||
|
.name("index-likes-actor-object-content")
|
||||||
|
.table(Likes::Table)
|
||||||
|
.to_owned()
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_index(
|
||||||
|
Index::create()
|
||||||
|
.unique()
|
||||||
|
.name("index-likes-actor-object")
|
||||||
|
.table(Likes::Table)
|
||||||
|
.col(Likes::Actor)
|
||||||
|
.col(Likes::Object)
|
||||||
|
.to_owned()
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Announces::Table)
|
||||||
|
.drop_column(Announces::Activity)
|
||||||
|
.to_owned()
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue