import { useMutation, useQueryClient } from '@tanstack/react-query' import { Alert } from 'react-native' import type { ConversationResource } from '../types' import type { ApiResource } from '../types' import { transformGetToPost } from '../utils/client/request_helpers' import type { ImagePickerAsset } from '../utils/native_adapters/image_picker' import { ApiClient, useApiClient } from './use_api_client' import { getConversationRequestArgs } from './use_conversation' import { getRequestQueryKey } from './use_suspense_api' import { useUploadClient } from './use_upload_client' export type AvatarType = 'icon' | 'emoji' | 'image' export type AvatarUpdatePayload = | { kind: Extract; key: string; color: string } | { kind: Extract; imageAsset: ImagePickerAsset; color: string } | { kind: 'clear' } export const useConversationAvatarUpdate = ({ conversationId }: { conversationId: number }) => { const apiClient = useApiClient() const uploadClient = useUploadClient() const queryClient = useQueryClient() const requestArgs = getConversationRequestArgs({ conversation_id: conversationId }) const queryKey = getRequestQueryKey(requestArgs) return useMutation({ mutationKey: ['updateConversationAvatar', conversationId], mutationFn: async (payload: AvatarUpdatePayload) => { const postArgs = transformGetToPost(requestArgs).data const attributes = await buildAttributes(payload, uploadClient) return apiClient.chat.patch>({ url: `/me/conversations/${conversationId}/`, data: { data: { type: '', attributes }, ...postArgs }, }) }, onMutate: async (payload: AvatarUpdatePayload) => { await queryClient.cancelQueries({ queryKey }) const previous = queryClient.getQueryData>(queryKey) queryClient.setQueryData>(queryKey, prev => { if (!prev?.data) return prev return { ...prev, data: buildOptimisticData(prev.data, payload) } }) return { previous } }, onSuccess: (response, _payload, _context) => { queryClient.setQueryData>(queryKey, () => response) queryClient.invalidateQueries({ queryKey: ['/me/conversations'] }) }, onError: (_error, _payload, context) => { if (context?.previous) { queryClient.setQueryData(queryKey, context.previous) } Alert.alert('Error', 'Failed to update conversation avatar. Please try again.') }, }) } export async function patchConversationAvatar( apiClient: ApiClient, uploadClient: ReturnType, conversationId: number, payload: Exclude ) { const requestArgs = getConversationRequestArgs({ conversation_id: conversationId }) const postArgs = transformGetToPost(requestArgs).data try { const attributes = await buildAttributes(payload, uploadClient) await apiClient.chat.patch({ url: `/me/conversations/${conversationId}/`, data: { data: { type: '', attributes }, ...postArgs }, }) } catch { Alert.alert( 'Avatar not saved', 'The conversation was created, but the avatar could not be saved. You can update it from the conversation details.' ) } } function buildOptimisticData( data: ConversationResource, payload: AvatarUpdatePayload ): ConversationResource { switch (payload.kind) { case 'icon': return { ...data, customAvatarType: 'icon', customAvatarKey: payload.key, customAvatarColor: payload.color, customAvatarImageUrl: null, } case 'emoji': return { ...data, customAvatarType: 'emoji', customAvatarKey: payload.key, customAvatarColor: payload.color, customAvatarImageUrl: null, } case 'image': return { ...data, customAvatarType: 'image', customAvatarKey: null, customAvatarColor: payload.color, customAvatarImageUrl: payload.imageAsset.uri, } case 'clear': return { ...data, customAvatarType: null, customAvatarKey: null, customAvatarColor: null, customAvatarImageUrl: null, } } } async function buildAttributes( payload: AvatarUpdatePayload, uploadClient: ReturnType ) { switch (payload.kind) { case 'icon': return { custom_avatar_type: 'icon', custom_avatar_key: payload.key, custom_avatar_color: payload.color, } case 'emoji': return { custom_avatar_type: 'emoji', custom_avatar_key: payload.key, custom_avatar_color: payload.color, } case 'image': { const uploaded = await uploadClient.uploadFile({ uri: payload.imageAsset.uri, name: payload.imageAsset.fileName || 'avatar.jpg', type: payload.imageAsset.mimeType || 'image/jpeg', }) return { custom_avatar_type: 'image', custom_avatar_color: payload.color, avatar_uploaded_file_id: uploaded.id, } } case 'clear': return { custom_avatar_type: null, custom_avatar_key: null, custom_avatar_color: null, } } }