More fetch, add urlencode (#903)

* feat: add fetchText

* minor: add urlencode
This commit is contained in:
nyagami
2024-01-21 15:35:01 +07:00
committed by GitHub
parent d299e91a93
commit 01e6e9873e
7 changed files with 153 additions and 92 deletions
+21 -1
View File
@@ -54,7 +54,8 @@
"react-native-tab-view": "^3.5.2",
"react-native-vector-icons": "^9.0.0",
"react-native-webview": "11.26.0",
"sanitize-html": "^2.7.0"
"sanitize-html": "^2.7.0",
"urlencode": "^2.0.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
@@ -16603,6 +16604,25 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
},
"node_modules/urlencode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/urlencode/-/urlencode-2.0.0.tgz",
"integrity": "sha512-K4+koEq4II9FqKKdLyMwfVFiWvTLJsdsIihXCprumjlOwpviO44E4hAhLYBLb6CEVTZh9hXXMTQHIT+Hwv5BPw==",
"dependencies": {
"iconv-lite": "~0.6.3"
}
},
"node_modules/urlencode/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/use-latest-callback": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.9.tgz",
+2 -1
View File
@@ -60,7 +60,8 @@
"react-native-tab-view": "^3.5.2",
"react-native-vector-icons": "^9.0.0",
"react-native-webview": "11.26.0",
"sanitize-html": "^2.7.0"
"sanitize-html": "^2.7.0",
"urlencode": "^2.0.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
+107
View File
@@ -0,0 +1,107 @@
import { getUserAgent } from '@hooks/persisted/useUserAgent';
type FetchInit = {
headers?: Record<string, string> | Headers;
method?: string;
body?: FormData | string;
[x: string]: string | Record<string, string> | undefined | FormData | Headers;
};
const makeInit = (init?: FetchInit) => {
const defaultHeaders = {
'User-Agent': getUserAgent(),
};
if (init?.headers) {
if (init.headers instanceof Headers) {
if (!init.headers.get('User-Agent') && defaultHeaders['User-Agent']) {
init.headers.set('User-Agent', defaultHeaders['User-Agent']);
}
} else {
init.headers = {
...defaultHeaders,
...init.headers,
};
}
} else {
init = {
...init,
headers: defaultHeaders,
};
}
return init;
};
export const fetchApi = async (
url: string,
init?: FetchInit,
): Promise<Response> => {
init = makeInit(init);
return await fetch(url, init);
};
const FILE_READER_PREFIX_LENGTH = 'data:application/octet-stream;base64,'
.length;
/**
*
* @param url
* @param init
* @returns base64 of file
*/
export const fetchFile = async (
url: string,
init?: FetchInit,
): Promise<string> => {
init = makeInit(init);
try {
const res = await fetch(url, init);
if (!res.ok) {
throw new Error();
}
const blob = await res.blob();
return await new Promise((resolve, reject) => {
const fr = new FileReader();
fr.onloadend = () => {
resolve(fr.result.slice(FILE_READER_PREFIX_LENGTH) as string);
};
fr.onerror = () => reject();
fr.onabort = () => reject();
fr.readAsDataURL(blob);
});
} catch (e) {
return '';
}
};
/**
*
* @param url
* @param init
* @param encoding link: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/encoding
* @returns plain text
*/
export const fetchText = async (
url: string,
init?: FetchInit,
encoding?: string,
): Promise<string> => {
init = makeInit(init);
try {
const res = await fetch(url, init);
if (!res.ok) {
throw new Error();
}
const blob = await res.blob();
return await new Promise((resolve, reject) => {
const fr = new FileReader();
fr.onloadend = () => {
resolve(fr.result as string);
};
fr.onerror = () => reject();
fr.onabort = () => reject();
fr.readAsText(blob, encoding);
});
} catch (e) {
return '';
}
};
+5 -3
View File
@@ -10,15 +10,17 @@ import dayjs from 'dayjs';
import { NovelStatus, Plugin, PluginItem } from './types';
import { FilterTypes } from './types/filterTypes';
import { parseMadaraDate } from './helpers/parseDate';
import { isUrlAbsolute } from '@utils/isAbsoluteUrl';
import { fetchApi, fetchFile } from '@utils/fetch/fetch';
import { isUrlAbsolute } from './helpers/isAbsoluteUrl';
import { fetchApi, fetchFile, fetchText } from './helpers/fetch';
import { defaultCover } from './helpers/constants';
import { encode, decode } from 'urlencode';
const packages: Record<string, any> = {
'cheerio': { load },
'dayjs': dayjs,
'urlencode': { encode, decode },
'@libs/novelStatus': { NovelStatus },
'@libs/fetch': { fetchApi, fetchFile },
'@libs/fetch': { fetchApi, fetchFile, fetchText },
'@libs/parseMadaraDate': { parseMadaraDate },
'@libs/isAbsoluteUrl': { isUrlAbsolute },
'@libs/filterInputs': { FilterTypes },
+18 -23
View File
@@ -2,7 +2,6 @@ import React from 'react';
import { Pressable, StyleSheet, View } from 'react-native';
import { IconButton } from 'react-native-paper';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { isUrlAbsolute } from '../../../utils/isAbsoluteUrl';
import * as WebBrowser from 'expo-web-browser';
import color from 'color';
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
@@ -66,28 +65,24 @@ const ChapterFooter = ({
iconColor={theme.onSurface}
/>
</Pressable>
{isUrlAbsolute(chapterUrl) ? (
<Pressable
android_ripple={rippleConfig}
style={styles.buttonStyles}
onPress={() => WebBrowser.openBrowserAsync(chapterUrl)}
>
<IconButton icon="earth" size={26} iconColor={theme.onSurface} />
</Pressable>
) : null}
<>
<Pressable
android_ripple={rippleConfig}
style={styles.buttonStyles}
onPress={() => scrollTo(0)}
>
<IconButton
icon="format-vertical-align-top"
size={26}
iconColor={theme.onSurface}
/>
</Pressable>
</>
<Pressable
android_ripple={rippleConfig}
style={styles.buttonStyles}
onPress={() => WebBrowser.openBrowserAsync(chapterUrl)}
>
<IconButton icon="earth" size={26} iconColor={theme.onSurface} />
</Pressable>
<Pressable
android_ripple={rippleConfig}
style={styles.buttonStyles}
onPress={() => scrollTo(0)}
>
<IconButton
icon="format-vertical-align-top"
size={26}
iconColor={theme.onSurface}
/>
</Pressable>
<Pressable
android_ripple={rippleConfig}
style={styles.buttonStyles}
-64
View File
@@ -1,67 +1,3 @@
import { getUserAgent } from '@hooks/persisted/useUserAgent';
export const fetchApi = async (
url: string,
init?: {
headers?: Record<string, string> | Headers;
[x: string]:
| string
| Record<string, string>
| undefined
| FormData
| Headers;
},
): Promise<Response> => {
const defaultHeaders = {
'User-Agent': getUserAgent(),
};
if (init?.headers) {
if (init.headers instanceof Headers) {
if (!init.headers.get('User-Agent') && defaultHeaders['User-Agent']) {
init.headers.set('User-Agent', defaultHeaders['User-Agent']);
}
} else {
init.headers = {
...defaultHeaders,
...init.headers,
};
}
} else {
init = {
...init,
headers: defaultHeaders,
};
}
return await fetch(url, init);
};
const FILE_READER_PREFIX_LENGTH = 'data:application/octet-stream;base64,'
.length;
export const fetchFile = async (url: string, init: any): Promise<string> => {
if (!init) {
init = {};
}
try {
const res = await fetch(url, init);
if (!res.ok) {
throw new Error();
}
const blob = await res.blob();
return await new Promise((resolve, reject) => {
const fr = new FileReader();
fr.onloadend = () => {
resolve(fr.result.slice(FILE_READER_PREFIX_LENGTH) as string);
};
fr.onerror = () => reject();
fr.onabort = () => reject();
fr.readAsDataURL(blob);
});
} catch (e) {
return '';
}
};
export const fetchTimeout = async (
url: string,
init?: any,