Fix media upload

This commit is contained in:
marcin mikołajczak 2024-05-12 19:29:04 +02:00
parent cb7976bd3d
commit d6ee14cb99
12 changed files with 56 additions and 79 deletions

View file

@ -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",

View file

@ -32,37 +32,18 @@ const updateMedia = (mediaId: string, params: Record<string, any>) =>
});
};
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,
};

View file

@ -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<string, Function>;
let mocks: Array<Function> = [];
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) => {

View file

@ -41,7 +41,10 @@ const getAuthBaseURL = createSelector([
});
export const getFetch = (accessToken?: string | null, baseURL: string = '') =>
<T = any>(input: URL | RequestInfo, init?: RequestInit & { params?: Record<string, any>} | undefined) => {
<T = any>(
input: URL | RequestInfo,
init?: RequestInit & { params?: Record<string, any>; 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<Response & { data: string; json: T }>((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 };
});
};

View file

@ -24,7 +24,7 @@ function useCreateEntity<TEntity extends Entity = Entity, Data = unknown>(
const [isSubmitting, setPromise] = useLoading();
const { entityType, listKey } = parseEntitiesPath(expandedPath);
async function createEntity(data: Data, callbacks: EntityCallbacks<TEntity> = {}): Promise<void> {
async function createEntity(data: Data, callbacks: EntityCallbacks<TEntity, { response?: Response & { json: any } }> = {}): Promise<void> {
const result = await setPromise(entityFn(data));
const schema = opts.schema || z.custom<TEntity>();
const entity = schema.parse(result.json);

View file

@ -50,7 +50,7 @@ export interface IEmojiPickerDropdown {
}
const perLine = 8;
const lines = 2;
const lines = 2;
const DEFAULTS = [
'+1',

View file

@ -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);
}

View file

@ -62,7 +62,7 @@ const EditGroup: React.FC<IEditGroup> = ({ 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);

View file

@ -57,37 +57,37 @@ const NotificationFilterBar = () => {
name: 'mention',
});
if (features.accountNotifies || features.accountSubscriptions) items.push({
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/bell-ringing.svg')} />,
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/bell-ringing.svg')} />,
title: intl.formatMessage(messages.statuses),
action: onClick('status'),
name: 'status',
});
items.push({
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/heart.svg')} />,
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/heart.svg')} />,
title: intl.formatMessage(messages.favourites),
action: onClick('favourite'),
name: 'favourite',
});
items.push({
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/repeat.svg')} />,
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/repeat.svg')} />,
title: intl.formatMessage(messages.boosts),
action: onClick('reblog'),
name: 'reblog',
});
if (features.polls) items.push({
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/chart-bar.svg')} />,
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/chart-bar.svg')} />,
title: intl.formatMessage(messages.polls),
action: onClick('poll'),
name: 'poll',
});
if (features.events) items.push({
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/calendar.svg')} />,
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/calendar.svg')} />,
title: intl.formatMessage(messages.events),
action: onClick('events'),
name: 'events',
});
items.push({
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/user-plus.svg')} />,
text: <Icon className='h-4 w-4' src={require('@tabler/icons/outline/user-plus.svg')} />,
title: intl.formatMessage(messages.follows),
action: onClick('follow'),
name: 'follow',

View file

@ -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);

View file

@ -154,7 +154,7 @@ const processImage = (
name?: string;
},
) => new Promise<File>((resolve, reject) => {
const canvas = document.createElement('canvas');
const canvas = document.createElement('canvas');
if (4 < orientation && orientation < 9) {
canvas.width = height;

View file

@ -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"