From 455a0d586711d93af9180b26688e3e22b1b1c892 Mon Sep 17 00:00:00 2001 From: Rohit <40559587+Rohit3523@users.noreply.github.com> Date: Sun, 22 Feb 2026 19:37:35 +0530 Subject: [PATCH 1/5] new API for avatar upload --- app/definitions/rest/v1/users.ts | 3 ++ .../helpers/fileUpload/Upload.android.ts | 5 +- app/lib/services/restApi.ts | 47 ++++++++++++++++--- app/views/ChangeAvatarView/index.tsx | 7 +-- app/views/ChangeAvatarView/submitServices.ts | 4 +- 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/app/definitions/rest/v1/users.ts b/app/definitions/rest/v1/users.ts index 751cfbf83fb..656e3d4440f 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..a33ddd668af 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, @@ -249,9 +251,9 @@ export const convertChannelToTeam = ({ rid, name, type }: { rid: string; name: s params = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '4.8.0') ? { channelId: rid } : { - channelId: rid, - channelName: name - }; + channelId: rid, + channelName: name + }; } else { params = { roomId: rid, @@ -700,14 +702,45 @@ 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 extension = url.split('.').pop() || 'png'; + 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..f5ab3c0df0c 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); 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'); } From 0babd11c1a0704563d4ae44fe87483ee4a3f90cc Mon Sep 17 00:00:00 2001 From: Rohit3523 Date: Sun, 22 Feb 2026 14:09:25 +0000 Subject: [PATCH 2/5] chore: format code and fix lint issues --- app/definitions/rest/v1/users.ts | 2 +- app/lib/services/restApi.ts | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/definitions/rest/v1/users.ts b/app/definitions/rest/v1/users.ts index 656e3d4440f..45aba54cc7e 100644 --- a/app/definitions/rest/v1/users.ts +++ b/app/definitions/rest/v1/users.ts @@ -70,6 +70,6 @@ export type UsersEndpoints = { POST: (params: { password: string; confirmRelinquish: boolean }) => { success: boolean }; }; 'users.setAvatar': { - POST: (params: { avatarUrl: string }) => { success: boolean; }; + POST: (params: { avatarUrl: string }) => { success: boolean }; }; }; diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts index a33ddd668af..c4a6ff12820 100644 --- a/app/lib/services/restApi.ts +++ b/app/lib/services/restApi.ts @@ -251,9 +251,9 @@ export const convertChannelToTeam = ({ rid, name, type }: { rid: string; name: s params = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '4.8.0') ? { channelId: rid } : { - channelId: rid, - channelName: name - }; + channelId: rid, + channelName: name + }; } else { params = { roomId: rid, @@ -712,20 +712,22 @@ export const setAvatarFromService = ({ service?: string | null; url?: string; server: string; - user: { id?: string, token?: string } + user: { id?: string; token?: string }; }): Promise => { // RC 0.51.0 - if (service === "url"){ - return sdk.post('users.setAvatar', { avatarUrl: data }) + if (service === 'url') { + return sdk.post('users.setAvatar', { avatarUrl: data }); } - if (service === "upload" && url && server && user?.id && user?.token) { + if (service === 'upload' && url && server && user?.id && user?.token) { const extension = url.split('.').pop() || 'png'; - const formData = [{ - name: 'image', - type: `image/${extension}`, - filename: `image.${extension}`, - uri: url - }]; + const formData = [ + { + name: 'image', + type: `image/${extension}`, + filename: `image.${extension}`, + uri: url + } + ]; const headers = { ...RocketChatSettings.customHeaders, @@ -736,7 +738,7 @@ export const setAvatarFromService = ({ const upload = new FileUpload(`${server}/api/v1/users.setAvatar`, headers, formData); - return upload.send() + return upload.send(); } return sdk.methodCallWrapper('setAvatarFromService', data, contentType, service); From dc773494da6f74005d88be4fd41406031b41505c Mon Sep 17 00:00:00 2001 From: Rohit <40559587+Rohit3523@users.noreply.github.com> Date: Sun, 22 Feb 2026 19:55:05 +0530 Subject: [PATCH 3/5] Remove base64 from avatar image picker --- app/views/ChangeAvatarView/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/ChangeAvatarView/index.tsx b/app/views/ChangeAvatarView/index.tsx index f5ab3c0df0c..ffff3c7b114 100644 --- a/app/views/ChangeAvatarView/index.tsx +++ b/app/views/ChangeAvatarView/index.tsx @@ -186,7 +186,7 @@ const ChangeAvatarView = () => { cropperAvoidEmptySpaceAroundImage: false, cropperChooseText: I18n.t('Choose'), cropperCancelText: I18n.t('Cancel'), - includeBase64: true + includeBase64: false }; try { const response: Image = @@ -195,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') { From 5cf4e8380b9418d4b39327698b4ea077c33578ee Mon Sep 17 00:00:00 2001 From: Rohit <40559587+Rohit3523@users.noreply.github.com> Date: Sun, 22 Feb 2026 19:55:10 +0530 Subject: [PATCH 4/5] improve --- app/lib/services/restApi.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts index c4a6ff12820..bd94748d826 100644 --- a/app/lib/services/restApi.ts +++ b/app/lib/services/restApi.ts @@ -719,7 +719,9 @@ export const setAvatarFromService = ({ return sdk.post('users.setAvatar', { avatarUrl: data }); } if (service === 'upload' && url && server && user?.id && user?.token) { - const extension = url.split('.').pop() || 'png'; + const rawExtension = url.split('.').pop() || 'png'; + const extension = rawExtension.split('?')[0].toLowerCase(); + const formData = [ { name: 'image', @@ -732,8 +734,8 @@ export const setAvatarFromService = ({ const headers = { ...RocketChatSettings.customHeaders, 'Content-Type': 'multipart/form-data', - 'X-Auth-Token': user?.token, - 'X-User-Id': user?.id + 'X-Auth-Token': user.token, + 'X-User-Id': user.id }; const upload = new FileUpload(`${server}/api/v1/users.setAvatar`, headers, formData); From 7d7655ead16c4a52f047298b100afeb704d7a4b3 Mon Sep 17 00:00:00 2001 From: Rohit3523 Date: Sun, 22 Feb 2026 14:26:52 +0000 Subject: [PATCH 5/5] chore: format code and fix lint issues --- app/lib/services/restApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts index bd94748d826..c91a76b8470 100644 --- a/app/lib/services/restApi.ts +++ b/app/lib/services/restApi.ts @@ -721,7 +721,7 @@ export const setAvatarFromService = ({ 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',