From ad36326c8348c92a3527725a75e69cbe0e31d59e Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 20 Aug 2021 23:46:57 +0300 Subject: [PATCH] Settings: New design for self profile (#1373) --- .../{right => common}/ChatExtra.tsx | 22 ++++--- .../{right => common}/ProfileInfo.scss | 21 +++++++ .../{right => common}/ProfileInfo.tsx | 28 +++++---- .../{right => common}/ProfilePhoto.scss | 0 .../{right => common}/ProfilePhoto.tsx | 11 +++- .../hooks/usePhotosPreload.ts | 0 src/components/left/LeftColumn.scss | 10 +++ src/components/left/settings/Settings.scss | 40 ++++-------- src/components/left/settings/Settings.tsx | 1 + .../left/settings/SettingsHeader.tsx | 12 ++++ src/components/left/settings/SettingsMain.tsx | 62 +++++++++++-------- .../mediaViewer/helpers/ghostAnimation.ts | 21 +++++-- src/components/right/Profile.tsx | 11 ++-- src/types/index.ts | 1 + 14 files changed, 151 insertions(+), 89 deletions(-) rename src/components/{right => common}/ChatExtra.tsx (91%) rename src/components/{right => common}/ProfileInfo.scss (91%) rename src/components/{right => common}/ProfileInfo.tsx (87%) rename src/components/{right => common}/ProfilePhoto.scss (100%) rename src/components/{right => common}/ProfilePhoto.tsx (93%) rename src/components/{right => common}/hooks/usePhotosPreload.ts (100%) diff --git a/src/components/right/ChatExtra.tsx b/src/components/common/ChatExtra.tsx similarity index 91% rename from src/components/right/ChatExtra.tsx rename to src/components/common/ChatExtra.tsx index 5e0dfc52..a64e5515 100644 --- a/src/components/right/ChatExtra.tsx +++ b/src/components/common/ChatExtra.tsx @@ -12,7 +12,7 @@ import { import { getChatDescription, getChatLink, getHasAdminRight, isChatChannel, isChatPrivate, isUserRightBanned, selectIsChatMuted, } from '../../modules/helpers'; -import renderText from '../common/helpers/renderText'; +import renderText from './helpers/renderText'; import { pick } from '../../util/iteratees'; import { copyTextToClipboard } from '../../util/clipboard'; import { formatPhoneNumberWithCode } from '../../util/phoneNumber'; @@ -118,15 +118,17 @@ const ChatExtra: FC = ({ {lang('SetUrlPlaceholder')} )} - - {lang('Notifications')} - - + {!forceShowSelf && ( + + {lang('Notifications')} + + + )} ); }; diff --git a/src/components/right/ProfileInfo.scss b/src/components/common/ProfileInfo.scss similarity index 91% rename from src/components/right/ProfileInfo.scss rename to src/components/common/ProfileInfo.scss index b352a37c..461b8519 100644 --- a/src/components/right/ProfileInfo.scss +++ b/src/components/common/ProfileInfo.scss @@ -159,4 +159,25 @@ transform: scaleX(-1); } } + + &.self { + margin: 0 -.5rem .75rem; + overflow: hidden; + + &.ghost { + margin: 0; + } + + .prev-avatar-media { + z-index: 0; + } + + .info { + padding-bottom: .75rem; + } + + .status { + line-height: 1rem; + } + } } diff --git a/src/components/right/ProfileInfo.tsx b/src/components/common/ProfileInfo.tsx similarity index 87% rename from src/components/right/ProfileInfo.tsx rename to src/components/common/ProfileInfo.tsx index 391ad267..6127b5f3 100644 --- a/src/components/right/ProfileInfo.tsx +++ b/src/components/common/ProfileInfo.tsx @@ -12,13 +12,14 @@ import { selectChat, selectUser } from '../../modules/selectors'; import { getUserFullName, getUserStatus, isChatChannel, isUserOnline, } from '../../modules/helpers'; -import renderText from '../common/helpers/renderText'; +import renderText from './helpers/renderText'; import { pick } from '../../util/iteratees'; import { captureEvents, SwipeDirection } from '../../util/captureEvents'; +import buildClassName from '../../util/buildClassName'; import usePhotosPreload from './hooks/usePhotosPreload'; import useLang from '../../hooks/useLang'; -import VerifiedIcon from '../common/VerifiedIcon'; +import VerifiedIcon from './VerifiedIcon'; import ProfilePhoto from './ProfilePhoto'; import Transition from '../ui/Transition'; @@ -35,15 +36,16 @@ type StateProps = { isSavedMessages?: boolean; animationLevel: 0 | 1 | 2; serverTimeOffset: number; -} & Pick; +} & Pick; type DispatchProps = Pick; -const PrivateChatInfo: FC = ({ +const ProfileInfo: FC = ({ + forceShowSelf, user, chat, isSavedMessages, - lastSyncTime, + connectionState, animationLevel, loadFullUser, openMediaViewer, @@ -69,10 +71,10 @@ const PrivateChatInfo: FC = ({ const lang = useLang(); useEffect(() => { - if (lastSyncTime && userId) { + if (connectionState === 'connectionStateReady' && userId && !forceShowSelf) { loadFullUser({ userId }); } - }, [userId, loadFullUser, lastSyncTime]); + }, [userId, loadFullUser, connectionState, forceShowSelf]); usePhotosPreload(user || chat, photos, currentPhotoIndex); @@ -80,9 +82,9 @@ const PrivateChatInfo: FC = ({ openMediaViewer({ avatarOwnerId: userId || chatId, profilePhotoIndex: currentPhotoIndex, - origin: MediaViewerOrigin.ProfileAvatar, + origin: forceShowSelf ? MediaViewerOrigin.SettingsAvatar : MediaViewerOrigin.ProfileAvatar, }); - }, [openMediaViewer, userId, chatId, currentPhotoIndex]); + }, [openMediaViewer, userId, chatId, currentPhotoIndex, forceShowSelf]); const selectPreviousMedia = useCallback(() => { if (isFirst) { @@ -174,7 +176,7 @@ const PrivateChatInfo: FC = ({ const isVerifiedIconShown = (user && user.isVerified) || (chat && chat.isVerified); return ( -
+
{renderPhotoTabs()} @@ -218,15 +220,15 @@ const PrivateChatInfo: FC = ({ export default memo(withGlobal( (global, { userId, forceShowSelf }): StateProps => { - const { lastSyncTime, serverTimeOffset } = global; + const { connectionState, serverTimeOffset } = global; const user = selectUser(global, userId); const chat = selectChat(global, userId); const isSavedMessages = !forceShowSelf && user && user.isSelf; const { animationLevel } = global.settings.byKey; return { - lastSyncTime, user, chat, isSavedMessages, animationLevel, serverTimeOffset, + connectionState, user, chat, isSavedMessages, animationLevel, serverTimeOffset, }; }, (setGlobal, actions): DispatchProps => pick(actions, ['loadFullUser', 'openMediaViewer']), -)(PrivateChatInfo)); +)(ProfileInfo)); diff --git a/src/components/right/ProfilePhoto.scss b/src/components/common/ProfilePhoto.scss similarity index 100% rename from src/components/right/ProfilePhoto.scss rename to src/components/common/ProfilePhoto.scss diff --git a/src/components/right/ProfilePhoto.tsx b/src/components/common/ProfilePhoto.tsx similarity index 93% rename from src/components/right/ProfilePhoto.tsx rename to src/components/common/ProfilePhoto.tsx index 6872838f..9740475f 100644 --- a/src/components/right/ProfilePhoto.tsx +++ b/src/components/common/ProfilePhoto.tsx @@ -7,7 +7,7 @@ import { import { getChatAvatarHash, isDeletedUser, getUserColorKey, getChatTitle, isChatPrivate, getUserFullName, } from '../../modules/helpers'; -import renderText from '../common/helpers/renderText'; +import renderText from './helpers/renderText'; import buildClassName from '../../util/buildClassName'; import { getFirstLetters } from '../../util/textFormat'; import useMedia from '../../hooks/useMedia'; @@ -59,11 +59,16 @@ const ProfilePhoto: FC = ({ } const imageHash = getMediaHash(); - const fullMediaData = useMedia(imageHash, false, ApiMediaFormat.BlobUrl, lastSyncTime); + const fullMediaData = useMedia( + imageHash, + false, + imageHash?.startsWith('avatar') ? ApiMediaFormat.DataUri : ApiMediaFormat.BlobUrl, + lastSyncTime, + ); const avatarThumbnailData = useMedia( !fullMediaData && isFirstPhoto ? getMediaHash('normal', true) : undefined, false, - ApiMediaFormat.BlobUrl, + ApiMediaFormat.DataUri, lastSyncTime, ); const thumbDataUri = useBlurSync(!fullMediaData && photo && photo.thumbnail && photo.thumbnail.dataUri); diff --git a/src/components/right/hooks/usePhotosPreload.ts b/src/components/common/hooks/usePhotosPreload.ts similarity index 100% rename from src/components/right/hooks/usePhotosPreload.ts rename to src/components/common/hooks/usePhotosPreload.ts diff --git a/src/components/left/LeftColumn.scss b/src/components/left/LeftColumn.scss index 484dc908..807dc4e9 100644 --- a/src/components/left/LeftColumn.scss +++ b/src/components/left/LeftColumn.scss @@ -19,6 +19,7 @@ font-size: 1.25rem; font-weight: 500; margin-left: 1.375rem; + margin-right: auto; } .SearchInput { @@ -33,4 +34,13 @@ @media (max-width: 600px) { padding: 0.5rem; } + + .Button.smaller { + width: 2.5rem; + height: 2.5rem; + + + .DropdownMenu { + margin-left: .25rem; + } + } } diff --git a/src/components/left/settings/Settings.scss b/src/components/left/settings/Settings.scss index 4a83a3f8..1433c7c7 100644 --- a/src/components/left/settings/Settings.scss +++ b/src/components/left/settings/Settings.scss @@ -6,6 +6,10 @@ flex-direction: column; overflow: hidden; } + + .left-header { + padding-right: .8125rem; + } } .settings-main-header { @@ -30,6 +34,7 @@ background: var(--color-background); height: calc(100% - var(--header-height)); overflow-y: auto; + overflow-y: overlay; &.infinite-scroll { display: flex; @@ -84,37 +89,18 @@ } .settings-main-menu { - padding: 0 0.5rem 1rem; -} + padding: 0 0.5rem .75rem; -.settings-current-user { - margin-bottom: 1.125rem; - text-align: center; + > .ChatExtra { + padding: 0 .5rem .3125rem; + margin: 0 -.5rem .625rem; + box-shadow: inset 0 -.0625rem 0 0 var(--color-background-secondary-accent); + border-bottom: .625rem solid var(--color-background-secondary); - .Avatar { - margin: 0 auto 1.5rem; - } - - .name { - display: flex; - justify-content: center; - align-items: center; - font-weight: 500; - font-size: 1.5rem; - line-height: 2rem; - margin: 0; - - .VerifiedIcon { - margin-left: 0.25rem; - margin-top: 0.1rem; + .ListItem.narrow { + margin-bottom: .25rem; } } - - .phone { - font-size: 0.875rem; - color: #868e96; - margin: 0; - } } .settings-item { diff --git a/src/components/left/settings/Settings.tsx b/src/components/left/settings/Settings.tsx index c4f40223..8767b5c8 100644 --- a/src/components/left/settings/Settings.tsx +++ b/src/components/left/settings/Settings.tsx @@ -334,6 +334,7 @@ const Settings: FC = ({ currentScreen={currentScreen} onReset={handleReset} onSaveFilter={handleSaveFilter} + onScreenSelect={onScreenSelect} editedFolderId={foldersState.folderId} /> {renderCurrentSectionContent(isScreenActive, currentKey)} diff --git a/src/components/left/settings/SettingsHeader.tsx b/src/components/left/settings/SettingsHeader.tsx index d3b4d8c1..a92ebdf5 100644 --- a/src/components/left/settings/SettingsHeader.tsx +++ b/src/components/left/settings/SettingsHeader.tsx @@ -20,6 +20,7 @@ type OwnProps = { editedFolderId?: number; onReset: () => void; onSaveFilter: () => void; + onScreenSelect: (screen: SettingsScreens) => void; }; type DispatchProps = Pick; @@ -31,6 +32,7 @@ const SettingsHeader: FC = ({ onSaveFilter, signOut, deleteChatFolder, + onScreenSelect, }) => { const [isSignOutDialogOpen, setIsSignOutDialogOpen] = useState(false); const [isDeleteFolderDialogOpen, setIsDeleteFolderDialogOpen] = useState(false); @@ -205,6 +207,16 @@ const SettingsHeader: FC = ({

{lang('SETTINGS')}

+ = ({ +type DispatchProps = Pick; + +const SettingsMain: FC = ({ isActive, onScreenSelect, onReset, + loadProfilePhotos, currentUser, + lastSyncTime, }) => { const lang = useLang(); - const fullName = getUserFullName(currentUser); + const profileId = currentUser ? currentUser.id : undefined; + + useEffect(() => { + if (profileId && lastSyncTime) { + loadProfilePhotos({ profileId }); + } + }, [lastSyncTime, profileId, loadProfilePhotos]); useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Main); @@ -39,24 +50,17 @@ const SettingsMain: FC = ({
{currentUser && ( -
- -

{fullName && renderText(fullName)}

-

{formatPhoneNumberWithCode(currentUser.phoneNumber)}

-
+ + )} + {currentUser && ( + )} - onScreenSelect(SettingsScreens.EditProfile)} - > - {lang('lng_settings_information')} - - onScreenSelect(SettingsScreens.Folders)} - > - {lang('Filters')} - onScreenSelect(SettingsScreens.General)} @@ -75,6 +79,12 @@ const SettingsMain: FC = ({ > {lang('PrivacySettings')} + onScreenSelect(SettingsScreens.Folders)} + > + {lang('Filters')} + onScreenSelect(SettingsScreens.Language)} @@ -88,10 +98,12 @@ const SettingsMain: FC = ({ export default memo(withGlobal( (global): StateProps => { - const { currentUserId } = global; + const { currentUserId, lastSyncTime } = global; return { currentUser: currentUserId ? selectUser(global, currentUserId) : undefined, + lastSyncTime, }; }, + (setGlobal, actions): DispatchProps => pick(actions, ['loadProfilePhotos']), )(SettingsMain)); diff --git a/src/components/mediaViewer/helpers/ghostAnimation.ts b/src/components/mediaViewer/helpers/ghostAnimation.ts index cc0cab03..c6b5714f 100644 --- a/src/components/mediaViewer/helpers/ghostAnimation.ts +++ b/src/components/mediaViewer/helpers/ghostAnimation.ts @@ -143,7 +143,7 @@ export function animateClosing(origin: MediaViewerOrigin, bestImageData: string, const existingGhost = document.getElementsByClassName('ghost')[0] as HTMLDivElement; - const ghost = existingGhost || createGhost(bestImageData || toImage, origin === MediaViewerOrigin.ProfileAvatar); + const ghost = existingGhost || createGhost(bestImageData || toImage, origin); if (!existingGhost) { applyStyles(ghost, { top: `${toTop}px`, @@ -203,7 +203,7 @@ export function animateClosing(origin: MediaViewerOrigin, bestImageData: string, }); } -function createGhost(source: string | HTMLImageElement | HTMLVideoElement, shouldAppendProfileInfo = false) { +function createGhost(source: string | HTMLImageElement | HTMLVideoElement, origin?: MediaViewerOrigin) { const ghost = document.createElement('div'); ghost.classList.add('ghost'); @@ -219,9 +219,16 @@ function createGhost(source: string | HTMLImageElement | HTMLVideoElement, shoul ghost.appendChild(img); - if (shouldAppendProfileInfo) { + if (origin === MediaViewerOrigin.ProfileAvatar || origin === MediaViewerOrigin.SettingsAvatar) { ghost.classList.add('ProfileInfo'); - const profileInfo = document.querySelector('#RightColumn .ProfileInfo .info'); + if (origin === MediaViewerOrigin.SettingsAvatar) { + ghost.classList.add('self'); + } + const profileInfo = document.querySelector( + origin === MediaViewerOrigin.ProfileAvatar + ? '#RightColumn .ProfileInfo .info' + : '#Settings .ProfileInfo .info', + ); if (profileInfo) { ghost.appendChild(profileInfo.cloneNode(true)); } @@ -314,6 +321,11 @@ function getNodes(origin: MediaViewerOrigin, message?: ApiMessage) { mediaSelector = 'img.avatar-media'; break; + case MediaViewerOrigin.SettingsAvatar: + containerSelector = '#Settings .ProfileInfo .active .ProfilePhoto'; + mediaSelector = 'img.avatar-media'; + break; + case MediaViewerOrigin.ProfileAvatar: containerSelector = '#RightColumn .ProfileInfo .active .ProfilePhoto'; mediaSelector = 'img.avatar-media'; @@ -345,6 +357,7 @@ function applyShape(ghost: HTMLDivElement, origin: MediaViewerOrigin) { break; case MediaViewerOrigin.SharedMedia: + case MediaViewerOrigin.SettingsAvatar: case MediaViewerOrigin.ProfileAvatar: case MediaViewerOrigin.SearchResult: (ghost.firstChild as HTMLElement).style.objectFit = 'cover'; diff --git a/src/components/right/Profile.tsx b/src/components/right/Profile.tsx index cfa33e6c..19c34b83 100644 --- a/src/components/right/Profile.tsx +++ b/src/components/right/Profile.tsx @@ -46,10 +46,10 @@ import TabList from '../ui/TabList'; import Spinner from '../ui/Spinner'; import ListItem from '../ui/ListItem'; import PrivateChatInfo from '../common/PrivateChatInfo'; -import ProfileInfo from './ProfileInfo'; +import ProfileInfo from '../common/ProfileInfo'; import Document from '../common/Document'; import Audio from '../common/Audio'; -import ChatExtra from './ChatExtra'; +import ChatExtra from '../common/ChatExtra'; import Media from '../common/Media'; import WebLink from '../common/WebLink'; import NothingFound from '../common/NothingFound'; @@ -405,11 +405,8 @@ const Profile: FC = ({ function renderProfileInfo(chatId: number, resolvedUserId?: number) { return (
- - + +
); } diff --git a/src/types/index.ts b/src/types/index.ts index ef8a0b87..9bb38039 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -230,6 +230,7 @@ export enum MediaViewerOrigin { ScheduledInline, SharedMedia, ProfileAvatar, + SettingsAvatar, MiddleHeaderAvatar, Album, ScheduledAlbum,