Fixed 267U.pre2
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import { publishNoteStream } from "../../stream.js";
|
||||
import { renderLike } from "../../../remote/activitypub/renderer/like.js";
|
||||
import DeliverManager from "../../../remote/activitypub/deliver-manager.js";
|
||||
import { renderActivity } from "../../../remote/activitypub/renderer/index.js";
|
||||
import { toDbReaction, decodeReaction } from "../../../misc/reaction-lib.js";
|
||||
import { NoteReactions, Users, NoteWatchings, Notes, Emojis, Blockings } from "../../../models/index.js";
|
||||
import { IsNull, Not } from "typeorm";
|
||||
import { perUserReactionsChart } from "../../chart/index.js";
|
||||
import { genId } from "../../../misc/gen-id.js";
|
||||
import { createNotification } from "../../create-notification.js";
|
||||
import deleteReaction from "./delete.js";
|
||||
import { isDuplicateKeyValueError } from "../../../misc/is-duplicate-key-value-error.js";
|
||||
import { IdentifiableError } from "../../../misc/identifiable-error.js";
|
||||
import { populateEmojiOrUserEmoji } from "../../../misc/populate-emojis.js";
|
||||
export default (async (user, note, reaction)=>{
|
||||
// Check blocking
|
||||
if (note.userId !== user.id) {
|
||||
const blocked = await Blockings.exist({
|
||||
where: [
|
||||
{
|
||||
blockerId: note.userId,
|
||||
blockeeId: user.id,
|
||||
groupId: null
|
||||
},
|
||||
...note.groupId ? [
|
||||
{
|
||||
groupId: note.groupId,
|
||||
blockeeId: user.id
|
||||
}
|
||||
] : []
|
||||
]
|
||||
});
|
||||
if (blocked) {
|
||||
throw new IdentifiableError("e70412a4-7197-4726-8e74-f3e0deb92aa7");
|
||||
}
|
||||
}
|
||||
// check visibility
|
||||
if (!await Notes.isVisibleForMe(note, user.id)) {
|
||||
throw new IdentifiableError("68e9d2d1-48bf-42c2-b90a-b20e09fd3d48", "Note not accessible for you.");
|
||||
}
|
||||
// TODO: cache
|
||||
reaction = await toDbReaction(reaction, user.host);
|
||||
const record = {
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
noteId: note.id,
|
||||
userId: user.id,
|
||||
groupId: user.groupId ?? null,
|
||||
reaction
|
||||
};
|
||||
// Create reaction
|
||||
try {
|
||||
await NoteReactions.insert(record);
|
||||
} catch (e) {
|
||||
if (isDuplicateKeyValueError(e)) {
|
||||
const exists = await NoteReactions.findOneByOrFail({
|
||||
noteId: note.id,
|
||||
...user.groupId ? {
|
||||
groupId: user.groupId
|
||||
} : {
|
||||
userId: user.id,
|
||||
groupId: null
|
||||
}
|
||||
});
|
||||
if (exists.reaction !== reaction) {
|
||||
// 別のリアクションがすでにされていたら置き換える
|
||||
await deleteReaction(user, note);
|
||||
await NoteReactions.insert(record);
|
||||
} else {
|
||||
// 同じリアクションがすでにされていたらエラー
|
||||
throw new IdentifiableError("51c42bb4-931a-456b-bff7-e5a8a70dd298", "Reaction already exists");
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
// Increment reactions count
|
||||
const sql = `jsonb_set("reactions", '{${reaction}}', (COALESCE("reactions"->>'${reaction}', '0')::int + 1)::text::jsonb)`;
|
||||
await Notes.createQueryBuilder().update().set({
|
||||
reactions: ()=>sql,
|
||||
score: ()=>'"score" + 1'
|
||||
}).where("id = :id", {
|
||||
id: note.id
|
||||
}).execute();
|
||||
perUserReactionsChart.update(user, note);
|
||||
// カスタム絵文字リアクションだったら絵文字情報も送る
|
||||
const decodedReaction = decodeReaction(reaction);
|
||||
const emoji = await Emojis.findOne({
|
||||
where: {
|
||||
name: decodedReaction.name,
|
||||
host: decodedReaction.host ?? IsNull()
|
||||
},
|
||||
select: [
|
||||
"name",
|
||||
"host",
|
||||
"originalUrl",
|
||||
"publicUrl"
|
||||
]
|
||||
});
|
||||
const populatedEmoji = emoji == null ? await populateEmojiOrUserEmoji(reaction.slice(1, -1), note.userHost) : null;
|
||||
publishNoteStream(note.id, "reacted", {
|
||||
reaction: decodedReaction.reaction,
|
||||
emoji: emoji != null ? {
|
||||
name: emoji.host ? `${emoji.name}@${emoji.host}` : `${emoji.name}@.`,
|
||||
url: emoji.publicUrl || emoji.originalUrl
|
||||
} : populatedEmoji != null ? {
|
||||
name: populatedEmoji.name,
|
||||
url: populatedEmoji.url
|
||||
} : null,
|
||||
userId: user.id,
|
||||
groupId: user.groupId ?? null
|
||||
});
|
||||
// Create notification if the reaction target is a local user.
|
||||
if (note.userHost === null) {
|
||||
createNotification(note.userId, "reaction", {
|
||||
notifierId: user.id,
|
||||
note: note,
|
||||
noteId: note.id,
|
||||
reaction: reaction
|
||||
});
|
||||
}
|
||||
// Fetch watchers
|
||||
NoteWatchings.findBy({
|
||||
noteId: note.id,
|
||||
userId: Not(user.id)
|
||||
}).then((watchers)=>{
|
||||
for (const watcher of watchers){
|
||||
createNotification(watcher.userId, "reaction", {
|
||||
notifierId: user.id,
|
||||
note: note,
|
||||
noteId: note.id,
|
||||
reaction: reaction
|
||||
});
|
||||
}
|
||||
});
|
||||
//#region deliver
|
||||
if (Users.isLocalUser(user) && !note.localOnly && note.visibility !== "hidden") {
|
||||
const content = renderActivity(await renderLike(record, note));
|
||||
const dm = new DeliverManager(user, content);
|
||||
if (note.userHost !== null) {
|
||||
const reactee = await Users.findOneBy({
|
||||
id: note.userId
|
||||
});
|
||||
dm.addDirectRecipe(reactee);
|
||||
}
|
||||
if ([
|
||||
"public",
|
||||
"home",
|
||||
"followers"
|
||||
].includes(note.visibility)) {
|
||||
dm.addFollowersRecipe();
|
||||
} else if (note.visibility === "specified") {
|
||||
const visibleUsers = await Promise.all(note.visibleUserIds.map((id)=>Users.findOneBy({
|
||||
id
|
||||
})));
|
||||
for (const u of visibleUsers.filter((u)=>u && Users.isRemoteUser(u))){
|
||||
dm.addDirectRecipe(u);
|
||||
}
|
||||
}
|
||||
dm.execute();
|
||||
}
|
||||
//#endregion
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import { publishNoteStream } from "../../stream.js";
|
||||
import { renderLike } from "../../../remote/activitypub/renderer/like.js";
|
||||
import renderUndo from "../../../remote/activitypub/renderer/undo.js";
|
||||
import { renderActivity } from "../../../remote/activitypub/renderer/index.js";
|
||||
import DeliverManager from "../../../remote/activitypub/deliver-manager.js";
|
||||
import { IdentifiableError } from "../../../misc/identifiable-error.js";
|
||||
import { NoteReactions, Users, Notes } from "../../../models/index.js";
|
||||
import { decodeReaction } from "../../../misc/reaction-lib.js";
|
||||
export default (async (user, note)=>{
|
||||
const reaction = await NoteReactions.findOneBy({
|
||||
noteId: note.id,
|
||||
...user.groupId ? {
|
||||
groupId: user.groupId
|
||||
} : {
|
||||
userId: user.id,
|
||||
groupId: null
|
||||
}
|
||||
});
|
||||
// if already unreacted
|
||||
if (reaction == null) {
|
||||
throw new IdentifiableError("60527ec9-b4cb-4a88-a6bd-32d3ad26817d", "not reacted");
|
||||
}
|
||||
// Delete reaction
|
||||
const result = await NoteReactions.delete(reaction.id);
|
||||
if (result.affected !== 1) {
|
||||
throw new IdentifiableError("60527ec9-b4cb-4a88-a6bd-32d3ad26817d", "not reacted");
|
||||
}
|
||||
// Decrement reactions count
|
||||
const sql = `jsonb_set("reactions", '{${reaction.reaction}}', (COALESCE("reactions"->>'${reaction.reaction}', '0')::int - 1)::text::jsonb)`;
|
||||
await Notes.createQueryBuilder().update().set({
|
||||
reactions: ()=>sql
|
||||
}).where("id = :id", {
|
||||
id: note.id
|
||||
}).execute();
|
||||
Notes.decrement({
|
||||
id: note.id
|
||||
}, "score", 1);
|
||||
publishNoteStream(note.id, "unreacted", {
|
||||
reaction: decodeReaction(reaction.reaction).reaction,
|
||||
userId: user.id,
|
||||
groupId: user.groupId ?? null
|
||||
});
|
||||
//#region 配信
|
||||
if (Users.isLocalUser(user) && !note.localOnly) {
|
||||
const content = renderActivity(renderUndo(await renderLike(reaction, note), user));
|
||||
const dm = new DeliverManager(user, content);
|
||||
if (note.userHost !== null) {
|
||||
const reactee = await Users.findOneBy({
|
||||
id: note.userId
|
||||
});
|
||||
dm.addDirectRecipe(reactee);
|
||||
}
|
||||
dm.addFollowersRecipe();
|
||||
dm.execute();
|
||||
}
|
||||
//#endregion
|
||||
});
|
||||
Reference in New Issue
Block a user