UNPKG

stream-chat-react-native-core

Version:

The official React Native and Expo components for Stream Chat, a service for building chat applications

574 lines (533 loc) 17.4 kB
import { useMemo } from 'react'; import { Channel } from 'stream-chat'; import type { ActionHandler, ActionOptions } from './types'; import { useNotificationApi } from '../../components/Notifications/hooks'; import { useChatContext, useTranslationContext } from '../../contexts'; import { File, GlobalFileUploadRequest } from '../../types/types'; import { useStableCallback } from '../useStableCallback'; export type ChannelActionOptions = ActionOptions; export type ChannelActionHandler = ActionHandler; export type ChannelActions = { addMembers: (memberIds: string[], options?: ChannelActionOptions) => Promise<void>; removeMembers: (memberIds: string[], options?: ChannelActionOptions) => Promise<void>; archive: ChannelActionHandler; deleteChannel: ChannelActionHandler; leave: ChannelActionHandler; pin: ChannelActionHandler; unarchive: ChannelActionHandler; unpin: ChannelActionHandler; updateImage: ( image: File | null, options?: ChannelActionOptions, doFileUploadRequest?: GlobalFileUploadRequest, ) => Promise<void>; updateName: (name: string, options?: ChannelActionOptions) => Promise<void>; muteUser: ChannelActionHandler; unmuteUser: ChannelActionHandler; muteChannel: ChannelActionHandler; unmuteChannel: ChannelActionHandler; blockUser: ChannelActionHandler; unblockUser: ChannelActionHandler; }; export const getOtherUserInDirectChannel = (channel: Channel) => { const members = Object.values(channel.state.members); return members.length === 2 ? members.find((member) => member.user?.id !== channel.getClient().userID) : undefined; }; const getNotificationError = (error: unknown): Error | undefined => { if (error instanceof Error) return error; if (typeof error === 'string') return new Error(error); if (error && typeof error === 'object' && 'message' in error) { const message = error.message; if (typeof message === 'string') return new Error(message); } return undefined; }; export const getNotificationErrorOptions = (error: unknown) => { const originalError = getNotificationError(error); return originalError ? { originalError } : {}; }; export const useChannelActions = (channel: Channel) => { const { client } = useChatContext(); const { addNotification } = useNotificationApi(); const { t } = useTranslationContext(); const ownUserId = client.userID; const pin = useStableCallback(async (options?: ChannelActionOptions) => { try { if (!channel) { return; } await channel.pin(); addNotification({ message: t('Channel pinned'), options: { severity: 'success', type: 'api:channel:pin:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to update channel pinned status'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:pin:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const unpin = useStableCallback(async (options?: ChannelActionOptions) => { try { if (!channel) { return; } await channel.unpin(); addNotification({ message: t('Channel unpinned'), options: { severity: 'success', type: 'api:channel:unpin:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to update channel pinned status'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:pin:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const archive = useStableCallback(async (options?: ChannelActionOptions) => { try { if (!channel) { return; } await channel.archive(); addNotification({ message: t('Channel archived'), options: { severity: 'success', type: 'api:channel:archive:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to update channel archive status'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:archive:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const unarchive = useStableCallback(async (options?: ChannelActionOptions) => { try { if (!channel) { return; } await channel.unarchive(); addNotification({ message: t('Channel unarchived'), options: { severity: 'success', type: 'api:channel:unarchive:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to update channel archive status'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:archive:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const leave = useStableCallback(async (options?: ChannelActionOptions) => { if (!channel) { return; } if (ownUserId) { try { await channel.removeMembers([ownUserId]); addNotification({ message: t('Left channel'), options: { severity: 'success', type: 'api:channel:leave:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to leave channel'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:leave:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } } }); const removeMembers = useStableCallback( async (memberIds: string[], options?: ChannelActionOptions) => { if (!channel) { return; } try { await channel.removeMembers(memberIds); addNotification({ message: t('{{count}} members removed', { count: memberIds.length }), options: { severity: 'success', type: 'api:channel:remove-members:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to remove members'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:remove-members:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }, ); const addMembers = useStableCallback( async (memberIds: string[], options?: ChannelActionOptions) => { if (!channel) { return; } try { await channel.addMembers(memberIds); addNotification({ message: t('{{count}} members added', { count: memberIds.length }), options: { severity: 'success', type: 'api:channel:add-members:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to add members'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:add-members:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }, ); const deleteChannel = useStableCallback(async (options?: ChannelActionOptions) => { if (!channel) { return; } try { await channel.delete(); addNotification({ message: t('Channel deleted'), options: { severity: 'success', type: 'api:channel:delete:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to delete channel'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:delete:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const muteUser = useStableCallback(async (options?: ChannelActionOptions) => { if (!channel) { return; } const otherUser = getOtherUserInDirectChannel(channel); try { if (otherUser?.user?.id) { await client.muteUser(otherUser.user.id); addNotification({ message: t('{{ user }} has been muted', { user: otherUser.user.name || otherUser.user.id, }), options: { severity: 'success', type: 'api:user:mute:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } } catch (error) { addNotification({ message: t('Error muting a user ...'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:user:mute:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const unmuteUser = useStableCallback(async (options?: ChannelActionOptions) => { if (!channel) { return; } const otherUser = getOtherUserInDirectChannel(channel); try { if (otherUser?.user?.id) { await client.unmuteUser(otherUser.user.id); addNotification({ message: t('{{ user }} has been unmuted', { user: otherUser.user.name || otherUser.user.id, }), options: { severity: 'success', type: 'api:user:unmute:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } } catch (error) { addNotification({ message: t('Error unmuting a user ...'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:user:unmute:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const muteChannel = useStableCallback(async (options?: ChannelActionOptions) => { if (!channel) { return; } try { await channel.mute(); addNotification({ message: t('Channel muted'), options: { severity: 'success', type: 'api:channel:mute:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to update channel mute status'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:mute:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const unmuteChannel = useStableCallback(async (options?: ChannelActionOptions) => { if (!channel) { return; } try { await channel.unmute(); addNotification({ message: t('Channel unmuted'), options: { severity: 'success', type: 'api:channel:unmute:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to update channel mute status'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:mute:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const blockUser = useStableCallback(async (options?: ChannelActionOptions) => { if (!channel) { return; } const otherUser = getOtherUserInDirectChannel(channel); try { if (otherUser?.user?.id) { await client.blockUser(otherUser.user.id); addNotification({ message: t('User blocked'), options: { severity: 'success', type: 'api:user:block:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } } catch (error) { addNotification({ message: t('Failed to block user'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:user:block:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const updateName = useStableCallback(async (name: string, options?: ChannelActionOptions) => { if (!channel) { return; } try { if (name.trim() === '') { await channel.updatePartial({ unset: ['name'] }); } else { await channel.updatePartial({ set: { name } }); } addNotification({ message: t('Channel name updated'), options: { severity: 'success', type: 'api:channel:update-name:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to update channel name'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:update-name:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); const updateImage = useStableCallback( async ( image: File | null, options?: ChannelActionOptions, doFileUploadRequest?: GlobalFileUploadRequest, ) => { if (!channel) { return; } try { if (image === null) { await channel.updatePartial({ unset: ['image'] }); } else { const { file } = doFileUploadRequest ? await doFileUploadRequest(image) : await client.uploadImage(image.uri, image.name, image.type); await channel.updatePartial({ set: { image: file } }); } addNotification({ message: t('Channel image updated'), options: { severity: 'success', type: 'api:channel:update-image:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } catch (error) { addNotification({ message: t('Failed to update channel image'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:channel:update-image:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }, ); const unblockUser = useStableCallback(async (options?: ChannelActionOptions) => { if (!channel) { return; } const otherUser = getOtherUserInDirectChannel(channel); try { if (otherUser?.user?.id) { await client.unBlockUser(otherUser.user.id); addNotification({ message: t('User unblocked'), options: { severity: 'success', type: 'api:user:unblock:success' }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onSuccess?.(); } } catch (error) { addNotification({ message: t('Failed to block user'), options: { ...getNotificationErrorOptions(error), severity: 'error', type: 'api:user:block:failed', }, origin: { context: { channel }, emitter: 'ChannelActions' }, }); options?.onFailure?.(error); } }); return useMemo<ChannelActions>( () => ({ addMembers, removeMembers, pin, unpin, archive, unarchive, leave, deleteChannel, updateName, updateImage, muteUser, unmuteUser, muteChannel, unmuteChannel, blockUser, unblockUser, }), [ addMembers, removeMembers, pin, unpin, archive, unarchive, leave, deleteChannel, updateName, updateImage, muteUser, unmuteUser, muteChannel, unmuteChannel, blockUser, unblockUser, ], ); };