Add note processing for parents of renote posts and introduced better timeline filtering
ci/woodpecker/push/ociImagePush Pipeline is pending
Details
ci/woodpecker/push/ociImagePush Pipeline is pending
Details
This commit is contained in:
parent
152c4e6fc6
commit
bbc4f84ceb
|
@ -9,7 +9,7 @@ use sea_orm::{
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use ck::{drive_file, note, note_reaction, user, user_note_pining};
|
use ck::{drive_file, note, note_reaction, user, user_note_pining};
|
||||||
use magnetar_sdk::types::RangeFilter;
|
use magnetar_sdk::types::{EndFilter, RangeFilter, SpanFilter, StartFilter};
|
||||||
|
|
||||||
use crate::{AliasSourceExt, CalckeyDbError, CalckeyModel};
|
use crate::{AliasSourceExt, CalckeyDbError, CalckeyModel};
|
||||||
|
|
||||||
|
@ -73,6 +73,12 @@ const RENOTE_INTERACTION_RENOTE: &str = "renote.interaction.renote.";
|
||||||
const RENOTE_USER: &str = "renote.user.";
|
const RENOTE_USER: &str = "renote.user.";
|
||||||
const RENOTE_USER_AVATAR: &str = "renote.user.avatar.";
|
const RENOTE_USER_AVATAR: &str = "renote.user.avatar.";
|
||||||
const RENOTE_USER_BANNER: &str = "renote.user.banner.";
|
const RENOTE_USER_BANNER: &str = "renote.user.banner.";
|
||||||
|
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, _pre: &str) -> Result<Self, DbErr> {
|
||||||
|
@ -99,6 +105,35 @@ impl FromQueryResult for NoteData {
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.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)?
|
let renote = note::Model::from_query_result_optional(res, RENOTE)?
|
||||||
.map::<Result<_, DbErr>, _>(|r| {
|
.map::<Result<_, DbErr>, _>(|r| {
|
||||||
Ok(Box::new(NoteData {
|
Ok(Box::new(NoteData {
|
||||||
|
@ -152,7 +187,8 @@ pub trait NoteVisibilityFilterFactory: Send + Sync {
|
||||||
pub struct NoteResolveOptions {
|
pub struct NoteResolveOptions {
|
||||||
pub ids: Option<Vec<String>>,
|
pub ids: Option<Vec<String>>,
|
||||||
pub visibility_filter: Box<dyn NoteVisibilityFilterFactory>,
|
pub visibility_filter: Box<dyn NoteVisibilityFilterFactory>,
|
||||||
pub time_range: Option<RangeFilter>,
|
pub time_range: Option<SpanFilter>,
|
||||||
|
pub limit: Option<u64>,
|
||||||
pub with_user: bool,
|
pub with_user: bool,
|
||||||
pub with_reply_target: bool,
|
pub with_reply_target: bool,
|
||||||
pub with_renote_target: bool,
|
pub with_renote_target: bool,
|
||||||
|
@ -284,15 +320,60 @@ lazy_static! {
|
||||||
static ref ALIAS_RENOTE_USER: Alias = Alias::new(RENOTE_USER);
|
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_AVATAR: Alias = Alias::new(RENOTE_USER_AVATAR);
|
||||||
static ref ALIAS_RENOTE_USER_BANNER: Alias = Alias::new(RENOTE_USER_BANNER);
|
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: &RangeFilter) -> SimpleExpr {
|
fn range_into_expr(filter: &SpanFilter) -> Option<SimpleExpr> {
|
||||||
match filter {
|
match filter {
|
||||||
RangeFilter::TimeStart(start) => note::Column::CreatedAt.gte(*start),
|
SpanFilter::Range(RangeFilter {
|
||||||
RangeFilter::TimeRange(range) => {
|
time_start,
|
||||||
note::Column::CreatedAt.between(*range.start(), *range.end())
|
time_end,
|
||||||
}
|
id_start,
|
||||||
RangeFilter::TimeEnd(end) => note::Column::CreatedAt.lt(*end),
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,7 +396,7 @@ 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().map(range_into_expr);
|
let time_filter = options.time_range.as_ref().and_then(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
|
||||||
|
@ -335,7 +416,7 @@ 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().map(range_into_expr);
|
let time_filter = options.time_range.as_ref().and_then(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
|
||||||
|
@ -348,6 +429,7 @@ impl NoteResolver {
|
||||||
user_note_pining::Column::CreatedAt,
|
user_note_pining::Column::CreatedAt,
|
||||||
)))
|
)))
|
||||||
})
|
})
|
||||||
|
.apply_if(options.limit, Select::<note::Entity>::limit)
|
||||||
.into_model::<NoteData>()
|
.into_model::<NoteData>()
|
||||||
.all(self.db.inner())
|
.all(self.db.inner())
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -418,6 +500,32 @@ impl NoteResolver {
|
||||||
.add_aliased_columns(Some(RENOTE_USER_AVATAR), drive_file::Entity)
|
.add_aliased_columns(Some(RENOTE_USER_AVATAR), drive_file::Entity)
|
||||||
.add_aliased_columns(Some(RENOTE_USER_BANNER), 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 {
|
if options.with_reply_target {
|
||||||
|
@ -446,6 +554,20 @@ impl NoteResolver {
|
||||||
note::Relation::User.with_alias(ALIAS_RENOTE.clone()),
|
note::Relation::User.with_alias(ALIAS_RENOTE.clone()),
|
||||||
ALIAS_RENOTE_USER.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(
|
select = select.join_as(
|
||||||
|
@ -493,6 +615,20 @@ impl NoteResolver {
|
||||||
user::Relation::DriveFile1.with_alias(ALIAS_RENOTE_USER.clone()),
|
user::Relation::DriveFile1.with_alias(ALIAS_RENOTE_USER.clone()),
|
||||||
ALIAS_RENOTE_USER_BANNER.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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,3 +42,8 @@ export { DriveFolderParentExt } from "./types/DriveFolderParentExt";
|
||||||
export { ImageMeta } from "./types/ImageMeta";
|
export { ImageMeta } from "./types/ImageMeta";
|
||||||
export { InstanceTicker } from "./types/InstanceTicker";
|
export { InstanceTicker } from "./types/InstanceTicker";
|
||||||
export { MovedTo } from "./types/MovedTo";
|
export { MovedTo } from "./types/MovedTo";
|
||||||
|
export { RangeFilter } from "./types/RangeFilter";
|
||||||
|
export { EndFilter } from "./types/EndFilter";
|
||||||
|
export { StartFilter } from "./types/StartFilter";
|
||||||
|
export { NoFilter } from "./types/NoFilter";
|
||||||
|
export { TimelineType } from "./types/TimelineType";
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export interface EndFilter { time_end: string, id_end: string, }
|
|
@ -1,4 +1,5 @@
|
||||||
// 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 { NoteListFilter } from "./NoteListFilter";
|
import type { NoteListFilter } from "./NoteListFilter";
|
||||||
|
import type { SpanFilter } from "./SpanFilter";
|
||||||
|
|
||||||
export interface GetTimelineReq { limit?: bigint, filter?: NoteListFilter, }
|
export interface GetTimelineReq { limit?: bigint, filter?: NoteListFilter, range?: SpanFilter, }
|
|
@ -0,0 +1,3 @@
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export type NoFilter = Record<string, never>;
|
|
@ -0,0 +1,3 @@
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export interface RangeFilter { time_start: string, time_end: string, id_start: string, id_end: string, }
|
|
@ -0,0 +1,7 @@
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { EndFilter } from "./EndFilter";
|
||||||
|
import type { NoFilter } from "./NoFilter";
|
||||||
|
import type { RangeFilter } from "./RangeFilter";
|
||||||
|
import type { StartFilter } from "./StartFilter";
|
||||||
|
|
||||||
|
export type SpanFilter = RangeFilter | StartFilter | EndFilter | NoFilter;
|
|
@ -0,0 +1,3 @@
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export interface StartFilter { time_start: string, id_start: string, }
|
|
@ -0,0 +1,3 @@
|
||||||
|
// 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";
|
|
@ -3,8 +3,8 @@ import type { GetTimelineReq } from "../GetTimelineReq";
|
||||||
import type { PackNoteMaybeFull } from "../packed/PackNoteMaybeFull";
|
import type { PackNoteMaybeFull } from "../packed/PackNoteMaybeFull";
|
||||||
|
|
||||||
export const GetTimeline = {
|
export const GetTimeline = {
|
||||||
endpoint: "/timeline",
|
endpoint: "/timeline/:timeline_type",
|
||||||
pathParams: [] as [],
|
pathParams: ["timeline_type"] as ["timeline_type"],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as GetTimelineReq,
|
request: undefined as unknown as GetTimelineReq,
|
||||||
response: undefined as unknown as Array<PackNoteMaybeFull>
|
response: undefined as unknown as Array<PackNoteMaybeFull>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::endpoints::Endpoint;
|
use crate::endpoints::Endpoint;
|
||||||
use crate::types::note::{NoteListFilter, PackNoteMaybeFull};
|
use crate::types::note::{NoteListFilter, PackNoteMaybeFull};
|
||||||
|
use crate::types::SpanFilter;
|
||||||
use crate::util_types::U64Range;
|
use crate::util_types::U64Range;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use magnetar_sdk_macros::Endpoint;
|
use magnetar_sdk_macros::Endpoint;
|
||||||
|
@ -14,6 +15,8 @@ pub struct GetTimelineReq {
|
||||||
pub limit: Option<U64Range<1, 100>>,
|
pub limit: Option<U64Range<1, 100>>,
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub filter: Option<NoteListFilter>,
|
pub filter: Option<NoteListFilter>,
|
||||||
|
#[ts(optional)]
|
||||||
|
pub range: Option<SpanFilter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_timeline_limit<const MIN: u64, const MAX: u64>() -> U64Range<MIN, MAX> {
|
pub fn default_timeline_limit<const MIN: u64, const MAX: u64>() -> U64Range<MIN, MAX> {
|
||||||
|
@ -22,7 +25,7 @@ pub fn default_timeline_limit<const MIN: u64, const MAX: u64>() -> U64Range<MIN,
|
||||||
|
|
||||||
#[derive(Endpoint)]
|
#[derive(Endpoint)]
|
||||||
#[endpoint(
|
#[endpoint(
|
||||||
endpoint = "/timeline",
|
endpoint = "/timeline/:timeline_type",
|
||||||
method = Method::GET,
|
method = Method::GET,
|
||||||
request = "GetTimelineReq",
|
request = "GetTimelineReq",
|
||||||
response = "Vec<PackNoteMaybeFull>",
|
response = "Vec<PackNoteMaybeFull>",
|
||||||
|
|
|
@ -11,10 +11,40 @@ use std::ops::RangeInclusive;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
pub enum RangeFilter {
|
#[ts(export)]
|
||||||
TimeRange(RangeInclusive<DateTime<Utc>>),
|
pub struct RangeFilter {
|
||||||
TimeStart(DateTime<Utc>),
|
pub time_start: DateTime<Utc>,
|
||||||
TimeEnd(DateTime<Utc>),
|
pub time_end: DateTime<Utc>,
|
||||||
|
pub id_start: DateTime<Utc>,
|
||||||
|
pub id_end: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct StartFilter {
|
||||||
|
pub time_start: DateTime<Utc>,
|
||||||
|
pub id_start: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct EndFilter {
|
||||||
|
pub time_end: DateTime<Utc>,
|
||||||
|
pub id_end: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct NoFilter {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum SpanFilter {
|
||||||
|
Range(RangeFilter),
|
||||||
|
Start(StartFilter),
|
||||||
|
End(EndFilter),
|
||||||
|
None(NoFilter),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
|
|
@ -97,8 +97,8 @@ pack!(
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct NoteDetailExt {
|
pub struct NoteDetailExt {
|
||||||
pub parent_note: Option<Box<PackNoteMaybeAttachments>>,
|
pub parent_note: Option<Box<PackNoteMaybeFull>>,
|
||||||
pub renoted_note: Option<Box<PackNoteMaybeAttachments>>,
|
pub renoted_note: Option<Box<PackNoteMaybeFull>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
|
|
|
@ -2,10 +2,12 @@ use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum TimelineType {
|
pub enum TimelineType {
|
||||||
Home,
|
Home,
|
||||||
Timeline,
|
|
||||||
Recommended,
|
Recommended,
|
||||||
Hybrid,
|
Hybrid,
|
||||||
Global,
|
Global,
|
||||||
|
Local,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use magnetar_calckey_model::ck;
|
use magnetar_calckey_model::ck;
|
||||||
use magnetar_calckey_model::note_model::sub_interaction_renote;
|
use magnetar_calckey_model::note_model::sub_interaction_renote;
|
||||||
use magnetar_sdk::types::note::{
|
use magnetar_sdk::types::note::{
|
||||||
NoteDetailExt, NoteSelfContextExt, PackNoteMaybeAttachments, PackPollBase, ReactionPair,
|
NoteDetailExt, NoteSelfContextExt, PackNoteMaybeAttachments, PackNoteMaybeFull, PackPollBase,
|
||||||
|
ReactionPair,
|
||||||
};
|
};
|
||||||
use magnetar_sdk::types::user::PackUserBase;
|
use magnetar_sdk::types::user::PackUserBase;
|
||||||
use magnetar_sdk::types::{
|
use magnetar_sdk::types::{
|
||||||
|
@ -83,8 +84,8 @@ impl PackType<&sub_interaction_renote::Model> for NoteSelfContextExt {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NoteDetailSource<'a> {
|
pub struct NoteDetailSource<'a> {
|
||||||
pub parent_note: Option<&'a PackNoteMaybeAttachments>,
|
pub parent_note: Option<&'a PackNoteMaybeFull>,
|
||||||
pub renoted_note: Option<&'a PackNoteMaybeAttachments>,
|
pub renoted_note: Option<&'a PackNoteMaybeFull>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PackType<NoteDetailSource<'a>> for NoteDetailExt {
|
impl<'a> PackType<NoteDetailSource<'a>> for NoteDetailExt {
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::model::processing::{get_mm_token_emoji, PackError, PackResult};
|
||||||
use crate::model::{PackType, PackingContext, UserRelationship};
|
use crate::model::{PackType, PackingContext, UserRelationship};
|
||||||
use compact_str::CompactString;
|
use compact_str::CompactString;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use futures_util::future::try_join_all;
|
use futures_util::future::{try_join_all, BoxFuture};
|
||||||
use futures_util::{StreamExt, TryFutureExt, TryStreamExt};
|
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::{
|
||||||
|
@ -429,65 +429,62 @@ impl NoteModel {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn pack_full_single(
|
fn pack_full_single<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
ctx: &PackingContext,
|
ctx: &'a PackingContext,
|
||||||
note: NoteData,
|
note: &'a NoteData,
|
||||||
) -> PackResult<PackNoteMaybeFull> {
|
) -> BoxFuture<'a, PackResult<PackNoteMaybeFull>> {
|
||||||
let drive_model = DriveModel;
|
async move {
|
||||||
|
let drive_model = DriveModel;
|
||||||
|
|
||||||
let reply_target = async {
|
let reply_target = async {
|
||||||
match note.reply.as_ref() {
|
match note.reply.as_ref() {
|
||||||
Some(r) if self.with_context => self
|
Some(r) if self.with_context => self.pack_full_single(ctx, r).await.map(Some),
|
||||||
.pack_single_attachments(ctx, &drive_model, r)
|
_ => Ok(None),
|
||||||
.await
|
}
|
||||||
.map(Some),
|
};
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let renote_target = async {
|
let renote_target = async {
|
||||||
match note.renote.as_ref() {
|
match note.renote.as_ref() {
|
||||||
Some(r) if self.with_context => self
|
Some(r) if self.with_context => self.pack_full_single(ctx, r).await.map(Some),
|
||||||
.pack_single_attachments(ctx, &drive_model, r)
|
_ => Ok(None),
|
||||||
.await
|
}
|
||||||
.map(Some),
|
};
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (
|
let (
|
||||||
PackNoteMaybeAttachments {
|
PackNoteMaybeAttachments {
|
||||||
|
id,
|
||||||
|
note,
|
||||||
|
user_context,
|
||||||
|
attachment,
|
||||||
|
},
|
||||||
|
reply_target_pack,
|
||||||
|
renote_target_pack,
|
||||||
|
) = try_join!(
|
||||||
|
self.pack_single_attachments(ctx, &drive_model, ¬e),
|
||||||
|
reply_target,
|
||||||
|
renote_target
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let detail = self.with_context.then(|| {
|
||||||
|
NoteDetailExt::extract(
|
||||||
|
ctx,
|
||||||
|
NoteDetailSource {
|
||||||
|
parent_note: reply_target_pack.as_ref(),
|
||||||
|
renoted_note: renote_target_pack.as_ref(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(PackNoteMaybeFull::pack_from((
|
||||||
id,
|
id,
|
||||||
note,
|
note,
|
||||||
user_context,
|
user_context,
|
||||||
attachment,
|
attachment,
|
||||||
},
|
Optional(detail),
|
||||||
reply_target_pack,
|
)))
|
||||||
renote_target_pack,
|
}
|
||||||
) = try_join!(
|
.boxed()
|
||||||
self.pack_single_attachments(ctx, &drive_model, ¬e),
|
|
||||||
reply_target,
|
|
||||||
renote_target
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let detail = self.with_context.then(|| {
|
|
||||||
NoteDetailExt::extract(
|
|
||||||
ctx,
|
|
||||||
NoteDetailSource {
|
|
||||||
parent_note: reply_target_pack.as_ref(),
|
|
||||||
renoted_note: renote_target_pack.as_ref(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(PackNoteMaybeFull::pack_from((
|
|
||||||
id,
|
|
||||||
note,
|
|
||||||
user_context,
|
|
||||||
attachment,
|
|
||||||
Optional(detail),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_single(
|
pub async fn fetch_single(
|
||||||
|
@ -503,6 +500,7 @@ impl NoteModel {
|
||||||
ctx.self_user.as_deref().map(ck::user::Model::get_id),
|
ctx.self_user.as_deref().map(ck::user::Model::get_id),
|
||||||
)),
|
)),
|
||||||
time_range: None,
|
time_range: None,
|
||||||
|
limit: None,
|
||||||
with_user: self.with_context,
|
with_user: self.with_context,
|
||||||
with_reply_target: self.with_context,
|
with_reply_target: self.with_context,
|
||||||
with_renote_target: self.with_context,
|
with_renote_target: self.with_context,
|
||||||
|
@ -522,7 +520,7 @@ impl NoteModel {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(self.pack_full_single(ctx, note).await?))
|
Ok(Some(self.pack_full_single(ctx, ¬e).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_pins(
|
pub async fn fetch_pins(
|
||||||
|
@ -538,6 +536,7 @@ impl NoteModel {
|
||||||
ctx.self_user.as_deref().map(ck::user::Model::get_id),
|
ctx.self_user.as_deref().map(ck::user::Model::get_id),
|
||||||
)),
|
)),
|
||||||
time_range: None,
|
time_range: None,
|
||||||
|
limit: None,
|
||||||
with_user: self.with_context,
|
with_user: self.with_context,
|
||||||
with_reply_target: self.with_context,
|
with_reply_target: self.with_context,
|
||||||
with_renote_target: self.with_context,
|
with_renote_target: self.with_context,
|
||||||
|
@ -555,8 +554,9 @@ impl NoteModel {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let fut_iter = notes
|
let fut_iter = notes
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|note| self.pack_full_single(ctx, note));
|
.map(|note| self.pack_full_single(ctx, note))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let processed = futures::stream::iter(fut_iter)
|
let processed = futures::stream::iter(fut_iter)
|
||||||
.buffered(10)
|
.buffered(10)
|
||||||
|
|
Loading…
Reference in New Issue