257 lines
6.9 KiB
Vue
257 lines
6.9 KiB
Vue
<template>
|
|
<MkInfo
|
|
v-if="tlHint && !tlHintClosed"
|
|
:closeable="true"
|
|
class="_gap"
|
|
@close="closeHint"
|
|
>
|
|
<I18n :src="tlHint">
|
|
<template #icon></template>
|
|
</I18n>
|
|
</MkInfo>
|
|
<XNotes
|
|
ref="tlComponent"
|
|
:no-gap="!$store.state.showGapBetweenNotesInTimeline"
|
|
:pagination="pagination"
|
|
@queue="emit('queue', $event)"
|
|
/>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { onUnmounted, ref } from "vue";
|
|
import XNotes from "@/components/MkNotes.vue";
|
|
import MkInfo from "@/components/MkInfo.vue";
|
|
import * as os from "@/os";
|
|
import { stream } from "@/stream";
|
|
import * as sound from "@/scripts/sound";
|
|
import { $i } from "@/account";
|
|
import { i18n } from "@/i18n";
|
|
import { defaultStore } from "@/store";
|
|
import { endpoints, packed } from "magnetar-common";
|
|
import { debounce } from "throttle-debounce";
|
|
import * as misskey from "calckey-js";
|
|
|
|
const props = defineProps<{
|
|
src: string;
|
|
list?: string;
|
|
antenna?: string;
|
|
sound?: boolean;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(ev: "note"): void;
|
|
(ev: "queue", count: number): void;
|
|
}>();
|
|
|
|
const displayLimit = 30;
|
|
|
|
const tlComponent = ref<InstanceType<typeof XNotes>>();
|
|
|
|
let debounceBuffer: Array<string> = [];
|
|
const prependMany = async () => {
|
|
let items = debounceBuffer;
|
|
debounceBuffer = [];
|
|
|
|
if (!tlComponent.value?.pagingComponent?.isFresh()) {
|
|
items = debounceBuffer.slice(-displayLimit);
|
|
}
|
|
|
|
const notes = (
|
|
await Promise.allSettled(
|
|
items.map((note) =>
|
|
os.magApi(
|
|
endpoints.GetNoteById,
|
|
{ context: true, attachments: true },
|
|
{
|
|
id: note,
|
|
}
|
|
)
|
|
)
|
|
)
|
|
)
|
|
.filter((p) => p.status === "fulfilled")
|
|
.map(
|
|
(p) => (p as PromiseFulfilledResult<packed.PackNoteMaybeFull>).value
|
|
);
|
|
|
|
for (const n of notes) {
|
|
tlComponent.value?.pagingComponent?.prepend(n);
|
|
|
|
emit("note");
|
|
}
|
|
|
|
if (props.sound) {
|
|
if (notes.some((nn) => nn.user.id !== $i?.id)) sound.play("note");
|
|
if (notes.some((nn) => nn.user.id === $i?.id)) sound.play("noteMy");
|
|
}
|
|
};
|
|
const debouncePrepend = debounce(40, prependMany);
|
|
|
|
const prepend = (note: string) => {
|
|
debounceBuffer.push(note);
|
|
debouncePrepend();
|
|
};
|
|
|
|
const onUserAdded = () => {
|
|
tlComponent.value?.pagingComponent?.reload();
|
|
};
|
|
|
|
const onUserRemoved = () => {
|
|
tlComponent.value?.pagingComponent?.reload();
|
|
};
|
|
|
|
const onChangeFollowing = () => {
|
|
if (!tlComponent.value?.pagingComponent?.backed) {
|
|
tlComponent.value?.pagingComponent?.reload();
|
|
}
|
|
};
|
|
|
|
let endpoint: keyof misskey.Endpoints;
|
|
let query;
|
|
let connection;
|
|
let connection2;
|
|
|
|
let tlHint;
|
|
let tlHintClosed;
|
|
|
|
if (props.src === "antenna") {
|
|
endpoint = "antennas/notes";
|
|
query = {
|
|
antennaId: props.antenna,
|
|
};
|
|
connection = stream.useChannel("antenna", {
|
|
antennaId: props.antenna,
|
|
});
|
|
connection.on("note", prepend);
|
|
} else if (props.src === "home") {
|
|
endpoint = "notes/timeline";
|
|
query = {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
};
|
|
connection = stream.useChannel("homeTimeline", {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
});
|
|
connection.on("note", prepend);
|
|
|
|
connection2 = stream.useChannel("main");
|
|
connection2.on("follow", onChangeFollowing);
|
|
connection2.on("unfollow", onChangeFollowing);
|
|
|
|
tlHint = i18n.ts._tutorial.step5_3;
|
|
tlHintClosed = defaultStore.state.tlHomeHintClosed;
|
|
} else if (props.src === "local") {
|
|
endpoint = "notes/local-timeline";
|
|
query = {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
};
|
|
connection = stream.useChannel("localTimeline", {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
});
|
|
connection.on("note", prepend);
|
|
|
|
tlHint = i18n.ts._tutorial.step5_4;
|
|
tlHintClosed = defaultStore.state.tlLocalHintClosed;
|
|
} else if (props.src === "recommended") {
|
|
endpoint = "notes/recommended-timeline";
|
|
query = {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
};
|
|
connection = stream.useChannel("recommendedTimeline", {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
});
|
|
connection.on("note", prepend);
|
|
|
|
tlHint = i18n.ts._tutorial.step5_6;
|
|
tlHintClosed = defaultStore.state.tlRecommendedHintClosed;
|
|
} else if (props.src === "social") {
|
|
endpoint = "notes/hybrid-timeline";
|
|
query = {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
};
|
|
connection = stream.useChannel("hybridTimeline", {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
});
|
|
connection.on("note", prepend);
|
|
|
|
tlHint = i18n.ts._tutorial.step5_5;
|
|
tlHintClosed = defaultStore.state.tlSocialHintClosed;
|
|
} else if (props.src === "global") {
|
|
endpoint = "notes/global-timeline";
|
|
query = {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
};
|
|
connection = stream.useChannel("globalTimeline", {
|
|
withReplies: defaultStore.state.showTimelineReplies,
|
|
});
|
|
connection.on("note", prepend);
|
|
|
|
tlHint = i18n.ts._tutorial.step5_7;
|
|
tlHintClosed = defaultStore.state.tlGlobalHintClosed;
|
|
} else if (props.src === "mentions") {
|
|
endpoint = "notes/mentions";
|
|
connection = stream.useChannel("main");
|
|
connection.on("mention", prepend);
|
|
} else if (props.src === "directs") {
|
|
endpoint = "notes/mentions";
|
|
query = {
|
|
visibility: "specified",
|
|
};
|
|
const onNote = (note) => {
|
|
if (note.visibility === "specified") {
|
|
prepend(note);
|
|
}
|
|
};
|
|
connection = stream.useChannel("main");
|
|
connection.on("mention", onNote);
|
|
} else if (props.src === "list") {
|
|
endpoint = "notes/user-list-timeline";
|
|
query = {
|
|
listId: props.list,
|
|
};
|
|
connection = stream.useChannel("userList", {
|
|
listId: props.list,
|
|
});
|
|
connection.on("note", prepend);
|
|
connection.on("userAdded", onUserAdded);
|
|
connection.on("userRemoved", onUserRemoved);
|
|
}
|
|
|
|
function closeHint() {
|
|
switch (props.src) {
|
|
case "home":
|
|
defaultStore.set("tlHomeHintClosed", true);
|
|
break;
|
|
case "local":
|
|
defaultStore.set("tlLocalHintClosed", true);
|
|
break;
|
|
case "recommended":
|
|
defaultStore.set("tlRecommendedHintClosed", true);
|
|
break;
|
|
case "social":
|
|
defaultStore.set("tlSocialHintClosed", true);
|
|
break;
|
|
case "global":
|
|
defaultStore.set("tlGlobalHintClosed", true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
const pagination = {
|
|
endpoint: endpoint,
|
|
limit: 10,
|
|
params: query,
|
|
};
|
|
|
|
onUnmounted(() => {
|
|
connection.dispose();
|
|
if (connection2) connection2.dispose();
|
|
});
|
|
|
|
/* TODO
|
|
const timetravel = (date?: Date) => {
|
|
this.date = date;
|
|
this.$refs.tl.reload();
|
|
};
|
|
*/
|
|
</script>
|