Skip to content

Commit

Permalink
+ ref initialPageCursor to fix the very first fetching not carrying…
Browse files Browse the repository at this point in the history
… the cursor route param

* fix still showing `<PlaceholderPostList>` when fetching next page
- the indicator of the current cursor route param
- ref `lastFetchingRoute` since the only usage after 2ed3aa0 that is `scrollToPostListItemByRoute()` no longer requires using  different route as of c7571bb
@ `<Post>`

* replace param `enabled` with `options` for `useApi*()` to allow any caller to override any `Use*QueryOptions` @ index.ts
- interface `CursorPaginationQueryParam` due to 2ed3aa0 @ index.d.ts
@ api

* fix typing definations now exported under files with extension `.d.ts` as of `[email protected]` since 3b65bba @ `shared/echarts.ts` & `<BilibiliVote>`
* fix `eslint-disable-next-line` not working since 8ec64b0 @ `<RendererTable>`
* fix violation of rule `unicorn/no-abusive-eslint-disable` @ gtag.js & stats.js
* fix violation of rule `vue/no-dupe-keys` @ `components/User/<QueryForm>`
* add an option `type-literal` for rule `vue/define-emits-declaration` @ .eslintrc.cjs
@ fe
  • Loading branch information
n0099 committed Mar 10, 2024
1 parent 2ed3aa0 commit a870c10
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 28 deletions.
2 changes: 1 addition & 1 deletion fe/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ const eslintPluginVue = { // as of [email protected]
}],
'vue/match-component-import-name': 'error',
'vue/define-props-declaration': 'error',
'vue/define-emits-declaration': 'error',
'vue/define-emits-declaration': ['error', 'type-literal'],
'vue/no-required-prop-with-default': 'error',
'vue/v-on-handler-style': ['error', 'inline-function'],
'vue/multiline-ternary': 'error',
Expand Down
5 changes: 2 additions & 3 deletions fe/src/api/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export type ApiStatsForumPostCount = Api<{
}>;

export type Cursor = string;
interface CursorPaginationQueryParam { cursor?: Cursor }
interface CursorPagination {
pages: {
currentCursor: Cursor,
Expand All @@ -55,7 +54,7 @@ interface CursorPagination {

export type ApiUsers = Api<
CursorPagination & { users: User[] },
CursorPaginationQueryParam & SelectUserParams & { gender?: UserGenderQueryParam }
SelectUserParams & { gender?: UserGenderQueryParam }
>;

export type JsonString = string;
Expand All @@ -72,4 +71,4 @@ export type ApiPosts = Api<CursorPagination & {
}>
}>,
users: User[]
}, CursorPaginationQueryParam & { query: JsonString }>;
}, { query: JsonString }>;
19 changes: 12 additions & 7 deletions fe/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Api, ApiError, ApiForums, ApiPosts, ApiStatsForumPostCount, ApiStatus, ApiUsers, Cursor, CursorPagination } from '@/api/index.d';
import type { MaybeRefOrGetter, Ref } from 'vue';
import type { InfiniteData, QueryKey } from '@tanstack/vue-query';
import type { Ref } from 'vue';
import type { InfiniteData, QueryKey, UseInfiniteQueryOptions, UseQueryOptions } from '@tanstack/vue-query';
import { useInfiniteQuery, useQuery } from '@tanstack/vue-query';
import nprogress from 'nprogress';
import { stringify } from 'qs';
Expand Down Expand Up @@ -86,18 +86,23 @@ const useApi = <
TResponse = TApi['response'],
TQueryParam = TApi['queryParam']>
(endpoint: string, queryFn: QueryFunctions) =>
(queryParam?: Ref<TQueryParam | undefined>, enabled?: MaybeRefOrGetter<boolean>) =>
(queryParam?: Ref<TQueryParam | undefined>, options?: Partial<UseQueryOptions<TResponse, ApiErrorClass>>) =>
useQuery<TResponse, ApiErrorClass>({
queryKey: [endpoint, queryParam],
queryFn: async () => queryFn<TResponse, TQueryParam>(`/${endpoint}`, queryParam?.value),
enabled
...options
});
const useApiWithCursor = <
TApi extends Api<TResponse, TQueryParam>,
TResponse = TApi['response'] & CursorPagination,
TQueryParam = TApi['queryParam']>
(endpoint: string, queryFn: QueryFunctions) =>
(queryParam?: Ref<TQueryParam | undefined>, enabled?: MaybeRefOrGetter<boolean>) =>
(queryParam?: Ref<TQueryParam | undefined>, options?: Partial<UseInfiniteQueryOptions<
TResponse & CursorPagination, ApiErrorClass,
InfiniteData<TResponse & CursorPagination, Cursor>,
TResponse & CursorPagination,
QueryKey, Cursor
>>) =>
useInfiniteQuery<
TResponse & CursorPagination, ApiErrorClass,
InfiniteData<TResponse & CursorPagination, Cursor>,
Expand All @@ -108,9 +113,9 @@ const useApiWithCursor = <
`/${endpoint}`,
{ ...queryParam?.value as TQueryParam, cursor: pageParam === '' ? undefined : pageParam }
),
initialPageParam: '',
getNextPageParam: lastPage => lastPage.pages.nextCursor,
enabled
initialPageParam: '',
...options
});

export const useApiForums = () => useApi<ApiForums>('forums', queryFunction)();
Expand Down
3 changes: 2 additions & 1 deletion fe/src/components/Post/renderers/RendererTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@
</template>
</template>
<template #expandedRowRender="{ record: { pid, content, authorUid: replyAuthorUid } }">
<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component vue/no-v-html -->
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
<component :is="subRepliesKeyByPid[pid] === undefined
? 'span'
: 'p'"
v-viewer.static v-html="content" />

Check warning on line 56 in fe/src/components/Post/renderers/RendererTable.vue

View workflow job for this annotation

GitHub Actions / eslint

'v-html' directive can lead to XSS attack
<!-- eslint-enable vue/no-v-text-v-html-on-component -->
<Table v-if="subRepliesKeyByPid[pid] !== undefined"
:columns="subReplyColumns" :dataSource="subRepliesKeyByPid[pid]"
defaultExpandAllRows expandRowByClick
Expand Down
6 changes: 3 additions & 3 deletions fe/src/components/User/QueryForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<script setup lang="ts">
import type { SelectUserBy, SelectUserModel, SelectUserParams } from '../widgets/selectUser';
import SelectUser from '../widgets/SelectUser.vue';
import { selectUserBy } from '../widgets/selectUser';
import { selectUserBy as selectUserByAll } from '../widgets/selectUser';
import type { BaiduUserID, UserGenderQueryParam } from '@/api/user';
import { boolPropToStr, boolStrPropToBool, removeEnd } from '@/shared';
Expand Down Expand Up @@ -63,8 +63,8 @@ const submitQueryForm = async () => {
return router.push({
name: `user${_.isEmpty(params) ? '' : `/${routeName}`}`,
query: omitDefaultParamsValue({ ..._.omit(params, selectUserBy), gender: gender.value }),
params: _.pick(params, selectUserBy)
query: omitDefaultParamsValue({ ..._.omit(params, selectUserByAll), gender: gender.value }),
params: _.pick(params, selectUserByAll)
});
};
Expand Down
4 changes: 2 additions & 2 deletions fe/src/gtag.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable */
window.dataLayer = window.dataLayer || [];
window.dataLayer ||= [];
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, prefer-rest-params, @typescript-eslint/no-unsafe-member-access, no-undef
function gtag() { dataLayer.push(arguments) }
gtag('js', new Date());

Expand Down
3 changes: 2 additions & 1 deletion fe/src/shared/echarts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import * as _ from 'lodash-es';
import type { BarSeriesOption, LineSeriesOption } from 'echarts/charts';
import type { ToolboxComponentOption } from 'echarts/components';
import * as echarts from 'echarts/core';
import type { ColorPaletteOptionMixin } from 'echarts/types/src/util/types';
// eslint-disable-next-line import/extensions
import type { ColorPaletteOptionMixin } from 'echarts/types/src/util/types.d.ts';

addEventListener('resize', _.throttle(() => {
document.querySelectorAll<HTMLElement>('.echarts')
Expand Down
5 changes: 4 additions & 1 deletion fe/src/stats.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* eslint-disable */
/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
import Stats from 'stats.js';

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const stats = new Stats();
stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom

Expand All @@ -10,6 +12,7 @@ const style = document.createElement('style');
// https://github.com/mrdoob/stats.js/issues/115
style.textContent = '.statsjs canvas { display: block !important; }';
container.append(style);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
container.append(stats.dom);
document.body.append(container);

Expand Down
6 changes: 4 additions & 2 deletions fe/src/views/BilibiliVote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ import { DataZoomComponent, DatasetComponent, GraphicComponent, GridComponent, L
import * as echarts from 'echarts/core';
import { LabelLayout } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import type { TimelineChangePayload } from 'echarts/types/src/component/timeline/timelineAction';
import type { OptionDataItem } from 'echarts/types/src/util/types';
// eslint-disable-next-line import/extensions
import type { TimelineChangePayload } from 'echarts/types/src/component/timeline/timelineAction.d.ts';
// eslint-disable-next-line import/extensions
import type { OptionDataItem } from 'echarts/types/src/util/types.d.ts';
echarts.use([BarChart, CanvasRenderer, DataZoomComponent, DatasetComponent, GraphicComponent, GridComponent, LabelLayout, LegendComponent, MarkLineComponent, LineChart, PieChart, TimelineComponent, TitleComponent, ToolboxComponent, TooltipComponent]);
Expand Down
13 changes: 6 additions & 7 deletions fe/src/views/Post.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<template>
<div class="container">
<QueryForm ref="queryFormRef" :isLoading="isFetching" />
<p>当前页数:{{ getRouteCursorParam(route) }}</p>
<Menu v-show="!_.isEmpty(data?.pages)" v-model:selectedKeys="selectedRenderTypes" mode="horizontal">
<MenuItem key="list">列表视图</MenuItem>
<MenuItem key="table">表格视图</MenuItem>
Expand All @@ -21,7 +20,7 @@
</div>
<div class="container">
<PlaceholderError :error="error" class="border-top" />
<PlaceholderPostList v-show="isPending" :isLoading="isFetching" />
<PlaceholderPostList v-show="isPending || isFetchingNextPage" :isLoading="isFetching" />
</div>
</template>
Expand Down Expand Up @@ -53,12 +52,12 @@ export type PostRenderer = 'list' | 'table';
const route = useRoute();
const queryParam = ref<ApiPosts['queryParam']>();
const shouldFetch = ref<boolean>(false);
const { data, error, isPending, isRefetching, isFetching, isFetchedAfterMount, dataUpdatedAt, errorUpdatedAt, fetchNextPage, hasNextPage } =
useApiPosts(queryParam, shouldFetch);
const initialPageCursor = ref<Cursor>('');
const { data, error, isPending, isRefetching, isFetching, isFetchedAfterMount, dataUpdatedAt, errorUpdatedAt, fetchNextPage, isFetchingNextPage, hasNextPage } =
useApiPosts(queryParam, { initialPageParam: initialPageCursor, enabled: shouldFetch });
const selectedRenderTypes = ref<[PostRenderer]>(['list']);
const renderType = computed(() => selectedRenderTypes.value[0]);
const queryFormRef = ref<InstanceType<typeof QueryForm>>();
const lastFetchingRoute = ref<RouteLocationNormalized>(route);
useHead({
title: computed(() => titleTemplate((() => {
const firstPostPage = data.value?.pages[0];
Expand All @@ -83,6 +82,7 @@ useHead({
const fetchPosts = (queryParams: ObjUnknown[], cursor: Cursor) => {
const startTime = Date.now();
queryParam.value = { query: JSON.stringify(queryParams) };
initialPageCursor.value = cursor;
shouldFetch.value = true;
watchOnce(isFetchedAfterMount, value => {
if (value)
Expand Down Expand Up @@ -113,7 +113,7 @@ const fetchPosts = (queryParams: ObjUnknown[], cursor: Cursor) => {
watch(isFetchedAfterMount, async () => {
if (isFetchedAfterMount.value && renderType.value === 'list') {
await nextTick();
scrollToPostListItemByRoute(lastFetchingRoute.value);
scrollToPostListItemByRoute(route);
}
});
Expand All @@ -123,7 +123,6 @@ const parseRouteThenFetch = async (newRoute: RouteLocationNormalized) => {
const flattenParams = await queryFormRef.value.parseRouteToGetFlattenParams(newRoute);
if (flattenParams === false)
return;
lastFetchingRoute.value = newRoute;
fetchPosts(flattenParams, getRouteCursorParam(newRoute));
};
onBeforeRouteUpdate(async (to, from) => {
Expand Down

0 comments on commit a870c10

Please sign in to comment.