hippofish/packages/client/src/scripts/use-note-capture.ts

151 lines
3.6 KiB
TypeScript
Raw Normal View History

2023-09-02 01:27:33 +02:00
import type { Ref } from "vue";
import { onUnmounted } from "vue";
import type { entities } from "firefish-js";
import { useStream } from "@/stream";
2024-03-16 16:49:12 +01:00
import { isSignedIn, me } from "@/me";
import * as os from "@/os";
export function useNoteCapture(props: {
2024-04-12 10:37:32 +02:00
rootEl: Ref<HTMLElement | null>;
note: Ref<entities.Note>;
isDeletedRef: Ref<boolean>;
2024-04-20 14:55:47 +02:00
onReplied?: (note: entities.Note) => void;
}) {
const note = props.note;
2024-04-22 04:32:41 +02:00
const connection = isSignedIn(me) ? useStream() : null;
async function onStreamNoteUpdated(noteData): Promise<void> {
const { type, id, body } = noteData;
if (id !== note.value.id) return;
switch (type) {
2024-04-20 14:55:47 +02:00
case "replied": {
note.value.repliesCount += 1;
2024-04-20 14:55:47 +02:00
if (props.onReplied) {
const { id: createdId } = body;
const replyNote = await os.api("notes/show", {
noteId: createdId,
});
props.onReplied(replyNote);
}
break;
}
2023-01-13 05:40:33 +01:00
case "reacted": {
const reaction = body.reaction;
if (body.emoji) {
const emojis = note.value.emojis || [];
if (!emojis.includes(body.emoji)) {
note.value.emojis = [...emojis, body.emoji];
}
}
// TODO: reactionsプロパティがない場合ってあったっけ なければ || {} は消せる
2023-01-13 05:40:33 +01:00
const currentCount = note.value.reactions?.[reaction] || 0;
note.value.reactions[reaction] = currentCount + 1;
2024-04-22 04:32:41 +02:00
if (isSignedIn(me) && body.userId === me.id) {
note.value.myReaction = reaction;
}
break;
}
2023-01-13 05:40:33 +01:00
case "unreacted": {
const reaction = body.reaction;
// TODO: reactionsプロパティがない場合ってあったっけ なければ || {} は消せる
2023-01-13 05:40:33 +01:00
const currentCount = note.value.reactions?.[reaction] || 0;
note.value.reactions[reaction] = Math.max(0, currentCount - 1);
2024-04-22 04:32:41 +02:00
if (isSignedIn(me) && body.userId === me.id) {
note.value.myReaction = undefined;
}
break;
}
2023-01-13 05:40:33 +01:00
case "pollVoted": {
const choice = body.choice;
if (note.value.poll) {
const choices = [...note.value.poll.choices];
choices[choice] = {
...choices[choice],
votes: choices[choice].votes + 1,
2024-04-22 04:32:41 +02:00
...(isSignedIn(me) && body.userId === me.id
? {
isVoted: true,
}
: {}),
};
note.value.poll.choices = choices;
}
break;
}
2023-01-13 05:40:33 +01:00
case "deleted": {
props.isDeletedRef.value = true;
break;
}
case "updated": {
try {
const editedNote = await os.api("notes/show", {
noteId: id,
});
const keys = new Set<string>();
Object.keys(editedNote)
.concat(Object.keys(note.value))
.forEach((key) => keys.add(key));
keys.forEach((key) => {
note.value[key] = editedNote[key];
});
} catch {
// delete the note if failing to get the edited note
props.isDeletedRef.value = true;
}
break;
}
}
}
function capture(withHandler = false): void {
if (connection) {
// TODO: このノートがストリーミング経由で流れてきた場合のみ sr する
2023-01-13 05:40:33 +01:00
connection.send(document.body.contains(props.rootEl.value) ? "sr" : "s", {
id: note.value.id,
});
if (withHandler) connection.on("noteUpdated", onStreamNoteUpdated);
}
}
function decapture(withHandler = false): void {
if (connection) {
2023-01-13 05:40:33 +01:00
connection.send("un", {
id: note.value.id,
});
2023-01-13 05:40:33 +01:00
if (withHandler) connection.off("noteUpdated", onStreamNoteUpdated);
}
}
function onStreamConnected() {
capture(false);
}
2023-01-13 05:40:33 +01:00
capture(true);
if (connection) {
2023-01-13 05:40:33 +01:00
connection.on("_connected_", onStreamConnected);
}
2023-01-13 05:40:33 +01:00
onUnmounted(() => {
decapture(true);
if (connection) {
2023-01-13 05:40:33 +01:00
connection.off("_connected_", onStreamConnected);
}
});
}