Settings: Support deleting profile photos (#2133)
This commit is contained in:
parent
2b37128066
commit
6138d1a5f7
@ -54,7 +54,7 @@ export {
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
updateProfile, checkUsername, updateUsername, fetchBlockedContacts, blockContact, unblockContact,
|
updateProfile, checkUsername, updateUsername, fetchBlockedContacts, blockContact, unblockContact,
|
||||||
updateProfilePhoto, uploadProfilePhoto, fetchWallpapers, uploadWallpaper,
|
updateProfilePhoto, uploadProfilePhoto, deleteProfilePhoto, fetchWallpapers, uploadWallpaper,
|
||||||
fetchAuthorizations, terminateAuthorization, terminateAllAuthorizations,
|
fetchAuthorizations, terminateAuthorization, terminateAllAuthorizations,
|
||||||
fetchWebAuthorizations, terminateWebAuthorization, terminateAllWebAuthorizations,
|
fetchWebAuthorizations, terminateWebAuthorization, terminateAllWebAuthorizations,
|
||||||
fetchNotificationExceptions, fetchNotificationSettings, updateContactSignUpNotification, updateNotificationSettings,
|
fetchNotificationExceptions, fetchNotificationSettings, updateContactSignUpNotification, updateNotificationSettings,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import type {
|
|||||||
ApiError,
|
ApiError,
|
||||||
ApiLangString,
|
ApiLangString,
|
||||||
ApiLanguage,
|
ApiLanguage,
|
||||||
ApiNotifyException,
|
ApiNotifyException, ApiPhoto,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import type { ApiPrivacyKey, InputPrivacyRules, LangCode } from '../../../types';
|
import type { ApiPrivacyKey, InputPrivacyRules, LangCode } from '../../../types';
|
||||||
|
|
||||||
@ -26,7 +26,9 @@ import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
|||||||
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||||
import { buildAppConfig } from '../apiBuilders/appConfig';
|
import { buildAppConfig } from '../apiBuilders/appConfig';
|
||||||
import { omitVirtualClassFields } from '../apiBuilders/helpers';
|
import { omitVirtualClassFields } from '../apiBuilders/helpers';
|
||||||
import { buildInputEntity, buildInputPeer, buildInputPrivacyKey } from '../gramjsBuilders';
|
import {
|
||||||
|
buildInputEntity, buildInputPeer, buildInputPrivacyKey, buildInputPhoto,
|
||||||
|
} from '../gramjsBuilders';
|
||||||
import { getClient, invokeRequest, uploadFile } from './client';
|
import { getClient, invokeRequest, uploadFile } from './client';
|
||||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||||
import { getServerTime } from '../../../util/serverTime';
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
@ -79,6 +81,16 @@ export async function uploadProfilePhoto(file: File) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteProfilePhoto(photo: ApiPhoto) {
|
||||||
|
const photoId = buildInputPhoto(photo);
|
||||||
|
if (!photoId) return false;
|
||||||
|
const isDeleted = await invokeRequest(new GramJs.photos.DeletePhotos({ id: [photoId] }), true);
|
||||||
|
if (isDeleted) {
|
||||||
|
delete localDb.photos[photo.id];
|
||||||
|
}
|
||||||
|
return isDeleted;
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchWallpapers() {
|
export async function fetchWallpapers() {
|
||||||
const result = await invokeRequest(new GramJs.account.GetWallPapers({ hash: BigInt('0') }));
|
const result = await invokeRequest(new GramJs.account.GetWallPapers({ hash: BigInt('0') }));
|
||||||
|
|
||||||
|
|||||||
@ -127,7 +127,7 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const userId = user?.id;
|
const userId = user?.id;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shouldShowVideo && !profilePhoto) {
|
if (userId && shouldShowVideo && !profilePhoto) {
|
||||||
loadFullUser({ userId });
|
loadFullUser({ userId });
|
||||||
}
|
}
|
||||||
}, [loadFullUser, profilePhoto, userId, shouldShowVideo]);
|
}, [loadFullUser, profilePhoto, userId, shouldShowVideo]);
|
||||||
|
|||||||
@ -29,7 +29,8 @@ export type OwnProps = {
|
|||||||
isSchedule: boolean;
|
isSchedule: boolean;
|
||||||
message: ApiMessage;
|
message: ApiMessage;
|
||||||
album?: IAlbum;
|
album?: IAlbum;
|
||||||
onClose: () => void;
|
onClose: NoneToVoidFunction;
|
||||||
|
onConfirm?: NoneToVoidFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -48,6 +49,7 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
|
|||||||
contactName,
|
contactName,
|
||||||
willDeleteForCurrentUserOnly,
|
willDeleteForCurrentUserOnly,
|
||||||
willDeleteForAll,
|
willDeleteForAll,
|
||||||
|
onConfirm,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
@ -56,14 +58,16 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
|
|||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const handleDeleteMessageForAll = useCallback(() => {
|
const handleDeleteMessageForAll = useCallback(() => {
|
||||||
|
onConfirm?.();
|
||||||
const messageIds = album?.messages
|
const messageIds = album?.messages
|
||||||
? album.messages.map(({ id }) => id)
|
? album.messages.map(({ id }) => id)
|
||||||
: [message.id];
|
: [message.id];
|
||||||
deleteMessages({ messageIds, shouldDeleteForAll: true });
|
deleteMessages({ messageIds, shouldDeleteForAll: true });
|
||||||
onClose();
|
onClose();
|
||||||
}, [deleteMessages, message.id, onClose, album]);
|
}, [onConfirm, album, message.id, deleteMessages, onClose]);
|
||||||
|
|
||||||
const handleDeleteMessageForSelf = useCallback(() => {
|
const handleDeleteMessageForSelf = useCallback(() => {
|
||||||
|
onConfirm?.();
|
||||||
const messageIds = album?.messages
|
const messageIds = album?.messages
|
||||||
? album.messages.map(({ id }) => id)
|
? album.messages.map(({ id }) => id)
|
||||||
: [message.id];
|
: [message.id];
|
||||||
@ -76,7 +80,7 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
onClose();
|
onClose();
|
||||||
}, [album, message.id, isSchedule, onClose, deleteScheduledMessages, deleteMessages]);
|
}, [onConfirm, album, message.id, isSchedule, onClose, deleteScheduledMessages, deleteMessages]);
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
|||||||
54
src/components/common/DeleteProfilePhotoModal.tsx
Normal file
54
src/components/common/DeleteProfilePhotoModal.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import type { FC } from '../../lib/teact/teact';
|
||||||
|
import React, { useCallback, memo } from '../../lib/teact/teact';
|
||||||
|
import { getActions } from '../../global';
|
||||||
|
|
||||||
|
import type { ApiPhoto } from '../../api/types';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
|
import Modal from '../ui/Modal';
|
||||||
|
import Button from '../ui/Button';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
isOpen: boolean;
|
||||||
|
photo: ApiPhoto;
|
||||||
|
profileId: string;
|
||||||
|
onConfirm?: NoneToVoidFunction;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeleteProfilePhotoModal: FC<OwnProps> = ({
|
||||||
|
isOpen,
|
||||||
|
photo,
|
||||||
|
profileId,
|
||||||
|
onClose,
|
||||||
|
onConfirm,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
deleteProfilePhoto,
|
||||||
|
} = getActions();
|
||||||
|
|
||||||
|
const handleDeletePhoto = useCallback(() => {
|
||||||
|
onConfirm?.();
|
||||||
|
deleteProfilePhoto({ photo, profileId });
|
||||||
|
onClose();
|
||||||
|
}, [onConfirm, deleteProfilePhoto, photo, profileId, onClose]);
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
onEnter={handleDeletePhoto}
|
||||||
|
className="delete"
|
||||||
|
title="Are you sure?"
|
||||||
|
>
|
||||||
|
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeletePhoto}>
|
||||||
|
{lang('Preview.DeletePhoto')}
|
||||||
|
</Button>
|
||||||
|
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(DeleteProfilePhotoModal);
|
||||||
@ -59,6 +59,7 @@ type StateProps = {
|
|||||||
mediaId?: number;
|
mediaId?: number;
|
||||||
senderId?: string;
|
senderId?: string;
|
||||||
isChatWithSelf?: boolean;
|
isChatWithSelf?: boolean;
|
||||||
|
canDeleteMedia?: boolean;
|
||||||
origin?: MediaViewerOrigin;
|
origin?: MediaViewerOrigin;
|
||||||
avatarOwner?: ApiChat | ApiUser;
|
avatarOwner?: ApiChat | ApiUser;
|
||||||
message?: ApiMessage;
|
message?: ApiMessage;
|
||||||
@ -77,6 +78,7 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
mediaId,
|
mediaId,
|
||||||
senderId,
|
senderId,
|
||||||
isChatWithSelf,
|
isChatWithSelf,
|
||||||
|
canDeleteMedia,
|
||||||
origin,
|
origin,
|
||||||
avatarOwner,
|
avatarOwner,
|
||||||
message,
|
message,
|
||||||
@ -145,6 +147,12 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
animationKey.current = selectedMediaIndex;
|
animationKey.current = selectedMediaIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen && !mediaIds.length) {
|
||||||
|
closeMediaViewer();
|
||||||
|
}
|
||||||
|
}, [isOpen, closeMediaViewer, mediaIds.length]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -279,12 +287,23 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}, [mediaIds]);
|
}, [mediaIds]);
|
||||||
|
|
||||||
|
const handleBeforeDelete = useCallback(() => {
|
||||||
|
if (mediaIds.length <= 1) {
|
||||||
|
handleClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let index = mediaId ? mediaIds.indexOf(mediaId) : -1;
|
||||||
|
// Before deleting, select previous media or the first one
|
||||||
|
index = index > 0 ? index - 1 : 0;
|
||||||
|
selectMedia(mediaIds[index]);
|
||||||
|
}, [handleClose, mediaId, mediaIds, selectMedia]);
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
function renderSenderInfo() {
|
function renderSenderInfo() {
|
||||||
return avatarOwner ? (
|
return avatarOwner ? (
|
||||||
<SenderInfo
|
<SenderInfo
|
||||||
key={avatarOwner.id}
|
key={mediaId}
|
||||||
chatId={avatarOwner.id}
|
chatId={avatarOwner.id}
|
||||||
isAvatar
|
isAvatar
|
||||||
/>
|
/>
|
||||||
@ -324,8 +343,12 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
mediaData={fullMediaBlobUrl || previewBlobUrl}
|
mediaData={fullMediaBlobUrl || previewBlobUrl}
|
||||||
isVideo={isVideo}
|
isVideo={isVideo}
|
||||||
message={message}
|
message={message}
|
||||||
|
canDeleteAvatar={canDeleteMedia && !!avatarPhoto}
|
||||||
|
avatarPhoto={avatarPhoto}
|
||||||
|
avatarOwnerId={avatarOwner?.id}
|
||||||
fileName={fileName}
|
fileName={fileName}
|
||||||
canReport={canReport}
|
canReport={canReport}
|
||||||
|
onBeforeDelete={handleBeforeDelete}
|
||||||
onReport={openReportModal}
|
onReport={openReportModal}
|
||||||
onCloseMediaViewer={handleClose}
|
onCloseMediaViewer={handleClose}
|
||||||
onForward={handleForward}
|
onForward={handleForward}
|
||||||
@ -377,8 +400,7 @@ export default memo(withGlobal(
|
|||||||
animationLevel,
|
animationLevel,
|
||||||
} = global.settings.byKey;
|
} = global.settings.byKey;
|
||||||
|
|
||||||
const { shouldSkipHistoryAnimations } = global;
|
const { shouldSkipHistoryAnimations, currentUserId } = global;
|
||||||
|
|
||||||
let isChatWithSelf = !!chatId && selectIsChatWithSelf(global, chatId);
|
let isChatWithSelf = !!chatId && selectIsChatWithSelf(global, chatId);
|
||||||
|
|
||||||
if (origin === MediaViewerOrigin.SearchResult) {
|
if (origin === MediaViewerOrigin.SearchResult) {
|
||||||
@ -405,14 +427,20 @@ export default memo(withGlobal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (avatarOwnerId) {
|
if (avatarOwnerId) {
|
||||||
const sender = selectUser(global, avatarOwnerId) || selectChat(global, avatarOwnerId);
|
const user = selectUser(global, avatarOwnerId);
|
||||||
|
const chat = selectChat(global, avatarOwnerId);
|
||||||
|
let canDeleteMedia = false;
|
||||||
|
if (user) canDeleteMedia = avatarOwnerId === currentUserId;
|
||||||
|
// TODO Support deleting chat photos
|
||||||
|
// if (chat) canDeleteMedia = isChatAdmin(chat);
|
||||||
isChatWithSelf = selectIsChatWithSelf(global, avatarOwnerId);
|
isChatWithSelf = selectIsChatWithSelf(global, avatarOwnerId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mediaId,
|
mediaId,
|
||||||
senderId: avatarOwnerId,
|
senderId: avatarOwnerId,
|
||||||
avatarOwner: sender,
|
avatarOwner: user || chat,
|
||||||
isChatWithSelf,
|
isChatWithSelf,
|
||||||
|
canDeleteMedia,
|
||||||
animationLevel,
|
animationLevel,
|
||||||
origin,
|
origin,
|
||||||
shouldSkipHistoryAnimations,
|
shouldSkipHistoryAnimations,
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import React, {
|
|||||||
import { getActions, withGlobal } from '../../global';
|
import { getActions, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import type { ApiMessage } from '../../api/types';
|
import type {
|
||||||
|
ApiMessage, ApiPhoto,
|
||||||
|
} from '../../api/types';
|
||||||
import type { MessageListType } from '../../global/types';
|
import type { MessageListType } from '../../global/types';
|
||||||
import type { MenuItemProps } from '../ui/MenuItem';
|
import type { MenuItemProps } from '../ui/MenuItem';
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ import DropdownMenu from '../ui/DropdownMenu';
|
|||||||
import MenuItem from '../ui/MenuItem';
|
import MenuItem from '../ui/MenuItem';
|
||||||
import ProgressSpinner from '../ui/ProgressSpinner';
|
import ProgressSpinner from '../ui/ProgressSpinner';
|
||||||
import DeleteMessageModal from '../common/DeleteMessageModal';
|
import DeleteMessageModal from '../common/DeleteMessageModal';
|
||||||
|
import DeleteProfilePhotoModal from '../common/DeleteProfilePhotoModal';
|
||||||
|
|
||||||
import './MediaViewerActions.scss';
|
import './MediaViewerActions.scss';
|
||||||
|
|
||||||
@ -45,9 +48,13 @@ type OwnProps = {
|
|||||||
isVideo: boolean;
|
isVideo: boolean;
|
||||||
zoomLevelChange: number;
|
zoomLevelChange: number;
|
||||||
message?: ApiMessage;
|
message?: ApiMessage;
|
||||||
|
canDeleteAvatar?: boolean;
|
||||||
|
avatarPhoto?: ApiPhoto;
|
||||||
|
avatarOwnerId?: string;
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
canReport?: boolean;
|
canReport?: boolean;
|
||||||
onReport: NoneToVoidFunction;
|
onReport: NoneToVoidFunction;
|
||||||
|
onBeforeDelete: NoneToVoidFunction;
|
||||||
onCloseMediaViewer: NoneToVoidFunction;
|
onCloseMediaViewer: NoneToVoidFunction;
|
||||||
onForward: NoneToVoidFunction;
|
onForward: NoneToVoidFunction;
|
||||||
setZoomLevelChange: (change: number) => void;
|
setZoomLevelChange: (change: number) => void;
|
||||||
@ -57,6 +64,8 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
|
|||||||
mediaData,
|
mediaData,
|
||||||
isVideo,
|
isVideo,
|
||||||
message,
|
message,
|
||||||
|
avatarPhoto,
|
||||||
|
avatarOwnerId,
|
||||||
fileName,
|
fileName,
|
||||||
isChatProtected,
|
isChatProtected,
|
||||||
isDownloading,
|
isDownloading,
|
||||||
@ -67,6 +76,7 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
|
|||||||
messageListType,
|
messageListType,
|
||||||
onReport,
|
onReport,
|
||||||
onCloseMediaViewer,
|
onCloseMediaViewer,
|
||||||
|
onBeforeDelete,
|
||||||
onForward,
|
onForward,
|
||||||
setZoomLevelChange,
|
setZoomLevelChange,
|
||||||
}) => {
|
}) => {
|
||||||
@ -118,6 +128,28 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
function renderDeleteModals() {
|
||||||
|
return message
|
||||||
|
? (
|
||||||
|
<DeleteMessageModal
|
||||||
|
isOpen={isDeleteModalOpen}
|
||||||
|
isSchedule={messageListType === 'scheduled'}
|
||||||
|
onClose={closeDeleteModal}
|
||||||
|
onConfirm={onBeforeDelete}
|
||||||
|
message={message}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (avatarOwnerId && avatarPhoto) ? (
|
||||||
|
<DeleteProfilePhotoModal
|
||||||
|
isOpen={isDeleteModalOpen}
|
||||||
|
onClose={closeDeleteModal}
|
||||||
|
onConfirm={onBeforeDelete}
|
||||||
|
profileId={avatarOwnerId}
|
||||||
|
photo={avatarPhoto}
|
||||||
|
/>
|
||||||
|
) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function renderDownloadButton() {
|
function renderDownloadButton() {
|
||||||
if (isProtected) {
|
if (isProtected) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -218,14 +250,7 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
|
|||||||
))}
|
))}
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
{isDownloading && <ProgressSpinner progress={downloadProgress} size="s" noCross />}
|
{isDownloading && <ProgressSpinner progress={downloadProgress} size="s" noCross />}
|
||||||
{message && canDelete && (
|
{canDelete && renderDeleteModals()}
|
||||||
<DeleteMessageModal
|
|
||||||
isOpen={isDeleteModalOpen}
|
|
||||||
isSchedule={messageListType === 'scheduled'}
|
|
||||||
onClose={closeDeleteModal}
|
|
||||||
message={message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -293,26 +318,21 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
|
|||||||
>
|
>
|
||||||
<i className="icon-close" />
|
<i className="icon-close" />
|
||||||
</Button>
|
</Button>
|
||||||
{message && canDelete && (
|
{canDelete && renderDeleteModals()}
|
||||||
<DeleteMessageModal
|
|
||||||
isOpen={isDeleteModalOpen}
|
|
||||||
isSchedule={messageListType === 'scheduled'}
|
|
||||||
onClose={closeDeleteModal}
|
|
||||||
message={message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, { message }): StateProps => {
|
(global, { message, canDeleteAvatar }): StateProps => {
|
||||||
const currentMessageList = selectCurrentMessageList(global);
|
const currentMessageList = selectCurrentMessageList(global);
|
||||||
const { threadId } = selectCurrentMessageList(global) || {};
|
const { threadId } = selectCurrentMessageList(global) || {};
|
||||||
const isDownloading = message ? selectIsDownloading(global, message) : false;
|
const isDownloading = message ? selectIsDownloading(global, message) : false;
|
||||||
const isProtected = selectIsMessageProtected(global, message);
|
const isProtected = selectIsMessageProtected(global, message);
|
||||||
const isChatProtected = message && selectIsChatProtected(global, message?.chatId);
|
const isChatProtected = message && selectIsChatProtected(global, message?.chatId);
|
||||||
const { canDelete } = (threadId && message && selectAllowedMessageActions(global, message, threadId)) || {};
|
const { canDelete: canDeleteMessage } = (threadId
|
||||||
|
&& message && selectAllowedMessageActions(global, message, threadId)) || {};
|
||||||
|
const canDelete = canDeleteMessage || canDeleteAvatar;
|
||||||
const messageListType = currentMessageList?.type;
|
const messageListType = currentMessageList?.type;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -45,11 +45,11 @@ const AvatarEditable: FC<OwnProps> = ({
|
|||||||
setSelectedFile(undefined);
|
setSelectedFile(undefined);
|
||||||
onChange(croppedImg);
|
onChange(croppedImg);
|
||||||
|
|
||||||
if (croppedBlobUrl) {
|
if (croppedBlobUrl && croppedBlobUrl !== currentAvatarBlobUrl) {
|
||||||
URL.revokeObjectURL(croppedBlobUrl);
|
URL.revokeObjectURL(croppedBlobUrl);
|
||||||
}
|
}
|
||||||
setCroppedBlobUrl(URL.createObjectURL(croppedImg));
|
setCroppedBlobUrl(URL.createObjectURL(croppedImg));
|
||||||
}, [croppedBlobUrl, onChange]);
|
}, [croppedBlobUrl, currentAvatarBlobUrl, onChange]);
|
||||||
|
|
||||||
const handleModalClose = useCallback(() => {
|
const handleModalClose = useCallback(() => {
|
||||||
setSelectedFile(undefined);
|
setSelectedFile(undefined);
|
||||||
|
|||||||
@ -39,7 +39,10 @@ addActionHandler('updateProfile', async (global, actions, payload) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (photo) {
|
if (photo) {
|
||||||
await callApi('updateProfilePhoto', photo);
|
const result = await callApi('updateProfilePhoto', photo);
|
||||||
|
if (result) {
|
||||||
|
actions.loadProfilePhotos({ profileId: currentUserId });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstName || lastName || about) {
|
if (firstName || lastName || about) {
|
||||||
@ -80,6 +83,18 @@ addActionHandler('updateProfile', async (global, actions, payload) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('deleteProfilePhoto', async (global, actions, payload) => {
|
||||||
|
const { photo, profileId } = payload;
|
||||||
|
const result = await callApi('deleteProfilePhoto', photo);
|
||||||
|
if (!result) return;
|
||||||
|
if (isUserId(profileId)) {
|
||||||
|
actions.loadFullUser({ userId: profileId });
|
||||||
|
} else {
|
||||||
|
actions.loadFullChat({ chatId: profileId });
|
||||||
|
}
|
||||||
|
actions.loadProfilePhotos({ profileId });
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('checkUsername', async (global, actions, payload) => {
|
addActionHandler('checkUsername', async (global, actions, payload) => {
|
||||||
const { username } = payload!;
|
const { username } = payload!;
|
||||||
|
|
||||||
|
|||||||
@ -736,18 +736,19 @@ export interface ActionPayloads {
|
|||||||
type?: MessageListType;
|
type?: MessageListType;
|
||||||
shouldReplaceHistory?: boolean;
|
shouldReplaceHistory?: boolean;
|
||||||
};
|
};
|
||||||
|
loadFullChat: {
|
||||||
|
chatId: string;
|
||||||
|
force?: boolean;
|
||||||
|
};
|
||||||
openChatWithDraft: {
|
openChatWithDraft: {
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
resetOpenChatWithDraft: never;
|
resetOpenChatWithDraft: never;
|
||||||
|
|
||||||
toggleJoinToSend: {
|
toggleJoinToSend: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleJoinRequest: {
|
toggleJoinRequest: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
@ -857,6 +858,9 @@ export interface ActionPayloads {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
|
loadFullUser: {
|
||||||
|
userId: string;
|
||||||
|
};
|
||||||
openAddContactDialog: {
|
openAddContactDialog: {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
};
|
};
|
||||||
@ -874,6 +878,13 @@ export interface ActionPayloads {
|
|||||||
isMuted?: boolean;
|
isMuted?: boolean;
|
||||||
shouldSharePhoneNumber?: boolean;
|
shouldSharePhoneNumber?: boolean;
|
||||||
};
|
};
|
||||||
|
loadProfilePhotos: {
|
||||||
|
profileId: string;
|
||||||
|
};
|
||||||
|
deleteProfilePhoto: {
|
||||||
|
profileId: string;
|
||||||
|
photo: ApiPhoto;
|
||||||
|
};
|
||||||
|
|
||||||
// Forwards
|
// Forwards
|
||||||
openForwardMenu: {
|
openForwardMenu: {
|
||||||
@ -1222,11 +1233,11 @@ export type NonTypedActionNames = (
|
|||||||
// chats
|
// chats
|
||||||
'preloadTopChatMessages' | 'loadAllChats' | 'openChatWithInfo' | 'openLinkedChat' |
|
'preloadTopChatMessages' | 'loadAllChats' | 'openChatWithInfo' | 'openLinkedChat' |
|
||||||
'openSupportChat' | 'focusMessageInComments' | 'openChatByPhoneNumber' |
|
'openSupportChat' | 'focusMessageInComments' | 'openChatByPhoneNumber' |
|
||||||
'loadChatSettings' | 'loadFullChat' | 'loadTopChats' | 'requestChatUpdate' | 'updateChatMutedState' |
|
'loadChatSettings' | 'loadTopChats' | 'requestChatUpdate' | 'updateChatMutedState' |
|
||||||
'joinChannel' | 'leaveChannel' | 'deleteChannel' | 'toggleChatPinned' | 'toggleChatArchived' | 'toggleChatUnread' |
|
'joinChannel' | 'leaveChannel' | 'deleteChannel' | 'toggleChatPinned' | 'toggleChatArchived' | 'toggleChatUnread' |
|
||||||
'loadChatFolders' | 'loadRecommendedChatFolders' | 'editChatFolder' | 'addChatFolder' | 'deleteChatFolder' |
|
'loadChatFolders' | 'loadRecommendedChatFolders' | 'editChatFolder' | 'addChatFolder' | 'deleteChatFolder' |
|
||||||
'updateChat' | 'toggleSignatures' | 'loadGroupsForDiscussion' | 'linkDiscussionGroup' | 'unlinkDiscussionGroup' |
|
'updateChat' | 'toggleSignatures' | 'loadGroupsForDiscussion' | 'linkDiscussionGroup' | 'unlinkDiscussionGroup' |
|
||||||
'loadProfilePhotos' | 'loadMoreMembers' | 'setActiveChatFolder' | 'openNextChat' | 'setChatEnabledReactions' |
|
'loadMoreMembers' | 'setActiveChatFolder' | 'openNextChat' | 'setChatEnabledReactions' |
|
||||||
'addChatMembers' | 'deleteChatMember' | 'openPreviousChat' | 'editChatFolders' | 'toggleIsProtected' |
|
'addChatMembers' | 'deleteChatMember' | 'openPreviousChat' | 'editChatFolders' | 'toggleIsProtected' |
|
||||||
// messages
|
// messages
|
||||||
'loadViewportMessages' | 'selectMessage' | 'sendMessage' | 'cancelSendingMessage' | 'pinMessage' | 'deleteMessages' |
|
'loadViewportMessages' | 'selectMessage' | 'sendMessage' | 'cancelSendingMessage' | 'pinMessage' | 'deleteMessages' |
|
||||||
@ -1261,7 +1272,7 @@ export type NonTypedActionNames = (
|
|||||||
'togglePreHistoryHidden' | 'updateChatDefaultBannedRights' | 'updateChatMemberBannedRights' | 'updateChatAdmin' |
|
'togglePreHistoryHidden' | 'updateChatDefaultBannedRights' | 'updateChatMemberBannedRights' | 'updateChatAdmin' |
|
||||||
'acceptInviteConfirmation' |
|
'acceptInviteConfirmation' |
|
||||||
// users
|
// users
|
||||||
'loadFullUser' | 'loadNearestCountry' | 'loadTopUsers' | 'loadContactList' |
|
'loadNearestCountry' | 'loadTopUsers' | 'loadContactList' |
|
||||||
'loadCurrentUser' | 'updateProfile' | 'checkUsername' |
|
'loadCurrentUser' | 'updateProfile' | 'checkUsername' |
|
||||||
'deleteContact' | 'loadUser' | 'setUserSearchQuery' | 'loadCommonChats' | 'reportSpam' |
|
'deleteContact' | 'loadUser' | 'setUserSearchQuery' | 'loadCommonChats' | 'reportSpam' |
|
||||||
// chat creation
|
// chat creation
|
||||||
|
|||||||
@ -1233,6 +1233,7 @@ updates.getState#edd4882a = updates.State;
|
|||||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||||
photos.uploadProfilePhoto#89f30f69 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
|
photos.uploadProfilePhoto#89f30f69 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
|
||||||
|
photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
|
||||||
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
|
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
|
||||||
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
|
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
|
||||||
upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File;
|
upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File;
|
||||||
|
|||||||
@ -164,6 +164,7 @@
|
|||||||
"updates.getChannelDifference",
|
"updates.getChannelDifference",
|
||||||
"photos.uploadProfilePhoto",
|
"photos.uploadProfilePhoto",
|
||||||
"photos.getUserPhotos",
|
"photos.getUserPhotos",
|
||||||
|
"photos.deletePhotos",
|
||||||
"upload.saveFilePart",
|
"upload.saveFilePart",
|
||||||
"upload.getFile",
|
"upload.getFile",
|
||||||
"upload.saveBigFilePart",
|
"upload.saveBigFilePart",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user