Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix collection pages fetching and make them crawlable #3886

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
110 changes: 0 additions & 110 deletions frontend/src/components/VCollectionPage.vue
@@ -1,110 +0,0 @@
<template>
<div class="p-6 pt-0 lg:p-10 lg:pt-2">
<VCollectionHeader
v-if="collectionParams"
:collection-params="collectionParams"
:creator-url="creatorUrl"
:media-type="mediaType"
class="mb-2 md:mb-3"
/>
<VAudioList
v-if="results.type === 'audio'"
:collection-label="collectionLabel"
:fetch-state="fetchState"
kind="collection"
:results="results.items"
/>
<VImageGrid
v-if="results.type === 'image'"
:image-grid-label="collectionLabel"
:fetch-state="fetchState"
kind="collection"
:results="results.items"
/>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from "vue"

import { useMediaStore } from "~/stores/media"
import { useSearchStore } from "~/stores/search"
import type { SupportedMediaType } from "~/constants/media"

import { Results } from "~/types/result"

import { useI18n } from "~/composables/use-i18n"

import VCollectionHeader from "~/components/VCollectionHeader/VCollectionHeader.vue"
import VAudioList from "~/components/VSearchResultsGrid/VAudioList.vue"
import VImageGrid from "~/components/VSearchResultsGrid/VImageGrid.vue"

export default defineComponent({
name: "VCollectionPage",
components: { VAudioList, VImageGrid, VCollectionHeader },
props: {
mediaType: {
type: String as PropType<SupportedMediaType>,
required: true,
},
},
setup(props) {
const i18n = useI18n()
const mediaStore = useMediaStore()

const fetchState = computed(() => mediaStore.fetchState)
const results = computed<Results>(() => {
return {
type: props.mediaType,
items: mediaStore.resultItems[props.mediaType],
} as Results
})

const creatorUrl = computed(() => {
const media = results.value.items
return media.length > 0 ? media[0].creator_url : undefined
})

const searchStore = useSearchStore()
const collectionParams = computed(() => searchStore.collectionParams)

const collectionLabel = computed(() => {
const collection = collectionParams.value?.collection
switch (collection) {
case "tag": {
return i18n
.t(`collection.ariaLabel.tag.${props.mediaType}`, {
tag: collectionParams.value?.tag,
})
.toString()
}
case "source": {
return i18n
.t(`collection.ariaLabel.source.${props.mediaType}`, {
source: collectionParams.value?.source,
})
.toString()
}
case "creator": {
return i18n
.t(`collection.ariaLabel.creator.${props.mediaType}`, {
creator: collectionParams.value?.creator,
source: collectionParams.value?.source,
})
.toString()
}
default: {
return ""
}
}
})

return {
fetchState,
results,
creatorUrl,
collectionParams,
collectionLabel,
}
},
})
</script>
90 changes: 52 additions & 38 deletions frontend/src/components/VLoadMore.vue
Expand Up @@ -5,7 +5,7 @@
class="label-bold lg:description-bold h-16 w-full lg:h-18"
variant="filled-gray"
size="disabled"
:disabled="fetchState.isFetching"
:disabled="isFetching"
data-testid="load-more"
@click="onLoadMore"
>
Expand All @@ -14,7 +14,14 @@
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onMounted, ref, watch } from "vue"
import {
computed,
defineComponent,
onMounted,
type PropType,
ref,
watch,
} from "vue"
import { storeToRefs } from "pinia"
import { useElementVisibility } from "@vueuse/core"

Expand All @@ -24,6 +31,10 @@ import { useAnalytics } from "~/composables/use-analytics"
import { useMediaStore } from "~/stores/media"
import { useSearchStore } from "~/stores/search"
import { useI18n } from "~/composables/use-i18n"
import { defineEvent } from "~/types/emits"

import type { ResultKind } from "~/types/result"
import type { SupportedSearchType } from "~/constants/media"

import VButton from "~/components/VButton.vue"

Expand All @@ -32,40 +43,46 @@ export default defineComponent({
components: {
VButton,
},
setup() {
props: {
searchType: {
type: String as PropType<SupportedSearchType>,
required: true,
},
searchTerm: {
type: String,
required: true,
},
kind: {
type: String as PropType<ResultKind>,
required: true,
},
isFetching: {
type: Boolean,
required: true,
},
},
emits: {
"load-more": defineEvent(),
},
setup(props, { emit }) {
const loadMoreSectionRef = ref(null)
const route = useRoute()
const i18n = useI18n()
const mediaStore = useMediaStore()
const searchStore = useSearchStore()
const { sendCustomEvent } = useAnalytics()

// Use the `_searchType` from mediaStore because it falls back to ALL_MEDIA
// for unsupported search types.
const { fetchState, resultCount, currentPage, _searchType } =
storeToRefs(mediaStore)
const { searchTerm } = storeToRefs(searchStore)

const searchStarted = computed(() => {
return searchStore.strategy === "default"
? searchTerm.value !== ""
: searchStore.collectionParams !== null
})
const { currentPage } = storeToRefs(mediaStore)

/**
* Whether we should show the "Load more" button.
* If the user has entered a search term, there is at least 1 page of results,
* there has been no fetching error, and there are more results to fetch,
* we show the button.
*/
const canLoadMore = computed(() => {
return Boolean(
searchStarted.value &&
!fetchState.value.fetchingError &&
!fetchState.value.isFinished &&
resultCount.value > 0
)
})
const canLoadMore = computed(
() => mediaStore.canLoadMore && searchStore.searchStarted
)

const reachResultEndEventSent = ref(false)
/**
Expand All @@ -75,37 +92,35 @@ export default defineComponent({
*
*/
const onLoadMore = async () => {
if (fetchState.value.isFetching) {
if (props.isFetching) {
return
}

reachResultEndEventSent.value = false

sendCustomEvent("LOAD_MORE_RESULTS", {
query: searchStore.searchTerm,
searchType: searchStore.searchType,
resultPage: currentPage.value || 1,
})
sendCustomEvent("LOAD_MORE_RESULTS", eventPayload.value)

await mediaStore.fetchMedia({
shouldPersistMedia: true,
})
emit("load-more")
}

const eventPayload = computed(() => {
return {
searchType: props.searchType,
query: props.searchTerm,
resultPage: currentPage.value || 1,
}
})

const sendReachResultEnd = () => {
// This function can be called before the media is fetched and
// currentPage is updated from 0, so we use the value or 1.
// The currentPage can never be 0 here because then the loadMore
// button would not be visible.
sendCustomEvent("REACH_RESULT_END", {
searchType: _searchType.value,
query: searchTerm.value,
resultPage: currentPage.value || 1,
})
sendCustomEvent("REACH_RESULT_END", eventPayload.value)
}

const buttonLabel = computed(() =>
fetchState.value.isFetching
props.isFetching
? i18n.t("browsePage.loading")
: i18n.t("browsePage.load")
)
Expand Down Expand Up @@ -136,7 +151,6 @@ export default defineComponent({

return {
buttonLabel,
fetchState,
onLoadMore,
canLoadMore,

Expand Down