adjusting schema
This commit is contained in:
parent
168001271f
commit
b211788d3c
10 changed files with 147 additions and 38 deletions
|
@ -42,9 +42,9 @@ CREATE TABLE IF NOT EXISTS note ( -- Models timeline
|
||||||
"url" text,
|
"url" text,
|
||||||
"score" int,
|
"score" int,
|
||||||
"files" set<frozen<drive_file>>,
|
"files" set<frozen<drive_file>>,
|
||||||
"visibleUsersId" set<ascii>,
|
"visibleUserIds" set<ascii>,
|
||||||
"mentions" set<ascii>,
|
"mentions" set<ascii>,
|
||||||
"emojis" set<frozen<emoji>>,
|
"emojis" set<text>,
|
||||||
"tags" set<text>,
|
"tags" set<text>,
|
||||||
"hasPoll" boolean,
|
"hasPoll" boolean,
|
||||||
"threadId" ascii,
|
"threadId" ascii,
|
||||||
|
@ -54,7 +54,6 @@ CREATE TABLE IF NOT EXISTS note ( -- Models timeline
|
||||||
"replyId" ascii, -- Reply
|
"replyId" ascii, -- Reply
|
||||||
"renoteId" ascii, -- Boost
|
"renoteId" ascii, -- Boost
|
||||||
"reactions" map<text, int>,
|
"reactions" map<text, int>,
|
||||||
"reactionEmojis" map<text, frozen<emoji>>,
|
|
||||||
"noteEdit" set<frozen<note_edit_history>>, -- Edit History
|
"noteEdit" set<frozen<note_edit_history>>, -- Edit History
|
||||||
"updatedAt" timestamp,
|
"updatedAt" timestamp,
|
||||||
PRIMARY KEY ("createdAtDate", "createdAt", "id")
|
PRIMARY KEY ("createdAtDate", "createdAt", "id")
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import type { PopulatedEmoji } from "@/misc/populate-emojis.js";
|
import type { PopulatedEmoji } from "@/misc/populate-emojis.js";
|
||||||
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||||
import { Client, types } from "cassandra-driver";
|
import { Client, types } from "cassandra-driver";
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ export const prepared = {
|
||||||
"url",
|
"url",
|
||||||
"score",
|
"score",
|
||||||
"files",
|
"files",
|
||||||
"visibleUsersId",
|
"visibleUserIds",
|
||||||
"mentions",
|
"mentions",
|
||||||
"emojis",
|
"emojis",
|
||||||
"tags",
|
"tags",
|
||||||
|
@ -46,12 +47,11 @@ export const prepared = {
|
||||||
"replyId",
|
"replyId",
|
||||||
"renoteId",
|
"renoteId",
|
||||||
"reactions",
|
"reactions",
|
||||||
"reactionEmojis",
|
|
||||||
"noteEdit",
|
"noteEdit",
|
||||||
"updatedAt"
|
"updatedAt"
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
select: {
|
select: {
|
||||||
byDate: `SELECT * FROM note WHERE "createdAtDate" IN ?`,
|
byDate: `SELECT * FROM note WHERE "createdAtDate" IN ?`,
|
||||||
byId: `SELECT * FROM note WHERE "id" IN ?`,
|
byId: `SELECT * FROM note WHERE "id" IN ?`,
|
||||||
|
@ -59,10 +59,17 @@ export const prepared = {
|
||||||
byUrl: `SELECT * FROM note WHERE "url" IN ?`,
|
byUrl: `SELECT * FROM note WHERE "url" IN ?`,
|
||||||
byUserId: `SELECT * FROM note_by_userid WHERE "userId" IN ?`,
|
byUserId: `SELECT * FROM note_by_userid WHERE "userId" IN ?`,
|
||||||
},
|
},
|
||||||
delete: `DELETE FROM note WHERE "createdAtDate" = ? AND "createdAt" = ?`,
|
delete: `DELETE FROM note WHERE "createdAtDate" = ? AND "createdAt" = ? AND "id" = ?`,
|
||||||
update: {
|
update: {
|
||||||
renoteCount: `UPDATE note SET "renoteCount" = ?, "score" = ? WHERE "createdAtDate" = ? AND "createdAt" = ? IF EXISTS`,
|
renoteCount: `UPDATE note SET
|
||||||
reactions: `UPDATE note SET "reactions" = ?, "score" = ? WHERE "createdAtDate" = ? AND "createdAt" = ? IF EXISTS`,
|
"renoteCount" = ?,
|
||||||
|
"score" = ?
|
||||||
|
WHERE "createdAtDate" = ? AND "createdAt" = ? AND "id" = ? IF EXISTS`,
|
||||||
|
reactions: `UPDATE note SET
|
||||||
|
"emojis" = ?,
|
||||||
|
"reactions" = ?,
|
||||||
|
"score" = ?
|
||||||
|
WHERE "createdAtDate" = ? AND "createdAt" = ? AND "id" = ? IF EXISTS`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
reaction: {
|
reaction: {
|
||||||
|
@ -92,12 +99,60 @@ export interface ScyllaDriveFile {
|
||||||
isLink: boolean;
|
isLink: boolean;
|
||||||
md5: string;
|
md5: string;
|
||||||
size: number;
|
size: number;
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ScyllaNoteReaction = NoteReaction & {
|
export interface ScyllaNoteEditHistory {
|
||||||
emoji: PopulatedEmoji
|
content: string;
|
||||||
|
cw: string;
|
||||||
|
files: ScyllaDriveFile[];
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ScyllaNote = Partial<Note> & {
|
||||||
|
createdAtDate: Date;
|
||||||
|
files: ScyllaDriveFile[];
|
||||||
|
channelName: string;
|
||||||
|
noteEdit: ScyllaNoteEditHistory[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parseScyllaNote(row: types.Row): ScyllaNote {
|
||||||
|
const files: ScyllaDriveFile[] = row.get("files");
|
||||||
|
return {
|
||||||
|
createdAtDate: row.get("createdAtDate"),
|
||||||
|
createdAt: row.get("createdAt"),
|
||||||
|
id: row.get("id"),
|
||||||
|
visibility: row.get("visibility"),
|
||||||
|
text: row.get("content"),
|
||||||
|
name: row.get("name"),
|
||||||
|
cw: row.get("cw"),
|
||||||
|
localOnly: row.get("localOnly"),
|
||||||
|
renoteCount: row.get("renoteCount"),
|
||||||
|
repliesCount: row.get("repliesCount"),
|
||||||
|
uri: row.get("uri"),
|
||||||
|
url: row.get("url"),
|
||||||
|
score: row.get("score"),
|
||||||
|
files,
|
||||||
|
fileIds: files.map((file) => file.id),
|
||||||
|
attachedFileTypes: files.map((file) => file.type),
|
||||||
|
visibleUserIds: row.get("visibleUserIds"),
|
||||||
|
mentions: row.get("mentions"),
|
||||||
|
emojis: row.get("emojis"),
|
||||||
|
tags: row.get("tags"),
|
||||||
|
hasPoll: row.get("hasPoll"),
|
||||||
|
threadId: row.get("threadId"),
|
||||||
|
channelId: row.get("channelId"),
|
||||||
|
channelName: row.get("channelName"),
|
||||||
|
userId: row.get("userId"),
|
||||||
|
replyId: row.get("replyId"),
|
||||||
|
renoteId: row.get("replyId"),
|
||||||
|
reactions: row.get("reactions"),
|
||||||
|
noteEdit: row.get("noteEdit"),
|
||||||
|
updatedAt: row.get("updatedAt"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScyllaNoteReaction extends NoteReaction {
|
||||||
|
emoji: PopulatedEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
|
export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
|
||||||
|
@ -108,5 +163,5 @@ export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
|
||||||
reaction: row.get("reaction"),
|
reaction: row.get("reaction"),
|
||||||
createdAt: row.get("createdAt"),
|
createdAt: row.get("createdAt"),
|
||||||
emoji: row.get("emoji"),
|
emoji: row.get("emoji"),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,10 @@ import config from "@/config/index.js";
|
||||||
import { query } from "@/prelude/url.js";
|
import { query } from "@/prelude/url.js";
|
||||||
import { redisClient } from "@/db/redis.js";
|
import { redisClient } from "@/db/redis.js";
|
||||||
|
|
||||||
export const EmojiCache = new Cache<Emoji | null>("populateEmojis", 60 * 60 * 12);
|
export const EmojiCache = new Cache<Emoji | null>(
|
||||||
|
"populateEmojis",
|
||||||
|
60 * 60 * 12,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添付用絵文字情報
|
* 添付用絵文字情報
|
||||||
|
@ -71,13 +74,12 @@ export async function populateEmoji(
|
||||||
host: host ?? IsNull(),
|
host: host ?? IsNull(),
|
||||||
})) || null;
|
})) || null;
|
||||||
|
|
||||||
const cacheKey = `${name} ${host}`;
|
const emoji = await EmojiCache.fetch(
|
||||||
let emoji = await EmojiCache.fetch(cacheKey, queryOrNull);
|
`${name} ${host}`,
|
||||||
|
queryOrNull,
|
||||||
if (emoji && !(emoji.width && emoji.height)) {
|
false,
|
||||||
emoji = await queryOrNull();
|
(cache) => !!cache?.width && !!cache?.height,
|
||||||
await EmojiCache.set(cacheKey, emoji);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (emoji == null) return null;
|
if (emoji == null) return null;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { User } from "@/models/entities/user.js";
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import { Notes, Users } from "@/models/index.js";
|
import { Notes, Users } from "@/models/index.js";
|
||||||
import { generateVisibilityQuery } from "./generate-visibility-query.js";
|
import { generateVisibilityQuery } from "./generate-visibility-query.js";
|
||||||
|
import { prepared, scyllaClient } from "@/db/scylla.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get note for API processing, taking into account visibility.
|
* Get note for API processing, taking into account visibility.
|
||||||
|
@ -11,13 +12,27 @@ export async function getNote(
|
||||||
noteId: Note["id"],
|
noteId: Note["id"],
|
||||||
me: { id: User["id"] } | null,
|
me: { id: User["id"] } | null,
|
||||||
) {
|
) {
|
||||||
|
let note: Note | null = null;
|
||||||
|
if (scyllaClient) {
|
||||||
|
const result = await scyllaClient.execute(
|
||||||
|
prepared.note.select.byId,
|
||||||
|
[noteId],
|
||||||
|
{ prepare: true },
|
||||||
|
);
|
||||||
|
if (result.rowLength > 0) {
|
||||||
|
const visibility: string = result.rows[0].get("visibility");
|
||||||
|
if (!me) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const query = Notes.createQueryBuilder("note").where("note.id = :id", {
|
const query = Notes.createQueryBuilder("note").where("note.id = :id", {
|
||||||
id: noteId,
|
id: noteId,
|
||||||
});
|
});
|
||||||
|
|
||||||
generateVisibilityQuery(query, me);
|
generateVisibilityQuery(query, me);
|
||||||
|
|
||||||
const note = await query.getOne();
|
note = await query.getOne();
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
throw new IdentifiableError(
|
throw new IdentifiableError(
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { verifyLink } from "@/services/fetch-rel-me.js";
|
||||||
import { ApiError } from "../../error.js";
|
import { ApiError } from "../../error.js";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import define from "../../define.js";
|
import define from "../../define.js";
|
||||||
|
import { userByIdCache } from "@/services/user-cache.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["account"],
|
tags: ["account"],
|
||||||
|
@ -305,7 +306,13 @@ export default define(meta, paramDef, async (ps, _user, token) => {
|
||||||
updateUsertags(user, tags);
|
updateUsertags(user, tags);
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if (Object.keys(updates).length > 0) await Users.update(user.id, updates);
|
if (Object.keys(updates).length > 0) {
|
||||||
|
await Users.update(user.id, updates);
|
||||||
|
await userByIdCache.set(
|
||||||
|
user.id,
|
||||||
|
await Users.findOneByOrFail({ id: user.id }),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (Object.keys(profileUpdates).length > 0)
|
if (Object.keys(profileUpdates).length > 0)
|
||||||
await UserProfiles.update(user.id, profileUpdates);
|
await UserProfiles.update(user.id, profileUpdates);
|
||||||
|
|
||||||
|
@ -319,7 +326,7 @@ export default define(meta, paramDef, async (ps, _user, token) => {
|
||||||
publishUserEvent(
|
publishUserEvent(
|
||||||
user.id,
|
user.id,
|
||||||
"updateUserProfile",
|
"updateUserProfile",
|
||||||
await UserProfiles.findOneBy({ userId: user.id }),
|
await UserProfiles.findOneByOrFail({ userId: user.id }),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認
|
// 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
import create from "@/services/note/create.js";
|
import create from "@/services/note/create.js";
|
||||||
import type { User } from "@/models/entities/user.js";
|
import type { CacheableUser, User } from "@/models/entities/user.js";
|
||||||
import {
|
import {
|
||||||
Users,
|
Users,
|
||||||
DriveFiles,
|
DriveFiles,
|
||||||
|
@ -17,6 +17,7 @@ import { ApiError } from "../../error.js";
|
||||||
import define from "../../define.js";
|
import define from "../../define.js";
|
||||||
import { HOUR } from "@/const.js";
|
import { HOUR } from "@/const.js";
|
||||||
import { getNote } from "../../common/getters.js";
|
import { getNote } from "../../common/getters.js";
|
||||||
|
import { userByIdCache } from "@/services/user-cache.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -190,15 +191,25 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
if (user.movedToUri != null) throw new ApiError(meta.errors.accountLocked);
|
if (user.movedToUri != null) throw new ApiError(meta.errors.accountLocked);
|
||||||
let visibleUsers: User[] = [];
|
let visibleUsers: User[] = [];
|
||||||
if (ps.visibleUserIds) {
|
if (ps.visibleUserIds) {
|
||||||
visibleUsers = await Users.findBy({
|
visibleUsers = (
|
||||||
id: In(ps.visibleUserIds),
|
await Promise.all(
|
||||||
});
|
ps.visibleUserIds.map((id) =>
|
||||||
|
userByIdCache.fetchMaybe(id, () =>
|
||||||
|
Users.findOneBy({ id }).then((user) => user ?? undefined),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
).filter((user) => user !== undefined) as CacheableUser[];
|
||||||
}
|
}
|
||||||
|
|
||||||
let files: DriveFile[] = [];
|
let files: DriveFile[] = [];
|
||||||
const fileIds =
|
const fileIds =
|
||||||
ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null;
|
ps.fileIds && ps.fileIds.length > 0
|
||||||
if (fileIds != null) {
|
? ps.fileIds
|
||||||
|
: ps.mediaIds && ps.mediaIds.length > 0
|
||||||
|
? ps.mediaIds
|
||||||
|
: null;
|
||||||
|
if (fileIds && fileIds.length > 0) {
|
||||||
files = await DriveFiles.createQueryBuilder("file")
|
files = await DriveFiles.createQueryBuilder("file")
|
||||||
.where("file.userId = :userId AND file.id IN (:...fileIds)", {
|
.where("file.userId = :userId AND file.id IN (:...fileIds)", {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
@ -210,7 +221,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let renote: Note | null = null;
|
let renote: Note | null = null;
|
||||||
if (ps.renoteId != null) {
|
if (ps.renoteId) {
|
||||||
// Fetch renote to note
|
// Fetch renote to note
|
||||||
renote = await getNote(ps.renoteId, user).catch((e) => {
|
renote = await getNote(ps.renoteId, user).catch((e) => {
|
||||||
if (e.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24")
|
if (e.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24")
|
||||||
|
|
|
@ -677,9 +677,12 @@ async function renderNoteOrRenoteActivity(data: Option, note: Note) {
|
||||||
|
|
||||||
function incRenoteCount(renote: Note) {
|
function incRenoteCount(renote: Note) {
|
||||||
if (scyllaClient) {
|
if (scyllaClient) {
|
||||||
|
const date = new Date(renote.createdAt.getTime());
|
||||||
scyllaClient.execute(prepared.note.update.renoteCount, [
|
scyllaClient.execute(prepared.note.update.renoteCount, [
|
||||||
renote.renoteCount + 1,
|
renote.renoteCount + 1,
|
||||||
renote.score + 1,
|
renote.score + 1,
|
||||||
|
date,
|
||||||
|
date,
|
||||||
renote.id,
|
renote.id,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -768,7 +771,6 @@ async function insertNote(
|
||||||
// 投稿を作成
|
// 投稿を作成
|
||||||
try {
|
try {
|
||||||
if (scyllaClient) {
|
if (scyllaClient) {
|
||||||
const noteEmojis = await populateEmojis(insert.emojis, user.host);
|
|
||||||
await scyllaClient.execute(
|
await scyllaClient.execute(
|
||||||
prepared.note.insert,
|
prepared.note.insert,
|
||||||
[
|
[
|
||||||
|
@ -788,7 +790,7 @@ async function insertNote(
|
||||||
data.files,
|
data.files,
|
||||||
insert.visibleUserIds,
|
insert.visibleUserIds,
|
||||||
insert.mentions,
|
insert.mentions,
|
||||||
noteEmojis,
|
insert.emojis,
|
||||||
insert.tags,
|
insert.tags,
|
||||||
insert.hasPoll,
|
insert.hasPoll,
|
||||||
insert.threadId,
|
insert.threadId,
|
||||||
|
@ -800,7 +802,6 @@ async function insertNote(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
],
|
],
|
||||||
{ prepare: true },
|
{ prepare: true },
|
||||||
);
|
);
|
||||||
|
|
|
@ -119,7 +119,7 @@ export default async function (
|
||||||
|
|
||||||
if (scyllaClient) {
|
if (scyllaClient) {
|
||||||
const date = new Date(note.createdAt.getTime());
|
const date = new Date(note.createdAt.getTime());
|
||||||
await scyllaClient.execute(prepared.note.delete, [date, date], {
|
await scyllaClient.execute(prepared.note.delete, [date, date, note.id], {
|
||||||
prepare: true,
|
prepare: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js
|
||||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
||||||
import { prepared, scyllaClient } from "@/db/scylla.js";
|
import { prepared, scyllaClient } from "@/db/scylla.js";
|
||||||
|
import { populateEmojis } from "@/misc/populate-emojis.js";
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
user: { id: User["id"]; host: User["host"] },
|
user: { id: User["id"]; host: User["host"] },
|
||||||
|
@ -105,10 +106,18 @@ export default async (
|
||||||
if (scyllaClient) {
|
if (scyllaClient) {
|
||||||
const current = Math.max(note.reactions[_reaction] ?? 0, 0);
|
const current = Math.max(note.reactions[_reaction] ?? 0, 0);
|
||||||
note.reactions[_reaction] = current + 1;
|
note.reactions[_reaction] = current + 1;
|
||||||
|
const emojiName = decodeReaction(_reaction).reaction.replaceAll(":", "");
|
||||||
const date = new Date(note.createdAt.getTime());
|
const date = new Date(note.createdAt.getTime());
|
||||||
await scyllaClient.execute(
|
await scyllaClient.execute(
|
||||||
prepared.note.update.reactions,
|
prepared.note.update.reactions,
|
||||||
[note.reactions, (note.score ?? 0) + 1, date, date],
|
[
|
||||||
|
note.emojis.concat(emojiName),
|
||||||
|
note.reactions,
|
||||||
|
(note.score ?? 0) + 1,
|
||||||
|
date,
|
||||||
|
date,
|
||||||
|
note.id,
|
||||||
|
],
|
||||||
{ prepare: true },
|
{ prepare: true },
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -56,9 +56,19 @@ export default async (
|
||||||
note.reactions[reaction.reaction] = count;
|
note.reactions[reaction.reaction] = count;
|
||||||
}
|
}
|
||||||
const date = new Date(note.createdAt.getTime());
|
const date = new Date(note.createdAt.getTime());
|
||||||
|
const emojiName = reaction.reaction.replaceAll(":", "");
|
||||||
|
const emojiIndex = note.emojis.indexOf(emojiName);
|
||||||
|
if (emojiIndex >= 0 && count === 0) note.emojis.splice(emojiIndex, 1);
|
||||||
await scyllaClient.execute(
|
await scyllaClient.execute(
|
||||||
prepared.note.update.reactions,
|
prepared.note.update.reactions,
|
||||||
[note.reactions, Math.max((note.score ?? 0) - 1, 0), date, date],
|
[
|
||||||
|
note.emojis,
|
||||||
|
note.reactions,
|
||||||
|
Math.max((note.score ?? 0) - 1, 0),
|
||||||
|
date,
|
||||||
|
date,
|
||||||
|
note.id,
|
||||||
|
],
|
||||||
{ prepare: true },
|
{ prepare: true },
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue