Merge branch 'development'
ci/woodpecker/push/ociImagePush Pipeline was successful
Details
ci/woodpecker/push/ociImagePush Pipeline was successful
Details
This commit is contained in:
commit
a34829c63d
|
@ -1460,6 +1460,7 @@ dependencies = [
|
||||||
"hyper",
|
"hyper",
|
||||||
"idna",
|
"idna",
|
||||||
"itertools 0.11.0",
|
"itertools 0.11.0",
|
||||||
|
"lazy_static",
|
||||||
"lru",
|
"lru",
|
||||||
"magnetar_calckey_model",
|
"magnetar_calckey_model",
|
||||||
"magnetar_common",
|
"magnetar_common",
|
||||||
|
@ -1517,6 +1518,7 @@ dependencies = [
|
||||||
"ext_calckey_model_migration",
|
"ext_calckey_model_migration",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"lazy_static",
|
||||||
"magnetar_common",
|
"magnetar_common",
|
||||||
"magnetar_sdk",
|
"magnetar_sdk",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -39,6 +39,7 @@ http = "0.2"
|
||||||
hyper = "0.14"
|
hyper = "0.14"
|
||||||
idna = "0.4"
|
idna = "0.4"
|
||||||
itertools = "0.11"
|
itertools = "0.11"
|
||||||
|
lazy_static = "1.4"
|
||||||
lru = "0.12"
|
lru = "0.12"
|
||||||
miette = "5.9"
|
miette = "5.9"
|
||||||
nom = "7"
|
nom = "7"
|
||||||
|
@ -90,20 +91,21 @@ tower-http = { workspace = true, features = ["cors", "trace", "fs"] }
|
||||||
|
|
||||||
idna = { workspace = true }
|
idna = { workspace = true }
|
||||||
|
|
||||||
|
regex = { workspace = true }
|
||||||
|
|
||||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
cfg-if = { workspace = true }
|
cfg-if = { workspace = true }
|
||||||
|
|
||||||
itertools = { workspace = true }
|
|
||||||
|
|
||||||
compact_str = { workspace = true }
|
compact_str = { workspace = true }
|
||||||
either = { workspace = true }
|
either = { workspace = true }
|
||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
|
itertools = { workspace = true }
|
||||||
|
lazy_static = { workspace = true }
|
||||||
|
miette = { workspace = true, features = ["fancy"] }
|
||||||
strum = { workspace = true, features = ["derive"] }
|
strum = { workspace = true, features = ["derive"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
miette = { workspace = true, features = ["fancy"] }
|
|
||||||
regex = { workspace = true }
|
|
||||||
|
|
||||||
percent-encoding = { workspace = true }
|
percent-encoding = { workspace = true }
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ magnetar_sdk = { path = "../magnetar_sdk" }
|
||||||
dotenvy = { workspace = true}
|
dotenvy = { workspace = true}
|
||||||
futures-core = { workspace = true }
|
futures-core = { workspace = true }
|
||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
|
lazy_static = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
tokio-util = { workspace = true}
|
tokio-util = { workspace = true}
|
||||||
redis = { workspace = true, features = ["tokio-comp", "json", "serde_json"]}
|
redis = { workspace = true, features = ["tokio-comp", "json", "serde_json"]}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod emoji;
|
pub mod emoji;
|
||||||
pub mod note_model;
|
pub mod note_model;
|
||||||
|
pub mod poll;
|
||||||
|
|
||||||
pub use ck;
|
pub use ck;
|
||||||
use ck::*;
|
use ck::*;
|
||||||
|
@ -13,8 +14,8 @@ use redis::IntoConnectionInfo;
|
||||||
use sea_orm::sea_query::IntoIden;
|
use sea_orm::sea_query::IntoIden;
|
||||||
use sea_orm::ActiveValue::Set;
|
use sea_orm::ActiveValue::Set;
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
ColumnTrait, ConnectOptions, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QueryOrder,
|
ColumnTrait, ConnectOptions, DatabaseConnection, DbErr, EntityTrait, QueryFilter, RelationDef,
|
||||||
RelationDef, RelationTrait, TransactionTrait,
|
RelationTrait, TransactionTrait,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
|
@ -1,19 +1,52 @@
|
||||||
use sea_orm::sea_query::{Alias, Expr, IntoIden, SelectExpr, SimpleExpr};
|
use lazy_static::lazy_static;
|
||||||
|
use sea_orm::sea_query::{Alias, Asterisk, Expr, IntoIden, Query, SelectExpr, SimpleExpr};
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
ColumnTrait, DbErr, EntityTrait, FromQueryResult, Iden, Iterable, JoinType, QueryFilter,
|
ColumnTrait, DbErr, EntityName, EntityTrait, FromQueryResult, Iden, Iterable, JoinType,
|
||||||
QueryResult, QuerySelect, QueryTrait, RelationTrait, Select,
|
QueryFilter, QueryResult, QuerySelect, QueryTrait, RelationTrait, Select,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
use ck::{drive_file, note, user};
|
use ck::{drive_file, note, note_reaction, user};
|
||||||
use magnetar_sdk::types::RangeFilter;
|
use magnetar_sdk::types::RangeFilter;
|
||||||
use once_cell::unsync::Lazy;
|
|
||||||
|
|
||||||
use crate::{AliasSourceExt, CalckeyDbError, CalckeyModel};
|
use crate::{AliasSourceExt, CalckeyDbError, CalckeyModel};
|
||||||
|
|
||||||
|
pub mod sub_interaction_renote {
|
||||||
|
use sea_orm::{DeriveColumn, EnumIter, FromQueryResult};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)]
|
||||||
|
pub struct Model {
|
||||||
|
pub renotes: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Renotes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod sub_interaction_reaction {
|
||||||
|
use sea_orm::{DeriveColumn, EnumIter, FromQueryResult};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)]
|
||||||
|
pub struct Model {
|
||||||
|
pub reaction_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
ReactionName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct NoteData {
|
pub struct NoteData {
|
||||||
pub note: note::Model,
|
pub note: note::Model,
|
||||||
|
pub interaction_user_renote: Option<sub_interaction_renote::Model>,
|
||||||
|
pub interaction_user_reaction: Option<sub_interaction_reaction::Model>,
|
||||||
pub user: user::Model,
|
pub user: user::Model,
|
||||||
pub avatar: Option<drive_file::Model>,
|
pub avatar: Option<drive_file::Model>,
|
||||||
pub banner: Option<drive_file::Model>,
|
pub banner: Option<drive_file::Model>,
|
||||||
|
@ -21,14 +54,20 @@ pub struct NoteData {
|
||||||
pub renote: Option<Box<NoteData>>,
|
pub renote: Option<Box<NoteData>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const INTERACTION_REACTION: &str = "interaction.reaction.";
|
||||||
|
const INTERACTION_RENOTE: &str = "interaction.renote.";
|
||||||
const USER: &str = "user.";
|
const USER: &str = "user.";
|
||||||
const USER_AVATAR: &str = "user.avatar.";
|
const USER_AVATAR: &str = "user.avatar.";
|
||||||
const USER_BANNER: &str = "user.banner.";
|
const USER_BANNER: &str = "user.banner.";
|
||||||
const REPLY: &str = "reply.";
|
const REPLY: &str = "reply.";
|
||||||
|
const REPLY_INTERACTION_REACTION: &str = "reply.interaction.reaction.";
|
||||||
|
const REPLY_INTERACTION_RENOTE: &str = "reply.interaction.renote.";
|
||||||
const REPLY_USER: &str = "reply.user.";
|
const REPLY_USER: &str = "reply.user.";
|
||||||
const REPLY_USER_AVATAR: &str = "reply.user.avatar.";
|
const REPLY_USER_AVATAR: &str = "reply.user.avatar.";
|
||||||
const REPLY_USER_BANNER: &str = "reply.user.banner.";
|
const REPLY_USER_BANNER: &str = "reply.user.banner.";
|
||||||
const RENOTE: &str = "renote.";
|
const RENOTE: &str = "renote.";
|
||||||
|
const RENOTE_INTERACTION_REACTION: &str = "renote.interaction.reaction.";
|
||||||
|
const RENOTE_INTERACTION_RENOTE: &str = "renote.interaction.renote.";
|
||||||
const RENOTE_USER: &str = "renote.user.";
|
const RENOTE_USER: &str = "renote.user.";
|
||||||
const RENOTE_USER_AVATAR: &str = "renote.user.avatar.";
|
const RENOTE_USER_AVATAR: &str = "renote.user.avatar.";
|
||||||
const RENOTE_USER_BANNER: &str = "renote.user.banner.";
|
const RENOTE_USER_BANNER: &str = "renote.user.banner.";
|
||||||
|
@ -39,6 +78,16 @@ impl FromQueryResult for NoteData {
|
||||||
.map::<Result<_, DbErr>, _>(|r| {
|
.map::<Result<_, DbErr>, _>(|r| {
|
||||||
Ok(Box::new(NoteData {
|
Ok(Box::new(NoteData {
|
||||||
note: r,
|
note: r,
|
||||||
|
interaction_user_renote:
|
||||||
|
sub_interaction_renote::Model::from_query_result_optional(
|
||||||
|
res,
|
||||||
|
REPLY_INTERACTION_RENOTE,
|
||||||
|
)?,
|
||||||
|
interaction_user_reaction:
|
||||||
|
sub_interaction_reaction::Model::from_query_result_optional(
|
||||||
|
res,
|
||||||
|
REPLY_INTERACTION_REACTION,
|
||||||
|
)?,
|
||||||
user: user::Model::from_query_result(res, REPLY_USER)?,
|
user: user::Model::from_query_result(res, REPLY_USER)?,
|
||||||
avatar: drive_file::Model::from_query_result_optional(res, REPLY_USER_AVATAR)?,
|
avatar: drive_file::Model::from_query_result_optional(res, REPLY_USER_AVATAR)?,
|
||||||
banner: drive_file::Model::from_query_result_optional(res, REPLY_USER_BANNER)?,
|
banner: drive_file::Model::from_query_result_optional(res, REPLY_USER_BANNER)?,
|
||||||
|
@ -52,6 +101,16 @@ impl FromQueryResult for NoteData {
|
||||||
.map::<Result<_, DbErr>, _>(|r| {
|
.map::<Result<_, DbErr>, _>(|r| {
|
||||||
Ok(Box::new(NoteData {
|
Ok(Box::new(NoteData {
|
||||||
note: r,
|
note: r,
|
||||||
|
interaction_user_renote:
|
||||||
|
sub_interaction_renote::Model::from_query_result_optional(
|
||||||
|
res,
|
||||||
|
RENOTE_INTERACTION_RENOTE,
|
||||||
|
)?,
|
||||||
|
interaction_user_reaction:
|
||||||
|
sub_interaction_reaction::Model::from_query_result_optional(
|
||||||
|
res,
|
||||||
|
RENOTE_INTERACTION_REACTION,
|
||||||
|
)?,
|
||||||
user: user::Model::from_query_result(res, RENOTE_USER)?,
|
user: user::Model::from_query_result(res, RENOTE_USER)?,
|
||||||
avatar: drive_file::Model::from_query_result_optional(res, RENOTE_USER_AVATAR)?,
|
avatar: drive_file::Model::from_query_result_optional(res, RENOTE_USER_AVATAR)?,
|
||||||
banner: drive_file::Model::from_query_result_optional(res, RENOTE_USER_BANNER)?,
|
banner: drive_file::Model::from_query_result_optional(res, RENOTE_USER_BANNER)?,
|
||||||
|
@ -63,6 +122,14 @@ impl FromQueryResult for NoteData {
|
||||||
|
|
||||||
Ok(NoteData {
|
Ok(NoteData {
|
||||||
note: note::Model::from_query_result(res, "")?,
|
note: note::Model::from_query_result(res, "")?,
|
||||||
|
interaction_user_renote: sub_interaction_renote::Model::from_query_result_optional(
|
||||||
|
res,
|
||||||
|
INTERACTION_RENOTE,
|
||||||
|
)?,
|
||||||
|
interaction_user_reaction: sub_interaction_reaction::Model::from_query_result_optional(
|
||||||
|
res,
|
||||||
|
INTERACTION_REACTION,
|
||||||
|
)?,
|
||||||
user: user::Model::from_query_result(res, USER)?,
|
user: user::Model::from_query_result(res, USER)?,
|
||||||
avatar: drive_file::Model::from_query_result_optional(res, USER_AVATAR)?,
|
avatar: drive_file::Model::from_query_result_optional(res, USER_AVATAR)?,
|
||||||
banner: drive_file::Model::from_query_result_optional(res, USER_BANNER)?,
|
banner: drive_file::Model::from_query_result_optional(res, USER_BANNER)?,
|
||||||
|
@ -87,10 +154,25 @@ pub struct NoteResolveOptions {
|
||||||
pub with_user: bool,
|
pub with_user: bool,
|
||||||
pub with_reply_target: bool,
|
pub with_reply_target: bool,
|
||||||
pub with_renote_target: bool,
|
pub with_renote_target: bool,
|
||||||
|
pub with_interactions_from: Option<String>, // User ID
|
||||||
}
|
}
|
||||||
|
|
||||||
trait SelectColumnsExt {
|
trait SelectColumnsExt {
|
||||||
fn add_aliased_columns<T: EntityTrait>(self, alias: Option<&str>, entity: T) -> Self;
|
fn add_aliased_columns<T: EntityTrait>(self, alias: Option<&str>, entity: T) -> Self;
|
||||||
|
|
||||||
|
fn add_sub_select_reaction(
|
||||||
|
self,
|
||||||
|
source_note_alias: Option<&str>,
|
||||||
|
alias: &str,
|
||||||
|
user_id: &str,
|
||||||
|
) -> Select<note::Entity>;
|
||||||
|
|
||||||
|
fn add_sub_select_renote(
|
||||||
|
self,
|
||||||
|
source_note_alias: Option<&str>,
|
||||||
|
alias: &str,
|
||||||
|
user_id: &str,
|
||||||
|
) -> Select<note::Entity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectColumnsExt for Select<note::Entity> {
|
impl SelectColumnsExt for Select<note::Entity> {
|
||||||
|
@ -116,19 +198,89 @@ impl SelectColumnsExt for Select<note::Entity> {
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_sub_select_reaction(
|
||||||
|
mut self: Select<note::Entity>,
|
||||||
|
source_note_alias: Option<&str>,
|
||||||
|
prefix_alias: &str,
|
||||||
|
user_id: &str,
|
||||||
|
) -> Select<note::Entity> {
|
||||||
|
let iden = source_note_alias.unwrap_or_else(|| note::Entity.table_name());
|
||||||
|
let note_id_col = Expr::col((Alias::new(iden), note::Column::Id));
|
||||||
|
|
||||||
|
let column = sub_interaction_reaction::Column::ReactionName;
|
||||||
|
let alias = format!("{}{}", prefix_alias, column.to_string());
|
||||||
|
|
||||||
|
let sub_query = Query::select()
|
||||||
|
.expr(SelectExpr {
|
||||||
|
expr: Expr::col(note_reaction::Column::Reaction).into(),
|
||||||
|
alias: Some(Alias::new(alias).into_iden()),
|
||||||
|
window: None,
|
||||||
|
})
|
||||||
|
.from(note_reaction::Entity)
|
||||||
|
.cond_where(Expr::col(note_reaction::Column::NoteId).eq(note_id_col))
|
||||||
|
.and_where(Expr::col(note_reaction::Column::UserId).eq(user_id))
|
||||||
|
.take()
|
||||||
|
.into_sub_query_statement();
|
||||||
|
|
||||||
|
QuerySelect::query(&mut self).expr(SimpleExpr::SubQuery(None, Box::new(sub_query)));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_sub_select_renote(
|
||||||
|
mut self: Select<note::Entity>,
|
||||||
|
source_note_alias: Option<&str>,
|
||||||
|
prefix_alias: &str,
|
||||||
|
user_id: &str,
|
||||||
|
) -> Select<note::Entity> {
|
||||||
|
let iden = source_note_alias.unwrap_or_else(|| note::Entity.table_name());
|
||||||
|
let note_id_col = Expr::col((Alias::new(iden), note::Column::Id));
|
||||||
|
|
||||||
|
let renote_note_tbl = Alias::new(format!("{}{}", prefix_alias, "note"));
|
||||||
|
|
||||||
|
let column = sub_interaction_renote::Column::Renotes;
|
||||||
|
let alias = format!("{}{}", prefix_alias, column.to_string());
|
||||||
|
|
||||||
|
let sub_query = Query::select()
|
||||||
|
.expr(SelectExpr {
|
||||||
|
expr: Expr::count(Expr::col(Asterisk)),
|
||||||
|
alias: Some(Alias::new(alias).into_iden()),
|
||||||
|
window: None,
|
||||||
|
})
|
||||||
|
.from_as(note::Entity, renote_note_tbl.clone())
|
||||||
|
.cond_where(
|
||||||
|
Expr::col((renote_note_tbl.clone(), note::Column::RenoteId)).eq(note_id_col),
|
||||||
|
)
|
||||||
|
.and_where(Expr::col((renote_note_tbl.clone(), note::Column::UserId)).eq(user_id))
|
||||||
|
.take()
|
||||||
|
.into_sub_query_statement();
|
||||||
|
|
||||||
|
QuerySelect::query(&mut self).expr(SimpleExpr::SubQuery(None, Box::new(sub_query)));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALIAS_USER: Lazy<Alias> = Lazy::new(|| Alias::new(USER));
|
lazy_static! {
|
||||||
const ALIAS_USER_AVATAR: Lazy<Alias> = Lazy::new(|| Alias::new(USER_AVATAR));
|
static ref ALIAS_INTERACTION_RENOTE: Alias = Alias::new(INTERACTION_RENOTE);
|
||||||
const ALIAS_USER_BANNER: Lazy<Alias> = Lazy::new(|| Alias::new(USER_BANNER));
|
static ref ALIAS_INTERACTION_REACTION: Alias = Alias::new(INTERACTION_REACTION);
|
||||||
const ALIAS_REPLY: Lazy<Alias> = Lazy::new(|| Alias::new(REPLY));
|
static ref ALIAS_USER: Alias = Alias::new(USER);
|
||||||
const ALIAS_REPLY_USER: Lazy<Alias> = Lazy::new(|| Alias::new(REPLY_USER));
|
static ref ALIAS_USER_AVATAR: Alias = Alias::new(USER_AVATAR);
|
||||||
const ALIAS_REPLY_USER_AVATAR: Lazy<Alias> = Lazy::new(|| Alias::new(REPLY_USER_AVATAR));
|
static ref ALIAS_USER_BANNER: Alias = Alias::new(USER_BANNER);
|
||||||
const ALIAS_REPLY_USER_BANNER: Lazy<Alias> = Lazy::new(|| Alias::new(REPLY_USER_BANNER));
|
static ref ALIAS_REPLY: Alias = Alias::new(REPLY);
|
||||||
const ALIAS_RENOTE: Lazy<Alias> = Lazy::new(|| Alias::new(RENOTE));
|
static ref ALIAS_REPLY_INTERACTION_RENOTE: Alias = Alias::new(REPLY_INTERACTION_RENOTE);
|
||||||
const ALIAS_RENOTE_USER: Lazy<Alias> = Lazy::new(|| Alias::new(RENOTE_USER));
|
static ref ALIAS_REPLY_INTERACTION_REACTION: Alias = Alias::new(REPLY_INTERACTION_REACTION);
|
||||||
const ALIAS_RENOTE_USER_AVATAR: Lazy<Alias> = Lazy::new(|| Alias::new(RENOTE_USER_AVATAR));
|
static ref ALIAS_REPLY_USER: Alias = Alias::new(REPLY_USER);
|
||||||
const ALIAS_RENOTE_USER_BANNER: Lazy<Alias> = Lazy::new(|| Alias::new(RENOTE_USER_BANNER));
|
static ref ALIAS_REPLY_USER_AVATAR: Alias = Alias::new(REPLY_USER_AVATAR);
|
||||||
|
static ref ALIAS_REPLY_USER_BANNER: Alias = Alias::new(REPLY_USER_BANNER);
|
||||||
|
static ref ALIAS_RENOTE: Alias = Alias::new(RENOTE);
|
||||||
|
static ref ALIAS_RENOTE_INTERACTION_RENOTE: Alias = Alias::new(RENOTE_INTERACTION_RENOTE);
|
||||||
|
static ref ALIAS_RENOTE_INTERACTION_REACTION: Alias = Alias::new(RENOTE_INTERACTION_REACTION);
|
||||||
|
static ref ALIAS_RENOTE_USER: Alias = Alias::new(RENOTE_USER);
|
||||||
|
static ref ALIAS_RENOTE_USER_AVATAR: Alias = Alias::new(RENOTE_USER_AVATAR);
|
||||||
|
static ref ALIAS_RENOTE_USER_BANNER: Alias = Alias::new(RENOTE_USER_BANNER);
|
||||||
|
}
|
||||||
|
|
||||||
impl NoteResolver {
|
impl NoteResolver {
|
||||||
pub fn new(db: CalckeyModel) -> Self {
|
pub fn new(db: CalckeyModel) -> Self {
|
||||||
|
@ -142,11 +294,11 @@ impl NoteResolver {
|
||||||
let select = self.resolve(options);
|
let select = self.resolve(options);
|
||||||
let visibility_filter = options.visibility_filter.with_note_and_user_tables(None);
|
let visibility_filter = options.visibility_filter.with_note_and_user_tables(None);
|
||||||
let time_filter = options.time_range.as_ref().map(|f| match f {
|
let time_filter = options.time_range.as_ref().map(|f| match f {
|
||||||
RangeFilter::TimeStart(start) => note::Column::CreatedAt.gte(start.clone()),
|
RangeFilter::TimeStart(start) => note::Column::CreatedAt.gte(*start),
|
||||||
RangeFilter::TimeRange(range) => {
|
RangeFilter::TimeRange(range) => {
|
||||||
note::Column::CreatedAt.between(range.start().clone(), range.end().clone())
|
note::Column::CreatedAt.between(*range.start(), *range.end())
|
||||||
}
|
}
|
||||||
RangeFilter::TimeEnd(end) => note::Column::CreatedAt.lt(end.clone()),
|
RangeFilter::TimeEnd(end) => note::Column::CreatedAt.lt(*end),
|
||||||
});
|
});
|
||||||
|
|
||||||
let id_filter = options.ids.as_ref().map(|ids| {
|
let id_filter = options.ids.as_ref().map(|ids| {
|
||||||
|
@ -164,12 +316,19 @@ impl NoteResolver {
|
||||||
.into_model::<NoteData>()
|
.into_model::<NoteData>()
|
||||||
.one(self.db.inner())
|
.one(self.db.inner())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(notes)
|
Ok(notes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve(&self, options: &NoteResolveOptions) -> Select<note::Entity> {
|
pub fn resolve(&self, options: &NoteResolveOptions) -> Select<note::Entity> {
|
||||||
let mut select = note::Entity::find().add_aliased_columns(Some(USER), user::Entity);
|
let mut select = note::Entity::find().add_aliased_columns(Some(USER), user::Entity);
|
||||||
|
|
||||||
|
if let Some(user_id) = &options.with_interactions_from {
|
||||||
|
select = select
|
||||||
|
.add_sub_select_reaction(None, INTERACTION_REACTION, user_id)
|
||||||
|
.add_sub_select_renote(None, INTERACTION_RENOTE, user_id);
|
||||||
|
}
|
||||||
|
|
||||||
if options.with_user {
|
if options.with_user {
|
||||||
select = select
|
select = select
|
||||||
.add_aliased_columns(Some(USER_AVATAR), drive_file::Entity)
|
.add_aliased_columns(Some(USER_AVATAR), drive_file::Entity)
|
||||||
|
@ -181,6 +340,12 @@ impl NoteResolver {
|
||||||
.add_aliased_columns(Some(REPLY), note::Entity)
|
.add_aliased_columns(Some(REPLY), note::Entity)
|
||||||
.add_aliased_columns(Some(REPLY_USER), user::Entity);
|
.add_aliased_columns(Some(REPLY_USER), user::Entity);
|
||||||
|
|
||||||
|
if let Some(user_id) = &options.with_interactions_from {
|
||||||
|
select = select
|
||||||
|
.add_sub_select_reaction(Some(REPLY), REPLY_INTERACTION_REACTION, user_id)
|
||||||
|
.add_sub_select_renote(Some(REPLY), REPLY_INTERACTION_RENOTE, user_id);
|
||||||
|
}
|
||||||
|
|
||||||
if options.with_user {
|
if options.with_user {
|
||||||
select = select
|
select = select
|
||||||
.add_aliased_columns(Some(REPLY_USER_AVATAR), drive_file::Entity)
|
.add_aliased_columns(Some(REPLY_USER_AVATAR), drive_file::Entity)
|
||||||
|
@ -193,6 +358,12 @@ impl NoteResolver {
|
||||||
.add_aliased_columns(Some(RENOTE), note::Entity)
|
.add_aliased_columns(Some(RENOTE), note::Entity)
|
||||||
.add_aliased_columns(Some(RENOTE_USER), user::Entity);
|
.add_aliased_columns(Some(RENOTE_USER), user::Entity);
|
||||||
|
|
||||||
|
if let Some(user_id) = &options.with_interactions_from {
|
||||||
|
select = select
|
||||||
|
.add_sub_select_reaction(Some(RENOTE), RENOTE_INTERACTION_REACTION, user_id)
|
||||||
|
.add_sub_select_renote(Some(RENOTE), RENOTE_INTERACTION_RENOTE, user_id);
|
||||||
|
}
|
||||||
|
|
||||||
if options.with_user {
|
if options.with_user {
|
||||||
select = select
|
select = select
|
||||||
.add_aliased_columns(Some(RENOTE_USER_AVATAR), drive_file::Entity)
|
.add_aliased_columns(Some(RENOTE_USER_AVATAR), drive_file::Entity)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::{CalckeyDbError, CalckeyModel};
|
||||||
|
use ck::{poll, poll_vote};
|
||||||
|
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
|
||||||
|
|
||||||
|
pub struct PollResolver(CalckeyModel);
|
||||||
|
|
||||||
|
impl PollResolver {
|
||||||
|
pub fn new(db: CalckeyModel) -> Self {
|
||||||
|
PollResolver(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_poll(&self, note_id: &str) -> Result<Option<poll::Model>, CalckeyDbError> {
|
||||||
|
Ok(poll::Entity::find()
|
||||||
|
.filter(poll::Column::NoteId.eq(note_id))
|
||||||
|
.one(self.0.inner())
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_poll_votes_by(
|
||||||
|
&self,
|
||||||
|
note_id: &str,
|
||||||
|
user_id: &str,
|
||||||
|
) -> Result<Vec<poll_vote::Model>, CalckeyDbError> {
|
||||||
|
Ok(poll_vote::Entity::find()
|
||||||
|
.filter(
|
||||||
|
poll_vote::Column::NoteId
|
||||||
|
.eq(note_id)
|
||||||
|
.and(poll_vote::Column::UserId.eq(user_id)),
|
||||||
|
)
|
||||||
|
.all(self.0.inner())
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,13 +33,13 @@ pub enum NoteVisibility {
|
||||||
pub struct PollChoice {
|
pub struct PollChoice {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub votes_count: u64,
|
pub votes_count: u64,
|
||||||
pub voted: bool,
|
pub voted: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct PollBase {
|
pub struct PollBase {
|
||||||
pub expires_at: DateTime<Utc>,
|
pub expires_at: Option<DateTime<Utc>>,
|
||||||
pub expired: bool,
|
pub expired: bool,
|
||||||
pub multiple_choice: bool,
|
pub multiple_choice: bool,
|
||||||
pub options: Vec<PollChoice>,
|
pub options: Vec<PollChoice>,
|
||||||
|
@ -74,6 +74,12 @@ pub struct NoteBase {
|
||||||
|
|
||||||
pack!(PackNoteBase, Required<Id> as id & Required<NoteBase> as note);
|
pack!(PackNoteBase, Required<Id> as id & Required<NoteBase> as note);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct NoteSelfContextExt {
|
||||||
|
pub self_renote_count: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct NoteAttachmentExt {
|
pub struct NoteAttachmentExt {
|
||||||
|
@ -82,19 +88,19 @@ pub struct NoteAttachmentExt {
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackNoteWithMaybeAttachments,
|
PackNoteMaybeAttachments,
|
||||||
Required<Id> as id & Required<NoteBase> as note & Option<NoteAttachmentExt> as attachment
|
Required<Id> as id & Required<NoteBase> as note & Option<NoteSelfContextExt> as user_context & Option<NoteAttachmentExt> as attachment
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
pub struct NoteDetailExt {
|
pub struct NoteDetailExt {
|
||||||
pub parent_note: Option<Box<PackNoteWithMaybeAttachments>>,
|
pub parent_note: Option<Box<PackNoteMaybeAttachments>>,
|
||||||
pub renoted_note: Option<Box<PackNoteWithMaybeAttachments>>,
|
pub renoted_note: Option<Box<PackNoteMaybeAttachments>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackNoteMaybeFull,
|
PackNoteMaybeFull,
|
||||||
Required<Id> as id & Required<NoteBase> as note & Option<NoteAttachmentExt> as attachment & Option<NoteDetailExt> as detail
|
Required<Id> as id & Required<NoteBase> as note & Option<NoteSelfContextExt> as user_context & Option<NoteAttachmentExt> as attachment & Option<NoteDetailExt> as detail
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
@ -111,4 +117,9 @@ pub enum Reaction {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ReactionPair = (Reaction, usize);
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ReactionPair {
|
||||||
|
WithoutContext(Reaction, u64),
|
||||||
|
WithContext(Reaction, u64, bool),
|
||||||
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub async fn handle_note(
|
||||||
}
|
}
|
||||||
.fetch_single(&ctx, self_user.as_deref(), &id)
|
.fetch_single(&ctx, self_user.as_deref(), &id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| ObjectNotFound(id))?;
|
.ok_or(ObjectNotFound(id))?;
|
||||||
|
|
||||||
Ok(Json(note.into()))
|
Ok(Json(note))
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,3 +17,9 @@ macro_rules! impl_id {
|
||||||
impl_id!(ck::emoji::Model);
|
impl_id!(ck::emoji::Model);
|
||||||
impl_id!(ck::user::Model);
|
impl_id!(ck::user::Model);
|
||||||
impl_id!(ck::note::Model);
|
impl_id!(ck::note::Model);
|
||||||
|
|
||||||
|
impl BaseId for ck::poll::Model {
|
||||||
|
fn get_id(&self) -> &str {
|
||||||
|
&self.note_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,4 +2,5 @@ pub mod drive;
|
||||||
pub mod emoji;
|
pub mod emoji;
|
||||||
pub mod id;
|
pub mod id;
|
||||||
pub mod note;
|
pub mod note;
|
||||||
|
pub mod poll;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use magnetar_calckey_model::ck;
|
use magnetar_calckey_model::ck;
|
||||||
|
use magnetar_calckey_model::note_model::sub_interaction_renote;
|
||||||
use magnetar_sdk::types::note::{
|
use magnetar_sdk::types::note::{
|
||||||
NoteDetailExt, PackNoteWithMaybeAttachments, PackPollBase, ReactionPair,
|
NoteDetailExt, NoteSelfContextExt, PackNoteMaybeAttachments, PackPollBase, ReactionPair,
|
||||||
};
|
};
|
||||||
use magnetar_sdk::types::user::PackUserBase;
|
use magnetar_sdk::types::user::PackUserBase;
|
||||||
use magnetar_sdk::types::{
|
use magnetar_sdk::types::{
|
||||||
|
@ -70,9 +71,17 @@ impl PackType<NoteBaseSource<'_>> for NoteBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PackType<&sub_interaction_renote::Model> for NoteSelfContextExt {
|
||||||
|
fn extract(_context: &PackingContext, data: &sub_interaction_renote::Model) -> Self {
|
||||||
|
NoteSelfContextExt {
|
||||||
|
self_renote_count: data.renotes.map(|v| v as u64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NoteDetailSource<'a> {
|
pub struct NoteDetailSource<'a> {
|
||||||
pub parent_note: Option<&'a PackNoteWithMaybeAttachments>,
|
pub parent_note: Option<&'a PackNoteMaybeAttachments>,
|
||||||
pub renoted_note: Option<&'a PackNoteWithMaybeAttachments>,
|
pub renoted_note: Option<&'a PackNoteMaybeAttachments>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PackType<NoteDetailSource<'a>> for NoteDetailExt {
|
impl<'a> PackType<NoteDetailSource<'a>> for NoteDetailExt {
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
use crate::model::{PackType, PackingContext};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use magnetar_calckey_model::ck;
|
||||||
|
use magnetar_sdk::types::note::{PollBase, PollChoice};
|
||||||
|
|
||||||
|
pub type PollPackInput<'a> = (&'a ck::poll::Model, Option<&'a [ck::poll_vote::Model]>);
|
||||||
|
|
||||||
|
impl PackType<PollPackInput<'_>> for PollBase {
|
||||||
|
fn extract(_context: &PackingContext, (poll, votes): PollPackInput<'_>) -> Self {
|
||||||
|
PollBase {
|
||||||
|
expires_at: poll.expires_at.map(DateTime::<Utc>::from),
|
||||||
|
expired: poll.expires_at.is_some_and(|e| e < Utc::now()),
|
||||||
|
multiple_choice: poll.multiple,
|
||||||
|
options: poll
|
||||||
|
.choices
|
||||||
|
.iter()
|
||||||
|
.zip(poll.votes.iter())
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, (name, count))| PollChoice {
|
||||||
|
title: name.to_string(),
|
||||||
|
votes_count: *count as u64,
|
||||||
|
voted: votes.map(|v| v.iter().any(|v| v.choice as usize == i)),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -85,12 +85,8 @@ impl PackingContext {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.is_relationship_between(
|
self.is_relationship_between(Either::Right(self_user), Either::Right(user), rel_type)
|
||||||
Either::Right(self_user.into()),
|
.await
|
||||||
Either::Right(user.into()),
|
|
||||||
rel_type,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_relationship_between(
|
async fn is_relationship_between(
|
||||||
|
|
|
@ -13,6 +13,7 @@ use magnetar_calckey_model::emoji::EmojiTag;
|
||||||
use magnetar_calckey_model::note_model::{
|
use magnetar_calckey_model::note_model::{
|
||||||
NoteData, NoteResolveOptions, NoteVisibilityFilterFactory,
|
NoteData, NoteResolveOptions, NoteVisibilityFilterFactory,
|
||||||
};
|
};
|
||||||
|
use magnetar_calckey_model::poll::PollResolver;
|
||||||
use magnetar_calckey_model::sea_orm::prelude::Expr;
|
use magnetar_calckey_model::sea_orm::prelude::Expr;
|
||||||
use magnetar_calckey_model::sea_orm::sea_query::{Alias, IntoIden, PgFunc, Query, SimpleExpr};
|
use magnetar_calckey_model::sea_orm::sea_query::{Alias, IntoIden, PgFunc, Query, SimpleExpr};
|
||||||
use magnetar_calckey_model::sea_orm::{ActiveEnum, ColumnTrait, IntoSimpleExpr};
|
use magnetar_calckey_model::sea_orm::{ActiveEnum, ColumnTrait, IntoSimpleExpr};
|
||||||
|
@ -22,8 +23,8 @@ use magnetar_sdk::mmm::Token;
|
||||||
use magnetar_sdk::types::drive::PackDriveFileBase;
|
use magnetar_sdk::types::drive::PackDriveFileBase;
|
||||||
use magnetar_sdk::types::emoji::EmojiContext;
|
use magnetar_sdk::types::emoji::EmojiContext;
|
||||||
use magnetar_sdk::types::note::{
|
use magnetar_sdk::types::note::{
|
||||||
NoteAttachmentExt, NoteBase, NoteDetailExt, PackNoteBase, PackNoteMaybeFull,
|
NoteAttachmentExt, NoteBase, NoteDetailExt, NoteSelfContextExt, PackNoteBase,
|
||||||
PackNoteWithMaybeAttachments, Reaction,
|
PackNoteMaybeAttachments, PackNoteMaybeFull, PackPollBase, PollBase, Reaction, ReactionPair,
|
||||||
};
|
};
|
||||||
use magnetar_sdk::types::{Id, MmXml};
|
use magnetar_sdk::types::{Id, MmXml};
|
||||||
use magnetar_sdk::{mmm, Packed, Required};
|
use magnetar_sdk::{mmm, Packed, Required};
|
||||||
|
@ -151,7 +152,7 @@ impl NoteVisibilityFilterModel {
|
||||||
return Ok(following || user.host.is_some() && note.user_host.is_some());
|
return Ok(following || user.host.is_some() && note.user_host.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(false);
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_note_visibility_filter(&self, user: Option<&str>) -> NoteVisibilityFilterSimple {
|
pub fn new_note_visibility_filter(&self, user: Option<&str>) -> NoteVisibilityFilterSimple {
|
||||||
|
@ -225,18 +226,28 @@ impl NoteModel {
|
||||||
let reactions_raw =
|
let reactions_raw =
|
||||||
serde_json::Map::<String, serde_json::Value>::deserialize(¬e_data.note.reactions)?
|
serde_json::Map::<String, serde_json::Value>::deserialize(¬e_data.note.reactions)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(code, count)| {
|
.map(|(ref code, count)| {
|
||||||
|
let reaction = parse_reaction(code)
|
||||||
|
.map_or_else(|| Either::Left(code.to_string()), Either::Right);
|
||||||
|
|
||||||
(
|
(
|
||||||
parse_reaction(&code).map_or_else(|| Either::Left(code), Either::Right),
|
reaction,
|
||||||
count,
|
count,
|
||||||
|
note_data
|
||||||
|
.interaction_user_reaction
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|r| r.reaction_name.as_deref())
|
||||||
|
.map(|r| r == code),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|(code, count)| Ok((code, usize::deserialize(count)?)))
|
.map(|(code, count, self_reacted)| {
|
||||||
|
Ok((code, u64::deserialize(count)?, self_reacted))
|
||||||
|
})
|
||||||
.collect::<Result<Vec<_>, serde_json::Error>>()?;
|
.collect::<Result<Vec<_>, serde_json::Error>>()?;
|
||||||
// Pick out all successfully-parsed shortcode emojis
|
// Pick out all successfully-parsed shortcode emojis
|
||||||
let reactions_to_resolve = reactions_raw
|
let reactions_to_resolve = reactions_raw
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(code, _)| code)
|
.map(|(code, _, _)| code)
|
||||||
.map(Either::as_ref)
|
.map(Either::as_ref)
|
||||||
.filter_map(Either::right)
|
.filter_map(Either::right)
|
||||||
.filter_map(|c| match c {
|
.filter_map(|c| match c {
|
||||||
|
@ -261,34 +272,38 @@ impl NoteModel {
|
||||||
// Left reactions and the Right ones that didn't resolve to any emoji are turned back into Unknown
|
// Left reactions and the Right ones that didn't resolve to any emoji are turned back into Unknown
|
||||||
let reactions = &reactions_raw
|
let reactions = &reactions_raw
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(raw, count)| {
|
.map(|(raw, count, self_reaction)| {
|
||||||
(
|
let reaction = raw.either(
|
||||||
raw.either(
|
|raw| Reaction::Unknown { raw },
|
||||||
|raw| Reaction::Unknown { raw },
|
|raw| match raw {
|
||||||
|raw| match raw {
|
RawReaction::Unicode(text) => Reaction::Unicode(text),
|
||||||
RawReaction::Unicode(text) => Reaction::Unicode(text),
|
RawReaction::Shortcode { shortcode, host } => reactions_fetched
|
||||||
RawReaction::Shortcode { shortcode, host } => reactions_fetched
|
.iter()
|
||||||
.iter()
|
.find(|e| e.host == host && e.name == shortcode)
|
||||||
.find(|e| e.host == host && e.name == shortcode)
|
.map_or_else(
|
||||||
.map_or_else(
|
|| Reaction::Unknown {
|
||||||
|| Reaction::Unknown {
|
raw: format!(
|
||||||
raw: format!(
|
":{shortcode}{}:",
|
||||||
":{shortcode}{}:",
|
host.as_deref()
|
||||||
host.as_deref()
|
.map(|h| format!("@{h}"))
|
||||||
.map(|h| format!("@{h}"))
|
.unwrap_or_default()
|
||||||
.unwrap_or_default()
|
),
|
||||||
),
|
},
|
||||||
},
|
|e| Reaction::Shortcode {
|
||||||
|e| Reaction::Shortcode {
|
name: shortcode.clone(),
|
||||||
name: shortcode.clone(),
|
host: host.clone(),
|
||||||
host: host.clone(),
|
url: e.public_url.clone(),
|
||||||
url: e.public_url.clone(),
|
},
|
||||||
},
|
),
|
||||||
),
|
},
|
||||||
},
|
);
|
||||||
),
|
|
||||||
count,
|
match self_reaction {
|
||||||
)
|
Some(self_reaction) => {
|
||||||
|
ReactionPair::WithContext(reaction, count, self_reaction)
|
||||||
|
}
|
||||||
|
None => ReactionPair::WithoutContext(reaction, count),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
@ -322,54 +337,15 @@ impl NoteModel {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn extract_reply_target_base(
|
fn extract_interaction(
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
note: &NoteData,
|
note: &NoteData,
|
||||||
) -> PackResult<Option<PackNoteBase>> {
|
) -> PackResult<Option<NoteSelfContextExt>> {
|
||||||
match note.reply.as_ref() {
|
Ok(note
|
||||||
Some(r) if self.with_context => self.extract_base(ctx, r).await.map(Some),
|
.interaction_user_renote
|
||||||
_ => Ok(None),
|
.as_ref()
|
||||||
}
|
.map(|renote_info| NoteSelfContextExt::extract(ctx, renote_info)))
|
||||||
}
|
|
||||||
|
|
||||||
async fn extract_renote_target_base(
|
|
||||||
&self,
|
|
||||||
ctx: &PackingContext,
|
|
||||||
note: &NoteData,
|
|
||||||
) -> PackResult<Option<PackNoteBase>> {
|
|
||||||
match note.renote.as_ref() {
|
|
||||||
Some(r) if self.with_context => self.extract_base(ctx, r).await.map(Some),
|
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn extract_reply_target_attachemnts(
|
|
||||||
&self,
|
|
||||||
ctx: &PackingContext,
|
|
||||||
drive_model: &DriveModel,
|
|
||||||
note: &NoteData,
|
|
||||||
) -> PackResult<Option<Vec<PackDriveFileBase>>> {
|
|
||||||
match note.reply.as_ref() {
|
|
||||||
Some(r) if self.with_context => {
|
|
||||||
self.extract_attachments(ctx, drive_model, &r.note).await
|
|
||||||
}
|
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn extract_renote_target_attachemnts(
|
|
||||||
&self,
|
|
||||||
ctx: &PackingContext,
|
|
||||||
drive_model: &DriveModel,
|
|
||||||
note: &NoteData,
|
|
||||||
) -> PackResult<Option<Vec<PackDriveFileBase>>> {
|
|
||||||
match note.renote.as_ref() {
|
|
||||||
Some(r) if self.with_context => {
|
|
||||||
self.extract_attachments(ctx, drive_model, &r.note).await
|
|
||||||
}
|
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn extract_attachments(
|
async fn extract_attachments(
|
||||||
|
@ -393,6 +369,62 @@ impl NoteModel {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn extract_poll(
|
||||||
|
&self,
|
||||||
|
ctx: &PackingContext,
|
||||||
|
as_user: Option<&ck::user::Model>,
|
||||||
|
note: &ck::note::Model,
|
||||||
|
) -> PackResult<Option<PackPollBase>> {
|
||||||
|
if !note.has_poll {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let poll_resolver = PollResolver::new(ctx.service.db.clone());
|
||||||
|
|
||||||
|
let Some(poll) = poll_resolver.get_poll(¬e.id).await? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let votes = match as_user {
|
||||||
|
Some(u) => Some(poll_resolver.get_poll_votes_by(¬e.id, &u.id).await?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(PackPollBase::pack_from((
|
||||||
|
Required(Id::from(poll.get_id())),
|
||||||
|
Required(PollBase::extract(ctx, (&poll, votes.as_deref()))),
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn pack_single_attachments(
|
||||||
|
&self,
|
||||||
|
ctx: &PackingContext,
|
||||||
|
drive_model: &DriveModel,
|
||||||
|
as_user: Option<&ck::user::Model>,
|
||||||
|
note_data: &NoteData,
|
||||||
|
) -> PackResult<PackNoteMaybeAttachments> {
|
||||||
|
let (PackNoteBase { id, note }, attachments_pack, poll_pack) = try_join!(
|
||||||
|
self.extract_base(ctx, note_data),
|
||||||
|
self.extract_attachments(ctx, drive_model, ¬e_data.note),
|
||||||
|
self.extract_poll(ctx, as_user, ¬e_data.note)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(PackNoteMaybeAttachments::pack_from((
|
||||||
|
id,
|
||||||
|
note,
|
||||||
|
self.extract_interaction(ctx, note_data)?,
|
||||||
|
attachments_pack.map(|attachments| {
|
||||||
|
NoteAttachmentExt::extract(
|
||||||
|
ctx,
|
||||||
|
NoteAttachmentSource {
|
||||||
|
attachments: &attachments,
|
||||||
|
poll: poll_pack.as_ref(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn fetch_single(
|
pub async fn fetch_single(
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
|
@ -411,6 +443,10 @@ impl NoteModel {
|
||||||
with_user: self.with_context,
|
with_user: self.with_context,
|
||||||
with_reply_target: self.with_context,
|
with_reply_target: self.with_context,
|
||||||
with_renote_target: self.with_context,
|
with_renote_target: self.with_context,
|
||||||
|
with_interactions_from: self
|
||||||
|
.with_context
|
||||||
|
.then(|| as_user.map(ck::user::Model::get_id).map(str::to_string))
|
||||||
|
.flatten(),
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
else {
|
else {
|
||||||
|
@ -419,80 +455,47 @@ impl NoteModel {
|
||||||
|
|
||||||
let drive_model = DriveModel;
|
let drive_model = DriveModel;
|
||||||
|
|
||||||
let extract_reply_target = self.extract_reply_target_base(ctx, ¬e);
|
let reply_target = async {
|
||||||
let extract_renote_target = self.extract_renote_target_base(ctx, ¬e);
|
match note.reply.as_ref() {
|
||||||
let extract_attachments = self.extract_attachments(ctx, &drive_model, ¬e.note);
|
Some(r) if self.with_context => self
|
||||||
let extract_reply_attachments =
|
.pack_single_attachments(ctx, &drive_model, as_user, r)
|
||||||
self.extract_reply_target_attachemnts(ctx, &drive_model, ¬e);
|
.await
|
||||||
let extract_renote_attachments =
|
.map(Some),
|
||||||
self.extract_renote_target_attachemnts(ctx, &drive_model, ¬e);
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Polls, ...
|
let renote_target = async {
|
||||||
|
match note.renote.as_ref() {
|
||||||
|
Some(r) if self.with_context => self
|
||||||
|
.pack_single_attachments(ctx, &drive_model, as_user, r)
|
||||||
|
.await
|
||||||
|
.map(Some),
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let (
|
let (
|
||||||
PackNoteBase { id, note },
|
PackNoteMaybeAttachments {
|
||||||
|
id,
|
||||||
|
note,
|
||||||
|
user_context,
|
||||||
|
attachment,
|
||||||
|
},
|
||||||
reply_target_pack,
|
reply_target_pack,
|
||||||
renote_target_pack,
|
renote_target_pack,
|
||||||
attachments_pack,
|
|
||||||
reply_attachments_pack,
|
|
||||||
renote_attachments_pack,
|
|
||||||
) = try_join!(
|
) = try_join!(
|
||||||
self.extract_base(ctx, ¬e),
|
self.pack_single_attachments(ctx, &drive_model, as_user, ¬e),
|
||||||
extract_reply_target,
|
reply_target,
|
||||||
extract_renote_target,
|
renote_target
|
||||||
extract_attachments,
|
|
||||||
extract_reply_attachments,
|
|
||||||
extract_renote_attachments
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let detail = self.with_context.then(|| {
|
let detail = self.with_context.then(|| {
|
||||||
NoteDetailExt::extract(
|
NoteDetailExt::extract(
|
||||||
ctx,
|
ctx,
|
||||||
NoteDetailSource {
|
NoteDetailSource {
|
||||||
parent_note: reply_target_pack
|
parent_note: reply_target_pack.as_ref(),
|
||||||
.map(|base| {
|
renoted_note: renote_target_pack.as_ref(),
|
||||||
PackNoteWithMaybeAttachments::pack_from((
|
|
||||||
base.id,
|
|
||||||
base.note,
|
|
||||||
reply_attachments_pack.map(|attachments| {
|
|
||||||
NoteAttachmentExt::extract(
|
|
||||||
ctx,
|
|
||||||
NoteAttachmentSource {
|
|
||||||
attachments: &attachments,
|
|
||||||
poll: None, // TODO
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.as_ref(),
|
|
||||||
renoted_note: renote_target_pack
|
|
||||||
.map(|base| {
|
|
||||||
PackNoteWithMaybeAttachments::pack_from((
|
|
||||||
base.id,
|
|
||||||
base.note,
|
|
||||||
renote_attachments_pack.map(|attachments| {
|
|
||||||
NoteAttachmentExt::extract(
|
|
||||||
ctx,
|
|
||||||
NoteAttachmentSource {
|
|
||||||
attachments: &attachments,
|
|
||||||
poll: None, // TODO
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.as_ref(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let attachments = attachments_pack.map(|attachments| {
|
|
||||||
NoteAttachmentExt::extract(
|
|
||||||
ctx,
|
|
||||||
NoteAttachmentSource {
|
|
||||||
attachments: &attachments,
|
|
||||||
poll: None, // TODO
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -500,7 +503,8 @@ impl NoteModel {
|
||||||
Ok(Some(PackNoteMaybeFull::pack_from((
|
Ok(Some(PackNoteMaybeFull::pack_from((
|
||||||
id,
|
id,
|
||||||
note,
|
note,
|
||||||
attachments,
|
user_context,
|
||||||
|
attachment,
|
||||||
detail,
|
detail,
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue