wip: drop emoji column

This commit is contained in:
Namekuji 2023-09-08 22:10:36 -04:00
parent 4bde3f126b
commit bd1b5a9249
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
17 changed files with 590 additions and 51 deletions

View file

@ -2446,6 +2446,7 @@ dependencies = [
"chrono",
"clap",
"futures",
"indicatif",
"scylla",
"sea-orm",
"serde",

View file

@ -9,6 +9,7 @@ edition = "2021"
chrono = "0.4.26"
clap = { version = "4.3.11", features = ["derive"] }
futures = "0.3.28"
indicatif = { version = "0.17.6", features = ["tokio"] }
scylla = "0.8.2"
sea-orm = { version = "0.12.2", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
serde = { version = "1.0.171", features = ["derive"] }

View file

@ -0,0 +1,7 @@
CREATE TYPE emoji (
"name" text,
"url" text,
"width" int,
"height" int,
);
ALTER TABLE reaction ADD "emoji" frozen<emoji>;

View file

@ -0,0 +1,2 @@
ALTER TABLE reaction DROP "emoji";
DROP TYPE emoji;

View file

@ -0,0 +1,50 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "following")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "createdAt")]
pub created_at: DateTimeWithTimeZone,
#[sea_orm(column_name = "followeeId")]
pub followee_id: String,
#[sea_orm(column_name = "followerId")]
pub follower_id: String,
#[sea_orm(column_name = "followerHost")]
pub follower_host: Option<String>,
#[sea_orm(column_name = "followerInbox")]
pub follower_inbox: Option<String>,
#[sea_orm(column_name = "followerSharedInbox")]
pub follower_shared_inbox: Option<String>,
#[sea_orm(column_name = "followeeHost")]
pub followee_host: Option<String>,
#[sea_orm(column_name = "followeeInbox")]
pub followee_inbox: Option<String>,
#[sea_orm(column_name = "followeeSharedInbox")]
pub followee_shared_inbox: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::user::Entity",
from = "Column::FolloweeId",
to = "super::user::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
User2,
#[sea_orm(
belongs_to = "super::user::Entity",
from = "Column::FollowerId",
to = "super::user::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
User1,
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -1,6 +1,10 @@
pub mod drive_file;
pub mod emoji;
pub mod following;
pub mod note;
pub mod poll_vote;
pub mod note_edit;
pub mod note_reaction;
pub mod poll;
pub mod poll_vote;
pub mod user;
pub mod sea_orm_active_enums;

View file

@ -64,6 +64,34 @@ pub struct Model {
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
pub enum Relation {
#[sea_orm(has_many = "super::note_edit::Entity")]
NoteEdit,
#[sea_orm(has_many = "super::note_reaction::Entity")]
NoteReaction,
#[sea_orm(has_one = "super::poll::Entity")]
Poll,
#[sea_orm(has_many = "super::poll_vote::Entity")]
PollVote,
}
impl Related<super::note_edit::Entity> for Entity {
fn to() -> RelationDef {
Relation::NoteEdit.def()
}
}
impl Related<super::poll::Entity> for Entity {
fn to() -> RelationDef {
Relation::Poll.def()
}
}
impl Related<super::poll_vote::Entity> for Entity {
fn to() -> RelationDef {
Relation::PollVote.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -0,0 +1,39 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "note_edit")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "noteId")]
pub note_id: String,
#[sea_orm(column_type = "Text", nullable)]
pub text: Option<String>,
pub cw: Option<String>,
#[sea_orm(column_name = "fileIds")]
pub file_ids: Vec<String>,
#[sea_orm(column_name = "updatedAt")]
pub updated_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::note::Entity",
from = "Column::NoteId",
to = "super::note::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Note,
}
impl Related<super::note::Entity> for Entity {
fn to() -> RelationDef {
Relation::Note.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -0,0 +1,37 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "note_reaction")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "createdAt")]
pub created_at: DateTimeWithTimeZone,
#[sea_orm(column_name = "userId")]
pub user_id: String,
#[sea_orm(column_name = "noteId")]
pub note_id: String,
pub reaction: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::note::Entity",
from = "Column::NoteId",
to = "super::note::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Note,
}
impl Related<super::note::Entity> for Entity {
fn to() -> RelationDef {
Relation::Note.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -0,0 +1,79 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "user")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "createdAt")]
pub created_at: DateTimeWithTimeZone,
#[sea_orm(column_name = "updatedAt")]
pub updated_at: Option<DateTimeWithTimeZone>,
#[sea_orm(column_name = "lastFetchedAt")]
pub last_fetched_at: Option<DateTimeWithTimeZone>,
pub username: String,
#[sea_orm(column_name = "usernameLower")]
pub username_lower: String,
pub name: Option<String>,
#[sea_orm(column_name = "followersCount")]
pub followers_count: i32,
#[sea_orm(column_name = "followingCount")]
pub following_count: i32,
#[sea_orm(column_name = "notesCount")]
pub notes_count: i32,
#[sea_orm(column_name = "avatarId", unique)]
pub avatar_id: Option<String>,
#[sea_orm(column_name = "bannerId", unique)]
pub banner_id: Option<String>,
pub tags: Vec<String>,
#[sea_orm(column_name = "isSuspended")]
pub is_suspended: bool,
#[sea_orm(column_name = "isSilenced")]
pub is_silenced: bool,
#[sea_orm(column_name = "isLocked")]
pub is_locked: bool,
#[sea_orm(column_name = "isBot")]
pub is_bot: bool,
#[sea_orm(column_name = "isCat")]
pub is_cat: bool,
#[sea_orm(column_name = "isAdmin")]
pub is_admin: bool,
#[sea_orm(column_name = "isModerator")]
pub is_moderator: bool,
pub emojis: Vec<String>,
pub host: Option<String>,
pub inbox: Option<String>,
#[sea_orm(column_name = "sharedInbox")]
pub shared_inbox: Option<String>,
pub featured: Option<String>,
pub uri: Option<String>,
#[sea_orm(unique)]
pub token: Option<String>,
#[sea_orm(column_name = "isExplorable")]
pub is_explorable: bool,
#[sea_orm(column_name = "followersUri")]
pub followers_uri: Option<String>,
#[sea_orm(column_name = "lastActiveDate")]
pub last_active_date: Option<DateTimeWithTimeZone>,
#[sea_orm(column_name = "hideOnlineStatus")]
pub hide_online_status: bool,
#[sea_orm(column_name = "isDeleted")]
pub is_deleted: bool,
#[sea_orm(column_name = "driveCapacityOverrideMb")]
pub drive_capacity_override_mb: Option<i32>,
#[sea_orm(column_name = "movedToUri")]
pub moved_to_uri: Option<String>,
#[sea_orm(column_name = "alsoKnownAs", column_type = "Text", nullable)]
pub also_known_as: Option<String>,
#[sea_orm(column_name = "speakAsCat")]
pub speak_as_cat: bool,
#[sea_orm(column_name = "isIndexable")]
pub is_indexable: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -2,8 +2,8 @@ pub use clap::Parser;
pub mod cli;
pub mod config;
pub mod error;
pub mod entity;
pub mod error;
pub(crate) mod migrator;
pub(crate) mod setup;

View file

@ -183,7 +183,7 @@ impl Migrator {
self.down(1).await?;
return Err(e.into());
},
}
Ok(_) => {}
};
}

View file

@ -1,15 +1,18 @@
use std::collections::HashMap;
use std::{collections::HashMap, fmt::Write, sync::OnceLock};
use futures::TryStreamExt;
use scylla::{Session, SessionBuilder, IntoUserType, FromUserType, ValueList, FromRow};
use sea_orm::{query::*, entity::*, Database};
use chrono::{DateTime, NaiveDate, Utc};
use futures::{future, TryStreamExt};
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
use scylla::{
prepared_statement::PreparedStatement, FromUserType, IntoUserType, Session, SessionBuilder,
ValueList,
};
use sea_orm::{entity::*, query::*, Database, DatabaseConnection};
use urlencoding::encode;
use chrono::{DateTime, Utc, NaiveDate};
use crate::{
config::{DbConfig, ScyllaConfig},
entity::note,
entity::{drive_file, emoji, following, note, note_edit, note_reaction, poll, poll_vote, user},
error::Error,
};
@ -47,13 +50,7 @@ impl Initializer {
let pool = Database::connect(&self.postgres_url).await?;
let db_backend = pool.get_database_backend();
let num_notes: i64 = note::Entity::find().select_only().column_as(note::Column::Id.count(), "count").into_tuple().one(&pool).await?.unwrap_or_default();
println!("{num_notes} posts are being copied.")
let mut notes = note::Entity::find().stream(&pool).await?;
while let Some(note) = notes.try_next().await? {
}
self.copy(&pool).await?;
let fk_pairs = vec![
("channel_note_pining", "FK_10b19ef67d297ea9de325cd4502"),
@ -92,13 +89,308 @@ impl Initializer {
Ok(())
}
async fn copy(&self, db: &DatabaseConnection) -> Result<(), Error> {
let note_prepared = self.scylla.prepare(INSERT_NOTE).await?;
let home_prepared = self.scylla.prepare(INSERT_HOME_TIMELINE).await?;
let reaction_prepared = self.scylla.prepare(INSERT_REACTION).await?;
let num_notes: i64 = note::Entity::find()
.select_only()
.column_as(note::Column::Id.count(), "count")
.into_tuple()
.one(db)
.await?
.unwrap_or_default();
let num_reactions: i64 = note_reaction::Entity::find()
.select_only()
.column_as(note_reaction::Column::Id.count(), "count")
.into_tuple()
.one(db)
.await?
.unwrap_or_default();
println!("Copying notes from PostgreSQL to ScyllaDB.");
const PB_TMPL: &str =
"{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} ({eta})";
let pb_style = ProgressStyle::with_template(PB_TMPL)
.unwrap()
.progress_chars("#>-");
let note_pb = ProgressBar::new(num_notes as u64).with_style(pb_style.to_owned());
let reaction_pb = ProgressBar::new(num_reactions as u64).with_style(pb_style.to_owned());
let mut notes = note::Entity::find()
.order_by_asc(note::Column::Id)
.stream(db)
.await?;
let mut note_tasks = Vec::new();
while let Some(note) = notes.try_next().await? {
note_tasks.push(self.copy_note(note, db, &note_prepared, &home_prepared, &note_pb));
}
let mut reactions = note_reaction::Entity::find()
.order_by_asc(note_reaction::Column::Id)
.stream(db)
.await?;
let mut reaction_tasks = Vec::new();
while let Some(reaction) = reactions.try_next().await? {
reaction_tasks.push(self.copy_reaction(reaction, &reaction_prepared, &reaction_pb));
}
future::try_join_all(note_tasks).await?;
future::try_join_all(reaction_tasks).await?;
Ok(())
}
async fn copy_note(
&self,
note: note::Model,
db: &DatabaseConnection,
prepared_note: &PreparedStatement,
prepared_home: &PreparedStatement,
pb: &ProgressBar,
) -> Result<(), Error> {
let reply = match &note.reply_id {
None => None,
Some(id) => note::Entity::find_by_id(id).one(db).await?,
};
let renote = match &note.renote_id {
None => None,
Some(id) => note::Entity::find_by_id(id).one(db).await?,
};
let files = get_attached_files(Some(note.to_owned()), db).await?;
let reply_files = get_attached_files(reply.to_owned(), db).await?;
let renote_files = get_attached_files(renote.to_owned(), db).await?;
let note_poll = note.find_related(poll::Entity).one(db).await?;
let poll_type = match note_poll {
None => None,
Some(v) => Some(PollType {
multiple: v.multiple,
expires_at: v.expires_at.map(Into::into),
choices: HashMap::from_iter(
v.choices
.iter()
.enumerate()
.map(|(i, v)| ((i + 1) as i32, v.to_string())),
),
}),
};
let reactions: HashMap<String, i32> = match note.reactions.as_object() {
None => HashMap::new(),
Some(obj) => HashMap::from_iter(
obj.into_iter()
.map(|(k, v)| (k.to_string(), v.as_i64().unwrap_or_default() as i32)),
),
};
let edits = note.find_related(note_edit::Entity).all(db).await?;
let edits = edits.iter().map(|v| async {
let v = v.to_owned();
Ok(NoteEditHistoryType {
content: v.text,
cw: v.cw,
files: get_files(v.file_ids, db).await?,
updated_at: v.updated_at.into(),
})
});
let edits: Vec<Result<NoteEditHistoryType, Error>> = futures::future::join_all(edits).await;
let edits: Vec<NoteEditHistoryType> = edits
.iter()
.filter_map(|v| v.as_ref().ok())
.cloned()
.collect();
let scylla_note = NoteTable {
created_at_date: note.created_at.date_naive(),
created_at: note.created_at.into(),
id: note.id.to_owned(),
visibility: note.visibility.to_value(),
content: note.text,
name: note.name,
cw: note.cw,
local_only: note.local_only,
renote_count: note.renote_count as i32,
replies_count: note.replies_count as i32,
uri: note.uri,
url: note.url,
score: note.score,
files,
visible_user_ids: note.visible_user_ids,
mentions: note.mentions,
mentioned_remote_users: note.mentioned_remote_users,
emojis: note.emojis,
tags: note.tags,
has_poll: poll_type.is_some(),
poll: poll_type,
thread_id: note.thread_id,
channel_id: note.channel_id,
user_id: note.user_id.to_owned(),
user_host: note.user_host,
reply_id: note.reply_id,
reply_user_id: note.reply_user_id,
reply_user_host: note.reply_user_host,
reply_content: reply.as_ref().map(|v| v.text.to_owned()).flatten(),
reply_cw: reply.as_ref().map(|v| v.cw.to_owned()).flatten(),
reply_files,
renote_id: note.renote_id,
renote_user_id: note.renote_user_id,
renote_user_host: note.renote_user_host,
renote_content: renote.as_ref().map(|v| v.text.to_owned()).flatten(),
renote_cw: renote.as_ref().map(|v| v.cw.to_owned()).flatten(),
renote_files,
reactions,
note_edit: edits,
updated_at: note.updated_at.map(Into::into),
};
self.scylla
.execute(prepared_note, scylla_note.to_owned())
.await?;
let mut home_tasks = Vec::new();
let mut local_followers = following::Entity::find()
.select_only()
.column(following::Column::FollowerId)
.filter(following::Column::FolloweeId.eq(note.user_id))
.filter(following::Column::FollowerHost.is_null())
.into_tuple::<String>()
.stream(db)
.await?;
while let Some(follower_id) = local_followers.try_next().await? {
let s_note = scylla_note.to_owned();
let home = HomeTimelineTable {
feed_user_id: follower_id,
created_at_date: s_note.created_at_date,
created_at: s_note.created_at,
id: s_note.id,
visibility: s_note.visibility,
content: s_note.content,
name: s_note.name,
cw: s_note.cw,
local_only: s_note.local_only,
renote_count: s_note.renote_count,
replies_count: s_note.replies_count,
uri: s_note.uri,
url: s_note.url,
score: s_note.score,
files: s_note.files,
visible_user_ids: s_note.visible_user_ids,
mentions: s_note.mentions,
mentioned_remote_users: s_note.mentioned_remote_users,
emojis: s_note.emojis,
tags: s_note.tags,
has_poll: s_note.has_poll,
poll: s_note.poll,
thread_id: s_note.thread_id,
channel_id: s_note.channel_id,
user_id: s_note.user_id,
user_host: s_note.user_host,
reply_id: s_note.reply_id,
reply_user_id: s_note.reply_user_id,
reply_user_host: s_note.reply_user_host,
reply_content: s_note.reply_content,
reply_cw: s_note.reply_cw,
reply_files: s_note.reply_files,
renote_id: s_note.renote_id,
renote_user_id: s_note.renote_user_id,
renote_user_host: s_note.renote_user_host,
renote_content: s_note.renote_content,
renote_cw: s_note.renote_cw,
renote_files: s_note.renote_files,
reactions: s_note.reactions,
note_edit: s_note.note_edit,
updated_at: s_note.updated_at,
};
home_tasks.push(self.scylla.execute(prepared_home, home));
}
future::try_join_all(home_tasks).await?;
pb.inc(1);
Ok(())
}
async fn copy_reaction(
&self,
reaction: note_reaction::Model,
prepared: &PreparedStatement,
pb: &ProgressBar,
) -> Result<(), Error> {
let scylla_reaction = ReactionTable {
id: reaction.id,
note_id: reaction.note_id,
user_id: reaction.user_id,
reaction: reaction.reaction,
created_at: reaction.created_at.into(),
};
self.scylla.execute(prepared, scylla_reaction).await?;
pb.inc(1);
Ok(())
}
}
#[derive(Debug, IntoUserType, FromUserType)]
fn map_drive_file(file: drive_file::Model) -> DriveFileType {
DriveFileType {
id: file.id,
r#type: file.r#type,
created_at: file.created_at.into(),
name: file.name,
comment: file.comment,
blurhash: file.blurhash,
url: file.url,
thumbnail_url: file.thumbnail_url,
is_sensitive: file.is_sensitive,
is_link: file.is_link,
md5: file.md5,
size: file.size,
width: file
.properties
.get("width")
.filter(|v| v.is_number())
.map(|v| v.as_i64().unwrap() as i32),
height: file
.properties
.get("height")
.filter(|v| v.is_number())
.map(|v| v.as_i64().unwrap() as i32),
}
}
async fn get_attached_files(
note: Option<note::Model>,
db: &DatabaseConnection,
) -> Result<Vec<DriveFileType>, Error> {
match note {
None => Ok(vec![]),
Some(v) => Ok(get_files(v.file_ids, db).await?),
}
}
async fn get_files(
file_ids: Vec<String>,
db: &DatabaseConnection,
) -> Result<Vec<DriveFileType>, Error> {
if file_ids.is_empty() {
Ok(vec![])
} else {
let files = drive_file::Entity::find()
.filter(drive_file::Column::Id.is_in(file_ids))
.all(db)
.await?;
Ok(files.iter().map(|v| map_drive_file(v.to_owned())).collect())
}
}
#[derive(Debug, Clone, IntoUserType, FromUserType)]
struct DriveFileType {
id: String,
#[scylla_crate(rename = "type")]
kind: String,
r#type: String,
#[scylla_crate(rename = "createdAt")]
created_at: DateTime<Utc>,
name: String,
@ -117,24 +409,16 @@ struct DriveFileType {
height: Option<i32>,
}
#[derive(Debug, IntoUserType, FromUserType)]
#[derive(Debug, Clone, IntoUserType, FromUserType)]
struct NoteEditHistoryType {
content: Option<String>,
cw: Option<String>,
files: Vec<DriveFileType>,
#[scylla_crate(rename = "updatedAt")]
updated_at: DateTime<Utc>
updated_at: DateTime<Utc>,
}
#[derive(Debug, IntoUserType, FromUserType)]
struct EmojiType {
name: String,
url: String,
width: Option<i32>,
height: Option<i32>,
}
#[derive(Debug, IntoUserType, FromUserType)]
#[derive(Debug, Clone, IntoUserType, FromUserType)]
struct PollType {
#[scylla_crate(rename = "expiresAt")]
expires_at: Option<DateTime<Utc>>,
@ -142,7 +426,7 @@ struct PollType {
choices: HashMap<i32, String>,
}
#[derive(ValueList)]
#[derive(Clone, ValueList)]
struct NoteTable {
created_at_date: NaiveDate,
created_at: DateTime<Utc>,
@ -164,11 +448,11 @@ struct NoteTable {
emojis: Vec<String>,
tags: Vec<String>,
has_poll: bool,
poll: PollType,
poll: Option<PollType>,
thread_id: Option<String>,
channel_id: Option<String>,
user_id: String,
user_host: String,
user_host: Option<String>,
reply_id: Option<String>,
reply_user_id: Option<String>,
reply_user_host: Option<String>,
@ -183,7 +467,7 @@ struct NoteTable {
renote_files: Vec<DriveFileType>,
reactions: HashMap<String, i32>,
note_edit: Vec<NoteEditHistoryType>,
updated_at: DateTime<Utc>,
updated_at: Option<DateTime<Utc>>,
}
const INSERT_NOTE: &str = r#"
@ -254,11 +538,11 @@ struct HomeTimelineTable {
emojis: Vec<String>,
tags: Vec<String>,
has_poll: bool,
poll: PollType,
poll: Option<PollType>,
thread_id: Option<String>,
channel_id: Option<String>,
user_id: String,
user_host: String,
user_host: Option<String>,
reply_id: Option<String>,
reply_user_id: Option<String>,
reply_user_host: Option<String>,
@ -273,7 +557,7 @@ struct HomeTimelineTable {
renote_files: Vec<DriveFileType>,
reactions: HashMap<String, i32>,
note_edit: Vec<NoteEditHistoryType>,
updated_at: DateTime<Utc>,
updated_at: Option<DateTime<Utc>>,
}
const INSERT_HOME_TIMELINE: &str = r#"
@ -328,17 +612,16 @@ struct ReactionTable {
note_id: String,
user_id: String,
reaction: String,
emoji: EmojiType,
created_at: DateTime<Utc>,
}
const INSERT_REACTION: &str = r#"INSERT INTO reaction ("id", "noteId", "userId", "reaction", "emoji", "createdAt") VALUES (?, ?, ?, ?, ?, ?)"#;
const INSERT_REACTION: &str = r#"INSERT INTO reaction ("id", "noteId", "userId", "reaction", "createdAt") VALUES (?, ?, ?, ?, ?)"#;
#[derive(ValueList)]
struct PollVoteTable {
note_id: String,
user_id: String,
user_host: String,
user_host: Option<String>,
choice: Vec<i32>,
created_at: DateTime<Utc>,
}
@ -349,5 +632,16 @@ const INSERT_POLL_VOTE: &str = r#"INSERT INTO poll_vote ("noteId", "userId", "us
struct NotificationTable {
target_id: String,
created_at_date: NaiveDate,
created_at: DateTime<Utc>,
id: String,
notifier_id: Option<String>,
notifier_host: Option<String>,
r#type: String,
entity_id: Option<String>,
reatcion: Option<String>,
choice: Option<i32>,
custom_body: Option<String>,
custom_icon: Option<String>,
}
const INSERT_NOTIFICATION: &str = r#"INSERT INTO notification ("targetId", "createdAtDate", "createdAt", "id", "notifierId", "notifierHost", "type", "entityId", "reaction", "choice", "customBody", "customHeader", "customIcon") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#;

View file

@ -152,8 +152,8 @@ export const scyllaQueries = {
},
reaction: {
insert: `INSERT INTO reaction
("id", "noteId", "userId", "reaction", "emoji", "createdAt")
VALUES (?, ?, ?, ?, ?, ?)`,
("id", "noteId", "userId", "reaction", "createdAt")
VALUES (?, ?, ?, ?, ?)`,
select: {
byNoteId: `SELECT * FROM reaction_by_id WHERE "noteId" = ?`,
byUserId: `SELECT * FROM reaction_by_user_id WHERE "userId" = ?`,

View file

@ -294,9 +294,7 @@ export function parseHomeTimeline(
};
}
export interface ScyllaNoteReaction extends NoteReaction {
emoji: PopulatedEmoji;
}
export type ScyllaNoteReaction = NoteReaction;
export type FeedType =
| "home"
@ -318,7 +316,6 @@ export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
userId: row.get("userId"),
reaction: row.get("reaction"),
createdAt: row.get("createdAt"),
emoji: row.get("emoji"),
};
}

View file

@ -21,6 +21,7 @@ import {
} from "@/misc/reaction-lib.js";
import type { NoteReaction } from "@/models/entities/note-reaction.js";
import {
type PopulatedEmoji,
aggregateNoteEmojis,
populateEmojis,
prefetchEmojis,

View file

@ -49,7 +49,7 @@ export default async (
}
// Emoji data will be cached in toDbReaction.
const { name: _reaction, emoji: emojiData } = await toDbReaction(
const { name: _reaction } = await toDbReaction(
reaction,
user.host,
);
@ -74,7 +74,6 @@ export default async (
record.noteId,
record.userId,
_reaction,
emojiData,
record.createdAt,
],
{ prepare: true },