Skip to content

Commit 357f408

Browse files
committed
refactor: ♻️ 重构 spa 站点的实现
1 parent 92e6fbf commit 357f408

3 files changed

Lines changed: 120 additions & 121 deletions

File tree

src/site/kemono.tsx

Lines changed: 36 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,17 @@
11
import {
22
createEffectOn,
3-
onUrlChange,
43
querySelectorAll,
54
querySelectorClick,
6-
wait,
75
waitDom,
8-
waitUrlChange,
96
} from 'helper';
10-
import { useInit } from 'main';
7+
import { universalSPA } from 'main';
118

12-
(async () => {
13-
const isMangaPage = () => location.pathname.includes('/post/');
14-
await waitUrlChange(isMangaPage);
15-
16-
const { store, setState, showComic, init } = await useInit('kemono', {
17-
autoShow: false,
18-
defaultOption: { pageNum: 1 },
19-
/** 加载原图 */
20-
load_original_image: true,
21-
});
22-
23-
// 在切换时重新获取图片
24-
createEffectOn(
25-
() => store.options.load_original_image,
26-
(isOriginal, prev) => {
27-
setState('nowComic', isOriginal ? 'original' : 'thumbnail');
28-
if (prev) showComic();
29-
},
30-
);
31-
32-
await waitDom('.post__thumbnail');
33-
34-
const original = () =>
35-
querySelectorAll<HTMLAnchorElement>('.post__thumbnail a').map(
36-
(e) => e.href,
37-
);
38-
const thumbnail = () =>
39-
querySelectorAll<HTMLImageElement>('.post__thumbnail img').map(
40-
(e) => e.src,
41-
);
42-
setState((state) => {
43-
state.comicMap.original = { getImgList: original, imgList: original() };
44-
state.comicMap.thumbnail = { getImgList: thumbnail, imgList: thumbnail() };
45-
state.manga.onNext = querySelectorClick('.post__nav-link.next');
46-
state.manga.onPrev = querySelectorClick('.post__nav-link.prev');
47-
});
48-
init();
9+
const original = () =>
10+
querySelectorAll<HTMLAnchorElement>('.post__thumbnail a').map((e) => e.href);
11+
const thumbnail = () =>
12+
querySelectorAll<HTMLImageElement>('.post__thumbnail img').map((e) => e.src);
4913

14+
const handlePwa = () => {
5015
// 加上跳转至 pwa 的链接
5116
const zipExtension = new Set(['zip', 'rar', '7z', 'cbz', 'cbr', 'cb7']);
5217
for (const e of querySelectorAll<HTMLAnchorElement>('.post__attachment a')) {
@@ -58,26 +23,37 @@ import { useInit } from 'main';
5823
a.style.opacity = '.6';
5924
e.parentNode!.insertBefore(a, e.nextElementSibling);
6025
}
26+
};
6127

62-
onUrlChange(async (lastUrl) => {
63-
if (!lastUrl) return;
64-
65-
if (!isMangaPage())
66-
return setState((state) => {
67-
state.fab.show = false;
68-
state.manga.show = false;
69-
});
70-
71-
setState((state) => {
72-
state.fab.show = undefined;
73-
state.manga.show = false;
74-
});
28+
universalSPA('kemono', {
29+
options: {
30+
autoShow: false,
31+
defaultOption: { pageNum: 1 },
32+
/** 加载原图 */
33+
load_original_image: true,
34+
},
35+
isMangaPage: async () => {
36+
if (!location.pathname.includes('/post/')) return false;
37+
await waitDom('.post__thumbnail');
38+
handlePwa();
39+
return true;
40+
},
41+
42+
work: async ({ store, setState, showComic }) => {
43+
// 在切换时重新获取图片
44+
createEffectOn(
45+
() => store.options.load_original_image,
46+
(isOriginal, prev) => {
47+
setState('nowComic', isOriginal ? 'original' : 'thumbnail');
48+
if (prev) showComic();
49+
},
50+
);
7551

76-
await wait(() => thumbnail()[0] !== store.comicMap.thumbnail.imgList?.[0]);
7752
setState((state) => {
78-
state.comicMap.original.imgList = original();
79-
state.comicMap.thumbnail.imgList = thumbnail();
53+
state.comicMap.original = { getImgList: original };
54+
state.comicMap.thumbnail = { getImgList: thumbnail };
55+
state.manga.onNext = querySelectorClick('.post__nav-link.next');
56+
state.manga.onPrev = querySelectorClick('.post__nav-link.prev');
8057
});
81-
if (store.options.autoShow) await showComic();
82-
});
83-
})();
58+
},
59+
});

src/site/pixiv.tsx

Lines changed: 37 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1-
import { createEffectOn, onUrlChange, waitUrlChange } from 'helper';
2-
import { request, useInit } from 'main';
1+
import { createEffectOn } from 'helper';
2+
import { request, universalSPA } from 'main';
33

4-
(async () => {
5-
let imgs: {
6-
urls: { original: string; regular: string };
7-
height: number;
8-
width: number;
9-
}[] = [];
4+
let imgs: {
5+
urls: { original: string; regular: string };
6+
height: number;
7+
width: number;
8+
}[] = [];
9+
10+
universalSPA('pixiv', {
11+
options: {
12+
autoShow: false,
13+
defaultOption: { pageNum: 1 },
14+
/** 加载原图 */
15+
load_original_image: true,
16+
},
17+
isMangaPage: async () => {
18+
if (!location.pathname.startsWith('/artworks/')) return false;
1019

11-
const isMangaPage = async () => {
1220
const id = Number(location.pathname.split('/')[2]);
13-
if (!id || !location.pathname.startsWith('/artworks/')) {
21+
if (!id) {
1422
imgs.length = 0;
1523
return false;
1624
}
@@ -21,58 +29,27 @@ import { request, useInit } from 'main';
2129
);
2230
imgs = res.response.body;
2331
return imgs.length > 1;
24-
};
25-
await waitUrlChange(isMangaPage);
26-
27-
const { store, setState, showComic, init } = await useInit('pixiv', {
28-
autoShow: false,
29-
defaultOption: { pageNum: 1 },
30-
/** 加载原图 */
31-
load_original_image: true,
32-
});
33-
34-
// 在切换时重新获取图片
35-
createEffectOn(
36-
() => store.options.load_original_image,
37-
(isOriginal, prev) => {
38-
setState('nowComic', isOriginal ? 'original' : 'regular');
39-
if (prev) showComic();
40-
},
41-
);
42-
43-
const getImgList = (isOriginal: boolean) => () =>
44-
imgs.map((img) => {
45-
const src = isOriginal ? img.urls.original : img.urls.regular;
46-
return { src, height: img.height, width: img.width };
47-
});
48-
49-
setState((state) => {
50-
state.comicMap.original = {
51-
getImgList: getImgList(true),
52-
imgList: getImgList(true)(),
53-
};
54-
state.comicMap.regular = {
55-
getImgList: getImgList(false),
56-
imgList: getImgList(false)(),
57-
};
58-
});
59-
60-
init();
61-
62-
onUrlChange(async (lastUrl) => {
63-
if (!lastUrl) return;
32+
},
33+
34+
work: async ({ store, setState, showComic }) => {
35+
// 在切换时重新获取图片
36+
createEffectOn(
37+
() => store.options.load_original_image,
38+
(isOriginal, prev) => {
39+
setState('nowComic', isOriginal ? 'original' : 'regular');
40+
if (prev) showComic();
41+
},
42+
);
6443

65-
if (!isMangaPage())
66-
return setState((state) => {
67-
state.fab.show = false;
68-
state.manga.show = false;
44+
const getImgList = (isOriginal: boolean) => () =>
45+
imgs.map((img) => {
46+
const src = isOriginal ? img.urls.original : img.urls.regular;
47+
return { src, height: img.height, width: img.width };
6948
});
7049

7150
setState((state) => {
72-
state.fab.show = undefined;
73-
state.manga.show = false;
51+
state.comicMap.original = { getImgList: getImgList(true) };
52+
state.comicMap.regular = { getImgList: getImgList(false) };
7453
});
75-
76-
if (store.options.autoShow) await showComic();
77-
});
78-
})();
54+
},
55+
});

src/userscript/main/universal.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { MangaProps } from 'components/Manga';
22

3-
import { onUrlChange, wait, waitUrlChange } from 'helper';
3+
import { onUrlChange, sleep, wait, waitUrlChange } from 'helper';
44

55
import type { MainContext, SiteOptions } from '.';
66

@@ -105,3 +105,49 @@ export const universal = async ({
105105
]);
106106
}, SPA?.handleUrl);
107107
};
108+
109+
// TODO: 使用 universalSPA 重构 universal
110+
111+
/** 用于适配 SPA 站点的配置项 */
112+
export type SpaInitOptions = {
113+
options?: Partial<SiteOptions & Record<string, any>>;
114+
115+
/** 要确保返回 true 后,立刻就可以取得图片列表了。不然就在里面 wait 着 */
116+
isMangaPage: () => Promise<unknown> | unknown;
117+
work: (mainContext: MainContext) => Promise<void>;
118+
};
119+
120+
/** 对简单站点的通用解 */
121+
export const universalSPA = async (
122+
name: string,
123+
{ options: initOptions, isMangaPage, work }: SpaInitOptions,
124+
) => {
125+
await waitUrlChange(isMangaPage);
126+
127+
const mainContext = await useInit(name, initOptions);
128+
await work(mainContext);
129+
const { store, setState, showComic, loadComic, init } = mainContext;
130+
init();
131+
132+
onUrlChange(async (lastUrl) => {
133+
if (!lastUrl) return;
134+
135+
setState((state) => {
136+
state.fab.show = undefined;
137+
state.manga.show = false;
138+
});
139+
140+
if (!(await isMangaPage())) return setState('fab', 'show', false);
141+
142+
const lastImg = store.comicMap[store.nowComic].imgList?.[0];
143+
// 等到能加载出新图片
144+
const res = await wait(async () => {
145+
await sleep(200);
146+
await loadComic();
147+
return store.comicMap[store.nowComic].imgList?.[0] !== lastImg;
148+
}, 10 * 1000);
149+
if (!res) return;
150+
151+
if (store.options.autoShow) await showComic();
152+
});
153+
};

0 commit comments

Comments
 (0)