Refactored note fetching
ci/woodpecker/push/ociImagePush Pipeline failed
Details
ci/woodpecker/push/ociImagePush Pipeline failed
Details
This commit is contained in:
parent
074c6f999e
commit
deb7f6ef5e
|
@ -1507,7 +1507,6 @@ dependencies = [
|
||||||
"hyper",
|
"hyper",
|
||||||
"idna 0.5.0",
|
"idna 0.5.0",
|
||||||
"itertools 0.12.0",
|
"itertools 0.12.0",
|
||||||
"lazy_static",
|
|
||||||
"lru",
|
"lru",
|
||||||
"magnetar_calckey_model",
|
"magnetar_calckey_model",
|
||||||
"magnetar_common",
|
"magnetar_common",
|
||||||
|
@ -1568,7 +1567,6 @@ 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",
|
||||||
|
|
|
@ -41,7 +41,6 @@ http = "1.0"
|
||||||
hyper = "1.1"
|
hyper = "1.1"
|
||||||
idna = "0.5"
|
idna = "0.5"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
lazy_static = "1.4"
|
|
||||||
lru = "0.12"
|
lru = "0.12"
|
||||||
miette = "5.9"
|
miette = "5.9"
|
||||||
nom = "7"
|
nom = "7"
|
||||||
|
@ -108,7 +107,6 @@ either = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
lazy_static = { workspace = true }
|
|
||||||
miette = { workspace = true, features = ["fancy"] }
|
miette = { workspace = true, features = ["fancy"] }
|
||||||
strum = { workspace = true, features = ["derive"] }
|
strum = { workspace = true, features = ["derive"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM docker.io/rust:1.74-bookworm as build
|
FROM docker.io/rust:1.75-bookworm as build
|
||||||
|
|
||||||
RUN update-ca-certificates
|
RUN update-ca-certificates
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ 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,10 +1,14 @@
|
||||||
pub mod emoji;
|
pub mod emoji;
|
||||||
|
pub mod model_ext;
|
||||||
pub mod note_model;
|
pub mod note_model;
|
||||||
|
pub mod paginated;
|
||||||
pub mod poll;
|
pub mod poll;
|
||||||
|
pub mod user_model;
|
||||||
|
|
||||||
pub use ck;
|
pub use ck;
|
||||||
use ck::*;
|
use ck::*;
|
||||||
pub use sea_orm;
|
pub use sea_orm;
|
||||||
|
use user_model::UserResolver;
|
||||||
|
|
||||||
use crate::note_model::NoteResolver;
|
use crate::note_model::NoteResolver;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
@ -307,7 +311,11 @@ impl CalckeyModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_note_resolver(&self) -> NoteResolver {
|
pub fn get_note_resolver(&self) -> NoteResolver {
|
||||||
NoteResolver::new(self.clone())
|
NoteResolver::new(self.clone(), self.get_user_resolver())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_user_resolver(&self) -> UserResolver {
|
||||||
|
UserResolver::new(self.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
use ext_calckey_model_migration::{Alias, Expr, IntoIden, SelectExpr, SelectStatement, TableRef};
|
||||||
|
use sea_orm::{ColumnTrait, Condition, EntityTrait, Iden, Iterable, JoinType, RelationDef};
|
||||||
|
|
||||||
|
pub(crate) trait SelectColumnsExt {
|
||||||
|
fn add_aliased_columns<T: EntityTrait>(&mut self, alias: &str) -> &mut Self;
|
||||||
|
|
||||||
|
fn join_columns<I: IntoIden>(
|
||||||
|
&mut self,
|
||||||
|
join: JoinType,
|
||||||
|
rel: RelationDef,
|
||||||
|
alias: I,
|
||||||
|
) -> &mut Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn join_columns_default(rel: RelationDef) -> Condition {
|
||||||
|
let tbl_id = |tbl: &TableRef| {
|
||||||
|
match tbl {
|
||||||
|
TableRef::Table(id) => id,
|
||||||
|
TableRef::TableAlias(_, id) => id,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cond = Condition::all();
|
||||||
|
for (owner_key, foreign_key) in rel.from_col.into_iter().zip(rel.to_col.into_iter()) {
|
||||||
|
cond = cond.add(
|
||||||
|
Expr::col((tbl_id(&rel.from_tbl), owner_key))
|
||||||
|
.equals((tbl_id(&rel.to_tbl), foreign_key)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cond
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectColumnsExt for SelectStatement {
|
||||||
|
fn add_aliased_columns<T: EntityTrait>(&mut self, iden: &str) -> &mut Self {
|
||||||
|
for col in T::Column::iter() {
|
||||||
|
let column: &T::Column = &col;
|
||||||
|
|
||||||
|
let alias = format!("{}{}", iden, col.to_string());
|
||||||
|
|
||||||
|
let column_ref = Expr::col((Alias::new(iden), column.as_column_ref().1));
|
||||||
|
|
||||||
|
self.expr(SelectExpr {
|
||||||
|
expr: col.select_as(column_ref),
|
||||||
|
alias: Some(Alias::new(&alias).into_iden()),
|
||||||
|
window: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join_columns<I: IntoIden>(
|
||||||
|
&mut self,
|
||||||
|
join: JoinType,
|
||||||
|
mut rel: RelationDef,
|
||||||
|
alias: I,
|
||||||
|
) -> &mut Self {
|
||||||
|
let alias = alias.into_iden();
|
||||||
|
rel.to_tbl = rel.to_tbl.alias(alias);
|
||||||
|
self.join(join, rel.to_tbl.clone(), join_columns_default(rel));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn joined_prefix_str(prefix: &str, suffix: &str) -> String {
|
||||||
|
format!("{prefix}{suffix}")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn joined_prefix_alias(prefix: &str, suffix: &str) -> Alias {
|
||||||
|
Alias::new(joined_prefix_str(prefix, suffix))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn joined_prefix(prefix: &str, suffix: &str) -> impl IntoIden {
|
||||||
|
joined_prefix_alias(prefix, suffix).into_iden()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait EntityPrefixExt {
|
||||||
|
fn base_prefix_str(&self) -> String;
|
||||||
|
fn base_prefix(&self) -> impl IntoIden;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: EntityTrait> EntityPrefixExt for T {
|
||||||
|
fn base_prefix_str(&self) -> String {
|
||||||
|
format!("{}.", self.table_name())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base_prefix(&self) -> impl IntoIden {
|
||||||
|
Alias::new(self.base_prefix_str()).into_iden()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,21 @@
|
||||||
use lazy_static::lazy_static;
|
use ext_calckey_model_migration::SelectStatement;
|
||||||
use sea_orm::sea_query::{
|
use sea_orm::sea_query::{
|
||||||
Alias, Asterisk, Expr, IntoCondition, IntoIden, Query, SelectExpr, SimpleExpr,
|
Alias, Asterisk, Expr, IntoCondition, IntoIden, Query, SelectExpr, SimpleExpr,
|
||||||
};
|
};
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
ColumnTrait, DbErr, EntityName, EntityTrait, FromQueryResult, Iden, Iterable, JoinType,
|
DbErr, EntityName, EntityTrait, FromQueryResult, Iden, JoinType, QueryFilter, QueryOrder,
|
||||||
QueryFilter, QueryOrder, QueryResult, QuerySelect, QueryTrait, RelationTrait, Select,
|
QueryResult, QuerySelect, QueryTrait, Select,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use ck::{drive_file, note, note_reaction, user, user_note_pining};
|
use ck::{note, note_reaction, user, user_note_pining};
|
||||||
use magnetar_sdk::types::{EndFilter, RangeFilter, SpanFilter, StartFilter};
|
use magnetar_sdk::types::SpanFilter;
|
||||||
|
|
||||||
|
use crate::model_ext::{
|
||||||
|
joined_prefix, joined_prefix_alias, joined_prefix_str, EntityPrefixExt, SelectColumnsExt,
|
||||||
|
};
|
||||||
|
use crate::paginated::PaginatedModel;
|
||||||
|
use crate::user_model::{UserData, UserResolver};
|
||||||
use crate::{AliasSourceExt, CalckeyDbError, CalckeyModel};
|
use crate::{AliasSourceExt, CalckeyDbError, CalckeyModel};
|
||||||
|
|
||||||
pub mod sub_interaction_renote {
|
pub mod sub_interaction_renote {
|
||||||
|
@ -48,9 +53,7 @@ pub struct NoteData {
|
||||||
pub note: note::Model,
|
pub note: note::Model,
|
||||||
pub interaction_user_renote: Option<sub_interaction_renote::Model>,
|
pub interaction_user_renote: Option<sub_interaction_renote::Model>,
|
||||||
pub interaction_user_reaction: Option<sub_interaction_reaction::Model>,
|
pub interaction_user_reaction: Option<sub_interaction_reaction::Model>,
|
||||||
pub user: user::Model,
|
pub user: UserData,
|
||||||
pub avatar: Option<drive_file::Model>,
|
|
||||||
pub banner: Option<drive_file::Model>,
|
|
||||||
pub reply: Option<Box<NoteData>>,
|
pub reply: Option<Box<NoteData>>,
|
||||||
pub renote: Option<Box<NoteData>>,
|
pub renote: Option<Box<NoteData>>,
|
||||||
}
|
}
|
||||||
|
@ -59,125 +62,36 @@ const PIN: &str = "pin.";
|
||||||
const INTERACTION_REACTION: &str = "interaction.reaction.";
|
const INTERACTION_REACTION: &str = "interaction.reaction.";
|
||||||
const INTERACTION_RENOTE: &str = "interaction.renote.";
|
const INTERACTION_RENOTE: &str = "interaction.renote.";
|
||||||
const USER: &str = "user.";
|
const USER: &str = "user.";
|
||||||
const USER_AVATAR: &str = "user.avatar.";
|
|
||||||
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_AVATAR: &str = "reply.user.avatar.";
|
|
||||||
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_AVATAR: &str = "renote.user.avatar.";
|
|
||||||
const RENOTE_USER_BANNER: &str = "renote.user.banner.";
|
|
||||||
const RENOTE_REPLY: &str = "renote.reply.";
|
|
||||||
const RENOTE_REPLY_INTERACTION_REACTION: &str = "renote.reply.interaction.reaction.";
|
|
||||||
const RENOTE_REPLY_INTERACTION_RENOTE: &str = "renote.reply.interaction.renote.";
|
|
||||||
const RENOTE_REPLY_USER: &str = "renote.reply.user.";
|
|
||||||
const RENOTE_REPLY_USER_AVATAR: &str = "renote.reply.user.avatar.";
|
|
||||||
const RENOTE_REPLY_USER_BANNER: &str = "renote.reply.user.banner.";
|
|
||||||
|
|
||||||
impl FromQueryResult for NoteData {
|
impl FromQueryResult for NoteData {
|
||||||
fn from_query_result(res: &QueryResult, _pre: &str) -> Result<Self, DbErr> {
|
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
|
||||||
let reply = note::Model::from_query_result_optional(res, REPLY)?
|
let fallback = format!("{}.", note::Entity.table_name());
|
||||||
.map::<Result<_, DbErr>, _>(|r| {
|
let prefix = if prefix.is_empty() { &fallback } else { prefix };
|
||||||
Ok(Box::new(NoteData {
|
|
||||||
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)?,
|
|
||||||
avatar: drive_file::Model::from_query_result_optional(res, REPLY_USER_AVATAR)?,
|
|
||||||
banner: drive_file::Model::from_query_result_optional(res, REPLY_USER_BANNER)?,
|
|
||||||
reply: None,
|
|
||||||
renote: None,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let renote_reply = note::Model::from_query_result_optional(res, RENOTE_REPLY)?
|
|
||||||
.map::<Result<_, DbErr>, _>(|r| {
|
|
||||||
Ok(Box::new(NoteData {
|
|
||||||
note: r,
|
|
||||||
interaction_user_renote:
|
|
||||||
sub_interaction_renote::Model::from_query_result_optional(
|
|
||||||
res,
|
|
||||||
RENOTE_REPLY_INTERACTION_RENOTE,
|
|
||||||
)?,
|
|
||||||
interaction_user_reaction:
|
|
||||||
sub_interaction_reaction::Model::from_query_result_optional(
|
|
||||||
res,
|
|
||||||
RENOTE_REPLY_INTERACTION_REACTION,
|
|
||||||
)?,
|
|
||||||
user: user::Model::from_query_result(res, RENOTE_REPLY_USER)?,
|
|
||||||
avatar: drive_file::Model::from_query_result_optional(
|
|
||||||
res,
|
|
||||||
RENOTE_REPLY_USER_AVATAR,
|
|
||||||
)?,
|
|
||||||
banner: drive_file::Model::from_query_result_optional(
|
|
||||||
res,
|
|
||||||
RENOTE_REPLY_USER_BANNER,
|
|
||||||
)?,
|
|
||||||
reply: None,
|
|
||||||
renote: None,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let renote = note::Model::from_query_result_optional(res, RENOTE)?
|
|
||||||
.map::<Result<_, DbErr>, _>(|r| {
|
|
||||||
Ok(Box::new(NoteData {
|
|
||||||
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)?,
|
|
||||||
avatar: drive_file::Model::from_query_result_optional(res, RENOTE_USER_AVATAR)?,
|
|
||||||
banner: drive_file::Model::from_query_result_optional(res, RENOTE_USER_BANNER)?,
|
|
||||||
reply: renote_reply,
|
|
||||||
renote: None,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
Ok(NoteData {
|
Ok(NoteData {
|
||||||
note: note::Model::from_query_result(res, "")?,
|
note: note::Model::from_query_result(res, prefix)?,
|
||||||
interaction_user_renote: sub_interaction_renote::Model::from_query_result_optional(
|
interaction_user_renote: sub_interaction_renote::Model::from_query_result_optional(
|
||||||
res,
|
res,
|
||||||
INTERACTION_RENOTE,
|
&joined_prefix_str(prefix, INTERACTION_RENOTE),
|
||||||
)?,
|
)?,
|
||||||
interaction_user_reaction: sub_interaction_reaction::Model::from_query_result_optional(
|
interaction_user_reaction: sub_interaction_reaction::Model::from_query_result_optional(
|
||||||
res,
|
res,
|
||||||
INTERACTION_REACTION,
|
&joined_prefix_str(prefix, INTERACTION_REACTION),
|
||||||
)?,
|
)?,
|
||||||
user: user::Model::from_query_result(res, USER)?,
|
user: UserData::from_query_result(res, &joined_prefix_str(prefix, USER))?,
|
||||||
avatar: drive_file::Model::from_query_result_optional(res, USER_AVATAR)?,
|
reply: NoteData::from_query_result_optional(res, &joined_prefix_str(prefix, REPLY))?
|
||||||
banner: drive_file::Model::from_query_result_optional(res, USER_BANNER)?,
|
.map(Box::new),
|
||||||
reply,
|
renote: NoteData::from_query_result_optional(res, &joined_prefix_str(prefix, RENOTE))?
|
||||||
renote,
|
.map(Box::new),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NoteResolver {
|
pub struct NoteResolver {
|
||||||
db: CalckeyModel,
|
db: CalckeyModel,
|
||||||
|
user_resolver: UserResolver,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NoteVisibilityFilterFactory: Send + Sync {
|
pub trait NoteVisibilityFilterFactory: Send + Sync {
|
||||||
|
@ -196,55 +110,29 @@ pub struct NoteResolveOptions {
|
||||||
pub only_pins_from: Option<String>, // User ID
|
pub only_pins_from: Option<String>, // User ID
|
||||||
}
|
}
|
||||||
|
|
||||||
trait SelectColumnsExt {
|
trait SelectNoteInteractionsExt {
|
||||||
fn add_aliased_columns<T: EntityTrait>(self, alias: Option<&str>, entity: T) -> Self;
|
fn add_sub_select_interaction_reaction(
|
||||||
|
&mut self,
|
||||||
fn add_sub_select_reaction(
|
source_note_alias: &str,
|
||||||
self,
|
|
||||||
source_note_alias: Option<&str>,
|
|
||||||
alias: &str,
|
alias: &str,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
) -> Select<note::Entity>;
|
) -> &mut Self;
|
||||||
|
|
||||||
fn add_sub_select_renote(
|
fn add_sub_select_interaction_renote(
|
||||||
self,
|
&mut self,
|
||||||
source_note_alias: Option<&str>,
|
source_note_alias: &str,
|
||||||
alias: &str,
|
alias: &str,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
) -> Select<note::Entity>;
|
) -> &mut Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectColumnsExt for Select<note::Entity> {
|
impl SelectNoteInteractionsExt for SelectStatement {
|
||||||
fn add_aliased_columns<T: EntityTrait>(
|
fn add_sub_select_interaction_reaction(
|
||||||
mut self: Select<note::Entity>,
|
&mut self,
|
||||||
alias: Option<&str>,
|
iden: &str,
|
||||||
entity: T,
|
|
||||||
) -> Select<note::Entity> {
|
|
||||||
for col in T::Column::iter() {
|
|
||||||
let column: &T::Column = &col;
|
|
||||||
|
|
||||||
let iden = alias.unwrap_or_else(|| entity.table_name());
|
|
||||||
let alias = format!("{}{}", iden, col.to_string());
|
|
||||||
|
|
||||||
let column_ref = Expr::col((Alias::new(iden), column.as_column_ref().1));
|
|
||||||
|
|
||||||
QuerySelect::query(&mut self).expr(SelectExpr {
|
|
||||||
expr: col.select_as(column_ref),
|
|
||||||
alias: Some(Alias::new(&alias).into_iden()),
|
|
||||||
window: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_sub_select_reaction(
|
|
||||||
mut self: Select<note::Entity>,
|
|
||||||
source_note_alias: Option<&str>,
|
|
||||||
prefix_alias: &str,
|
prefix_alias: &str,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
) -> Select<note::Entity> {
|
) -> &mut Self {
|
||||||
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 note_id_col = Expr::col((Alias::new(iden), note::Column::Id));
|
||||||
|
|
||||||
let column = sub_interaction_reaction::Column::ReactionName;
|
let column = sub_interaction_reaction::Column::ReactionName;
|
||||||
|
@ -262,21 +150,19 @@ impl SelectColumnsExt for Select<note::Entity> {
|
||||||
.take()
|
.take()
|
||||||
.into_sub_query_statement();
|
.into_sub_query_statement();
|
||||||
|
|
||||||
QuerySelect::query(&mut self).expr(SimpleExpr::SubQuery(None, Box::new(sub_query)));
|
self.expr(SimpleExpr::SubQuery(None, Box::new(sub_query)));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_sub_select_renote(
|
fn add_sub_select_interaction_renote(
|
||||||
mut self: Select<note::Entity>,
|
&mut self,
|
||||||
source_note_alias: Option<&str>,
|
iden: &str,
|
||||||
prefix_alias: &str,
|
prefix_alias: &str,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
) -> Select<note::Entity> {
|
) -> &mut Self {
|
||||||
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 note_id_col = Expr::col((Alias::new(iden), note::Column::Id));
|
||||||
|
|
||||||
let renote_note_tbl = Alias::new(format!("{}{}", prefix_alias, "note"));
|
let renote_note_tbl = joined_prefix_alias(prefix_alias, "note");
|
||||||
|
|
||||||
let column = sub_interaction_renote::Column::Renotes;
|
let column = sub_interaction_renote::Column::Renotes;
|
||||||
let alias = format!("{}{}", prefix_alias, column.to_string());
|
let alias = format!("{}{}", prefix_alias, column.to_string());
|
||||||
|
@ -295,99 +181,24 @@ impl SelectColumnsExt for Select<note::Entity> {
|
||||||
.take()
|
.take()
|
||||||
.into_sub_query_statement();
|
.into_sub_query_statement();
|
||||||
|
|
||||||
QuerySelect::query(&mut self).expr(SimpleExpr::SubQuery(None, Box::new(sub_query)));
|
self.expr(SimpleExpr::SubQuery(None, Box::new(sub_query)));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref ALIAS_PIN: Alias = Alias::new(PIN);
|
|
||||||
static ref ALIAS_INTERACTION_RENOTE: Alias = Alias::new(INTERACTION_RENOTE);
|
|
||||||
static ref ALIAS_INTERACTION_REACTION: Alias = Alias::new(INTERACTION_REACTION);
|
|
||||||
static ref ALIAS_USER: Alias = Alias::new(USER);
|
|
||||||
static ref ALIAS_USER_AVATAR: Alias = Alias::new(USER_AVATAR);
|
|
||||||
static ref ALIAS_USER_BANNER: Alias = Alias::new(USER_BANNER);
|
|
||||||
static ref ALIAS_REPLY: Alias = Alias::new(REPLY);
|
|
||||||
static ref ALIAS_REPLY_INTERACTION_RENOTE: Alias = Alias::new(REPLY_INTERACTION_RENOTE);
|
|
||||||
static ref ALIAS_REPLY_INTERACTION_REACTION: Alias = Alias::new(REPLY_INTERACTION_REACTION);
|
|
||||||
static ref ALIAS_REPLY_USER: Alias = Alias::new(REPLY_USER);
|
|
||||||
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);
|
|
||||||
static ref ALIAS_RENOTE_REPLY: Alias = Alias::new(RENOTE_REPLY);
|
|
||||||
static ref ALIAS_RENOTE_REPLY_INTERACTION_RENOTE: Alias =
|
|
||||||
Alias::new(RENOTE_REPLY_INTERACTION_RENOTE);
|
|
||||||
static ref ALIAS_RENOTE_REPLY_INTERACTION_REACTION: Alias =
|
|
||||||
Alias::new(RENOTE_REPLY_INTERACTION_REACTION);
|
|
||||||
static ref ALIAS_RENOTE_REPLY_USER: Alias = Alias::new(RENOTE_REPLY_USER);
|
|
||||||
static ref ALIAS_RENOTE_REPLY_USER_AVATAR: Alias = Alias::new(RENOTE_REPLY_USER_AVATAR);
|
|
||||||
static ref ALIAS_RENOTE_REPLY_USER_BANNER: Alias = Alias::new(RENOTE_REPLY_USER_BANNER);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn range_into_expr(filter: &SpanFilter) -> Option<SimpleExpr> {
|
|
||||||
match filter {
|
|
||||||
SpanFilter::Range(RangeFilter {
|
|
||||||
time_start,
|
|
||||||
time_end,
|
|
||||||
id_start,
|
|
||||||
id_end,
|
|
||||||
}) => Some(
|
|
||||||
Expr::tuple([
|
|
||||||
Expr::col(note::Column::CreatedAt).into(),
|
|
||||||
Expr::col(note::Column::Id).into(),
|
|
||||||
])
|
|
||||||
.between(
|
|
||||||
Expr::tuple([
|
|
||||||
Expr::value(time_start.clone()),
|
|
||||||
Expr::value(id_start.clone()),
|
|
||||||
]),
|
|
||||||
Expr::tuple([Expr::value(time_end.clone()), Expr::value(id_end.clone())]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SpanFilter::Start(StartFilter {
|
|
||||||
id_start,
|
|
||||||
time_start,
|
|
||||||
}) => Some(
|
|
||||||
Expr::tuple([
|
|
||||||
Expr::col(note::Column::CreatedAt).into(),
|
|
||||||
Expr::col(note::Column::Id).into(),
|
|
||||||
])
|
|
||||||
.gt(Expr::tuple([
|
|
||||||
Expr::value(time_start.clone()),
|
|
||||||
Expr::value(id_start.clone()),
|
|
||||||
])),
|
|
||||||
),
|
|
||||||
SpanFilter::End(EndFilter { id_end, time_end }) => Some(
|
|
||||||
Expr::tuple([
|
|
||||||
Expr::col(note::Column::CreatedAt).into(),
|
|
||||||
Expr::col(note::Column::Id).into(),
|
|
||||||
])
|
|
||||||
.lt(Expr::tuple([
|
|
||||||
Expr::value(time_end.clone()),
|
|
||||||
Expr::value(id_end.clone()),
|
|
||||||
])),
|
|
||||||
),
|
|
||||||
SpanFilter::None(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ids_into_expr(ids: &Vec<String>) -> SimpleExpr {
|
fn ids_into_expr(ids: &Vec<String>) -> SimpleExpr {
|
||||||
|
let id_col = Expr::col((note::Entity.base_prefix(), note::Column::Id));
|
||||||
|
|
||||||
if ids.len() == 1 {
|
if ids.len() == 1 {
|
||||||
note::Column::Id.eq(&ids[0])
|
id_col.eq(&ids[0])
|
||||||
} else {
|
} else {
|
||||||
note::Column::Id.is_in(ids)
|
id_col.is_in(ids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoteResolver {
|
impl NoteResolver {
|
||||||
pub fn new(db: CalckeyModel) -> Self {
|
pub fn new(db: CalckeyModel, user_resolver: UserResolver) -> Self {
|
||||||
NoteResolver { db }
|
NoteResolver { db, user_resolver }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_one(
|
pub async fn get_one(
|
||||||
|
@ -396,7 +207,10 @@ impl NoteResolver {
|
||||||
) -> Result<Option<NoteData>, CalckeyDbError> {
|
) -> Result<Option<NoteData>, CalckeyDbError> {
|
||||||
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().and_then(range_into_expr);
|
let time_filter = options
|
||||||
|
.time_range
|
||||||
|
.as_ref()
|
||||||
|
.and_then(note::Model::range_into_expr);
|
||||||
let id_filter = options.ids.as_ref().map(ids_into_expr);
|
let id_filter = options.ids.as_ref().map(ids_into_expr);
|
||||||
|
|
||||||
let notes = select
|
let notes = select
|
||||||
|
@ -416,7 +230,10 @@ impl NoteResolver {
|
||||||
) -> Result<Vec<NoteData>, CalckeyDbError> {
|
) -> Result<Vec<NoteData>, CalckeyDbError> {
|
||||||
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().and_then(range_into_expr);
|
let time_filter = options
|
||||||
|
.time_range
|
||||||
|
.as_ref()
|
||||||
|
.and_then(note::Model::range_into_expr);
|
||||||
let id_filter = options.ids.as_ref().map(ids_into_expr);
|
let id_filter = options.ids.as_ref().map(ids_into_expr);
|
||||||
|
|
||||||
let notes = select
|
let notes = select
|
||||||
|
@ -425,7 +242,7 @@ impl NoteResolver {
|
||||||
.apply_if(time_filter, Select::<note::Entity>::filter)
|
.apply_if(time_filter, Select::<note::Entity>::filter)
|
||||||
.apply_if(options.only_pins_from.as_deref(), |s, _| {
|
.apply_if(options.only_pins_from.as_deref(), |s, _| {
|
||||||
s.order_by_desc(Expr::col((
|
s.order_by_desc(Expr::col((
|
||||||
ALIAS_PIN.clone(),
|
joined_prefix(¬e::Entity.base_prefix_str(), PIN),
|
||||||
user_note_pining::Column::CreatedAt,
|
user_note_pining::Column::CreatedAt,
|
||||||
)))
|
)))
|
||||||
})
|
})
|
||||||
|
@ -437,200 +254,110 @@ impl NoteResolver {
|
||||||
Ok(notes)
|
Ok(notes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve(&self, options: &NoteResolveOptions) -> Select<note::Entity> {
|
fn attach_note(
|
||||||
let mut select = note::Entity::find().add_aliased_columns(Some(USER), user::Entity);
|
&self,
|
||||||
|
q: &mut SelectStatement,
|
||||||
|
prefix: &str,
|
||||||
|
reply_depth: usize,
|
||||||
|
renote_depth: usize,
|
||||||
|
options: &NoteResolveOptions,
|
||||||
|
user_resolver: &UserResolver,
|
||||||
|
) {
|
||||||
|
q.add_aliased_columns::<note::Entity>(prefix);
|
||||||
|
|
||||||
if let Some(pins_user) = options.only_pins_from.clone() {
|
// Add the note's author
|
||||||
|
q.add_aliased_columns::<user::Entity>(&joined_prefix_str(prefix, USER));
|
||||||
|
q.join_columns(
|
||||||
|
JoinType::LeftJoin,
|
||||||
|
note::Relation::User.with_alias(Alias::new(prefix).into_iden()),
|
||||||
|
joined_prefix(prefix, USER),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Interactions like renotes or reactions from the specified user
|
||||||
|
if let Some(user_id) = &options.with_interactions_from {
|
||||||
|
q.add_sub_select_interaction_reaction(
|
||||||
|
prefix,
|
||||||
|
&joined_prefix_str(prefix, INTERACTION_REACTION),
|
||||||
|
user_id,
|
||||||
|
);
|
||||||
|
q.add_sub_select_interaction_renote(
|
||||||
|
prefix,
|
||||||
|
&joined_prefix_str(prefix, INTERACTION_RENOTE),
|
||||||
|
user_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively attach reply parents
|
||||||
|
if reply_depth > 0 {
|
||||||
|
q.join_columns(
|
||||||
|
JoinType::LeftJoin,
|
||||||
|
note::Relation::SelfRef2.with_alias(Alias::new(prefix).into_iden()),
|
||||||
|
joined_prefix(prefix, REPLY),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.attach_note(
|
||||||
|
q,
|
||||||
|
&joined_prefix_str(prefix, REPLY),
|
||||||
|
reply_depth - 1,
|
||||||
|
renote_depth,
|
||||||
|
options,
|
||||||
|
user_resolver,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively attach renote/quote targets
|
||||||
|
if renote_depth > 0 {
|
||||||
|
q.join_columns(
|
||||||
|
JoinType::LeftJoin,
|
||||||
|
note::Relation::SelfRef1.with_alias(Alias::new(prefix).into_iden()),
|
||||||
|
joined_prefix(prefix, RENOTE),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.attach_note(
|
||||||
|
q,
|
||||||
|
&joined_prefix_str(prefix, RENOTE),
|
||||||
|
reply_depth,
|
||||||
|
renote_depth - 1,
|
||||||
|
options,
|
||||||
|
user_resolver,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(&self, options: &NoteResolveOptions) -> Select<note::Entity> {
|
||||||
|
let mut select = note::Entity::find();
|
||||||
|
|
||||||
|
let prefix = note::Entity.base_prefix_str();
|
||||||
|
|
||||||
|
let query = QuerySelect::query(&mut select);
|
||||||
|
query.clear_selects();
|
||||||
|
query.from_as(note::Entity, Alias::new(&prefix));
|
||||||
|
|
||||||
|
if let Some(pins_user) = &options.only_pins_from {
|
||||||
select = select
|
select = select
|
||||||
.join_as(
|
.join_as(
|
||||||
JoinType::InnerJoin,
|
JoinType::InnerJoin,
|
||||||
note::Relation::UserNotePining.def(),
|
note::Relation::UserNotePining.with_alias(Alias::new(&prefix)),
|
||||||
ALIAS_PIN.clone(),
|
joined_prefix(&prefix, PIN),
|
||||||
)
|
)
|
||||||
.filter(
|
.filter(
|
||||||
Expr::col((ALIAS_PIN.clone(), user_note_pining::Column::UserId))
|
Expr::col((
|
||||||
.eq(&pins_user)
|
joined_prefix(&prefix, PIN),
|
||||||
|
user_note_pining::Column::UserId,
|
||||||
|
))
|
||||||
|
.eq(pins_user)
|
||||||
.into_condition(),
|
.into_condition(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(user_id) = &options.with_interactions_from {
|
self.attach_note(
|
||||||
select = select
|
QuerySelect::query(&mut select),
|
||||||
.add_sub_select_reaction(None, INTERACTION_REACTION, user_id)
|
&prefix,
|
||||||
.add_sub_select_renote(None, INTERACTION_RENOTE, user_id);
|
options.with_reply_target.then_some(1).unwrap_or_default(),
|
||||||
}
|
options.with_renote_target.then_some(1).unwrap_or_default(),
|
||||||
|
options,
|
||||||
if options.with_user {
|
&self.user_resolver,
|
||||||
select = select
|
|
||||||
.add_aliased_columns(Some(USER_AVATAR), drive_file::Entity)
|
|
||||||
.add_aliased_columns(Some(USER_BANNER), drive_file::Entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.with_reply_target {
|
|
||||||
select = select
|
|
||||||
.add_aliased_columns(Some(REPLY), note::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 {
|
|
||||||
select = select
|
|
||||||
.add_aliased_columns(Some(REPLY_USER_AVATAR), drive_file::Entity)
|
|
||||||
.add_aliased_columns(Some(REPLY_USER_BANNER), drive_file::Entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.with_renote_target {
|
|
||||||
select = select
|
|
||||||
.add_aliased_columns(Some(RENOTE), note::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 {
|
|
||||||
select = select
|
|
||||||
.add_aliased_columns(Some(RENOTE_USER_AVATAR), drive_file::Entity)
|
|
||||||
.add_aliased_columns(Some(RENOTE_USER_BANNER), drive_file::Entity)
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.with_reply_target {
|
|
||||||
select = select
|
|
||||||
.add_aliased_columns(Some(RENOTE_REPLY), note::Entity)
|
|
||||||
.add_aliased_columns(Some(RENOTE_REPLY_USER), user::Entity);
|
|
||||||
|
|
||||||
if let Some(user_id) = &options.with_interactions_from {
|
|
||||||
select = select
|
|
||||||
.add_sub_select_reaction(
|
|
||||||
Some(RENOTE_REPLY),
|
|
||||||
RENOTE_REPLY_INTERACTION_REACTION,
|
|
||||||
user_id,
|
|
||||||
)
|
|
||||||
.add_sub_select_renote(
|
|
||||||
Some(RENOTE_REPLY),
|
|
||||||
RENOTE_REPLY_INTERACTION_RENOTE,
|
|
||||||
user_id,
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if options.with_user {
|
|
||||||
select = select
|
|
||||||
.add_aliased_columns(Some(RENOTE_REPLY_USER_AVATAR), drive_file::Entity)
|
|
||||||
.add_aliased_columns(Some(RENOTE_REPLY_USER_BANNER), drive_file::Entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.with_reply_target {
|
|
||||||
select = select
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
note::Relation::SelfRef2.def(),
|
|
||||||
ALIAS_REPLY.clone(),
|
|
||||||
)
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
note::Relation::User.with_alias(ALIAS_REPLY.clone()),
|
|
||||||
ALIAS_REPLY_USER.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.with_renote_target {
|
|
||||||
select = select
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
note::Relation::SelfRef1.def(),
|
|
||||||
ALIAS_RENOTE.clone(),
|
|
||||||
)
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
note::Relation::User.with_alias(ALIAS_RENOTE.clone()),
|
|
||||||
ALIAS_RENOTE_USER.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if options.with_reply_target {
|
|
||||||
select = select
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
note::Relation::SelfRef2.with_alias(ALIAS_RENOTE.clone()),
|
|
||||||
ALIAS_RENOTE_REPLY.clone(),
|
|
||||||
)
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
note::Relation::User.with_alias(ALIAS_RENOTE_REPLY.clone()),
|
|
||||||
ALIAS_RENOTE_REPLY_USER.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
select = select.join_as(
|
|
||||||
JoinType::InnerJoin,
|
|
||||||
note::Relation::User.def(),
|
|
||||||
ALIAS_USER.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if options.with_user {
|
|
||||||
select = select
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
user::Relation::DriveFile2.with_alias(ALIAS_USER.clone()),
|
|
||||||
ALIAS_USER_AVATAR.clone(),
|
|
||||||
)
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
user::Relation::DriveFile1.with_alias(ALIAS_USER.clone()),
|
|
||||||
ALIAS_USER_BANNER.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if options.with_reply_target {
|
|
||||||
select = select
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
user::Relation::DriveFile2.with_alias(ALIAS_REPLY_USER.clone()),
|
|
||||||
ALIAS_REPLY_USER_AVATAR.clone(),
|
|
||||||
)
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
user::Relation::DriveFile1.with_alias(ALIAS_REPLY_USER.clone()),
|
|
||||||
ALIAS_REPLY_USER_BANNER.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.with_renote_target {
|
|
||||||
select = select
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
user::Relation::DriveFile2.with_alias(ALIAS_RENOTE_USER.clone()),
|
|
||||||
ALIAS_RENOTE_USER_AVATAR.clone(),
|
|
||||||
)
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
user::Relation::DriveFile1.with_alias(ALIAS_RENOTE_USER.clone()),
|
|
||||||
ALIAS_RENOTE_USER_BANNER.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if options.with_reply_target {
|
|
||||||
select = select
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
user::Relation::DriveFile2.with_alias(ALIAS_RENOTE_REPLY_USER.clone()),
|
|
||||||
ALIAS_RENOTE_REPLY_USER_AVATAR.clone(),
|
|
||||||
)
|
|
||||||
.join_as(
|
|
||||||
JoinType::LeftJoin,
|
|
||||||
user::Relation::DriveFile1.with_alias(ALIAS_RENOTE_REPLY_USER.clone()),
|
|
||||||
ALIAS_RENOTE_REPLY_USER_BANNER.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
select
|
select
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
use ext_calckey_model_migration::{Expr, SimpleExpr};
|
||||||
|
use magnetar_sdk::types::{EndFilter, RangeFilter, SpanFilter, StartFilter};
|
||||||
|
use sea_orm::Iden;
|
||||||
|
|
||||||
|
pub trait PaginatedModel: 'static {
|
||||||
|
fn time_column() -> impl Iden;
|
||||||
|
fn id_column() -> impl Iden;
|
||||||
|
|
||||||
|
fn range_into_expr(filter: &SpanFilter) -> Option<SimpleExpr> {
|
||||||
|
match filter {
|
||||||
|
SpanFilter::Range(RangeFilter {
|
||||||
|
time_start,
|
||||||
|
time_end,
|
||||||
|
id_start,
|
||||||
|
id_end,
|
||||||
|
}) => Some(
|
||||||
|
Expr::tuple([
|
||||||
|
Expr::col(Self::time_column()).into(),
|
||||||
|
Expr::col(Self::id_column()).into(),
|
||||||
|
])
|
||||||
|
.between(
|
||||||
|
Expr::tuple([
|
||||||
|
Expr::value(time_start.clone()),
|
||||||
|
Expr::value(id_start.clone()),
|
||||||
|
]),
|
||||||
|
Expr::tuple([Expr::value(time_end.clone()), Expr::value(id_end.clone())]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpanFilter::Start(StartFilter {
|
||||||
|
id_start,
|
||||||
|
time_start,
|
||||||
|
}) => Some(
|
||||||
|
Expr::tuple([
|
||||||
|
Expr::col(Self::time_column()).into(),
|
||||||
|
Expr::col(Self::id_column()).into(),
|
||||||
|
])
|
||||||
|
.gt(Expr::tuple([
|
||||||
|
Expr::value(time_start.clone()),
|
||||||
|
Expr::value(id_start.clone()),
|
||||||
|
])),
|
||||||
|
),
|
||||||
|
SpanFilter::End(EndFilter { id_end, time_end }) => Some(
|
||||||
|
Expr::tuple([
|
||||||
|
Expr::col(Self::time_column()).into(),
|
||||||
|
Expr::col(Self::id_column()).into(),
|
||||||
|
])
|
||||||
|
.lt(Expr::tuple([
|
||||||
|
Expr::value(time_end.clone()),
|
||||||
|
Expr::value(id_end.clone()),
|
||||||
|
])),
|
||||||
|
),
|
||||||
|
SpanFilter::None(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaginatedModel for ck::note::Model {
|
||||||
|
fn time_column() -> impl Iden {
|
||||||
|
ck::note::Column::CreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id_column() -> impl Iden {
|
||||||
|
ck::note::Column::Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaginatedModel for ck::user::Model {
|
||||||
|
fn time_column() -> impl Iden {
|
||||||
|
ck::user::Column::CreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id_column() -> impl Iden {
|
||||||
|
ck::user::Column::Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaginatedModel for ck::following::Model {
|
||||||
|
fn time_column() -> impl Iden {
|
||||||
|
ck::following::Column::CreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id_column() -> impl Iden {
|
||||||
|
ck::following::Column::Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaginatedModel for ck::follow_request::Model {
|
||||||
|
fn time_column() -> impl Iden {
|
||||||
|
ck::follow_request::Column::CreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id_column() -> impl Iden {
|
||||||
|
ck::follow_request::Column::Id
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
use crate::model_ext::EntityPrefixExt;
|
||||||
|
use crate::{
|
||||||
|
model_ext::{joined_prefix, joined_prefix_str, SelectColumnsExt},
|
||||||
|
AliasSourceExt, CalckeyModel,
|
||||||
|
};
|
||||||
|
use ck::{drive_file, follow_request, following, user};
|
||||||
|
use ext_calckey_model_migration::{Alias, SelectStatement};
|
||||||
|
use sea_orm::{DbErr, EntityName, FromQueryResult, JoinType, QueryResult};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct UserData {
|
||||||
|
pub user: user::Model,
|
||||||
|
pub avatar: Option<drive_file::Model>,
|
||||||
|
pub banner: Option<drive_file::Model>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UserResolveOptions {
|
||||||
|
pub with_avatar_and_banner: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
const AVATAR: &str = "avatar.";
|
||||||
|
const BANNER: &str = "banner.";
|
||||||
|
|
||||||
|
impl FromQueryResult for UserData {
|
||||||
|
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
|
||||||
|
let fallback = user::Entity.base_prefix_str();
|
||||||
|
let prefix = if prefix.is_empty() { &fallback } else { prefix };
|
||||||
|
|
||||||
|
Ok(UserData {
|
||||||
|
user: user::Model::from_query_result(res, prefix)?,
|
||||||
|
avatar: drive_file::Model::from_query_result_optional(
|
||||||
|
res,
|
||||||
|
&joined_prefix_str(prefix, AVATAR),
|
||||||
|
)?,
|
||||||
|
banner: drive_file::Model::from_query_result_optional(
|
||||||
|
res,
|
||||||
|
&joined_prefix_str(prefix, BANNER),
|
||||||
|
)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UserResolver {
|
||||||
|
db: CalckeyModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserResolver {
|
||||||
|
pub fn new(db: CalckeyModel) -> Self {
|
||||||
|
Self { db }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(
|
||||||
|
&self,
|
||||||
|
q: &mut SelectStatement,
|
||||||
|
prefix: &str,
|
||||||
|
UserResolveOptions {
|
||||||
|
with_avatar_and_banner,
|
||||||
|
}: &UserResolveOptions,
|
||||||
|
) {
|
||||||
|
q.add_aliased_columns::<user::Entity>(prefix);
|
||||||
|
|
||||||
|
if *with_avatar_and_banner {
|
||||||
|
q.add_aliased_columns::<drive_file::Entity>(&joined_prefix_str(prefix, AVATAR))
|
||||||
|
.add_aliased_columns::<drive_file::Entity>(&joined_prefix_str(prefix, BANNER));
|
||||||
|
|
||||||
|
q.join_columns(
|
||||||
|
JoinType::LeftJoin,
|
||||||
|
user::Relation::DriveFile2.with_alias(Alias::new(prefix)),
|
||||||
|
joined_prefix(prefix, AVATAR),
|
||||||
|
)
|
||||||
|
.join_columns(
|
||||||
|
JoinType::LeftJoin,
|
||||||
|
user::Relation::DriveFile1.with_alias(Alias::new(prefix)),
|
||||||
|
joined_prefix(prefix, BANNER),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_followers(&self, user_id: &str) -> Vec<UserData> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { PackNoteMaybeAttachments } from "./packed/PackNoteMaybeAttachments";
|
import type { PackNoteMaybeFull } from "./packed/PackNoteMaybeFull";
|
||||||
|
|
||||||
export interface NoteDetailExt { parent_note: PackNoteMaybeAttachments | null, renoted_note: PackNoteMaybeAttachments | null, }
|
export interface NoteDetailExt { parent_note: PackNoteMaybeFull | null, renoted_note: PackNoteMaybeFull | null, }
|
|
@ -1,3 +1,3 @@
|
||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
export type TimelineType = "home" | "timeline" | "recommended" | "hybrid" | "global";
|
export type TimelineType = "home" | "recommended" | "hybrid" | "global" | "local";
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::endpoints::Endpoint;
|
use crate::endpoints::{Empty, Endpoint};
|
||||||
use crate::util_types::deserialize_array_urlenc;
|
use crate::util_types::deserialize_array_urlenc;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use magnetar_sdk_macros::Endpoint;
|
use magnetar_sdk_macros::Endpoint;
|
||||||
|
@ -82,3 +82,48 @@ pub struct GetManyUsersById;
|
||||||
response = "PackUserMaybeAll"
|
response = "PackUserMaybeAll"
|
||||||
)]
|
)]
|
||||||
pub struct GetUserByAcct;
|
pub struct GetUserByAcct;
|
||||||
|
|
||||||
|
#[derive(Endpoint)]
|
||||||
|
#[endpoint(
|
||||||
|
endpoint = "/users/:id/followers",
|
||||||
|
method = Method::GET,
|
||||||
|
request = "Empty",
|
||||||
|
response = "Vec<PackUserBase>"
|
||||||
|
)]
|
||||||
|
pub struct GetFollowersById;
|
||||||
|
|
||||||
|
#[derive(Endpoint)]
|
||||||
|
#[endpoint(
|
||||||
|
endpoint = "/users/:id/following",
|
||||||
|
method = Method::GET,
|
||||||
|
request = "Empty",
|
||||||
|
response = "Vec<PackUserBase>"
|
||||||
|
)]
|
||||||
|
pub struct GetFollowingById;
|
||||||
|
|
||||||
|
#[derive(Endpoint)]
|
||||||
|
#[endpoint(
|
||||||
|
endpoint = "/users/@self/followers",
|
||||||
|
method = Method::GET,
|
||||||
|
request = "Empty",
|
||||||
|
response = "Vec<PackUserBase>"
|
||||||
|
)]
|
||||||
|
pub struct GetFollowersSelf;
|
||||||
|
|
||||||
|
#[derive(Endpoint)]
|
||||||
|
#[endpoint(
|
||||||
|
endpoint = "/users/@self/following",
|
||||||
|
method = Method::GET,
|
||||||
|
request = "Empty",
|
||||||
|
response = "Vec<PackUserBase>"
|
||||||
|
)]
|
||||||
|
pub struct GetFollowingSelf;
|
||||||
|
|
||||||
|
#[derive(Endpoint)]
|
||||||
|
#[endpoint(
|
||||||
|
endpoint = "/users/@self/follow-requests",
|
||||||
|
method = Method::GET,
|
||||||
|
request = "Empty",
|
||||||
|
response = "Vec<PackUserBase>"
|
||||||
|
)]
|
||||||
|
pub struct GetFollowRequestsSelf;
|
||||||
|
|
|
@ -7,7 +7,6 @@ pub mod user;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::RangeInclusive;
|
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
|
|
@ -99,6 +99,15 @@ pub struct UserProfilePinsEx {
|
||||||
// pub pinned_page: Option<Page>,
|
// pub pinned_page: Option<Page>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
pub enum UserRelationship {
|
||||||
|
Follow,
|
||||||
|
FollowRequest,
|
||||||
|
Mute,
|
||||||
|
Block,
|
||||||
|
RenoteMute,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserRelationExt {
|
pub struct UserRelationExt {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::model::processing::user::UserModel;
|
use crate::model::processing::user::{UserBorrowedData, UserModel};
|
||||||
use crate::model::processing::PackError;
|
use crate::model::processing::PackError;
|
||||||
use crate::model::PackingContext;
|
use crate::model::PackingContext;
|
||||||
use crate::service::MagnetarService;
|
use crate::service::MagnetarService;
|
||||||
|
@ -11,6 +11,7 @@ use futures_util::TryStreamExt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use magnetar_common::util::lenient_parse_tag_decode;
|
use magnetar_common::util::lenient_parse_tag_decode;
|
||||||
use magnetar_sdk::endpoints::user::{
|
use magnetar_sdk::endpoints::user::{
|
||||||
|
GetFollowRequestsSelf, GetFollowersById, GetFollowersSelf, GetFollowingById, GetFollowingSelf,
|
||||||
GetManyUsersById, GetUserByAcct, GetUserById, GetUserSelf, ManyUsersByIdReq,
|
GetManyUsersById, GetUserByAcct, GetUserById, GetUserSelf, ManyUsersByIdReq,
|
||||||
};
|
};
|
||||||
use magnetar_sdk::endpoints::{Req, Res};
|
use magnetar_sdk::endpoints::{Req, Res};
|
||||||
|
@ -24,7 +25,15 @@ pub async fn handle_user_info_self(
|
||||||
) -> Result<Json<Res<GetUserSelf>>, ApiError> {
|
) -> Result<Json<Res<GetUserSelf>>, ApiError> {
|
||||||
let ctx = PackingContext::new(service, Some(user.clone())).await?;
|
let ctx = PackingContext::new(service, Some(user.clone())).await?;
|
||||||
let user = UserModel
|
let user = UserModel
|
||||||
.self_full_from_base(&ctx, user.as_ref(), &req, None, None)
|
.self_full_from_base(
|
||||||
|
&ctx,
|
||||||
|
&UserBorrowedData {
|
||||||
|
user: user.as_ref(),
|
||||||
|
avatar: None,
|
||||||
|
banner: None,
|
||||||
|
},
|
||||||
|
&req,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(Json(user))
|
Ok(Json(user))
|
||||||
}
|
}
|
||||||
|
@ -43,7 +52,15 @@ pub async fn handle_user_info(
|
||||||
.ok_or(ObjectNotFound(id))?;
|
.ok_or(ObjectNotFound(id))?;
|
||||||
|
|
||||||
let user = UserModel
|
let user = UserModel
|
||||||
.foreign_full_from_base(&ctx, &user_model, &req, None, None)
|
.foreign_full_from_base(
|
||||||
|
&ctx,
|
||||||
|
&UserBorrowedData {
|
||||||
|
user: &user_model,
|
||||||
|
avatar: None,
|
||||||
|
banner: None,
|
||||||
|
},
|
||||||
|
&req,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(Json(user))
|
Ok(Json(user))
|
||||||
}
|
}
|
||||||
|
@ -67,7 +84,15 @@ pub async fn handle_user_info_by_acct(
|
||||||
.ok_or(ObjectNotFound(tag_str))?;
|
.ok_or(ObjectNotFound(tag_str))?;
|
||||||
|
|
||||||
let user = UserModel
|
let user = UserModel
|
||||||
.foreign_full_from_base(&ctx, &user_model, &req, None, None)
|
.foreign_full_from_base(
|
||||||
|
&ctx,
|
||||||
|
&UserBorrowedData {
|
||||||
|
user: &user_model,
|
||||||
|
avatar: None,
|
||||||
|
banner: None,
|
||||||
|
},
|
||||||
|
&req,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(Json(user))
|
Ok(Json(user))
|
||||||
}
|
}
|
||||||
|
@ -89,9 +114,18 @@ pub async fn handle_user_by_id_many(
|
||||||
let ctx = PackingContext::new(service, user.clone()).await?;
|
let ctx = PackingContext::new(service, user.clone()).await?;
|
||||||
let user_model = UserModel;
|
let user_model = UserModel;
|
||||||
|
|
||||||
let futures = users
|
let user_data = users
|
||||||
.iter()
|
.iter()
|
||||||
.map(|u| user_model.base_from_existing(&ctx, u, None))
|
.map(|user| UserBorrowedData {
|
||||||
|
user,
|
||||||
|
avatar: None,
|
||||||
|
banner: None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let futures = user_data
|
||||||
|
.iter()
|
||||||
|
.map(|user| user_model.base_from_existing(&ctx, user))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let users_proc = futures::stream::iter(futures)
|
let users_proc = futures::stream::iter(futures)
|
||||||
|
|
|
@ -71,6 +71,7 @@ impl PackType<NoteBaseSource<'_>> for NoteBase {
|
||||||
has_poll: note.has_poll,
|
has_poll: note.has_poll,
|
||||||
file_ids: note.file_ids.clone(),
|
file_ids: note.file_ids.clone(),
|
||||||
emojis: emoji_context.clone(),
|
emojis: emoji_context.clone(),
|
||||||
|
is_quote: note.is_quote.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::model::processing::PackResult;
|
||||||
use crate::service::MagnetarService;
|
use crate::service::MagnetarService;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use magnetar_calckey_model::{ck, CalckeyDbError};
|
use magnetar_calckey_model::{ck, CalckeyDbError};
|
||||||
|
use magnetar_sdk::types::user::UserRelationship;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
@ -21,15 +22,6 @@ impl Default for ProcessingLimits {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
|
||||||
enum UserRelationship {
|
|
||||||
Follow,
|
|
||||||
FollowRequest,
|
|
||||||
Mute,
|
|
||||||
Block,
|
|
||||||
RenoteMute,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
struct UserRelationshipLink {
|
struct UserRelationshipLink {
|
||||||
from: String,
|
from: String,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::model::data::id::BaseId;
|
use crate::model::data::id::BaseId;
|
||||||
use crate::model::data::note::{NoteAttachmentSource, NoteBaseSource, NoteDetailSource};
|
use crate::model::data::note::{NoteAttachmentSource, NoteBaseSource, NoteDetailSource};
|
||||||
use crate::model::processing::emoji::EmojiModel;
|
use crate::model::processing::emoji::EmojiModel;
|
||||||
use crate::model::processing::user::UserModel;
|
use crate::model::processing::user::UserModel;
|
||||||
use crate::model::processing::{get_mm_token_emoji, PackError, PackResult};
|
use crate::model::processing::{get_mm_token_emoji, PackError, PackResult};
|
||||||
use crate::model::{PackType, PackingContext, UserRelationship};
|
use crate::model::{PackType, PackingContext};
|
||||||
use compact_str::CompactString;
|
use compact_str::CompactString;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use futures_util::future::{try_join_all, BoxFuture};
|
use futures_util::future::{try_join_all, BoxFuture};
|
||||||
|
@ -11,7 +13,8 @@ use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt};
|
||||||
use magnetar_calckey_model::ck::sea_orm_active_enums::NoteVisibilityEnum;
|
use magnetar_calckey_model::ck::sea_orm_active_enums::NoteVisibilityEnum;
|
||||||
use magnetar_calckey_model::emoji::EmojiTag;
|
use magnetar_calckey_model::emoji::EmojiTag;
|
||||||
use magnetar_calckey_model::note_model::{
|
use magnetar_calckey_model::note_model::{
|
||||||
NoteData, NoteResolveOptions, NoteVisibilityFilterFactory,
|
sub_interaction_reaction, sub_interaction_renote, NoteData, NoteResolveOptions,
|
||||||
|
NoteVisibilityFilterFactory,
|
||||||
};
|
};
|
||||||
use magnetar_calckey_model::poll::PollResolver;
|
use magnetar_calckey_model::poll::PollResolver;
|
||||||
use magnetar_calckey_model::sea_orm::prelude::Expr;
|
use magnetar_calckey_model::sea_orm::prelude::Expr;
|
||||||
|
@ -27,12 +30,14 @@ use magnetar_sdk::types::note::{
|
||||||
PackNoteMaybeAttachments, PackNoteMaybeFull, PackPollBase, PollBase, Reaction, ReactionPair,
|
PackNoteMaybeAttachments, PackNoteMaybeFull, PackPollBase, PollBase, Reaction, ReactionPair,
|
||||||
ReactionShortcode, ReactionUnicode, ReactionUnknown,
|
ReactionShortcode, ReactionUnicode, ReactionUnknown,
|
||||||
};
|
};
|
||||||
|
use magnetar_sdk::types::user::UserRelationship;
|
||||||
use magnetar_sdk::types::{Id, MmXml};
|
use magnetar_sdk::types::{Id, MmXml};
|
||||||
use magnetar_sdk::{mmm, Optional, Packed, Required};
|
use magnetar_sdk::{mmm, Optional, Packed, Required};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::try_join;
|
use tokio::try_join;
|
||||||
|
|
||||||
use super::drive::DriveModel;
|
use super::drive::DriveModel;
|
||||||
|
use super::user::UserShapedData;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NoteVisibilityFilterSimple(Option<String>);
|
pub struct NoteVisibilityFilterSimple(Option<String>);
|
||||||
|
@ -161,6 +166,80 @@ impl NoteVisibilityFilterModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait NoteShapedData<'a>: Send + Sync {
|
||||||
|
fn note(&self) -> &'a ck::note::Model;
|
||||||
|
fn interaction_user_renote(&self) -> Option<&'a sub_interaction_renote::Model>;
|
||||||
|
fn interaction_user_reaction(&self) -> Option<&'a sub_interaction_reaction::Model>;
|
||||||
|
fn user(&self) -> Arc<dyn UserShapedData<'a> + 'a>;
|
||||||
|
fn reply(&self) -> Option<Arc<dyn NoteShapedData<'a> + 'a>>;
|
||||||
|
fn renote(&self) -> Option<Arc<dyn NoteShapedData<'a> + 'a>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NoteBorrowedData<'a> {
|
||||||
|
pub note: &'a ck::note::Model,
|
||||||
|
pub interaction_user_renote: Option<&'a sub_interaction_renote::Model>,
|
||||||
|
pub interaction_user_reaction: Option<&'a sub_interaction_reaction::Model>,
|
||||||
|
pub user: Arc<dyn UserShapedData<'a> + 'a>,
|
||||||
|
pub reply: Option<Arc<dyn NoteShapedData<'a> + 'a>>,
|
||||||
|
pub renote: Option<Arc<dyn NoteShapedData<'a> + 'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NoteShapedData<'a> for NoteBorrowedData<'a> {
|
||||||
|
fn note(&self) -> &'a ck::note::Model {
|
||||||
|
self.note
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interaction_user_renote(&self) -> Option<&'a sub_interaction_renote::Model> {
|
||||||
|
self.interaction_user_renote
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interaction_user_reaction(&self) -> Option<&'a sub_interaction_reaction::Model> {
|
||||||
|
self.interaction_user_reaction
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user(&self) -> Arc<dyn UserShapedData<'a> + 'a> {
|
||||||
|
self.user.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reply(&self) -> Option<Arc<dyn NoteShapedData<'a> + 'a>> {
|
||||||
|
self.reply.as_ref().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renote(&self) -> Option<Arc<dyn NoteShapedData<'a> + 'a>> {
|
||||||
|
self.renote.as_ref().cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NoteShapedData<'a> for &'a NoteData {
|
||||||
|
fn note(&self) -> &'a ck::note::Model {
|
||||||
|
&self.note
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interaction_user_renote(&self) -> Option<&'a sub_interaction_renote::Model> {
|
||||||
|
self.interaction_user_renote.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interaction_user_reaction(&self) -> Option<&'a sub_interaction_reaction::Model> {
|
||||||
|
self.interaction_user_reaction.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user(&self) -> Arc<dyn UserShapedData<'a> + 'a> {
|
||||||
|
Arc::new(&self.user)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reply(&self) -> Option<Arc<dyn NoteShapedData<'a> + 'a>> {
|
||||||
|
self.reply
|
||||||
|
.as_deref()
|
||||||
|
.map(|x| Arc::new(x) as Arc<dyn NoteShapedData<'a> + 'a>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renote(&self) -> Option<Arc<dyn NoteShapedData<'a> + 'a>> {
|
||||||
|
self.renote
|
||||||
|
.as_deref()
|
||||||
|
.map(|x| Arc::new(x) as Arc<dyn NoteShapedData<'a> + 'a>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SpeechTransformNyan;
|
struct SpeechTransformNyan;
|
||||||
|
|
||||||
impl SpeechTransformNyan {
|
impl SpeechTransformNyan {
|
||||||
|
@ -194,15 +273,17 @@ impl NoteModel {
|
||||||
pub async fn extract_base(
|
pub async fn extract_base(
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
note_data: &NoteData,
|
note_data: &dyn NoteShapedData<'_>,
|
||||||
) -> PackResult<PackNoteBase> {
|
) -> PackResult<PackNoteBase> {
|
||||||
|
let note = note_data.note();
|
||||||
|
|
||||||
let Required(ref user) = UserModel
|
let Required(ref user) = UserModel
|
||||||
.base_from_existing(ctx, ¬e_data.user, None)
|
.base_from_existing(ctx, note_data.user().as_ref())
|
||||||
.await?
|
.await?
|
||||||
.user;
|
.user;
|
||||||
|
|
||||||
let cw_tok = self.tokenize_note_cw(¬e_data.note);
|
let cw_tok = self.tokenize_note_cw(note);
|
||||||
let mut text_tok = self.tokenize_note_text(¬e_data.note);
|
let mut text_tok = self.tokenize_note_text(note);
|
||||||
|
|
||||||
let mut emoji_extracted = Vec::new();
|
let mut emoji_extracted = Vec::new();
|
||||||
|
|
||||||
|
@ -213,7 +294,7 @@ impl NoteModel {
|
||||||
if let Some(text_tok) = &mut text_tok {
|
if let Some(text_tok) = &mut text_tok {
|
||||||
emoji_extracted.extend_from_slice(&get_mm_token_emoji(text_tok));
|
emoji_extracted.extend_from_slice(&get_mm_token_emoji(text_tok));
|
||||||
|
|
||||||
if note_data.user.is_cat && note_data.user.speak_as_cat {
|
if note_data.user().user().is_cat && note_data.user().user().speak_as_cat {
|
||||||
let transformer = SpeechTransformNyan::new();
|
let transformer = SpeechTransformNyan::new();
|
||||||
text_tok.walk_speech_transform(&|text| transformer.transform(text));
|
text_tok.walk_speech_transform(&|text| transformer.transform(text));
|
||||||
}
|
}
|
||||||
|
@ -225,7 +306,7 @@ impl NoteModel {
|
||||||
// Parse the JSON into an ordered map and turn it into a Vec of pairs, parsing the reaction codes
|
// Parse the JSON into an ordered map and turn it into a Vec of pairs, parsing the reaction codes
|
||||||
// Failed reaction parses -> Left, Successful ones -> Right
|
// Failed reaction parses -> Left, Successful ones -> Right
|
||||||
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.reactions)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(ref code, count)| {
|
.map(|(ref code, count)| {
|
||||||
let reaction = parse_reaction(code)
|
let reaction = parse_reaction(code)
|
||||||
|
@ -235,7 +316,7 @@ impl NoteModel {
|
||||||
reaction,
|
reaction,
|
||||||
count,
|
count,
|
||||||
note_data
|
note_data
|
||||||
.interaction_user_reaction
|
.interaction_user_reaction()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|r| r.reaction_name.as_deref())
|
.and_then(|r| r.reaction_name.as_deref())
|
||||||
.map(|r| r == code),
|
.map(|r| r == code),
|
||||||
|
@ -266,8 +347,11 @@ impl NoteModel {
|
||||||
.emoji_cache
|
.emoji_cache
|
||||||
.get_many_tagged(&reactions_to_resolve)
|
.get_many_tagged(&reactions_to_resolve)
|
||||||
.map_err(PackError::from);
|
.map_err(PackError::from);
|
||||||
let emoji_fetch =
|
let emoji_fetch = emoji_model.fetch_many_emojis(
|
||||||
emoji_model.fetch_many_emojis(ctx, &shortcodes, note_data.user.host.as_deref());
|
ctx,
|
||||||
|
&shortcodes,
|
||||||
|
note_data.user().user().host.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
let (reactions_fetched, emojis) = try_join!(reaction_fetch, emoji_fetch)?;
|
let (reactions_fetched, emojis) = try_join!(reaction_fetch, emoji_fetch)?;
|
||||||
|
|
||||||
|
@ -318,7 +402,7 @@ impl NoteModel {
|
||||||
let note_base = NoteBase::extract(
|
let note_base = NoteBase::extract(
|
||||||
ctx,
|
ctx,
|
||||||
NoteBaseSource {
|
NoteBaseSource {
|
||||||
note: ¬e_data.note,
|
note,
|
||||||
cw_mm: cw_tok
|
cw_mm: cw_tok
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(mmm::to_xml_string)
|
.map(mmm::to_xml_string)
|
||||||
|
@ -338,7 +422,7 @@ impl NoteModel {
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(PackNoteBase::pack_from((
|
Ok(PackNoteBase::pack_from((
|
||||||
Required(Id::from(note_data.note.id.clone())),
|
Required(Id::from(note.id.clone())),
|
||||||
Required(note_base),
|
Required(note_base),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
@ -346,11 +430,10 @@ impl NoteModel {
|
||||||
fn extract_interaction(
|
fn extract_interaction(
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
note: &NoteData,
|
note: &dyn NoteShapedData<'_>,
|
||||||
) -> PackResult<Option<NoteSelfContextExt>> {
|
) -> PackResult<Option<NoteSelfContextExt>> {
|
||||||
Ok(note
|
Ok(note
|
||||||
.interaction_user_renote
|
.interaction_user_renote()
|
||||||
.as_ref()
|
|
||||||
.map(|renote_info| NoteSelfContextExt::extract(ctx, renote_info)))
|
.map(|renote_info| NoteSelfContextExt::extract(ctx, renote_info)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,12 +488,12 @@ impl NoteModel {
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
drive_model: &DriveModel,
|
drive_model: &DriveModel,
|
||||||
note_data: &NoteData,
|
note_data: &dyn NoteShapedData<'_>,
|
||||||
) -> PackResult<PackNoteMaybeAttachments> {
|
) -> PackResult<PackNoteMaybeAttachments> {
|
||||||
let (PackNoteBase { id, note }, attachments_pack, poll_pack) = try_join!(
|
let (PackNoteBase { id, note }, attachments_pack, poll_pack) = try_join!(
|
||||||
self.extract_base(ctx, note_data),
|
self.extract_base(ctx, note_data),
|
||||||
self.extract_attachments(ctx, drive_model, ¬e_data.note),
|
self.extract_attachments(ctx, drive_model, note_data.note()),
|
||||||
self.extract_poll(ctx, ¬e_data.note)
|
self.extract_poll(ctx, note_data.note())
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(PackNoteMaybeAttachments::pack_from((
|
Ok(PackNoteMaybeAttachments::pack_from((
|
||||||
|
|
|
@ -3,10 +3,11 @@ use crate::model::processing::drive::DriveModel;
|
||||||
use crate::model::processing::emoji::EmojiModel;
|
use crate::model::processing::emoji::EmojiModel;
|
||||||
use crate::model::processing::note::NoteModel;
|
use crate::model::processing::note::NoteModel;
|
||||||
use crate::model::processing::{get_mm_token_emoji, PackError, PackResult};
|
use crate::model::processing::{get_mm_token_emoji, PackError, PackResult};
|
||||||
use crate::model::{PackType, PackingContext, UserRelationship};
|
use crate::model::{PackType, PackingContext};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use futures_util::future::OptionFuture;
|
use futures_util::future::OptionFuture;
|
||||||
use magnetar_calckey_model::ck;
|
use magnetar_calckey_model::ck;
|
||||||
|
use magnetar_calckey_model::user_model::UserData;
|
||||||
use magnetar_sdk::endpoints::user::{UserByIdReq, UserSelfReq};
|
use magnetar_sdk::endpoints::user::{UserByIdReq, UserSelfReq};
|
||||||
use magnetar_sdk::mmm::Token;
|
use magnetar_sdk::mmm::Token;
|
||||||
use magnetar_sdk::types::drive::PackDriveFileBase;
|
use magnetar_sdk::types::drive::PackDriveFileBase;
|
||||||
|
@ -15,7 +16,7 @@ use magnetar_sdk::types::instance::InstanceTicker;
|
||||||
use magnetar_sdk::types::user::{
|
use magnetar_sdk::types::user::{
|
||||||
MovedTo, PackSecurityKeyBase, PackUserBase, PackUserMaybeAll, PackUserSelfMaybeAll,
|
MovedTo, PackSecurityKeyBase, PackUserBase, PackUserMaybeAll, PackUserSelfMaybeAll,
|
||||||
ProfileField, SecurityKeyBase, UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt,
|
ProfileField, SecurityKeyBase, UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt,
|
||||||
UserProfilePinsEx, UserRelationExt, UserSecretsExt,
|
UserProfilePinsEx, UserRelationExt, UserRelationship, UserSecretsExt,
|
||||||
};
|
};
|
||||||
use magnetar_sdk::types::{Id, MmXml};
|
use magnetar_sdk::types::{Id, MmXml};
|
||||||
use magnetar_sdk::{mmm, Optional, Packed, Required};
|
use magnetar_sdk::{mmm, Optional, Packed, Required};
|
||||||
|
@ -24,6 +25,46 @@ use tokio::{join, try_join};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
pub trait UserShapedData<'a>: Send + Sync {
|
||||||
|
fn user(&self) -> &'a ck::user::Model;
|
||||||
|
fn avatar(&self) -> Option<&'a ck::drive_file::Model>;
|
||||||
|
fn banner(&self) -> Option<&'a ck::drive_file::Model>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UserBorrowedData<'a> {
|
||||||
|
pub user: &'a ck::user::Model,
|
||||||
|
pub avatar: Option<&'a ck::drive_file::Model>,
|
||||||
|
pub banner: Option<&'a ck::drive_file::Model>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> UserShapedData<'a> for &'a UserData {
|
||||||
|
fn user(&self) -> &'a ck::user::Model {
|
||||||
|
&self.user
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avatar(&self) -> Option<&'a ck::drive_file::Model> {
|
||||||
|
self.avatar.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn banner(&self) -> Option<&'a ck::drive_file::Model> {
|
||||||
|
self.banner.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> UserShapedData<'a> for UserBorrowedData<'a> {
|
||||||
|
fn user(&self) -> &'a ck::user::Model {
|
||||||
|
self.user
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avatar(&self) -> Option<&'a ck::drive_file::Model> {
|
||||||
|
self.avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
fn banner(&self) -> Option<&'a ck::drive_file::Model> {
|
||||||
|
self.banner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ProfileFieldRaw<'a> {
|
pub struct ProfileFieldRaw<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
@ -74,14 +115,15 @@ impl UserModel {
|
||||||
})?)
|
})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn base_from_existing(
|
pub async fn base_from_existing<'a>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
user: &ck::user::Model,
|
user_data: &dyn UserShapedData<'a>,
|
||||||
hint_avatar_file: Option<&ck::drive_file::Model>,
|
|
||||||
) -> PackResult<PackUserBase> {
|
) -> PackResult<PackUserBase> {
|
||||||
|
let user = user_data.user();
|
||||||
|
|
||||||
let drive_file_pack = DriveModel;
|
let drive_file_pack = DriveModel;
|
||||||
let avatar = match (hint_avatar_file, &user.avatar_id) {
|
let avatar = match (user_data.avatar(), &user.avatar_id) {
|
||||||
(Some(avatar_file), _) => Some(drive_file_pack.pack_existing(ctx, avatar_file)),
|
(Some(avatar_file), _) => Some(drive_file_pack.pack_existing(ctx, avatar_file)),
|
||||||
(None, Some(av_id)) => drive_file_pack.get_cached_base(ctx, av_id).await?,
|
(None, Some(av_id)) => drive_file_pack.get_cached_base(ctx, av_id).await?,
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -110,7 +152,7 @@ impl UserModel {
|
||||||
let base = UserBase::extract(
|
let base = UserBase::extract(
|
||||||
ctx,
|
ctx,
|
||||||
UserBaseSource {
|
UserBaseSource {
|
||||||
user,
|
user: &user,
|
||||||
username_mm: mmm::to_xml_string(&username_mm).map(MmXml).as_ref().ok(),
|
username_mm: mmm::to_xml_string(&username_mm).map(MmXml).as_ref().ok(),
|
||||||
avatar_url,
|
avatar_url,
|
||||||
avatar: avatar.as_ref(),
|
avatar: avatar.as_ref(),
|
||||||
|
@ -137,17 +179,18 @@ impl UserModel {
|
||||||
.ok_or_else(|| PackError::DataError("Missing user profile".to_string()))
|
.ok_or_else(|| PackError::DataError("Missing user profile".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn profile_from_base(
|
pub async fn profile_from_base<'a>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
user: &ck::user::Model,
|
user_data: &dyn UserShapedData<'a>,
|
||||||
profile: &ck::user_profile::Model,
|
profile: &ck::user_profile::Model,
|
||||||
relation: Option<&UserRelationExt>,
|
relation: Option<&UserRelationExt>,
|
||||||
emoji_out: &mut EmojiContext,
|
emoji_out: &mut EmojiContext,
|
||||||
hint_banner_file: Option<&ck::drive_file::Model>,
|
|
||||||
) -> PackResult<UserProfileExt> {
|
) -> PackResult<UserProfileExt> {
|
||||||
|
let user = user_data.user();
|
||||||
|
|
||||||
let drive_file_pack = DriveModel;
|
let drive_file_pack = DriveModel;
|
||||||
let banner = match (hint_banner_file, &user.banner_id) {
|
let banner = match (user_data.banner(), &user.banner_id) {
|
||||||
(Some(banner_file), _) => Some(drive_file_pack.pack_existing(ctx, banner_file)),
|
(Some(banner_file), _) => Some(drive_file_pack.pack_existing(ctx, banner_file)),
|
||||||
(None, Some(av_id)) => drive_file_pack.get_cached_base(ctx, av_id).await?,
|
(None, Some(av_id)) => drive_file_pack.get_cached_base(ctx, av_id).await?,
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -244,22 +287,20 @@ impl UserModel {
|
||||||
Ok(UserSecretsExt::extract(ctx, (profile, &secrets)))
|
Ok(UserSecretsExt::extract(ctx, (profile, &secrets)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn self_full_from_base(
|
pub async fn self_full_from_base<'a>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
user: &ck::user::Model,
|
user_data: &impl UserShapedData<'a>,
|
||||||
req: &UserSelfReq,
|
req: &UserSelfReq,
|
||||||
hint_avatar_file: Option<&ck::drive_file::Model>,
|
|
||||||
hint_banner_file: Option<&ck::drive_file::Model>,
|
|
||||||
) -> PackResult<PackUserSelfMaybeAll> {
|
) -> PackResult<PackUserSelfMaybeAll> {
|
||||||
|
let user = user_data.user();
|
||||||
|
|
||||||
let should_fetch_profile =
|
let should_fetch_profile =
|
||||||
req.profile.unwrap_or_default() || req.secrets.unwrap_or_default();
|
req.profile.unwrap_or_default() || req.secrets.unwrap_or_default();
|
||||||
let profile_raw_promise =
|
let profile_raw_promise =
|
||||||
OptionFuture::from(should_fetch_profile.then(|| self.get_profile(ctx, user)));
|
OptionFuture::from(should_fetch_profile.then(|| self.get_profile(ctx, user)));
|
||||||
let (base_res, profile_res) = join!(
|
let (base_res, profile_res) =
|
||||||
self.base_from_existing(ctx, user, hint_avatar_file),
|
join!(self.base_from_existing(ctx, user_data), profile_raw_promise);
|
||||||
profile_raw_promise
|
|
||||||
);
|
|
||||||
let mut base = base_res?;
|
let mut base = base_res?;
|
||||||
let profile_raw = profile_res.transpose()?;
|
let profile_raw = profile_res.transpose()?;
|
||||||
|
|
||||||
|
@ -271,11 +312,10 @@ impl UserModel {
|
||||||
let profile = OptionFuture::from(req.profile.unwrap_or_default().then(|| {
|
let profile = OptionFuture::from(req.profile.unwrap_or_default().then(|| {
|
||||||
self.profile_from_base(
|
self.profile_from_base(
|
||||||
ctx,
|
ctx,
|
||||||
user,
|
user_data,
|
||||||
profile_raw.as_ref().unwrap(),
|
profile_raw.as_ref().unwrap(),
|
||||||
None,
|
None,
|
||||||
&mut base.user.0.emojis,
|
&mut base.user.0.emojis,
|
||||||
hint_banner_file,
|
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -380,18 +420,15 @@ impl UserModel {
|
||||||
pub async fn foreign_full_from_base(
|
pub async fn foreign_full_from_base(
|
||||||
&self,
|
&self,
|
||||||
ctx: &PackingContext,
|
ctx: &PackingContext,
|
||||||
user: &ck::user::Model,
|
user_data: &dyn UserShapedData<'_>,
|
||||||
req: &UserByIdReq,
|
req: &UserByIdReq,
|
||||||
hint_avatar_file: Option<&ck::drive_file::Model>,
|
|
||||||
hint_banner_file: Option<&ck::drive_file::Model>,
|
|
||||||
) -> PackResult<PackUserMaybeAll> {
|
) -> PackResult<PackUserMaybeAll> {
|
||||||
|
let user = user_data.user();
|
||||||
|
|
||||||
let should_fetch_profile = req.profile.unwrap_or_default() || req.auth.unwrap_or_default();
|
let should_fetch_profile = req.profile.unwrap_or_default() || req.auth.unwrap_or_default();
|
||||||
let profile_raw_promise =
|
let profile_raw_promise =
|
||||||
OptionFuture::from(should_fetch_profile.then(|| self.get_profile(ctx, user)));
|
OptionFuture::from(should_fetch_profile.then(|| self.get_profile(ctx, user)));
|
||||||
let (base, profile) = join!(
|
let (base, profile) = join!(self.base_from_existing(ctx, user_data), profile_raw_promise);
|
||||||
self.base_from_existing(ctx, user, hint_avatar_file),
|
|
||||||
profile_raw_promise
|
|
||||||
);
|
|
||||||
let mut base = base?;
|
let mut base = base?;
|
||||||
let profile_raw = profile.transpose()?;
|
let profile_raw = profile.transpose()?;
|
||||||
|
|
||||||
|
@ -403,11 +440,10 @@ impl UserModel {
|
||||||
let profile = OptionFuture::from(req.profile.unwrap_or_default().then(|| {
|
let profile = OptionFuture::from(req.profile.unwrap_or_default().then(|| {
|
||||||
self.profile_from_base(
|
self.profile_from_base(
|
||||||
ctx,
|
ctx,
|
||||||
user,
|
user_data,
|
||||||
profile_raw.as_ref().unwrap(),
|
profile_raw.as_ref().unwrap(),
|
||||||
None,
|
None,
|
||||||
&mut base.user.0.emojis,
|
&mut base.user.0.emojis,
|
||||||
hint_banner_file,
|
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue