Skip to content

Commit

Permalink
✨ feat(web): Convo Viewer (#41)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Raderschad <[email protected]>
  • Loading branch information
McPizza0 and cstrnt authored Feb 2, 2024
1 parent d43263e commit 7504116
Show file tree
Hide file tree
Showing 27 changed files with 1,553 additions and 1,272 deletions.
20 changes: 20 additions & 0 deletions apps/web-app/components/convos/convoAvatar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { ConvoParticipantEntry } from '~/composables/types';
type Props = {
participant: ConvoParticipantEntry;
size?: 'xs' | 'sm' | 'md' | 'lg';
};
const props = defineProps<Props>();
</script>
<template>
<UnUiAvatar
v-if="props.participant"
:avatar-id="props.participant.avatarPublicId"
:public-id="props.participant.avatarPublicId"
:alt="props.participant.name"
:type="props.participant.type"
:color="props.participant.color"
:show-icon="true"
:size="props.size ? props.size : 'sm'" />
</template>
186 changes: 107 additions & 79 deletions apps/web-app/components/convos/convoList.vue
Original file line number Diff line number Diff line change
@@ -1,104 +1,132 @@
<script setup lang="ts">
import { useVirtualList, useInfiniteScroll } from '@vueuse/core';
import { useConvoStore } from '@/stores/convoStore';
import { useInfiniteScroll } from '@vueuse/core';
const { $trpc } = useNuxtApp();
// const convoStore = useConvoStore();
// if (process.client && convoStore.convos.length === 0) {
// await convoStore.getInitialConvos();
// }
// console.log(convoStore.convos.length);
const orgSlug = useRoute().params.orgSlug as string;
const infiniteContainer = ref<HTMLElement | null>(null);
const convosPending = ref(true);
const convos = ref<UserConvosDataType>([]);
const convoCursor = {
lastUpdatedAt: null as Date | null,
lastPublicId: null as string | null
};
const getInitialConvos = async () => {
convos.value = [];
if (convos.value.length !== 0) return;
const { data: userConvos } = await $trpc.convos.getUserConvos.useLazyQuery(
{}
);
if (!userConvos.value) {
convosPending.value = false;
return;
}
convos.value.push(...userConvos.value.data);
convoCursor.lastUpdatedAt = userConvos.value.cursor.lastUpdatedAt;
convoCursor.lastPublicId = userConvos.value.cursor.lastPublicId;
convosPending.value = false;
return;
};
const getNextConvos = async () => {
if (
convoCursor.lastUpdatedAt !== null &&
convoCursor.lastPublicId !== null
) {
const { data: userConvos } =
await $trpc.convos.getUserConvos.useLazyQuery({
cursorLastUpdatedAt: convoCursor.lastUpdatedAt,
cursorLastPublicId: convoCursor.lastPublicId
});
if (!userConvos.value) return;
convos.value.push(...userConvos.value.data);
convoCursor.lastUpdatedAt = userConvos.value.cursor.lastUpdatedAt;
convoCursor.lastPublicId = userConvos.value.cursor.lastPublicId;
}
};
const convoCursor = ref<{
cursorLastUpdatedAt: Date | null;
cursorLastPublicId: string | null;
}>({
cursorLastUpdatedAt: null,
cursorLastPublicId: null
});
const userHasMoreConvos = ref(true);
const pauseLoading = ref(false);
const convos = ref<{}[]>([]);
type UserConvoQueryParams =
| {
cursorLastUpdatedAt: Date;
cursorLastPublicId: string;
}
| {};
const userConvoQueryParams = ref<UserConvoQueryParams>({});
const userConvoQueryPending = computed(() => {
return userConvosStatus.value === 'idle';
});
const userHasConvos = computed(() => {
return convos.value.length > 0;
});
const { list, containerProps, wrapperProps } = useVirtualList(convos.value, {
itemHeight: 127 + 16,
overscan: 3
const {
data: userConvosData,
status: userConvosStatus,
execute: getUserConvos
} = await $trpc.convos.getUserConvos.useLazyQuery(userConvoQueryParams, {
server: false,
queryKey: `userConvos-${orgSlug}`,
immediate: false,
watch: [userConvoQueryParams]
});
watch(
userConvosData,
(newVal) => {
if (!newVal) return;
if (!newVal.data || !newVal.cursor || newVal.data.length === 0) {
userHasMoreConvos.value = false;
return;
}
convos.value.push(...newVal.data);
convoCursor.value.cursorLastUpdatedAt = newVal.cursor.lastUpdatedAt;
convoCursor.value.cursorLastPublicId = newVal.cursor.lastPublicId;
},
{
immediate: true,
deep: true
}
);
useInfiniteScroll(
containerProps.ref,
() => {
getNextConvos();
infiniteContainer,
async () => {
if (pauseLoading.value) return;
if (!userHasMoreConvos.value) return;
if (userConvosStatus.value === 'pending') return;
pauseLoading.value = true;
userConvoQueryParams.value = {
cursorLastUpdatedAt: convoCursor.value.cursorLastUpdatedAt,
cursorLastPublicId: convoCursor.value.cursorLastPublicId
};
pauseLoading.value = false;
},
{ distance: 300 }
{ distance: 100, canLoadMore: () => userHasMoreConvos.value }
);
onMounted(async () => {
if (process.client) {
await getInitialConvos();
}
onMounted(() => {
if (convos.value.length === 0) getUserConvos();
});
</script>
<template>
<div
class="h-full max-h-full max-w-full w-full overflow-y-scroll"
v-bind="containerProps">
<div class="h-full max-h-full max-w-full w-full overflow-hidden">
<div
v-if="!convosPending"
class="mb-[48px] flex flex-col items-start gap-4"
v-bind="wrapperProps">
<div
v-if="convosPending"
class="w-full flex flex-row justify-center gap-4 rounded-xl rounded-tl-2xl bg-base-3 p-8">
<UnUiIcon
name="i-svg-spinners:3-dots-fade"
size="24" />
<span>Loading conversations</span>
</div>
v-if="userConvoQueryPending"
class="w-full flex flex-row justify-center gap-4 rounded-xl rounded-tl-2xl bg-base-3 p-8">
<UnUiIcon
name="i-svg-spinners:3-dots-fade"
size="24" />
<span>Loading conversations</span>
</div>
<div
v-if="!userConvoQueryPending"
class="mb-[48px] max-h-full flex flex-col items-start gap-4 overflow-hidden">
<div
v-if="!convosPending && convos.length === 0"
v-if="!userHasConvos"
class="w-full flex flex-row justify-center gap-4 rounded-xl rounded-tl-2xl bg-base-3 p-8">
<UnUiIcon
name="i-ph-chat-circle"
size="24" />
<span>No conversations found</span>
</div>
<div
v-if="!convosPending && convos.length !== 0"
class="max-w-full">
v-if="userHasConvos"
ref="infiniteContainer"
class="h-full max-h-full max-w-full w-full overflow-scroll">
<DynamicScroller
:items="convos"
key-field="publicId"
:min-item-size="48">
<template #default="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
:size-dependacies="[item.data]"
:data-index="index"
class="pb-4"
@click="navigateTo(`/${orgSlug}/convo/${item.publicId}`)">
<convos-convo-list-item :convo="item" />
</DynamicScrollerItem>
</template>
</DynamicScroller>
<div
v-for="convo of list"
:id="convo.data.publicId"
:key="convo.index"
class="max-w-full"
@click="navigateTo(`/h/convo/${convo.data.publicId}`)">
<convos-convo-list-item :convo="convo.data" />
v-if="userHasMoreConvos && pauseLoading"
class="w-full flex flex-row justify-center gap-4 rounded-xl rounded-tl-2xl bg-base-3 p-8">
<UnUiIcon
name="i-svg-spinners:3-dots-fade"
size="24" />
<span>Loading more conversations</span>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 7504116

Please sign in to comment.