191 lines
5.6 KiB
TypeScript
191 lines
5.6 KiB
TypeScript
import type {
|
|
ApiChat, ApiChatFullInfo, ApiPeerPhotos, ApiPhoto, ApiUser, ApiUserFullInfo,
|
|
} from '../../api/types';
|
|
import type { GlobalState } from '../types';
|
|
|
|
import { omit, uniqueByField } from '../../util/iteratees';
|
|
import { isChatChannel, isUserId } from '../helpers';
|
|
import {
|
|
selectChatFullInfo,
|
|
selectPeer,
|
|
selectPeerPhotos,
|
|
selectUserFullInfo,
|
|
} from '../selectors';
|
|
import { updateChat, updateChatFullInfo } from './chats';
|
|
import { updateUser, updateUserFullInfo } from './users';
|
|
|
|
// `type` has different types in ApiChat and ApiUser
|
|
type ApiPeerSharedFields = Omit<CommonProperties<ApiChat, ApiUser>, 'type'>;
|
|
type ApiPeerFullInfoSharedFields = CommonProperties<ApiChatFullInfo, ApiUserFullInfo>;
|
|
|
|
export function updatePeer<T extends GlobalState>(
|
|
global: T, peerId: string, peerUpdate: Partial<ApiPeerSharedFields>,
|
|
) {
|
|
if (isUserId(peerId)) {
|
|
return updateUser(global, peerId, peerUpdate);
|
|
}
|
|
|
|
return updateChat(global, peerId, peerUpdate);
|
|
}
|
|
|
|
export function updatePeerFullInfo<T extends GlobalState>(
|
|
global: T, peerId: string, peerFullInfoUpdate: Partial<ApiPeerFullInfoSharedFields>,
|
|
) {
|
|
if (isUserId(peerId)) {
|
|
return updateUserFullInfo(global, peerId, peerFullInfoUpdate);
|
|
}
|
|
|
|
return updateChatFullInfo(global, peerId, peerFullInfoUpdate);
|
|
}
|
|
|
|
export function updatePeerPhotosIsLoading<T extends GlobalState>(
|
|
global: T,
|
|
peerId: string,
|
|
isLoading: boolean,
|
|
) {
|
|
const profilePhotos = selectPeerPhotos(global, peerId);
|
|
if (!profilePhotos) {
|
|
return global;
|
|
}
|
|
|
|
return replacePeerPhotos(global, peerId, {
|
|
...profilePhotos,
|
|
isLoading,
|
|
});
|
|
}
|
|
|
|
export function replacePeerPhotos<T extends GlobalState>(
|
|
global: T,
|
|
peerId: string,
|
|
value?: ApiPeerPhotos,
|
|
) {
|
|
if (!value) {
|
|
return {
|
|
...global,
|
|
profilePhotosById: omit(global.profilePhotosById, [peerId]),
|
|
};
|
|
}
|
|
|
|
return {
|
|
...global,
|
|
profilePhotosById: {
|
|
...global.profilePhotosById,
|
|
[peerId]: value,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function updatePeerPhotos<T extends GlobalState>(
|
|
global: T,
|
|
peerId: string,
|
|
update: {
|
|
newPhotos: ApiPhoto[];
|
|
count: number;
|
|
nextOffset?: number;
|
|
fullInfo: ApiChatFullInfo | ApiUserFullInfo;
|
|
shouldInvalidateCache?: boolean;
|
|
},
|
|
) {
|
|
const profilePhotos = selectPeerPhotos(global, peerId);
|
|
|
|
const {
|
|
newPhotos, count, nextOffset, fullInfo, shouldInvalidateCache,
|
|
} = update;
|
|
const currentPhotos = profilePhotos;
|
|
const profilePhoto = fullInfo.profilePhoto;
|
|
const fallbackPhoto = 'fallbackPhoto' in fullInfo ? fullInfo.fallbackPhoto : undefined;
|
|
const personalPhoto = 'personalPhoto' in fullInfo ? fullInfo.personalPhoto : undefined;
|
|
|
|
if (!currentPhotos || shouldInvalidateCache) {
|
|
// In some channels, last service message with photo change is deleted, so we need to patch it in
|
|
if (profilePhoto && profilePhoto.id !== newPhotos[0]?.id) {
|
|
newPhotos.unshift(profilePhoto);
|
|
}
|
|
|
|
if (personalPhoto && personalPhoto.id !== newPhotos[0]?.id) {
|
|
newPhotos.unshift(personalPhoto);
|
|
}
|
|
|
|
if (fallbackPhoto) {
|
|
newPhotos.push(fallbackPhoto);
|
|
}
|
|
|
|
return replacePeerPhotos(global, peerId, {
|
|
fallbackPhoto,
|
|
personalPhoto,
|
|
photos: newPhotos,
|
|
count,
|
|
nextOffset,
|
|
isLoading: false,
|
|
});
|
|
}
|
|
|
|
const hasFallbackPhoto = currentPhotos.photos[currentPhotos.photos.length - 1].id === fallbackPhoto?.id;
|
|
const currentPhotoArray = hasFallbackPhoto ? currentPhotos.photos.slice(0, -1) : currentPhotos.photos;
|
|
|
|
const photos = uniqueByField([...currentPhotoArray, ...newPhotos, fallbackPhoto].filter(Boolean), 'id');
|
|
return replacePeerPhotos(global, peerId, {
|
|
fallbackPhoto,
|
|
personalPhoto,
|
|
photos,
|
|
count,
|
|
nextOffset,
|
|
isLoading: false,
|
|
});
|
|
}
|
|
|
|
export function deletePeerPhoto<T extends GlobalState>(
|
|
global: T,
|
|
peerId: string,
|
|
photoId: string,
|
|
isFromActionMessage?: boolean,
|
|
) {
|
|
const peer = selectPeer(global, peerId);
|
|
const profilePhotos = selectPeerPhotos(global, peerId);
|
|
if (!peer || !profilePhotos) {
|
|
return global;
|
|
}
|
|
const isChannel = 'title' in peer && isChatChannel(peer);
|
|
|
|
const userFullInfo = selectUserFullInfo(global, peerId);
|
|
const chatFullInfo = selectChatFullInfo(global, peerId);
|
|
|
|
const isAvatar = peer.avatarPhotoId === photoId && (!isChannel || isFromActionMessage);
|
|
const nextAvatarPhoto = isAvatar ? profilePhotos.photos[1] : undefined;
|
|
|
|
if (userFullInfo) {
|
|
const newFallbackPhoto = userFullInfo.fallbackPhoto?.id === photoId ? undefined : userFullInfo.fallbackPhoto;
|
|
const newPersonalPhoto = userFullInfo.personalPhoto?.id === photoId ? undefined : userFullInfo.personalPhoto;
|
|
const newProfilePhoto = userFullInfo.profilePhoto?.id === photoId ? nextAvatarPhoto : userFullInfo.profilePhoto;
|
|
global = updateUserFullInfo(global, peerId, {
|
|
fallbackPhoto: newFallbackPhoto,
|
|
personalPhoto: newPersonalPhoto,
|
|
profilePhoto: newProfilePhoto,
|
|
});
|
|
}
|
|
|
|
if (chatFullInfo) {
|
|
const newProfilePhoto = chatFullInfo.profilePhoto?.id === photoId ? nextAvatarPhoto : chatFullInfo.profilePhoto;
|
|
global = updateChatFullInfo(global, peerId, {
|
|
profilePhoto: newProfilePhoto,
|
|
});
|
|
}
|
|
|
|
const avatarPhotoId = isAvatar ? nextAvatarPhoto?.id : peer.avatarPhotoId;
|
|
const shouldKeepInPhotos = isAvatar && 'title' in peer && isChatChannel(peer);
|
|
const photos = shouldKeepInPhotos
|
|
? profilePhotos.photos.filter((photo) => photo.id !== photoId) : profilePhotos.photos.slice();
|
|
|
|
global = updatePeer(global, peerId, {
|
|
avatarPhotoId,
|
|
});
|
|
|
|
global = replacePeerPhotos(global, peerId, avatarPhotoId ? {
|
|
...profilePhotos,
|
|
photos,
|
|
count: profilePhotos.count - 1,
|
|
} : undefined);
|
|
|
|
return global;
|
|
}
|