Fix media upload
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 };
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -50,7 +50,7 @@ export interface IEmojiPickerDropdown {
|
||||
}
|
||||
|
||||
const perLine = 8;
|
||||
const lines = 2;
|
||||
const lines = 2;
|
||||
|
||||
const DEFAULTS = [
|
||||
'+1',
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user