hippofish/packages/client/src/components/MkTimeline.vue

360 lines
8.5 KiB
Vue
Raw Normal View History

<template>
2023-05-29 22:17:42 +02:00
<MkInfo
v-if="tlHint && !tlHintClosed && isSignedIn"
2023-05-29 22:17:42 +02:00
:closeable="true"
class="_gap"
@close="closeHint"
>
<I18n :src="tlHint">
2023-05-29 20:51:02 +02:00
<template #icon></template>
</I18n>
</MkInfo>
<div v-if="queue > 0" class="new">
<button
class="_buttonPrimary _shadow"
:class="{ instant: !defaultStore.state.animation }"
@click="tlComponent?.scrollTop()"
>
{{ i18n.ts.newNoteRecived }}
<i :class="icon('ph-arrow-up', false)"></i>
</button>
</div>
<MkPullToRefresh
v-if="defaultStore.state.enablePullToRefresh"
ref="pullToRefreshComponent"
:refresher="() => reloadTimeline()"
>
<XNotes
ref="tlComponent"
:no-gap="!defaultStore.state.showGapBetweenNotesInTimeline"
:pagination="pagination"
@queue="(x) => (queue = x)"
@status="pullToRefreshComponent?.setDisabled($event)"
/>
</MkPullToRefresh>
2023-04-08 02:01:42 +02:00
<XNotes
v-else
2023-04-08 02:01:42 +02:00
ref="tlComponent"
:no-gap="!defaultStore.state.showGapBetweenNotesInTimeline"
2023-04-08 02:01:42 +02:00
:pagination="pagination"
2023-07-02 05:46:33 +02:00
@queue="(x) => (queue = x)"
@status="pullToRefreshComponent?.setDisabled($event)"
2023-04-08 02:01:42 +02:00
/>
</template>
<script lang="ts" setup>
2023-09-02 01:27:33 +02:00
import { computed, onUnmounted, provide, ref } from "vue";
2024-03-02 06:24:05 +01:00
import type { Endpoints } from "firefish-js";
import MkPullToRefresh from "@/components/MkPullToRefresh.vue";
2023-04-08 02:01:42 +02:00
import XNotes from "@/components/MkNotes.vue";
2023-05-29 20:51:02 +02:00
import MkInfo from "@/components/MkInfo.vue";
import { useStream } from "@/stream";
2023-04-08 02:01:42 +02:00
import * as sound from "@/scripts/sound";
2024-03-07 03:06:45 +01:00
import { me, isSignedIn } from "@/me";
2023-05-29 20:51:02 +02:00
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
import icon from "@/scripts/icon";
import type { Paging } from "@/components/MkPagination.vue";
const props = defineProps<{
src: string;
list?: string;
antenna?: string;
channel?: string;
sound?: boolean;
fileId?: string;
}>();
const emit = defineEmits<{
2023-04-08 02:01:42 +02:00
(ev: "note"): void;
(ev: "queue", count: number): void;
}>();
const tlComponent = ref<InstanceType<typeof XNotes>>();
const pullToRefreshComponent = ref<InstanceType<typeof MkPullToRefresh>>();
let endpoint = ""; // keyof Endpoints
2024-03-02 06:24:05 +01:00
let query: {
antennaId?: string | undefined;
withReplies?: boolean;
visibility?: string;
listId?: string | undefined;
channelId?: string | undefined;
fileId?: string | undefined;
};
let connection: {
on: (
arg0: string,
arg1: { (note: any): void; (note: any): void; (note: any): void },
) => void;
dispose: () => void;
};
let connection2: { dispose: () => void } | null;
let tlHint: string;
let tlHintClosed: boolean;
let tlNotesCount = 0;
const queue = ref(0);
2023-04-08 02:01:42 +02:00
const prepend = (note) => {
tlNotesCount++;
tlComponent.value?.pagingComponent?.prepend(note);
2023-04-08 02:01:42 +02:00
emit("note");
if (props.sound) {
2024-03-07 03:06:45 +01:00
sound.play(isSignedIn && note.userId === me.id ? "noteMy" : "note");
}
};
2023-04-08 02:01:42 +02:00
if (props.src === "antenna") {
endpoint = "antennas/notes";
query = {
antennaId: props.antenna,
};
2023-04-08 02:01:42 +02:00
} else if (props.src === "home") {
endpoint = "notes/timeline";
query = {
withReplies: defaultStore.state.showTimelineReplies,
};
2023-05-29 20:51:02 +02:00
tlHint = i18n.ts._tutorial.step5_3;
tlHintClosed = defaultStore.state.tlHomeHintClosed;
2023-04-08 02:01:42 +02:00
} else if (props.src === "local") {
endpoint = "notes/local-timeline";
query = {
withReplies: defaultStore.state.showTimelineReplies,
};
2023-05-29 20:51:02 +02:00
tlHint = i18n.ts._tutorial.step5_4;
tlHintClosed = defaultStore.state.tlLocalHintClosed;
2023-04-08 02:01:42 +02:00
} else if (props.src === "recommended") {
endpoint = "notes/recommended-timeline";
query = {
withReplies: defaultStore.state.showTimelineReplies,
};
2023-05-29 20:51:02 +02:00
tlHint = i18n.ts._tutorial.step5_6;
tlHintClosed = defaultStore.state.tlRecommendedHintClosed;
2023-04-08 02:01:42 +02:00
} else if (props.src === "social") {
endpoint = "notes/hybrid-timeline";
query = {
withReplies: defaultStore.state.showTimelineReplies,
};
2023-05-29 20:51:02 +02:00
tlHint = i18n.ts._tutorial.step5_5;
tlHintClosed = defaultStore.state.tlSocialHintClosed;
2023-04-08 02:01:42 +02:00
} else if (props.src === "global") {
endpoint = "notes/global-timeline";
query = {
withReplies: defaultStore.state.showTimelineReplies,
};
2023-05-29 20:51:02 +02:00
tlHint = i18n.ts._tutorial.step5_7;
tlHintClosed = defaultStore.state.tlGlobalHintClosed;
2023-04-08 02:01:42 +02:00
} else if (props.src === "mentions") {
endpoint = "notes/mentions";
} else if (props.src === "directs") {
endpoint = "notes/mentions";
query = {
2023-04-08 02:01:42 +02:00
visibility: "specified",
};
2023-04-08 02:01:42 +02:00
} else if (props.src === "list") {
endpoint = "notes/user-list-timeline";
query = {
listId: props.list,
};
2023-04-08 02:01:42 +02:00
} else if (props.src === "channel") {
endpoint = "channels/timeline";
query = {
channelId: props.channel,
};
} else if (props.src === "file") {
endpoint = "drive/files/attached-notes";
query = {
fileId: props.fileId,
};
}
const stream = useStream();
function connectChannel() {
if (props.src === "antenna") {
connection = stream.useChannel("antenna", {
antennaId: props.antenna!,
});
} else if (props.src === "home") {
connection = stream.useChannel("homeTimeline", {
withReplies: defaultStore.state.showTimelineReplies,
});
connection2 = stream.useChannel("main");
} else if (props.src === "local") {
connection = stream.useChannel("localTimeline", {
withReplies: defaultStore.state.showTimelineReplies,
});
} else if (props.src === "recommended") {
connection = stream.useChannel("recommendedTimeline", {
withReplies: defaultStore.state.showTimelineReplies,
});
} else if (props.src === "social") {
connection = stream.useChannel("hybridTimeline", {
withReplies: defaultStore.state.showTimelineReplies,
});
} else if (props.src === "global") {
connection = stream.useChannel("globalTimeline", {
withReplies: defaultStore.state.showTimelineReplies,
});
} else if (props.src === "mentions") {
connection = stream.useChannel("main");
connection.on("mention", prepend);
} else if (props.src === "directs") {
const onNote = (note) => {
if (note.visibility === "specified") {
prepend(note);
}
};
connection = stream.useChannel("main");
connection.on("mention", onNote);
} else if (props.src === "list") {
connection = stream.useChannel("userList", {
listId: props.list,
});
} else if (props.src === "channel") {
connection = stream.useChannel("channel", {
channelId: props.channel,
});
}
if (props.src !== "directs" && props.src !== "mentions")
connection.on("note", prepend);
}
provide(
"inChannel",
computed(() => props.src === "channel"),
);
2023-05-29 20:51:02 +02:00
function closeHint() {
switch (props.src) {
2023-05-29 22:17:42 +02:00
case "home":
2023-05-29 20:51:02 +02:00
defaultStore.set("tlHomeHintClosed", true);
break;
2023-05-29 22:17:42 +02:00
case "local":
2023-05-29 20:51:02 +02:00
defaultStore.set("tlLocalHintClosed", true);
break;
2023-05-29 22:17:42 +02:00
case "recommended":
2023-05-29 20:51:02 +02:00
defaultStore.set("tlRecommendedHintClosed", true);
break;
2023-05-29 22:17:42 +02:00
case "social":
2023-05-29 20:51:02 +02:00
defaultStore.set("tlSocialHintClosed", true);
break;
2023-05-29 22:17:42 +02:00
case "global":
2023-05-29 20:51:02 +02:00
defaultStore.set("tlGlobalHintClosed", true);
break;
}
}
if (defaultStore.state.enableTimelineStreaming) {
connectChannel();
onUnmounted(() => {
connection.dispose();
if (connection2) connection2.dispose();
});
}
function reloadTimeline() {
return new Promise<void>((res) => {
tlNotesCount = 0;
tlComponent.value?.pagingComponent?.reload().then(() => {
res();
});
});
}
const pagination: Paging = {
endpoint: endpoint as keyof Endpoints,
limit: 10,
params: query,
};
onUnmounted(() => {
connection.dispose();
if (connection2 != null) connection2.dispose();
});
/* TODO
const timetravel = (date?: Date) => {
this.date = date;
this.$refs.tl.reload();
};
*/
defineExpose({
reloadTimeline,
});
</script>
<style lang="scss" scoped>
@keyframes slideUp {
to {
transform: translateY(-100%);
opacity: 0;
}
}
.new {
position: sticky;
display: flex;
justify-content: center;
top: calc(var(--stickyTop, 0px) - 60px);
width: 600px;
max-width: 100%;
height: 60px;
pointer-events: none;
margin: auto;
margin-top: -60px;
z-index: 1001;
box-shadow: 0 24px 24px -20px var(--accentedBg);
&::after {
content: "";
position: absolute;
inset: -2px 0;
border: 2px solid var(--accentDarken);
mask: linear-gradient(
to right,
transparent,
black 40% 60%,
transparent
);
-webkit-mask: linear-gradient(
to right,
transparent,
black 40% 60%,
transparent
);
}
> button {
display: flex;
position: absolute;
top: 120%;
margin-inline: auto;
border-radius: 2em;
padding: 0.5em 1.2em;
background: var(--accentedBg);
border: 0;
color: var(--accent);
overflow: hidden;
pointer-events: all;
transform: translateY(-100%);
opacity: 0;
2023-07-06 03:28:27 +02:00
animation:
reset 0.4s forwards cubic-bezier(0, 0.4, 0, 1.1),
slideUp 1s 5s forwards cubic-bezier(1, 0, 1, 1);
&::before {
content: "";
position: absolute;
inset: 0;
background: var(--bg);
z-index: -1;
}
i {
margin-left: 0.7em;
border-left: 1px solid var(--accentedBg);
padding-left: 0.4em;
}
}
}
</style>