refactor: ⚡ antenna notes in cache
Co-authored-by: Kainoa Kanter <kainoa@t1c.dev>
This commit is contained in:
parent
5e85d0761e
commit
fd1bc109d9
7 changed files with 113 additions and 123 deletions
9
packages/backend/migration/1680491187535-cleanup.js
Normal file
9
packages/backend/migration/1680491187535-cleanup.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
export class cleanup1680491187535 {
|
||||
name = "cleanup1680491187535";
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`DROP TABLE "antenna_note" `);
|
||||
}
|
||||
|
||||
async down(queryRunner) {}
|
||||
}
|
|
@ -123,7 +123,6 @@ export const ModerationLogs = ModerationLogRepository;
|
|||
export const Clips = ClipRepository;
|
||||
export const ClipNotes = db.getRepository(ClipNote);
|
||||
export const Antennas = AntennaRepository;
|
||||
export const AntennaNotes = db.getRepository(AntennaNote);
|
||||
export const PromoNotes = db.getRepository(PromoNote);
|
||||
export const PromoReads = db.getRepository(PromoRead);
|
||||
export const Relays = RelayRepository;
|
||||
|
|
|
@ -18,7 +18,6 @@ import { createPerson } from "@/remote/activitypub/models/person.js";
|
|||
import {
|
||||
AnnouncementReads,
|
||||
Announcements,
|
||||
AntennaNotes,
|
||||
Blockings,
|
||||
ChannelFollowings,
|
||||
DriveFiles,
|
||||
|
@ -258,23 +257,24 @@ export const UserRepository = db.getRepository(User).extend({
|
|||
},
|
||||
|
||||
async getHasUnreadAntenna(userId: User["id"]): Promise<boolean> {
|
||||
try {
|
||||
const myAntennas = (await getAntennas()).filter(
|
||||
(a) => a.userId === userId,
|
||||
);
|
||||
// try {
|
||||
// const myAntennas = (await getAntennas()).filter(
|
||||
// (a) => a.userId === userId,
|
||||
// );
|
||||
|
||||
const unread =
|
||||
myAntennas.length > 0
|
||||
? await AntennaNotes.findOneBy({
|
||||
antennaId: In(myAntennas.map((x) => x.id)),
|
||||
read: false,
|
||||
})
|
||||
: null;
|
||||
// const unread =
|
||||
// myAntennas.length > 0
|
||||
// ? await AntennaNotes.findOneBy({
|
||||
// antennaId: In(myAntennas.map((x) => x.id)),
|
||||
// read: false,
|
||||
// })
|
||||
// : null;
|
||||
|
||||
return unread != null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
// return unread != null;
|
||||
// } catch (e) {
|
||||
// return false;
|
||||
// }
|
||||
return false; // TODO
|
||||
},
|
||||
|
||||
async getHasUnreadChannel(userId: User["id"]): Promise<boolean> {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import define from "../../define.js";
|
||||
import { Antennas, AntennaNotes } from "@/models/index.js";
|
||||
import { Antennas } from "@/models/index.js";
|
||||
import { FindOptionsWhere } from "typeorm";
|
||||
import { AntennaNote } from "@/models/entities/antenna-note.js";
|
||||
|
||||
|
@ -29,15 +29,15 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
await AntennaNotes.update(
|
||||
{
|
||||
antennaId: antenna.id,
|
||||
read: false,
|
||||
},
|
||||
{
|
||||
read: true,
|
||||
},
|
||||
);
|
||||
// await AntennaNotes.update(
|
||||
// {
|
||||
// antennaId: antenna.id,
|
||||
// read: false,
|
||||
// },
|
||||
// {
|
||||
// read: true,
|
||||
// },
|
||||
// );
|
||||
|
||||
return true;
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import define from "../../define.js";
|
||||
import readNote from "@/services/note/read.js";
|
||||
import { Antennas, Notes, AntennaNotes } from "@/models/index.js";
|
||||
import { Antennas, Notes } from "@/models/index.js";
|
||||
import { redisClient } from "@/db/redis.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
||||
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
||||
|
@ -58,6 +60,26 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
throw new ApiError(meta.errors.noSuchAntenna);
|
||||
}
|
||||
|
||||
const noteIdsRes = await redisClient.xrevrange(
|
||||
`antennaTimeline:${antenna.id}`,
|
||||
ps.untilDate || "+",
|
||||
"-",
|
||||
"COUNT",
|
||||
ps.limit + 1,
|
||||
); // untilIdに指定したものも含まれるため+1
|
||||
|
||||
if (noteIdsRes.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const noteIds = noteIdsRes
|
||||
.map((x) => x[1][1])
|
||||
.filter((x) => x !== ps.untilId);
|
||||
|
||||
if (noteIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const query = makePaginationQuery(
|
||||
Notes.createQueryBuilder("note"),
|
||||
ps.sinceId,
|
||||
|
@ -65,11 +87,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
ps.sinceDate,
|
||||
ps.untilDate,
|
||||
)
|
||||
.innerJoin(
|
||||
AntennaNotes.metadata.targetName,
|
||||
"antennaNote",
|
||||
"antennaNote.noteId = note.id",
|
||||
)
|
||||
.where("note.id IN (:...noteIds)", { noteIds: noteIds })
|
||||
.innerJoinAndSelect("note.user", "user")
|
||||
.leftJoinAndSelect("user.avatar", "avatar")
|
||||
.leftJoinAndSelect("user.banner", "banner")
|
||||
|
@ -81,7 +99,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
.leftJoinAndSelect("renote.user", "renoteUser")
|
||||
.leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar")
|
||||
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner")
|
||||
.andWhere("antennaNote.antennaId = :antennaId", { antennaId: antenna.id })
|
||||
.andWhere("note.visibility != 'home'");
|
||||
|
||||
generateVisibilityQuery(query, user);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import type { Antenna } from "@/models/entities/antenna.js";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
import { AntennaNotes, Mutings, Notes } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||
import { publishAntennaStream, publishMainStream } from "@/services/stream.js";
|
||||
import { redisClient } from "@/db/redis.js";
|
||||
import { publishAntennaStream } from "@/services/stream.js";
|
||||
import type { User } from "@/models/entities/user.js";
|
||||
|
||||
export async function addNoteToAntenna(
|
||||
|
@ -14,48 +13,15 @@ export async function addNoteToAntenna(
|
|||
// 通知しない設定になっているか、自分自身の投稿なら既読にする
|
||||
const read = !antenna.notify || antenna.userId === noteUser.id;
|
||||
|
||||
AntennaNotes.insert({
|
||||
id: genId(),
|
||||
antennaId: antenna.id,
|
||||
noteId: note.id,
|
||||
read: read,
|
||||
});
|
||||
redisClient.xadd(
|
||||
`antennaTimeline:${antenna.id}`,
|
||||
"MAXLEN",
|
||||
"~",
|
||||
"200",
|
||||
`${genId(note.createdAt)}-*`,
|
||||
"note",
|
||||
note.id,
|
||||
);
|
||||
|
||||
publishAntennaStream(antenna.id, "note", note);
|
||||
|
||||
if (!read) {
|
||||
const mutings = await Mutings.find({
|
||||
where: {
|
||||
muterId: antenna.userId,
|
||||
},
|
||||
select: ["muteeId"],
|
||||
});
|
||||
|
||||
// Copy
|
||||
const _note: Note = {
|
||||
...note,
|
||||
};
|
||||
|
||||
if (note.replyId != null) {
|
||||
_note.reply = await Notes.findOneByOrFail({ id: note.replyId });
|
||||
}
|
||||
if (note.renoteId != null) {
|
||||
_note.renote = await Notes.findOneByOrFail({ id: note.renoteId });
|
||||
}
|
||||
|
||||
if (isUserRelated(_note, new Set<string>(mutings.map((x) => x.muteeId)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2秒経っても既読にならなかったら通知
|
||||
setTimeout(async () => {
|
||||
const unread = await AntennaNotes.findOneBy({
|
||||
antennaId: antenna.id,
|
||||
read: false,
|
||||
});
|
||||
if (unread) {
|
||||
publishMainStream(antenna.userId, "unreadAntenna", antenna);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import type { Note } from "@/models/entities/note.js";
|
|||
import type { User } from "@/models/entities/user.js";
|
||||
import {
|
||||
NoteUnreads,
|
||||
AntennaNotes,
|
||||
Users,
|
||||
Followings,
|
||||
ChannelFollowings,
|
||||
|
@ -55,7 +54,7 @@ export default async function (
|
|||
const readMentions: (Note | Packed<"Note">)[] = [];
|
||||
const readSpecifiedNotes: (Note | Packed<"Note">)[] = [];
|
||||
const readChannelNotes: (Note | Packed<"Note">)[] = [];
|
||||
const readAntennaNotes: (Note | Packed<"Note">)[] = [];
|
||||
// const readAntennaNotes: (Note | Packed<"Note">)[] = [];
|
||||
|
||||
for (const note of notes) {
|
||||
if (note.mentions?.includes(userId)) {
|
||||
|
@ -68,22 +67,22 @@ export default async function (
|
|||
readChannelNotes.push(note);
|
||||
}
|
||||
|
||||
if (note.user != null) {
|
||||
// たぶんnullになることは無いはずだけど一応
|
||||
for (const antenna of myAntennas) {
|
||||
if (
|
||||
await checkHitAntenna(
|
||||
antenna,
|
||||
note,
|
||||
note.user,
|
||||
undefined,
|
||||
Array.from(following),
|
||||
)
|
||||
) {
|
||||
readAntennaNotes.push(note);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (note.user != null) {
|
||||
// // たぶんnullになることは無いはずだけど一応
|
||||
// for (const antenna of myAntennas) {
|
||||
// if (
|
||||
// await checkHitAntenna(
|
||||
// antenna,
|
||||
// note,
|
||||
// note.user,
|
||||
// undefined,
|
||||
// Array.from(following),
|
||||
// )
|
||||
// ) {
|
||||
// readAntennaNotes.push(note);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -141,33 +140,33 @@ export default async function (
|
|||
});
|
||||
}
|
||||
|
||||
if (readAntennaNotes.length > 0) {
|
||||
await AntennaNotes.update(
|
||||
{
|
||||
antennaId: In(myAntennas.map((a) => a.id)),
|
||||
noteId: In(readAntennaNotes.map((n) => n.id)),
|
||||
},
|
||||
{
|
||||
read: true,
|
||||
},
|
||||
);
|
||||
// if (readAntennaNotes.length > 0) {
|
||||
// await AntennaNotes.update(
|
||||
// {
|
||||
// antennaId: In(myAntennas.map((a) => a.id)),
|
||||
// noteId: In(readAntennaNotes.map((n) => n.id)),
|
||||
// },
|
||||
// {
|
||||
// read: true,
|
||||
// },
|
||||
// );
|
||||
|
||||
// TODO: まとめてクエリしたい
|
||||
for (const antenna of myAntennas) {
|
||||
const count = await AntennaNotes.countBy({
|
||||
antennaId: antenna.id,
|
||||
read: false,
|
||||
});
|
||||
// // TODO: まとめてクエリしたい
|
||||
// for (const antenna of myAntennas) {
|
||||
// const count = await AntennaNotes.countBy({
|
||||
// antennaId: antenna.id,
|
||||
// read: false,
|
||||
// });
|
||||
|
||||
if (count === 0) {
|
||||
publishMainStream(userId, "readAntenna", antenna);
|
||||
}
|
||||
}
|
||||
// if (count === 0) {
|
||||
// publishMainStream(userId, "readAntenna", antenna);
|
||||
// }
|
||||
// }
|
||||
|
||||
Users.getHasUnreadAntenna(userId).then((unread) => {
|
||||
if (!unread) {
|
||||
publishMainStream(userId, "readAllAntennas");
|
||||
}
|
||||
});
|
||||
}
|
||||
// Users.getHasUnreadAntenna(userId).then((unread) => {
|
||||
// if (!unread) {
|
||||
// publishMainStream(userId, "readAllAntennas");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue