From daf6b5024929a0a78ec79e84d9604ec6d84d9fbf Mon Sep 17 00:00:00 2001
From: schroda <50052685+schroda@users.noreply.github.com>
Date: Tue, 5 Mar 2024 02:07:05 +0100
Subject: [PATCH] Fix/deprioritize image requests (#638)
* Use "fetch" instead of "axios"
Axios does not support prioritizing requests
* Deprioritize image requests
The browser requests images (
) with low priority by default.
Due to switching to loading images via axios (f852ce70e7b98c35f3a31e6981d5135b4ae50c8e), the image requests had the same priority as other requests and thus blocked e.g. xhr requests
---
package.json | 1 -
src/components/util/SpinnerImage.tsx | 3 +-
src/lib/requests/RequestManager.ts | 12 ++--
src/lib/requests/client/RestClient.ts | 80 +++++++++++--------------
src/screens/settings/ServerSettings.tsx | 1 -
yarn.lock | 57 ------------------
6 files changed, 45 insertions(+), 109 deletions(-)
diff --git a/package.json b/package.json
index 936814ef..190246ae 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,6 @@
"@mui/x-date-pickers": "^6.19.2",
"@vitejs/plugin-react-swc": "^3.5.0",
"apollo-upload-client": "^17.0.0",
- "axios": "^1.6.7",
"dayjs": "^1.11.10",
"file-selector": "^0.6.0",
"graphql-tag": "^2.12.6",
diff --git a/src/components/util/SpinnerImage.tsx b/src/components/util/SpinnerImage.tsx
index a36269b3..eb17e207 100644
--- a/src/components/util/SpinnerImage.tsx
+++ b/src/components/util/SpinnerImage.tsx
@@ -14,7 +14,6 @@ import { Theme, SxProps, Stack, Button } from '@mui/material';
import BrokenImageIcon from '@mui/icons-material/BrokenImage';
import RefreshIcon from '@mui/icons-material/Refresh';
import { useTranslation } from 'react-i18next';
-import { CanceledError } from 'axios';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
@@ -72,7 +71,7 @@ export const SpinnerImage = forwardRef((props: IProps, imgRef: ForwardedRef): void {
+ public updateClient(config: RequestInit): void {
this.restClient.updateConfig(config);
this.graphQLClient.updateConfig();
}
public getBaseUrl(): string {
- return this.restClient.getClient().defaults.baseURL!;
+ return this.restClient.getBaseUrl();
}
public getValidUrlFor(endpoint: string, apiVersion: string = RequestManager.API_VERSION): string {
@@ -764,8 +763,13 @@ export class RequestManager {
const response = this.restClient
.fetcher(url, {
checkResponseIsJson: false,
- config: { signal, responseType: 'blob' },
+ config: {
+ signal,
+ // @ts-ignore - typing has not been updated yet
+ priority: 'low',
+ },
})
+ .then((data) => data.blob())
.then((data) => URL.createObjectURL(data));
return { response, abortRequest };
diff --git a/src/lib/requests/client/RestClient.ts b/src/lib/requests/client/RestClient.ts
index a21adef1..b70da094 100644
--- a/src/lib/requests/client/RestClient.ts
+++ b/src/lib/requests/client/RestClient.ts
@@ -6,33 +6,30 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
-import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { BaseClient } from '@/lib/requests/client/BaseClient.ts';
export enum HttpMethod {
- GET = 'get',
- POST = 'post',
- PATCH = 'patch',
- DELETE = 'delete',
+ GET = 'GET',
+ POST = 'POST',
+ PATCH = 'PATCH',
+ DELETE = 'DELETE',
}
-type SimpleRestResponse = {
- data: Data;
-};
-
export interface IRestClient {
- get>(url: string): Promise;
- delete>(url: string): Promise;
- post>(url: string, data?: any): Promise;
- put>(url: string, data?: any): Promise;
- patch>(url: string, data?: any): Promise;
+ get(url: string): Promise;
+ delete(url: string): Promise;
+ post(url: string, data?: any): Promise;
+ put(url: string, data?: any): Promise;
+ patch(url: string, data?: any): Promise;
}
export class RestClient
- extends BaseClient(url: string, data: any) => Promise>
+ extends BaseClient Promise>
implements IRestClient
{
- public readonly fetcher = async (
+ private config: RequestInit = {};
+
+ public readonly fetcher = async (
url: string,
{
data,
@@ -42,20 +39,27 @@ export class RestClient
}: {
data?: any;
httpMethod?: HttpMethod;
- config?: AxiosRequestConfig;
+ config?: RequestInit;
checkResponseIsJson?: boolean;
} = {},
- ): Promise => {
- let result: AxiosResponse;
+ ): Promise => {
+ const updatedUrl = url.startsWith('http') ? url : `${this.getBaseUrl()}${url}`;
+
+ let result: Response;
switch (httpMethod) {
case HttpMethod.GET:
- result = await this.client[httpMethod](url, config);
+ result = await this.client(updatedUrl, { ...this.config, ...config, method: httpMethod });
break;
case HttpMethod.POST:
case HttpMethod.PATCH:
case HttpMethod.DELETE:
- result = await this.client[httpMethod](url, data, config);
+ result = await this.client(updatedUrl, {
+ ...this.config,
+ ...config,
+ method: httpMethod,
+ body: JSON.stringify(data),
+ });
break;
default:
throw new Error(`Unexpected HttpMethod "${httpMethod}"`);
@@ -65,54 +69,42 @@ export class RestClient
throw new Error(result.statusText);
}
- if (checkResponseIsJson && result.headers['content-type'] !== 'application/json') {
+ if (checkResponseIsJson && result.headers.get('content-type') !== 'application/json') {
throw new Error('Response is not json');
}
- return result.data;
+ return result;
};
protected override createClient(): void {
- const baseURL = this.getBaseUrl();
-
- this.client = axios.create({
- // baseURL must not have trailing slash
- baseURL,
- });
-
- this.client.interceptors.request.use((config) => {
- if (config.data instanceof FormData) {
- Object.assign(config.headers, { 'Content-Type': 'multipart/form-data' });
- }
- return config;
- });
+ this.client = fetch.bind(window);
}
- public updateConfig(config: Partial): void {
- this.client.defaults = { ...this.client.defaults, ...config };
+ public updateConfig(config: RequestInit): void {
+ this.config = { ...this.config, ...config };
}
- public getClient(): AxiosInstance {
+ public getClient(): typeof fetch {
return this.client;
}
get get() {
- return this.client.get;
+ return (url: string) => this.fetcher(url);
}
get post() {
- return this.client.post;
+ return (url: string, data?: any) => this.fetcher(url, { data, httpMethod: HttpMethod.POST });
}
get put() {
- return this.client.put;
+ return (url: string, data?: any) => this.fetcher(url, { data, httpMethod: HttpMethod.POST });
}
get patch() {
- return this.client.patch;
+ return (url: string, data?: any) => this.fetcher(url, { data, httpMethod: HttpMethod.PATCH });
}
get delete() {
- return this.client.delete;
+ return (url: string) => this.fetcher(url, { httpMethod: HttpMethod.DELETE });
}
}
diff --git a/src/screens/settings/ServerSettings.tsx b/src/screens/settings/ServerSettings.tsx
index 0b9cddff..dd0a4e21 100644
--- a/src/screens/settings/ServerSettings.tsx
+++ b/src/screens/settings/ServerSettings.tsx
@@ -88,7 +88,6 @@ export const ServerSettings = () => {
const handleServerAddressChange = (address: string) => {
const serverBaseUrl = address.replaceAll(/(\/)+$/g, '');
setServerAddress(serverBaseUrl);
- requestManager.updateClient({ baseURL: serverBaseUrl });
};
const updateSetting = (
diff --git a/yarn.lock b/yarn.lock
index 11147c1d..f3020d8f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2249,11 +2249,6 @@ asynciterator.prototype@^1.0.0:
dependencies:
has-symbols "^1.0.3"
-asynckit@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
- integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
-
auto-bind@~4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb"
@@ -2269,15 +2264,6 @@ axe-core@=4.7.0:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==
-axios@^1.6.7:
- version "1.6.7"
- resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7"
- integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==
- dependencies:
- follow-redirects "^1.15.4"
- form-data "^4.0.0"
- proxy-from-env "^1.1.0"
-
axobject-query@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
@@ -2610,13 +2596,6 @@ colorette@^2.0.16:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
-combined-stream@^1.0.8:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
- integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
- dependencies:
- delayed-stream "~1.0.0"
-
commander@^2.20.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -2786,11 +2765,6 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
-delayed-stream@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
- integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
-
dependency-graph@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27"
@@ -3412,11 +3386,6 @@ flatted@^3.2.9:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
-follow-redirects@^1.15.4:
- version "1.15.5"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020"
- integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==
-
for-each@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
@@ -3424,15 +3393,6 @@ for-each@^0.3.3:
dependencies:
is-callable "^1.1.3"
-form-data@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
- integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.8"
- mime-types "^2.1.12"
-
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -4314,18 +4274,6 @@ micromatch@^4.0.4, micromatch@^4.0.5:
braces "^3.0.2"
picomatch "^2.3.1"
-mime-db@1.52.0:
- version "1.52.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
- integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-
-mime-types@^2.1.12:
- version "2.1.35"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
- integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
- dependencies:
- mime-db "1.52.0"
-
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
@@ -4724,11 +4672,6 @@ prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
-proxy-from-env@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
- integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
-
punycode@^1.3.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"