forked from
vt3e.cat/bbell
wip bsky client for the web & android
1<script setup lang="ts">
2import { ref, onMounted } from 'vue'
3import type { AppBskyActorDefs } from '@atcute/bluesky'
4
5import Modal from '@/components/UI/BaseModal.vue'
6import Button from '@/components/UI/BaseButton.vue'
7import ProfileRow from '@/components/Profile/ProfileRow.vue'
8import SkeletonLoader from '@/components/UI/SkeletonLoader.vue'
9
10import { useInfiniteScroll } from '@/composables/useInfiniteScroll'
11
12const props = defineProps<{
13 title: string
14 users: AppBskyActorDefs.ProfileView[]
15 loading?: boolean
16 onReachedBottom?: () => void
17 hasMore?: boolean
18}>()
19
20const emit = defineEmits<{
21 (e: 'close'): void
22}>()
23
24const sentinel = ref<HTMLElement | null>(null)
25
26const { setup } = useInfiniteScroll(sentinel, () => {
27 if (props.onReachedBottom && !props.loading) {
28 props.onReachedBottom()
29 }
30})
31
32onMounted(() => {
33 setup()
34})
35</script>
36
37<template>
38 <Modal :title="title" width="640px" @close="emit('close')" edge-to-edge>
39 <div class="user-list" role="list">
40 <template v-if="loading && users.length === 0">
41 <SkeletonLoader v-for="n in 6" :key="n" />
42 </template>
43
44 <ProfileRow v-for="user in users" :key="user.did" :profile="user" @click="emit('close')" />
45
46 <div ref="sentinel" class="sentinel"></div>
47
48 <div v-if="loading && users.length > 0" class="loading-more">
49 <p>loading more...</p>
50 </div>
51 <div v-if="!hasMore && users.length > 0" class="loading-more">
52 <p>that's all!</p>
53 </div>
54 </div>
55
56 <template #footer>
57 <Button variant="primary" @click="emit('close')">Close</Button>
58 </template>
59 </Modal>
60</template>
61
62<style lang="scss" scoped>
63.user-list {
64 display: flex;
65 flex-direction: column;
66 max-height: 60vh;
67 overflow-y: auto;
68}
69
70.sentinel {
71 height: 1px;
72}
73
74.loading-more {
75 opacity: 0.6;
76 text-align: center;
77 padding: 1rem;
78}
79</style>