Skip to content

Commit

Permalink
Merge pull request #7 from ulu-telegram/TG-156-sidebar-profile
Browse files Browse the repository at this point in the history
[TG-156] sidebar profile
  • Loading branch information
iower authored Oct 18, 2023
2 parents d7438e5 + a02adde commit ee78e9a
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 77 deletions.
6 changes: 4 additions & 2 deletions src/components/common/ProfilePhoto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,18 @@ type OwnProps = {
isSavedMessages?: boolean;
photo?: ApiPhoto;
canPlayVideo: boolean;
onClick: NoneToVoidFunction;
onClick?: NoneToVoidFunction;
};

const noneToVoid: NoneToVoidFunction = () => void 0;

const ProfilePhoto: FC<OwnProps> = ({
chat,
user,
photo,
isSavedMessages,
canPlayVideo,
onClick,
onClick = noneToVoid,
}) => {
// eslint-disable-next-line no-null/no-null
const videoRef = useRef<HTMLVideoElement>(null);
Expand Down
2 changes: 1 addition & 1 deletion src/components/left/LeftColumn.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

.left-header {
height: var(--header-height);
padding: 0.375rem 0.8125rem 0.5rem 0.8125rem;
padding: 0.5rem;
display: flex;
align-items: center;
flex-shrink: 0;
Expand Down
1 change: 1 addition & 0 deletions src/components/left/main/LeftMain.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
position: relative;
display: flex;
flex-direction: column;
// gap: 1.25rem;
overflow: hidden;
background: var(--color-background);

Expand Down
1 change: 0 additions & 1 deletion src/components/left/main/LeftMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ const LeftMain: FC<OwnProps> = ({
onSelectContacts={handleSelectContacts}
onSelectArchived={handleSelectArchived}
onReset={onReset}
shouldSkipTransition={shouldSkipTransition}
isClosingSearch={isClosingSearch}
/>
<Transition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,10 @@
}
}
}

.profileWrapper {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
170 changes: 97 additions & 73 deletions src/components/left/main/LeftMainHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,19 @@ import Button from '../../ui/Button';
import DropdownMenu from '../../ui/DropdownMenu';
import SearchInput from '../../ui/SearchInput';
import ShowTransition from '../../ui/ShowTransition';
import UluSearchButton from '../../ui/UluSearchButton';
import ConnectionStatusOverlay from '../ConnectionStatusOverlay';
import LeftSideMenuItems from './LeftSideMenuItems';
import StatusButton from './StatusButton';
import UluHeaderProfile from './UluHeaderProfile';

import './LeftMainHeader.scss';
import styles from './LeftMainHeader.module.scss';

type OwnProps = {
shouldHideSearch?: boolean;
content: LeftColumnContent;
contactsFilter: string;
isClosingSearch?: boolean;
shouldSkipTransition?: boolean;
onSearchQuery: (query: string) => void;
onSelectSettings: NoneToVoidFunction;
onSelectContacts: NoneToVoidFunction;
Expand Down Expand Up @@ -88,7 +89,6 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
searchQuery,
isLoading,
isCurrentUserPremium,
shouldSkipTransition,
globalSearchChatId,
searchDate,
theme,
Expand Down Expand Up @@ -152,34 +152,41 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
...(IS_APP && { 'Mod+L': handleLockScreenHotkey }),
} : undefined);

const MainButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => {
return ({ onTrigger, isOpen }) => (
<Button
round
ripple={hasMenu && !isMobile}
size="smaller"
color="translucent"
className={isOpen ? 'active' : ''}
// eslint-disable-next-line react/jsx-no-bind
onClick={hasMenu ? onTrigger : () => onReset()}
ariaLabel={hasMenu ? lang('AccDescrOpenMenu2') : 'Return to chat list'}
>
<div className={buildClassName(
'animated-menu-icon',
!hasMenu && 'state-back',
shouldSkipTransition && 'no-animation',
)}
/>
</Button>
);
}, [hasMenu, isMobile, lang, onReset, shouldSkipTransition]);

const handleSearchFocus = useLastCallback(() => {
if (!searchQuery) {
onSearchQuery('');
}
});

// Cmd+K to open search
useEffect(() => {
function handleKeyDown(e: KeyboardEvent) {
if (((IS_MAC_OS && e.metaKey) || (!IS_MAC_OS && e.ctrlKey)) && e.key === 'k') {
if (hasMenu) {
handleSearchFocus();
return;
}

onReset();
}
}

document.addEventListener('keydown', handleKeyDown);

return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [hasMenu, onReset]);

const MainButton: FC<{ onTrigger: () => void }> = useMemo(() => {
return ({ onTrigger }) => (
<UluHeaderProfile
// eslint-disable-next-line react/jsx-no-bind
onClick={hasMenu ? onTrigger : () => onReset()}
/>
);
}, [hasMenu, onReset]);

const toggleConnectionStatus = useLastCallback(() => {
setSettingOption({ isConnectionStatusMinimized: !isConnectionStatusMinimized });
});
Expand Down Expand Up @@ -244,54 +251,71 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
<div className="LeftMainHeader">
<div id="LeftMainHeader" className="left-header" ref={headerRef}>
{lang.isRtl && <div className="DropdownMenuFiller" />}
<DropdownMenu
trigger={MainButton}
footer={`${APP_NAME} ${versionString}`}
className={buildClassName(
'main-menu',
lang.isRtl && 'rtl',
shouldHideSearch && lang.isRtl && 'right-aligned',
shouldDisableDropdownMenuTransitionRef.current && lang.isRtl && 'disable-transition',
)}
forceOpen={isBotMenuOpen}
positionX={shouldHideSearch && lang.isRtl ? 'right' : 'left'}
transformOriginX={IS_ELECTRON && IS_MAC_OS && !isFullscreen ? 90 : undefined}
onTransitionEnd={lang.isRtl ? handleDropdownMenuTransitionEnd : undefined}
>
<LeftSideMenuItems
onSelectArchived={onSelectArchived}
onSelectContacts={onSelectContacts}
onSelectSettings={onSelectSettings}
onBotMenuOpened={markBotMenuOpen}
onBotMenuClosed={unmarkBotMenuOpen}
/>
</DropdownMenu>
<SearchInput
inputId="telegram-search-input"
parentContainerClassName="LeftSearch"
className={buildClassName(
(globalSearchChatId || searchDate) ? 'with-picker-item' : undefined,
shouldHideSearch && 'SearchInput--hidden',
)}
value={isClosingSearch ? undefined : (contactsFilter || searchQuery)}
focused={isSearchFocused}
isLoading={isLoading || connectionStatusPosition === 'minimized'}
spinnerColor={connectionStatusPosition === 'minimized' ? 'yellow' : undefined}
spinnerBackgroundColor={connectionStatusPosition === 'minimized' && theme === 'light' ? 'light' : undefined}
placeholder={searchInputPlaceholder}
autoComplete="off"
canClose={Boolean(globalSearchChatId || searchDate)}
onChange={onSearchQuery}
onReset={onReset}
onFocus={handleSearchFocus}
onSpinnerClick={connectionStatusPosition === 'minimized' ? toggleConnectionStatus : undefined}
>
{searchContent}
{IS_STORIES_ENABLED && (
<StoryToggler canShow={!isSearchFocused && !selectedSearchDate && !globalSearchChatId} />
)}
</SearchInput>
{isCurrentUserPremium && <StatusButton />}
{ isSearchFocused ? (
<>
<Button
size="smaller"
round
color="translucent"
onClick={onReset}
>
<i className="icon icon-arrow-left" />
</Button>
<SearchInput
inputId="telegram-search-input"
parentContainerClassName="LeftSearch"
className={buildClassName(
(globalSearchChatId || searchDate) ? 'with-picker-item' : undefined,
shouldHideSearch && 'SearchInput--hidden',
)}
value={isClosingSearch ? undefined : (contactsFilter || searchQuery)}
focused={isSearchFocused}
isLoading={isLoading || connectionStatusPosition === 'minimized'}
spinnerColor={connectionStatusPosition === 'minimized' ? 'yellow' : undefined}
spinnerBackgroundColor={
connectionStatusPosition === 'minimized' && theme === 'light' ? 'light' : undefined
}
placeholder={searchInputPlaceholder}
autoComplete="off"
canClose={Boolean(globalSearchChatId || searchDate)}
onChange={onSearchQuery}
onReset={onReset}
onSpinnerClick={connectionStatusPosition === 'minimized' ? toggleConnectionStatus : undefined}
>
{searchContent}
{IS_STORIES_ENABLED && (
<StoryToggler canShow={!isSearchFocused && !selectedSearchDate && !globalSearchChatId} />
)}
</SearchInput>
{isCurrentUserPremium && <StatusButton />}
</>
) : (
<div className={styles.profileWrapper}>
<DropdownMenu
trigger={MainButton}
footer={`${APP_NAME} ${versionString}`}
className={buildClassName(
'main-menu',
lang.isRtl && 'rtl',
shouldHideSearch && lang.isRtl && 'right-aligned',
shouldDisableDropdownMenuTransitionRef.current && lang.isRtl && 'disable-transition',
)}
forceOpen={isBotMenuOpen}
positionX={shouldHideSearch && lang.isRtl ? 'right' : 'left'}
transformOriginX={IS_ELECTRON && IS_MAC_OS && !isFullscreen ? 90 : undefined}
onTransitionEnd={lang.isRtl ? handleDropdownMenuTransitionEnd : undefined}
>
<LeftSideMenuItems
onSelectArchived={onSelectArchived}
onSelectContacts={onSelectContacts}
onSelectSettings={onSelectSettings}
onBotMenuOpened={markBotMenuOpen}
onBotMenuClosed={unmarkBotMenuOpen}
/>
</DropdownMenu>
<UluSearchButton onClick={handleSearchFocus} />
</div>
) }
{hasPasscode && (
<Button
round
Expand Down
40 changes: 40 additions & 0 deletions src/components/left/main/UluHeaderProfile.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.375rem 0.75rem 0.375rem 0.375rem;
cursor: pointer;
border-radius: var(--border-radius-default-tiny);

&:hover {
background-color: var(--color-chat-hover);

.userName {
color: var(--color-text);
}
}
}

.photoWrapper {
height: 1.5rem;
width: 1.5rem;
border-radius: 0.5rem;

:global(.avatar-media) {
border-radius: 0.5rem;
vertical-align: inherit;
}

:global(.no-photo) {
border-radius: inherit;
font-size: 0.6875rem;
white-space: nowrap;
}
}

.userName {
color: var(--color-text-secondary);
font-weight: 600;
font-size: 0.875rem;
line-height: 1.5rem;
}
63 changes: 63 additions & 0 deletions src/components/left/main/UluHeaderProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { MouseEvent as ReactMouseEvent } from 'react';
import type { FC } from '../../../lib/teact/teact';
import React, { memo } from '../../../lib/teact/teact';
import { withGlobal } from '../../../global';

import type { ApiPhoto, ApiUser } from '../../../api/types';

import { selectUser, selectUserFullInfo } from '../../../global/selectors';

import ProfilePhoto from '../../common/ProfilePhoto';

import styles from './UluHeaderProfile.module.scss';

type OwnProps = {
onClick?: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void;
};

type StateProps = {
user?: ApiUser;
userPersonalPhoto?: ApiPhoto;
userProfilePhoto?: ApiPhoto;
userFallbackPhoto?: ApiPhoto;
};

const UluHeaderProfile: FC<OwnProps & StateProps> = ({
user, userFallbackPhoto, userPersonalPhoto, userProfilePhoto, onClick,
}) => {
function renderPhoto() {
const profilePhoto = userPersonalPhoto || userProfilePhoto || userFallbackPhoto;

return (
<ProfilePhoto
user={user}
photo={profilePhoto}
canPlayVideo
/>
);
}

return (
<div className={styles.wrapper} onClick={onClick}>
<div className={styles.photoWrapper}>
{renderPhoto()}
</div>
<div className={styles.userName}>
{ `${user!.firstName} ${user!.lastName}` }
</div>
</div>
);
};

export default memo(withGlobal<OwnProps>((global) => {
const { currentUserId } = global;
const user = selectUser(global, currentUserId!);
const userFullInfo = selectUserFullInfo(global, currentUserId!);

return {
user,
userPersonalPhoto: userFullInfo?.personalPhoto,
userProfilePhoto: userFullInfo?.profilePhoto,
userFallbackPhoto: userFullInfo?.fallbackPhoto,
};
})(UluHeaderProfile));
Loading

0 comments on commit ee78e9a

Please sign in to comment.