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