From d6ee14cb99abb80fc4e01b67736fc8d0b9187e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 12 May 2024 19:29:04 +0200 Subject: [PATCH] Fix media upload --- package.json | 2 - src/actions/media.ts | 37 ++++--------------- src/api/__mocks__/index.ts | 4 +- src/api/index.ts | 36 +++++++++++++++++- src/entity-store/hooks/useCreateEntity.ts | 2 +- .../components/emoji-picker-dropdown.tsx | 2 +- .../group/components/group-action-button.tsx | 2 +- src/features/group/edit-group.tsx | 2 +- .../notifications/components/filter-bar.tsx | 12 +++--- src/reducers/auth.test.ts | 2 +- src/utils/resize-image.ts | 2 +- yarn.lock | 32 ---------------- 12 files changed, 56 insertions(+), 79 deletions(-) diff --git a/package.json b/package.json index 5e373bd03..0b324f202 100644 --- a/package.json +++ b/package.json @@ -98,8 +98,6 @@ "@vitejs/plugin-react": "^4.0.4", "@webbtc/webln-types": "^3.0.0", "autoprefixer": "^10.4.15", - "axios": "^1.2.2", - "axios-mock-adapter": "^1.22.0", "babel-plugin-formatjs": "^10.5.6", "babel-plugin-preval": "^5.1.0", "blurhash": "^2.0.0", diff --git a/src/actions/media.ts b/src/actions/media.ts index 5cbac7381..63b433821 100644 --- a/src/actions/media.ts +++ b/src/actions/media.ts @@ -32,37 +32,18 @@ const updateMedia = (mediaId: string, params: Record) => }); }; -const uploadMediaV1 = (body: FormData, onUploadProgress = noOp) => - (dispatch: any, getState: () => RootState) => - api(getState)('/api/v1/media', { - method: 'POST', - body, - headers: { 'Content-Type': '' }, - // }, { - // onUploadProgress: onUploadProgress, - }); - -const uploadMediaV2 = (body: FormData, onUploadProgress = noOp) => - (dispatch: any, getState: () => RootState) => - api(getState)('/api/v2/media', { - method: 'POST', - body, - headers: { 'Content-Type': '' }, - // }, { - // onUploadProgress: onUploadProgress, - }); - -const uploadMedia = (data: FormData, onUploadProgress = noOp) => +const uploadMedia = (body: FormData, onUploadProgress: (e: ProgressEvent) => void = noOp) => (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); const instance = state.instance; const features = getFeatures(instance); - if (features.mediaV2) { - return dispatch(uploadMediaV2(data, onUploadProgress)); - } else { - return dispatch(uploadMediaV1(data, onUploadProgress)); - } + return api(getState)(features.mediaV2 ? '/api/v2/media' : '/api/v1/media', { + method: 'POST', + body, + headers: { 'Content-Type': '' }, + onUploadProgress, + }); }; const uploadFile = ( @@ -70,7 +51,7 @@ const uploadFile = ( intl: IntlShape, onSuccess: (data: APIEntity) => void = () => {}, onFail: (error: unknown) => void = () => {}, - onProgress: (loaded: number) => void = () => {}, + onProgress: (e: ProgressEvent) => void = () => {}, changeTotal: (value: number) => void = () => {}, ) => async (dispatch: AppDispatch, getState: () => RootState) => { @@ -135,8 +116,6 @@ const uploadFile = ( export { fetchMedia, updateMedia, - uploadMediaV1, - uploadMediaV2, uploadMedia, uploadFile, }; diff --git a/src/api/__mocks__/index.ts b/src/api/__mocks__/index.ts index ddc259665..0e6e513cc 100644 --- a/src/api/__mocks__/index.ts +++ b/src/api/__mocks__/index.ts @@ -1,11 +1,11 @@ -import MockAdapter from 'axios-mock-adapter'; +// import MockAdapter from 'axios-mock-adapter'; import LinkHeader from 'http-link-header'; import { vi } from 'vitest'; const api = await vi.importActual('../index') as Record; let mocks: Array = []; -export const __stub = (func: (mock: MockAdapter) => void) => mocks.push(func); +export const __stub = (func: (mock: any) => void) => mocks.push(func); export const __clear = (): Function[] => mocks = []; // const setupMock = (axios: AxiosInstance) => { diff --git a/src/api/index.ts b/src/api/index.ts index 036713e05..d385131f5 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -41,7 +41,10 @@ const getAuthBaseURL = createSelector([ }); export const getFetch = (accessToken?: string | null, baseURL: string = '') => - (input: URL | RequestInfo, init?: RequestInit & { params?: Record} | undefined) => { + ( + input: URL | RequestInfo, + init?: RequestInit & { params?: Record; onUploadProgress?: (e: ProgressEvent) => void } | undefined, + ) => { const fullPath = buildFullPath(input.toString(), isURL(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : baseURL, init?.params); const headers = new Headers(init?.headers); @@ -56,18 +59,47 @@ export const getFetch = (accessToken?: string | null, baseURL: string = '') => headers.set('Content-Type', headers.get('Content-Type') || 'application/json'); } + // Fetch API doesn't report upload progress, use XHR + if (init?.onUploadProgress) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + + xhr.addEventListener('progress', init.onUploadProgress!); + xhr.addEventListener('loadend', () => { + const data = xhr.response; + let json: T = undefined!; + + try { + json = JSON.parse(data); + } catch (e) { + // + } + + if (xhr.status >= 400) reject({ response: { status: xhr.status, data, json } }); + resolve({ status: xhr.status, data, json } as any); + }); + + xhr.open(init?.method || 'GET', fullPath, true); + headers.forEach((value, key) => xhr.setRequestHeader(key, value)); + xhr.responseType = 'text'; + xhr.send(init.body as FormData); + }); + } + return fetch(fullPath, { ...init, headers, }).then(async (response) => { - if (!response.ok) throw { response }; const data = await response.text(); let json: T = undefined!; + try { json = JSON.parse(data); } catch (e) { // } + + if (!response.ok) throw { response: { ...response, data, json } }; return { ...response, data, json }; }); }; diff --git a/src/entity-store/hooks/useCreateEntity.ts b/src/entity-store/hooks/useCreateEntity.ts index a6ac67428..2e75dbc5d 100644 --- a/src/entity-store/hooks/useCreateEntity.ts +++ b/src/entity-store/hooks/useCreateEntity.ts @@ -24,7 +24,7 @@ function useCreateEntity( const [isSubmitting, setPromise] = useLoading(); const { entityType, listKey } = parseEntitiesPath(expandedPath); - async function createEntity(data: Data, callbacks: EntityCallbacks = {}): Promise { + async function createEntity(data: Data, callbacks: EntityCallbacks = {}): Promise { const result = await setPromise(entityFn(data)); const schema = opts.schema || z.custom(); const entity = schema.parse(result.json); diff --git a/src/features/emoji/components/emoji-picker-dropdown.tsx b/src/features/emoji/components/emoji-picker-dropdown.tsx index f3167f61b..f106e8623 100644 --- a/src/features/emoji/components/emoji-picker-dropdown.tsx +++ b/src/features/emoji/components/emoji-picker-dropdown.tsx @@ -50,7 +50,7 @@ export interface IEmojiPickerDropdown { } const perLine = 8; -const lines = 2; +const lines = 2; const DEFAULTS = [ '+1', diff --git a/src/features/group/components/group-action-button.tsx b/src/features/group/components/group-action-button.tsx index 3f349c6d9..3779f683e 100644 --- a/src/features/group/components/group-action-button.tsx +++ b/src/features/group/components/group-action-button.tsx @@ -50,7 +50,7 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { ); }, onError(error) { - const message = (error.response?.data as any).error; + const message = error.response?.json?.error; if (message) { toast.error(message); } diff --git a/src/features/group/edit-group.tsx b/src/features/group/edit-group.tsx index 13c6279b1..b2203eb9c 100644 --- a/src/features/group/edit-group.tsx +++ b/src/features/group/edit-group.tsx @@ -62,7 +62,7 @@ const EditGroup: React.FC = ({ params: { groupId } }) => { toast.success(intl.formatMessage(messages.groupSaved)); }, onError(error) { - const message = (error.response?.data as any)?.error; + const message = error.response?.json?.error; if (error.response?.status === 422 && typeof message !== 'undefined') { toast.error(message); diff --git a/src/features/notifications/components/filter-bar.tsx b/src/features/notifications/components/filter-bar.tsx index 4fd2f75f4..d4ec1948e 100644 --- a/src/features/notifications/components/filter-bar.tsx +++ b/src/features/notifications/components/filter-bar.tsx @@ -57,37 +57,37 @@ const NotificationFilterBar = () => { name: 'mention', }); if (features.accountNotifies || features.accountSubscriptions) items.push({ - text: , + text: , title: intl.formatMessage(messages.statuses), action: onClick('status'), name: 'status', }); items.push({ - text: , + text: , title: intl.formatMessage(messages.favourites), action: onClick('favourite'), name: 'favourite', }); items.push({ - text: , + text: , title: intl.formatMessage(messages.boosts), action: onClick('reblog'), name: 'reblog', }); if (features.polls) items.push({ - text: , + text: , title: intl.formatMessage(messages.polls), action: onClick('poll'), name: 'poll', }); if (features.events) items.push({ - text: , + text: , title: intl.formatMessage(messages.events), action: onClick('events'), name: 'events', }); items.push({ - text: , + text: , title: intl.formatMessage(messages.follows), action: onClick('follow'), name: 'follow', diff --git a/src/reducers/auth.test.ts b/src/reducers/auth.test.ts index fbee6005f..e499e8800 100644 --- a/src/reducers/auth.test.ts +++ b/src/reducers/auth.test.ts @@ -117,7 +117,7 @@ describe('auth reducer', () => { }; const expected = ImmutableMap({ - 'https://gleasonator.com/users/alex': AuthUserRecord({ id: '1234', access_token: 'ABCDEFG', url: 'https://gleasonator.com/users/alex' }), + 'https://gleasonator.com/users/alex': AuthUserRecord({ id: '1234', access_token: 'ABCDEFG', url: 'https://gleasonator.com/users/alex' }), }); const result = reducer(undefined, action); diff --git a/src/utils/resize-image.ts b/src/utils/resize-image.ts index f25e66cd1..553cb1f5c 100644 --- a/src/utils/resize-image.ts +++ b/src/utils/resize-image.ts @@ -154,7 +154,7 @@ const processImage = ( name?: string; }, ) => new Promise((resolve, reject) => { - const canvas = document.createElement('canvas'); + const canvas = document.createElement('canvas'); if (4 < orientation && orientation < 9) { canvas.width = height; diff --git a/yarn.lock b/yarn.lock index ba3319f26..bbfc911df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3267,23 +3267,6 @@ axe-core@^4.6.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.8.1.tgz#6948854183ee7e7eae336b9877c5bafa027998ea" integrity sha512-9l850jDDPnKq48nbad8SiEelCv4OrUWrKab/cPj0GScVg6cb6NbCCt/Ulk26QEq5jP9NnGr04Bit1BHyV6r5CQ== -axios-mock-adapter@^1.22.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz#0f3e6be0fc9b55baab06f2d49c0b71157e7c053d" - integrity sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw== - dependencies: - fast-deep-equal "^3.1.3" - is-buffer "^2.0.5" - -axios@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.2.tgz#72681724c6e6a43a9fea860fc558127dbe32f9f1" - integrity sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - axobject-query@^3.1.1: version "3.2.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" @@ -4891,11 +4874,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.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== - for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -5560,11 +5538,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - is-builtin-module@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" @@ -7264,11 +7237,6 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0, 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== - pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"