diff --git a/app/definitions/rest/v1/users.ts b/app/definitions/rest/v1/users.ts index 751cfbf83fb..45aba54cc7e 100644 --- a/app/definitions/rest/v1/users.ts +++ b/app/definitions/rest/v1/users.ts @@ -69,4 +69,7 @@ export type UsersEndpoints = { 'users.deleteOwnAccount': { POST: (params: { password: string; confirmRelinquish: boolean }) => { success: boolean }; }; + 'users.setAvatar': { + POST: (params: { avatarUrl: string }) => { success: boolean }; + }; }; diff --git a/app/lib/methods/helpers/fileUpload/Upload.android.ts b/app/lib/methods/helpers/fileUpload/Upload.android.ts index d83fdb3ac76..0ca1f635699 100644 --- a/app/lib/methods/helpers/fileUpload/Upload.android.ts +++ b/app/lib/methods/helpers/fileUpload/Upload.android.ts @@ -10,6 +10,7 @@ export class Upload { type: string | undefined; name: string | undefined; } | null; + private fieldName: string; private headers: { [key: string]: string }; private formData: any; private uploadTask: FileSystem.UploadTask | null; @@ -19,6 +20,7 @@ export class Upload { constructor() { this.uploadUrl = ''; this.file = null; + this.fieldName = 'file'; this.headers = {}; this.formData = {}; this.uploadTask = null; @@ -37,6 +39,7 @@ export class Upload { public appendFile(item: IFormData): void { if (item.uri) { + this.fieldName = item.name; this.file = { uri: item.uri, type: item.type, name: item.filename }; } else { this.formData[item.name] = item.data; @@ -56,7 +59,7 @@ export class Upload { headers: this.headers, httpMethod: 'POST', uploadType: FileSystem.FileSystemUploadType.MULTIPART, - fieldName: 'file', + fieldName: this.fieldName, mimeType: this.file.type, parameters: this.formData }, diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts index fae53447715..c91a76b8470 100644 --- a/app/lib/services/restApi.ts +++ b/app/lib/services/restApi.ts @@ -1,3 +1,5 @@ +import { settings as RocketChatSettings } from '@rocket.chat/sdk'; + import { type IAvatarSuggestion, type IMessage, @@ -25,7 +27,7 @@ import { compareServerVersion, getBundleId, isIOS } from '../methods/helpers'; import { getDeviceToken } from '../notifications'; import { store as reduxStore } from '../store/auxStore'; import sdk from './sdk'; -import fetch from '../methods/helpers/fetch'; +import FileUpload from '../methods/helpers/fileUpload'; export const createChannel = ({ name, @@ -700,14 +702,49 @@ export const resetAvatar = (userId: string) => export const setAvatarFromService = ({ data, contentType = '', - service = null + service = null, + url, + server, + user }: { data: any; contentType?: string; service?: string | null; -}): Promise => + url?: string; + server: string; + user: { id?: string; token?: string }; +}): Promise => { // RC 0.51.0 - sdk.methodCallWrapper('setAvatarFromService', data, contentType, service); + if (service === 'url') { + return sdk.post('users.setAvatar', { avatarUrl: data }); + } + if (service === 'upload' && url && server && user?.id && user?.token) { + const rawExtension = url.split('.').pop() || 'png'; + const extension = rawExtension.split('?')[0].toLowerCase(); + + const formData = [ + { + name: 'image', + type: `image/${extension}`, + filename: `image.${extension}`, + uri: url + } + ]; + + const headers = { + ...RocketChatSettings.customHeaders, + 'Content-Type': 'multipart/form-data', + 'X-Auth-Token': user.token, + 'X-User-Id': user.id + }; + + const upload = new FileUpload(`${server}/api/v1/users.setAvatar`, headers, formData); + + return upload.send(); + } + + return sdk.methodCallWrapper('setAvatarFromService', data, contentType, service); +}; export const getUsernameSuggestion = () => // RC 0.65.0 diff --git a/app/views/ChangeAvatarView/index.tsx b/app/views/ChangeAvatarView/index.tsx index de335584c4c..ffff3c7b114 100644 --- a/app/views/ChangeAvatarView/index.tsx +++ b/app/views/ChangeAvatarView/index.tsx @@ -79,11 +79,12 @@ const ChangeAvatarView = () => { const [state, dispatch] = useReducer(reducer, initialState); const [saving, setSaving] = useState(false); const { colors } = useTheme(); - const { userId, username, server } = useAppSelector( + const { userId, username, server, user } = useAppSelector( state => ({ userId: getUserSelector(state).id, username: getUserSelector(state).username, - server: state.server.server + server: state.server.server, + user: state.login.user }), shallowEqual ); @@ -162,7 +163,7 @@ const ChangeAvatarView = () => { await changeRoomsAvatar(room.rid, state?.data); } else if (state?.url) { // Change User's Avatar - await changeUserAvatar(state); + await changeUserAvatar(state, server, { id: user.id, token: user.token }); } else if (state.resetUserAvatar) { // Change User's Avatar await resetUserAvatar(userId); @@ -185,7 +186,7 @@ const ChangeAvatarView = () => { cropperAvoidEmptySpaceAroundImage: false, cropperChooseText: I18n.t('Choose'), cropperCancelText: I18n.t('Cancel'), - includeBase64: true + includeBase64: false }; try { const response: Image = @@ -194,7 +195,7 @@ const ChangeAvatarView = () => { : await ImagePicker.openPicker(options); dispatchAvatar({ type: AvatarStateActions.CHANGE_AVATAR, - payload: { url: response.path, data: `data:image/jpeg;base64,${response.data}`, service: 'upload' } + payload: { url: response.path, service: 'upload' } }); } catch (error: any) { if (error?.code !== 'E_PICKER_CANCELLED') { diff --git a/app/views/ChangeAvatarView/submitServices.ts b/app/views/ChangeAvatarView/submitServices.ts index c125e4e7b2d..7402e883834 100644 --- a/app/views/ChangeAvatarView/submitServices.ts +++ b/app/views/ChangeAvatarView/submitServices.ts @@ -12,9 +12,9 @@ export const changeRoomsAvatar = async (rid: string, roomAvatar: string | null) } }; -export const changeUserAvatar = async (avatarUpload: IAvatar) => { +export const changeUserAvatar = async (avatarUpload: IAvatar, server: string, user: { id?: string; token?: string }) => { try { - await setAvatarFromService(avatarUpload); + await setAvatarFromService({ ...avatarUpload, server, user }); } catch (e) { return handleError(e, 'changing_avatar'); }