UNPKG

stream-chat-react-native-core

Version:

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

112 lines (94 loc) 3.93 kB
import { Channel, StateStore } from 'stream-chat'; import { useStateStore } from '../hooks/useStateStore'; import type { File } from '../types/types'; export type EditChannelImagePendingAction = 'camera' | 'library' | 'reset'; export type EditChannelDetailsState = { /** Current value of the name input. */ currentName: string; /** * Channel image URL snapshotted at construction; not updated by WS events. */ initialImage: string | undefined; /** * Channel name snapshotted at construction; not updated by WS events. */ initialName: string; /** Pending action from the {@link ChannelEditImageSheet}, or `null` when idle. */ pendingAction: EditChannelImagePendingAction | null; /** `undefined` = untouched, `File` = newly picked, `null` = reset. */ updatedImage: File | null | undefined; }; /** * Holds the editable state for the channel details form (name + image) plus the * pending image-picker action. The channel's name and image are snapshotted * once at construction and are intentionally **not** updated by WebSocket * events, so an inbound `channel.updated` does not clobber the user's * in-progress edits. Leaf components can subscribe to narrow slices via * {@link useStateStore}. */ export class EditChannelDetailsStore { public state: StateStore<EditChannelDetailsState>; constructor(channel: Channel) { const initialName = channel.data?.name ?? ''; const initialImage = channel.data?.image; this.state = new StateStore<EditChannelDetailsState>({ currentName: initialName, initialImage, initialName, pendingAction: null, updatedImage: undefined, }); } /** Updates the current value of the name input. */ setCurrentName(currentName: string) { this.state.partialNext({ currentName }); } /** * Updates the picked image. `undefined` = untouched, `File` = newly picked, * `null` = reset. */ setUpdatedImage(updatedImage: File | null | undefined) { this.state.partialNext({ updatedImage }); } /** Sets the pending image-picker action, or `null` to clear it. */ setPendingAction(pendingAction: EditChannelImagePendingAction | null) { this.state.partialNext({ pendingAction }); } } /** Whether the name input differs from the channel's initial name. */ export const isNameEdited = (state: EditChannelDetailsState) => state.currentName !== state.initialName; /** * Whether the image has unsaved changes. The image is edited once touched * (`updatedImage !== undefined`), except when both the initial and updated * image are falsy (no image before, none now). */ export const isImageEdited = (state: EditChannelDetailsState) => !(state.updatedImage === undefined || (!state.updatedImage && !state.initialImage)); const selectIsNameEdited = (state: EditChannelDetailsState) => ({ isNameEdited: isNameEdited(state), }); const selectIsImageEdited = (state: EditChannelDetailsState) => ({ isImageEdited: isImageEdited(state), }); const selectAreChannelDetailsEdited = (state: EditChannelDetailsState) => ({ areChannelDetailsEdited: isNameEdited(state) || isImageEdited(state), }); /** * Subscribes to an {@link EditChannelDetailsStore} and returns whether the name * input has unsaved changes. */ export const useIsNameEdited = (store: EditChannelDetailsStore) => useStateStore(store.state, selectIsNameEdited).isNameEdited; /** * Subscribes to an {@link EditChannelDetailsStore} and returns whether the image * has unsaved changes. */ export const useIsImageEdited = (store: EditChannelDetailsStore) => useStateStore(store.state, selectIsImageEdited).isImageEdited; /** * Subscribes to an {@link EditChannelDetailsStore} and returns whether the form * has any unsaved changes (name or image). */ export const useAreChannelDetailsEdited = (store: EditChannelDetailsStore) => useStateStore(store.state, selectAreChannelDetailsEdited).areChannelDetailsEdited;