|
|
|
@ -4,7 +4,7 @@ |
|
|
|
<hr /> |
|
|
|
<hr /> |
|
|
|
|
|
|
|
|
|
|
|
<div |
|
|
|
<div |
|
|
|
v-if="isLoading" |
|
|
|
v-if="isLoading && courses.length === 0" |
|
|
|
class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" |
|
|
|
class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" |
|
|
|
> |
|
|
|
> |
|
|
|
<Skeleton height="16rem" /> |
|
|
|
<Skeleton height="16rem" /> |
|
|
|
@ -23,10 +23,11 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div |
|
|
|
<div |
|
|
|
v-if="!isLoading && courses.length > 0" |
|
|
|
v-if="courses.length > 0" |
|
|
|
class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" |
|
|
|
class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" |
|
|
|
> |
|
|
|
> |
|
|
|
<CourseCardList :courses="courses" /> |
|
|
|
<CourseCardList :courses="courses" /> |
|
|
|
|
|
|
|
<div ref="lastCourseRef"></div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<EmptyState |
|
|
|
<EmptyState |
|
|
|
v-else-if="!isLoading && 0 === courses.length" |
|
|
|
v-else-if="!isLoading && 0 === courses.length" |
|
|
|
@ -37,7 +38,7 @@ |
|
|
|
</template> |
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
|
|
<script setup> |
|
|
|
<script setup> |
|
|
|
import { onMounted, computed } from "vue" |
|
|
|
import { ref, watch, onMounted, nextTick } from "vue" |
|
|
|
import { useQuery } from "@vue/apollo-composable" |
|
|
|
import { useQuery } from "@vue/apollo-composable" |
|
|
|
import { useI18n } from "vue-i18n" |
|
|
|
import { useI18n } from "vue-i18n" |
|
|
|
import { GET_COURSE_REL_USER } from "../../../graphql/queries/CourseRelUser.js" |
|
|
|
import { GET_COURSE_REL_USER } from "../../../graphql/queries/CourseRelUser.js" |
|
|
|
@ -50,15 +51,96 @@ import { useSecurityStore } from "../../../store/securityStore" |
|
|
|
const securityStore = useSecurityStore() |
|
|
|
const securityStore = useSecurityStore() |
|
|
|
const { t } = useI18n() |
|
|
|
const { t } = useI18n() |
|
|
|
|
|
|
|
|
|
|
|
const { result, loading, refetch } = useQuery(GET_COURSE_REL_USER, () => ({ |
|
|
|
const courses = ref([]) |
|
|
|
|
|
|
|
const isLoading = ref(false) |
|
|
|
|
|
|
|
const endCursor = ref(null) |
|
|
|
|
|
|
|
const hasMore = ref(true) |
|
|
|
|
|
|
|
const lastCourseRef = ref(null) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { result, fetchMore } = useQuery(GET_COURSE_REL_USER, { |
|
|
|
|
|
|
|
user: securityStore.user["@id"], |
|
|
|
|
|
|
|
first: 30, |
|
|
|
|
|
|
|
after: null, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
watch(result, (newResult) => { |
|
|
|
|
|
|
|
if (newResult?.courseRelUsers) { |
|
|
|
|
|
|
|
const newCourses = newResult.courseRelUsers.edges.map(({ node }) => node.course) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const filteredCourses = newCourses.filter( |
|
|
|
|
|
|
|
(newCourse) => !courses.value.some((existingCourse) => existingCourse._id === newCourse._id) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
courses.value.push(...filteredCourses) |
|
|
|
|
|
|
|
endCursor.value = newResult.courseRelUsers.pageInfo.endCursor |
|
|
|
|
|
|
|
hasMore.value = newResult.courseRelUsers.pageInfo.hasNextPage |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nextTick(() => { |
|
|
|
|
|
|
|
if (lastCourseRef.value) { |
|
|
|
|
|
|
|
observer.observe(lastCourseRef.value) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
isLoading.value = false |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const loadMoreCourses = () => { |
|
|
|
|
|
|
|
if (!hasMore.value || isLoading.value) return |
|
|
|
|
|
|
|
isLoading.value = true |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fetchMore({ |
|
|
|
|
|
|
|
variables: { |
|
|
|
user: securityStore.user["@id"], |
|
|
|
user: securityStore.user["@id"], |
|
|
|
})) |
|
|
|
first: 10, |
|
|
|
|
|
|
|
after: endCursor.value, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
updateQuery: (previousResult, {fetchMoreResult}) => { |
|
|
|
|
|
|
|
if (!fetchMoreResult) return previousResult |
|
|
|
|
|
|
|
|
|
|
|
const isLoading = computed(() => loading.value) |
|
|
|
const newCourses = fetchMoreResult.courseRelUsers.edges.map(({ node }) => node.course) |
|
|
|
|
|
|
|
const filteredCourses = newCourses.filter( |
|
|
|
|
|
|
|
(newCourse) => !courses.value.some((existingCourse) => existingCourse._id === newCourse._id) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
courses.value.push(...filteredCourses) |
|
|
|
|
|
|
|
endCursor.value = fetchMoreResult.courseRelUsers.pageInfo.endCursor |
|
|
|
|
|
|
|
hasMore.value = fetchMoreResult.courseRelUsers.pageInfo.hasNextPage |
|
|
|
|
|
|
|
|
|
|
|
const courses = computed(() => result.value?.courseRelUsers.edges.map(({ node }) => node.course) ?? []) |
|
|
|
return { |
|
|
|
|
|
|
|
...previousResult, |
|
|
|
|
|
|
|
courseRelUsers: { |
|
|
|
|
|
|
|
...fetchMoreResult.courseRelUsers, |
|
|
|
|
|
|
|
edges: [...previousResult.courseRelUsers.edges, ...fetchMoreResult.courseRelUsers.edges], |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}).finally(() => { |
|
|
|
|
|
|
|
isLoading.value = false |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let observer = new IntersectionObserver((entries) => { |
|
|
|
|
|
|
|
if (entries[0].isIntersecting) { |
|
|
|
|
|
|
|
loadMoreCourses(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, { |
|
|
|
|
|
|
|
rootMargin: '300px', |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
onMounted(() => { |
|
|
|
refetch() |
|
|
|
courses.value = [] |
|
|
|
|
|
|
|
endCursor.value = null |
|
|
|
|
|
|
|
hasMore.value = true |
|
|
|
|
|
|
|
isLoading.value = false |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (observer) observer.disconnect() |
|
|
|
|
|
|
|
observer = new IntersectionObserver((entries) => { |
|
|
|
|
|
|
|
if (entries[0].isIntersecting) { |
|
|
|
|
|
|
|
loadMoreCourses() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, { |
|
|
|
|
|
|
|
rootMargin: '300px', |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
loadMoreCourses() |
|
|
|
}) |
|
|
|
}) |
|
|
|
</script> |
|
|
|
</script> |
|
|
|
|