Skip to content

Commit

Permalink
fix: items changing on navigation, library page reactivity
Browse files Browse the repository at this point in the history
Signed-off-by: Fernando Fernández <[email protected]>
  • Loading branch information
ferferga committed Sep 6, 2024
1 parent b609bfa commit c6a4b22
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 49 deletions.
2 changes: 1 addition & 1 deletion frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
:mode="defaultTransitionMode ?? route.meta.layout.transition.mode">
<Suspense suspensible>
<JView
:key="route.path"
:key="route.name"
:comp="Component" />
</Suspense>
</JTransition>
Expand Down
53 changes: 32 additions & 21 deletions frontend/src/composables/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Api } from '@jellyfin/sdk';
import type { BaseItemDto, BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
import type { AxiosResponse } from 'axios';
import { deepEqual } from 'fast-equals';
import { computed, getCurrentScope, isRef, shallowRef, toValue, unref, watch, type ComputedRef, type Ref } from 'vue';
import { computed, effectScope, getCurrentScope, isRef, shallowRef, toValue, unref, watch, type ComputedRef, type Ref } from 'vue';
import { until } from '@vueuse/core';
import { useLoading } from '@/composables/use-loading';
import { useSnackbar } from '@/composables/use-snackbar';
Expand All @@ -11,6 +11,7 @@ import { remote } from '@/plugins/remote';
import { isConnectedToServer } from '@/store';
import { apiStore } from '@/store/api';
import { isArray, isNil } from '@/utils/validation';
import { router } from '@/plugins/router';

/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */
type OmittedKeys = 'fields' | 'userId' | 'enableImages' | 'enableTotalRecordCount' | 'enableImageTypes';
Expand Down Expand Up @@ -191,6 +192,7 @@ function _sharedInternalLogic<T extends Record<K, (...args: any[]) => any>, K ex
const stringArgs = computed(() => {
return JSON.stringify(argsRef.value);
});

/**
* TODO: Check why previous returns unknown by default without the type annotation
*/
Expand Down Expand Up @@ -275,29 +277,38 @@ function _sharedInternalLogic<T extends Record<K, (...args: any[]) => any>, K ex
argsRef.value = normalizeArgs();

if (getCurrentScope() !== undefined) {
if (args.length) {
watch(args, async (_newVal, oldVal) => {
const normalizedArgs = normalizeArgs();

/**
* Does a deep comparison to avoid useless double requests
*/
if (!normalizedArgs.every((a, index) => deepEqual(a, toValue(oldVal[index])))) {
argsRef.value = normalizedArgs;
await runNormally();
}
});
}
const handleArgsChange = async (_: typeof args, old: typeof args | undefined): Promise<void> => {
const normalizedArgs = normalizeArgs();

/**
* Does a deep comparison to avoid useless double requests
*/
if (old && !normalizedArgs.every((a, index) => deepEqual(a, toValue(old[index])))) {
argsRef.value = normalizedArgs;
await runNormally();
}
};
const scope = effectScope();

watch(isConnectedToServer, runWithRetry);
scope.run(() => {
if (args.length) {
watch(args, handleArgsChange);
}

if (isRef(api)) {
watch(api, runNormally);
}
watch(isConnectedToServer, runWithRetry);

if (isRef(methodName)) {
watch(methodName, runNormally);
}
if (isRef(api)) {
watch(api, runNormally);
}

if (isRef(methodName)) {
watch(methodName, runNormally);
}
});

watch(() => router.currentRoute.value.name, () => scope.stop(),
{ once: true, flush: 'sync' }
);
}

/**
Expand Down
16 changes: 6 additions & 10 deletions frontend/src/pages/item/[itemId].vue
Original file line number Diff line number Diff line change
Expand Up @@ -321,16 +321,12 @@ const { data: relatedItems } = await useBaseItem(getLibraryApi, 'getSimilarItems
itemId: route.params.itemId,
limit: 12
}));
const { data: currentSeries } = await useBaseItem(getUserLibraryApi, 'getItem')(
() => ({
itemId: item.value.SeriesId ?? ''
})
);
const { data: childItems } = await useBaseItem(getItemsApi, 'getItems')(
() => ({
parentId: item.value.Id
})
);
const { data: currentSeries } = await useBaseItem(getUserLibraryApi, 'getItem')(() => ({
itemId: item.value.SeriesId ?? ''
}));
const { data: childItems } = await useBaseItem(getItemsApi, 'getItems')(() => ({
parentId: item.value.Id
}));
const selectedSource = ref<MediaSourceInfo>();
const currentVideoTrack = ref<number>();
Expand Down
28 changes: 11 additions & 17 deletions frontend/src/pages/library/[itemId].vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
<VChip
size="small"
class="ma-2 hidden-sm-and-down">
<template v-if="!fullQueryIsCached">
<template v-if="loading && items.length === lazyLoadLimit && initialId === route.params.itemId">
{{ t('lazyLoading', { value: items.length }) }}
</template>
<VProgressCircular
v-else-if="loading"
indeterminate
width="2"
size="16" />
<template v-else>
{{ items?.length ?? 0 }}
{{ items.length ?? 0 }}
</template>
</VChip>
<VDivider
Expand Down Expand Up @@ -63,7 +68,7 @@

<script setup lang="ts">
import {
BaseItemKind, SortOrder, type BaseItemDto
BaseItemKind, SortOrder
} from '@jellyfin/sdk/lib/generated-client';
import { getArtistsApi } from '@jellyfin/sdk/lib/utils/api/artists-api';
import { getGenresApi } from '@jellyfin/sdk/lib/utils/api/genres-api';
Expand All @@ -74,7 +79,6 @@ import { getStudiosApi } from '@jellyfin/sdk/lib/utils/api/studios-api';
import { computed, onBeforeMount, ref, shallowRef } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { apiStore } from '@/store/api';
import { methodsAsObject, useBaseItem } from '@/composables/apis';
import type { Filters } from '@/components/Buttons/FilterButton.vue';
import { useItemPageTitle } from '@/composables/page-title';
Expand All @@ -83,6 +87,7 @@ const { t } = useI18n();
const route = useRoute('/library/[itemId]');

const lazyLoadLimit = 50;
const initialId = route.params.itemId;
const COLLECTION_TYPES_MAPPINGS: Record<string, BaseItemKind> = {
tvshows: BaseItemKind.Series,
movies: BaseItemKind.Movie,
Expand All @@ -95,7 +100,6 @@ const innerItemKind = shallowRef<BaseItemKind>();
const sortBy = shallowRef<string>();
const sortAscending = shallowRef(true);
const queryLimit = shallowRef<number | undefined>(lazyLoadLimit);
const lazyLoadIds = shallowRef<BaseItemDto['Id'][]>([]);
const filters = ref<Filters>({
status: [],
features: [],
Expand Down Expand Up @@ -158,7 +162,6 @@ const isSortable = computed(
'Person',
'Genre',
'MusicGenre',
'MusicGenre',
'Studio'
].includes(viewType.value)
);
Expand Down Expand Up @@ -201,7 +204,7 @@ const method = computed(() => methods.value.methodName);
/**
* TODO: Improve the type situation of this statement
*/
const { loading, data: queryItems } = await useBaseItem(api, method)(() => ({
const { loading, data: items } = await useBaseItem(api, method)(() => ({
parentId: parentId.value,
personTypes: viewType.value === 'Person' ? ['Actor'] : undefined,
includeItemTypes: viewType.value ? [viewType.value] : undefined,
Expand All @@ -220,24 +223,15 @@ const { loading, data: queryItems } = await useBaseItem(api, method)(() => ({
isHd: filters.value.types.includes('isHD') || undefined,
is4K: filters.value.types.includes('is4K') || undefined,
is3D: filters.value.types.includes('is3D') || undefined,
startIndex: queryLimit.value ? undefined : lazyLoadLimit,
limit: queryLimit.value
}));

/**
* The queryItems for the 2nd request will return the items from (lazyloadLimit, n],
* so checking if just the first matches is a good solution
*/
const fullQueryIsCached = computed(() => loading.value ? !queryLimit.value && queryItems.value[0].Id !== lazyLoadIds.value[0] : true);
const items = computed(() => fullQueryIsCached.value ? [...(apiStore.getItemsById(lazyLoadIds.value) as BaseItemDto[]), ...queryItems.value] : queryItems.value);

useItemPageTitle(library);

/**
* We fetch the 1st 100 items and, after mount, we fetch the rest.
* We fetch the 1st 50 items and, after mount, we fetch the rest.
*/
onBeforeMount(() => {
lazyLoadIds.value = queryItems.value.map(i => i.Id);
queryLimit.value = undefined;
});
</script>
Expand Down

0 comments on commit c6a4b22

Please sign in to comment.