diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2024-11-01 16:52:31 -0400 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2024-11-20 22:22:29 -0500 |
| commit | ca94959fff3f202c90166db8868a7613c424fe0b (patch) | |
| tree | 6379866df378b69be44adf7689156c2ed1b1ee5a /packages/frontend/src/components | |
| parent | merge: Bump develop version (!766) (diff) | |
| download | sharkey-ca94959fff3f202c90166db8868a7613c424fe0b.tar.gz sharkey-ca94959fff3f202c90166db8868a7613c424fe0b.tar.bz2 sharkey-ca94959fff3f202c90166db8868a7613c424fe0b.zip | |
factor out Following Feed list into SkFollowingRecentNotes.vue
Diffstat (limited to 'packages/frontend/src/components')
| -rw-r--r-- | packages/frontend/src/components/SkFollowingRecentNotes.vue | 122 | ||||
| -rw-r--r-- | packages/frontend/src/components/global/SkLazy.vue | 57 |
2 files changed, 179 insertions, 0 deletions
diff --git a/packages/frontend/src/components/SkFollowingRecentNotes.vue b/packages/frontend/src/components/SkFollowingRecentNotes.vue new file mode 100644 index 0000000000..35fa83812f --- /dev/null +++ b/packages/frontend/src/components/SkFollowingRecentNotes.vue @@ -0,0 +1,122 @@ +<!-- +SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkPullToRefresh :refresher="() => reload()"> + <MkPagination ref="latestNotesPaging" :pagination="latestNotesPagination" @init="onListReady"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost" :alt="i18n.ts.noNotes" aria-hidden="true"/> + <div>{{ i18n.ts.noNotes }}</div> + </div> + </template> + + <template #default="{ items: notes }"> + <MkDateSeparatedList v-slot="{ item: note }" :items="notes" :class="$style.panel" :noGap="true"> + <SkFollowingFeedEntry v-if="!isHardMuted(note)" :isMuted="isSoftMuted(note)" :note="note" @select="u => selectUser(u.id)"/> + </MkDateSeparatedList> + </template> + </MkPagination> +</MkPullToRefresh> +</template> + +<script setup lang="ts"> +import * as Misskey from 'misskey-js'; +import { computed, shallowRef } from 'vue'; +import { infoImageUrl } from '@/instance.js'; +import { i18n } from '@/i18n.js'; +import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; +import MkPagination, { Paging } from '@/components/MkPagination.vue'; +import SkFollowingFeedEntry from '@/components/SkFollowingFeedEntry.vue'; +import { $i } from '@/account.js'; +import { checkWordMute } from '@/scripts/check-word-mute.js'; +import { FollowingFeedTab } from '@/scripts/following-feed-utils.js'; +import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; + +const props = defineProps<{ + userList: FollowingFeedTab; + withNonPublic: boolean; + withQuotes: boolean; + withReplies: boolean; + withBots: boolean; + onlyFiles: boolean; +}>(); + +const emit = defineEmits<{ + (event: 'loaded', initialUserId?: string): void; + (event: 'userSelected', userId: string): void; +}>(); + +defineExpose({ reload }); + +async function reload() { + await latestNotesPaging.value?.reload(); +} + +function selectUser(userId: string) { + emit('userSelected', userId); +} + +async function onListReady(): Promise<void> { + // This looks complicated, but it's really just a trick to get the first user ID from the pagination. + const initialUserId = latestNotesPaging.value?.items.size + ? latestNotesPaging.value.items.values().next().value?.userId + : undefined; + + emit('loaded', initialUserId); +} + +const latestNotesPagination: Paging<'notes/following'> = { + endpoint: 'notes/following' as const, + limit: 20, + params: computed(() => ({ + list: props.userList, + filesOnly: props.onlyFiles, + includeNonPublic: props.withNonPublic, + includeReplies: props.withReplies, + includeQuotes: props.withQuotes, + includeBots: props.withBots, + })), +}; + +const latestNotesPaging = shallowRef<InstanceType<typeof MkPagination>>(); + +function isSoftMuted(note: Misskey.entities.Note): boolean { + return isMuted(note, $i?.mutedWords); +} + +function isHardMuted(note: Misskey.entities.Note): boolean { + return isMuted(note, $i?.hardMutedWords); +} + +// Match the typing used by Misskey +type Mutes = (string | string[])[] | null | undefined; + +// Adapted from MkNote.ts +function isMuted(note: Misskey.entities.Note, mutes: Mutes): boolean { + return checkMute(note, mutes) + || checkMute(note.reply, mutes) + || checkMute(note.renote, mutes); +} + +// Adapted from check-word-mute.ts +function checkMute(note: Misskey.entities.Note | undefined | null, mutes: Mutes): boolean { + if (!note) { + return false; + } + + if (!mutes || mutes.length < 1) { + return false; + } + + return checkWordMute(note, $i, mutes); +} +</script> + +<style module lang="scss"> +.panel { + background: var(--panel); +} +</style> diff --git a/packages/frontend/src/components/global/SkLazy.vue b/packages/frontend/src/components/global/SkLazy.vue new file mode 100644 index 0000000000..40add97db7 --- /dev/null +++ b/packages/frontend/src/components/global/SkLazy.vue @@ -0,0 +1,57 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<!-- Based on MkLazy.vue --> + +<template> +<div ref="rootEl" :class="$style.root"> + <slot v-if="showing"></slot> + <div v-else :class="$style.placeholder"></div> +</div> +</template> + +<script lang="ts" setup> +import { nextTick, onMounted, onActivated, onBeforeUnmount, ref, shallowRef } from 'vue'; + +const rootEl = shallowRef<HTMLDivElement>(); +const showing = ref(false); + +defineExpose({ rootEl, showing }); + +const observer = new IntersectionObserver(entries => + showing.value = entries.some((entry) => entry.isIntersecting), +); + +onMounted(() => { + nextTick(() => { + if (rootEl.value) { + observer.observe(rootEl.value); + } + }); +}); + +onActivated(() => { + nextTick(() => { + if (rootEl.value) { + observer.observe(rootEl.value); + } + }); +}); + +onBeforeUnmount(() => { + observer.disconnect(); +}); +</script> + +<style lang="scss" module> +.root { + display: block; +} + +.placeholder { + display: block; + min-height: 150px; +} +</style> |