UI: Design edits (#5668)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
parent
e31a36c41d
commit
2e39a15660
@ -15,7 +15,7 @@
|
||||
// Prevent loading additional 10 kB of icomoon font on initial load.
|
||||
.css-icon-down {
|
||||
position: absolute;
|
||||
top: 1.125rem;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
@ -29,7 +29,7 @@
|
||||
&.open {
|
||||
border-color: var(--color-primary);
|
||||
transform: scaleY(-1) rotate(45deg);
|
||||
top: 1.5rem;
|
||||
top: 1.3125rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import { ANIMATION_END_DELAY } from '../../config';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { isoToEmoji } from '../../util/emoji/emoji';
|
||||
import { prepareSearchWordsForNeedle } from '../../util/searchWords';
|
||||
import { IS_EMOJI_SUPPORTED } from '../../util/windowEnvironment';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -104,7 +105,9 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
|
||||
handleTrigger();
|
||||
};
|
||||
|
||||
const inputValue = filter ?? (value?.name || value?.defaultName || '');
|
||||
const emoji = value && IS_EMOJI_SUPPORTED && renderText(isoToEmoji(value.iso2), ['hq_emoji']);
|
||||
const name = value?.name || value?.defaultName || '';
|
||||
const inputValue = filter ?? [emoji, name].filter(Boolean).join(' ');
|
||||
|
||||
return (
|
||||
<div className={buildClassName('input-group', value && 'touched')}>
|
||||
|
||||
@ -537,6 +537,7 @@ const GroupCall: FC<OwnProps & StateProps> = ({
|
||||
<p>{lang(isEndGroupCallModal ? 'VoipGroupEndAlertText' : 'VoipGroupLeaveAlertText')}</p>
|
||||
{!isEndGroupCallModal && (
|
||||
<Checkbox
|
||||
className="dialog-checkbox"
|
||||
label={lang('VoipGroupEndChat')}
|
||||
checked={shouldEndGroupCall}
|
||||
onCheck={setShouldEndGroupCall}
|
||||
|
||||
@ -18,7 +18,10 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-secondary);
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.share {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
@ -115,6 +115,7 @@ const InviteLink: FC<OwnProps> = ({
|
||||
size="smaller"
|
||||
disabled={isDisabled}
|
||||
onClick={handleShare}
|
||||
className={styles.share}
|
||||
>
|
||||
{lang('FolderLinkScreen.LinkActionShare')}
|
||||
</Button>
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
.header {
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: 2rem;
|
||||
position: relative;
|
||||
|
||||
&[dir="rtl"] {
|
||||
@ -22,7 +21,6 @@
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
|
||||
@ -38,13 +38,14 @@
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
border-top: 1px solid var(--color-borders);
|
||||
border-top: 1px solid transparent;
|
||||
transition: border-top-color 250ms ease-in-out;
|
||||
height: calc(100% - var(--header-height));
|
||||
|
||||
overflow-y: scroll;
|
||||
|
||||
&.no-border, &.two-fa, &.local-passcode, &.password-form {
|
||||
border-top: none;
|
||||
&.scrolled {
|
||||
border-top-color: var(--color-borders);
|
||||
}
|
||||
|
||||
&.password-form .input-group.error label::first-letter {
|
||||
@ -83,11 +84,9 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0 1.5rem 1rem;
|
||||
padding: 0 1.5rem;
|
||||
text-align: center;
|
||||
|
||||
@include mixins.side-panel-section;
|
||||
|
||||
&.no-border {
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
@ -125,7 +124,7 @@
|
||||
.settings-item-simple,
|
||||
.settings-item {
|
||||
text-align: initial;
|
||||
padding: 1.5rem 0.5rem 1rem;
|
||||
padding: 0.5rem 0.5rem 1rem;
|
||||
|
||||
@include mixins.adapt-padding-to-scrollbar(0.5rem);
|
||||
@include mixins.side-panel-section;
|
||||
@ -152,10 +151,9 @@
|
||||
|
||||
&-description {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.3125;
|
||||
color: var(--color-text-secondary);
|
||||
margin-top: -0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-inline-start: 1rem;
|
||||
margin: -0.5rem 1rem 1rem;
|
||||
|
||||
.settings-content.two-fa &,
|
||||
.settings-content.password-form &,
|
||||
@ -163,11 +161,6 @@
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.settings-edit-profile & {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
&[dir="rtl"] {
|
||||
text-align: right;
|
||||
unicode-bidi: plaintext;
|
||||
@ -296,10 +289,13 @@
|
||||
padding: 0.5rem 1rem 0 1rem;
|
||||
}
|
||||
|
||||
.settings-group {
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
.settings-fab-wrapper {
|
||||
height: calc(100% - var(--header-height));
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.settings-content {
|
||||
height: 100%;
|
||||
@ -315,10 +311,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.settings-edit-profile {
|
||||
padding: 0 1.5rem !important;
|
||||
}
|
||||
|
||||
.settings-quick-reaction {
|
||||
.Radio-main .label {
|
||||
display: flex;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useState } from '../../../lib/teact/teact';
|
||||
import React, { memo, useRef, useState } from '../../../lib/teact/teact';
|
||||
import { getActions, getGlobal } from '../../../global';
|
||||
|
||||
import type { FolderEditDispatch, FoldersState } from '../../../hooks/reducers/useFoldersReducer';
|
||||
@ -10,6 +10,7 @@ import { LAYERS_ANIMATION_NAME } from '../../../util/windowEnvironment';
|
||||
|
||||
import useTwoFaReducer from '../../../hooks/reducers/useTwoFaReducer';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useMarkScrolled from '../../../hooks/useMarkScrolled/useMarkScrolled';
|
||||
|
||||
import Transition from '../../ui/Transition';
|
||||
import SettingsFolders from './folders/SettingsFolders';
|
||||
@ -161,9 +162,17 @@ const Settings: FC<OwnProps> = ({
|
||||
}) => {
|
||||
const { closeShareChatFolderModal } = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [twoFaState, twoFaDispatch] = useTwoFaReducer();
|
||||
const [privacyPasscode, setPrivacyPasscode] = useState<string>('');
|
||||
|
||||
useMarkScrolled({
|
||||
containerRef,
|
||||
selector: '.settings-content',
|
||||
}, [currentScreen]);
|
||||
|
||||
const handleReset = useLastCallback((forceReturnToChatList?: true | Event) => {
|
||||
const isFromSettings = selectTabState(getGlobal()).shareFolderScreen?.isFromSettings;
|
||||
|
||||
@ -506,6 +515,7 @@ const Settings: FC<OwnProps> = ({
|
||||
|
||||
return (
|
||||
<Transition
|
||||
ref={containerRef}
|
||||
id="Settings"
|
||||
name={shouldSkipTransition ? 'none' : LAYERS_ANIMATION_NAME}
|
||||
activeKey={currentScreen}
|
||||
|
||||
@ -211,34 +211,36 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
return (
|
||||
<div className="settings-fab-wrapper">
|
||||
<div className="settings-content no-border custom-scroll">
|
||||
<div className="settings-edit-profile settings-item">
|
||||
<AvatarEditable
|
||||
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
||||
onChange={handlePhotoChange}
|
||||
title="Edit your profile photo"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<InputText
|
||||
value={firstName}
|
||||
onChange={handleFirstNameChange}
|
||||
label={lang('FirstName')}
|
||||
disabled={isLoading}
|
||||
error={error === ERROR_FIRST_NAME_MISSING ? error : undefined}
|
||||
/>
|
||||
<InputText
|
||||
value={lastName}
|
||||
onChange={handleLastNameChange}
|
||||
label={lang('LastName')}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<TextArea
|
||||
value={bio}
|
||||
onChange={handleBioChange}
|
||||
label={lang('UserBio')}
|
||||
disabled={isLoading}
|
||||
maxLength={maxBioLength}
|
||||
maxLengthIndicator={maxBioLength ? (maxBioLength - bio.length).toString() : undefined}
|
||||
/>
|
||||
<div className="settings-item">
|
||||
<div className="settings-input">
|
||||
<AvatarEditable
|
||||
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
||||
onChange={handlePhotoChange}
|
||||
title="Edit your profile photo"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<InputText
|
||||
value={firstName}
|
||||
onChange={handleFirstNameChange}
|
||||
label={lang('FirstName')}
|
||||
disabled={isLoading}
|
||||
error={error === ERROR_FIRST_NAME_MISSING ? error : undefined}
|
||||
/>
|
||||
<InputText
|
||||
value={lastName}
|
||||
onChange={handleLastNameChange}
|
||||
label={lang('LastName')}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<TextArea
|
||||
value={bio}
|
||||
onChange={handleBioChange}
|
||||
label={lang('UserBio')}
|
||||
disabled={isLoading}
|
||||
maxLength={maxBioLength}
|
||||
maxLengthIndicator={maxBioLength ? (maxBioLength - bio.length).toString() : undefined}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="settings-item-description" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{renderText(lang('lng_settings_about_bio'), ['br', 'simple_markdown'])}
|
||||
|
||||
@ -133,7 +133,7 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="settings-content custom-scroll">
|
||||
<div className="settings-item pt-3">
|
||||
<div className="settings-item">
|
||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('Settings')}</h4>
|
||||
|
||||
<RangeSlider
|
||||
|
||||
@ -122,7 +122,7 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="SettingsGeneralBackground settings-content custom-scroll">
|
||||
<div className="settings-item pt-3">
|
||||
<div className="settings-item">
|
||||
<ListItem
|
||||
icon="camera-add"
|
||||
className="mb-0"
|
||||
|
||||
@ -67,7 +67,7 @@ const SettingsPasswordForm: FC<OwnProps> = ({
|
||||
<PasswordMonkey isBig isPasswordVisible={shouldShowPassword} />
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-2">
|
||||
<div className="settings-item settings-group">
|
||||
<PasswordForm
|
||||
error={validationError || error}
|
||||
hint={hint}
|
||||
|
||||
@ -145,7 +145,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="settings-content custom-scroll">
|
||||
<div className="settings-item pt-3">
|
||||
<div className="settings-item">
|
||||
<ListItem
|
||||
icon="delete-user"
|
||||
narrow
|
||||
|
||||
@ -306,7 +306,7 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
|
||||
{!isOnlyInvites && (
|
||||
<div className="settings-item pt-3">
|
||||
<div className="settings-item">
|
||||
{state.error && state.error === ERROR_NO_CHATS && (
|
||||
<p className="settings-item-description color-danger mb-2" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{lang(state.error)}
|
||||
|
||||
@ -43,7 +43,7 @@ const SettingsPasscodeCongratulations: FC<OwnProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-2">
|
||||
<div className="settings-item settings-group">
|
||||
<Button onClick={fullReset}>{lang('Back')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -41,7 +41,7 @@ const SettingsPasscodeEnabled: FC<OwnProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-2">
|
||||
<div className="settings-item">
|
||||
<ListItem
|
||||
icon="edit"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
|
||||
@ -44,7 +44,7 @@ const SettingsPasscodeStart: FC<OwnProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-2">
|
||||
<div className="settings-item settings-group">
|
||||
<Button onClick={onStart}>{lang('EnablePasscode')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -46,7 +46,7 @@ const SettingsTwoFaCongratulations: FC<OwnProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-2">
|
||||
<div className="settings-item settings-group">
|
||||
<Button onClick={handleClick}>{lang('TwoStepVerificationPasswordReturnSettings')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -86,12 +86,14 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
||||
<div className="settings-content two-fa custom-scroll">
|
||||
<div className="settings-content-header no-border">
|
||||
<AnimatedIconFromSticker sticker={animatedEmoji} size={ICON_SIZE} className="settings-content-icon" />
|
||||
<p className="settings-item-description mb-3" dir="auto">
|
||||
{lang('TwoStepAuth.ConfirmEmailDescription', recoveryEmail)}
|
||||
</p>
|
||||
{recoveryEmail && (
|
||||
<p className="settings-item-description mb-3" dir="auto">
|
||||
{lang('TwoStepAuth.ConfirmEmailDescription', recoveryEmail)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-2">
|
||||
<div className="settings-item settings-group">
|
||||
<InputText
|
||||
value={value}
|
||||
ref={inputRef}
|
||||
|
||||
@ -45,7 +45,7 @@ const SettingsTwoFaEnabled: FC<OwnProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-2">
|
||||
<div className="settings-item">
|
||||
<ListItem
|
||||
icon="edit"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
|
||||
@ -105,12 +105,14 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
||||
<div className="settings-content two-fa custom-scroll">
|
||||
<div className="settings-content-header no-border">
|
||||
<AnimatedIconFromSticker sticker={animatedEmoji} size={ICON_SIZE} className="settings-content-icon" />
|
||||
<p className="settings-item-description mb-3" dir="auto">
|
||||
{lang('RecoveryEmailSubtitle')}
|
||||
</p>
|
||||
{type === 'email' && (
|
||||
<p className="settings-item-description mb-3" dir="auto">
|
||||
{lang('RecoveryEmailSubtitle')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-2">
|
||||
<div className="settings-item settings-group">
|
||||
<form action="" onSubmit={handleSubmit}>
|
||||
<InputText
|
||||
ref={inputRef}
|
||||
|
||||
@ -42,7 +42,7 @@ const SettingsTwoFaStart: FC<OwnProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="settings-item pt-0">
|
||||
<div className="settings-item settings-group">
|
||||
<Button onClick={onStart}>{lang('EditAdminTransferSetPassword')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -8,6 +8,13 @@
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
border-top: 1px solid transparent;
|
||||
transition: border-top-color 250ms ease-in-out;
|
||||
|
||||
&.scrolled {
|
||||
border-top-color: var(--color-borders);
|
||||
}
|
||||
|
||||
> .profile-info > .ChatInfo {
|
||||
grid-area: chat_info;
|
||||
|
||||
|
||||
@ -32,6 +32,15 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
border-top: 1px solid transparent;
|
||||
transition: border-top-color 250ms ease-in-out;
|
||||
|
||||
&.scrolled {
|
||||
border-top-color: var(--color-borders);
|
||||
}
|
||||
}
|
||||
|
||||
.Management .section > .ChatInfo {
|
||||
padding: 0 1.5rem;
|
||||
margin: 1rem 0;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, { memo, useEffect, useState } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useEffect, useRef, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { ProfileTabType, ThreadId } from '../../types';
|
||||
@ -22,6 +24,7 @@ import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps';
|
||||
import useMarkScrolled from '../../hooks/useMarkScrolled/useMarkScrolled';
|
||||
import useWindowSize from '../../hooks/window/useWindowSize';
|
||||
|
||||
import Transition from '../ui/Transition';
|
||||
@ -106,6 +109,9 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
||||
closeMonetizationStatistics,
|
||||
} = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { width: windowWidth } = useWindowSize();
|
||||
const [profileState, setProfileState] = useState<ProfileState>(
|
||||
isSavedMessages && !isSavedDialog ? ProfileState.SavedDialogs : ProfileState.Profile,
|
||||
@ -135,6 +141,11 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const renderingContentKey = useCurrentOrPrev(contentKey, true, !isChatSelected) ?? -1;
|
||||
|
||||
useMarkScrolled({
|
||||
containerRef,
|
||||
selector: ':scope .custom-scroll, :scope .panel-content',
|
||||
}, [contentKey, managementScreen, chatId, threadId]);
|
||||
|
||||
const close = useLastCallback((shouldScrollUp = true) => {
|
||||
switch (contentKey) {
|
||||
case RightColumnContent.AddingMembers:
|
||||
@ -389,6 +400,7 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
||||
onScreenSelect={setManagementScreen}
|
||||
/>
|
||||
<Transition
|
||||
ref={containerRef}
|
||||
name={(shouldSkipTransition || shouldSkipHistoryAnimations) ? 'none' : 'zoomFade'}
|
||||
renderCount={MAIN_SCREENS_COUNT + MANAGEMENT_SCREENS_COUNT}
|
||||
activeKey={isManagement ? MAIN_SCREENS_COUNT + managementScreen : renderingContentKey}
|
||||
|
||||
@ -212,7 +212,7 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section">
|
||||
<AvatarEditable
|
||||
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
||||
|
||||
@ -111,7 +111,7 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section">
|
||||
<ListItem
|
||||
icon="recent"
|
||||
|
||||
@ -22,6 +22,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
||||
import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated';
|
||||
|
||||
import Icon from '../../common/icons/Icon';
|
||||
import LinkField from '../../common/LinkField';
|
||||
import ManageUsernames from '../../common/ManageUsernames';
|
||||
import SafeLink from '../../common/SafeLink';
|
||||
import UsernameInput from '../../common/UsernameInput';
|
||||
@ -198,7 +199,7 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
<h3 className="section-heading">{lang(`${langPrefix2}Type`)}</h3>
|
||||
<RadioGroup
|
||||
@ -212,7 +213,7 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{privateInviteLink ? (
|
||||
<>
|
||||
<SafeLink url={privateInviteLink} className="group-link" text={privateInviteLink} />
|
||||
<LinkField link={privateInviteLink} className="invite-link" />
|
||||
<p className="section-info" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{lang(`${langPrefix1}PrivateLinkHelp`)}
|
||||
</p>
|
||||
@ -235,14 +236,16 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
) : (
|
||||
<div className="section no-border">
|
||||
<UsernameInput
|
||||
asLink
|
||||
currentUsername={currentUsername}
|
||||
isLoading={isLoading}
|
||||
isUsernameAvailable={isUsernameAvailable}
|
||||
checkedUsername={checkedUsername}
|
||||
onChange={handleUsernameChange}
|
||||
/>
|
||||
<div className="settings-input">
|
||||
<UsernameInput
|
||||
asLink
|
||||
currentUsername={currentUsername}
|
||||
isLoading={isLoading}
|
||||
isUsernameAvailable={isUsernameAvailable}
|
||||
checkedUsername={checkedUsername}
|
||||
onChange={handleUsernameChange}
|
||||
/>
|
||||
</div>
|
||||
{error === USERNAME_PURCHASE_ERROR && renderPurchaseLink()}
|
||||
<p className="section-info" dir="auto">
|
||||
{lang(`${langPrefix2}.Username.CreatePublicLinkHelp`)}
|
||||
|
||||
@ -83,7 +83,7 @@ const ManageChatRemovedUsers: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
<p className="section-help">{lang(isChannel ? 'NoBlockedChannel2' : 'NoBlockedGroup2')}</p>
|
||||
|
||||
|
||||
@ -246,7 +246,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section">
|
||||
<AnimatedIconWithPreview
|
||||
tgsUrl={LOCAL_TGS_URLS.DiscussionGroups}
|
||||
|
||||
@ -321,7 +321,7 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section">
|
||||
<AvatarEditable
|
||||
isForForum={isForumEnabled}
|
||||
|
||||
@ -205,7 +205,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section">
|
||||
<ListItem inactive className="chat-item-clickable">
|
||||
<PrivateChatInfo
|
||||
|
||||
@ -208,7 +208,7 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
||||
return (
|
||||
<div className="Management">
|
||||
{noAdmins && renderSearchField()}
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
{canHideParticipants && !isChannel && (
|
||||
<div className="section">
|
||||
<ListItem icon="group" ripple onClick={handleToggleParticipantsHidden}>
|
||||
|
||||
@ -169,7 +169,7 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
||||
style={`--shift-height: ${ITEMS_COUNT * ITEM_HEIGHT}px;`
|
||||
+ `--before-shift-height: ${BEFORE_ITEMS_COUNT * ITEM_HEIGHT}px;`}
|
||||
>
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section without-bottom-shadow">
|
||||
<h3 className="section-heading" dir="auto">{lang('ChannelPermissionsHeader')}</h3>
|
||||
<PermissionCheckboxList
|
||||
|
||||
@ -158,7 +158,7 @@ const ManageInvite: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management ManageInvite">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section">
|
||||
<Checkbox
|
||||
label={lang('ApproveNewMembers')}
|
||||
@ -177,7 +177,7 @@ const ManageInvite: FC<OwnProps & StateProps> = ({
|
||||
<p className="section-help hint">{lang('LinkNameHelp')}</p>
|
||||
</div>
|
||||
<div className="section">
|
||||
<div className="section-header">{lang('LimitByPeriod')}</div>
|
||||
<div className="section-heading">{lang('LimitByPeriod')}</div>
|
||||
<RadioGroup
|
||||
name="expireOptions"
|
||||
options={[
|
||||
@ -214,7 +214,7 @@ const ManageInvite: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
{!isRequestNeeded && (
|
||||
<div className="section">
|
||||
<div className="section-header">{lang('LimitNumberOfUses')}</div>
|
||||
<div className="section-heading">{lang('LimitNumberOfUses')}</div>
|
||||
<RadioGroup
|
||||
name="usageOptions"
|
||||
options={[
|
||||
|
||||
@ -1,20 +1,19 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useEffect } from '../../../lib/teact/teact';
|
||||
import React, { memo, useEffect } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiChatInviteImporter, ApiExportedInvite, ApiUser } from '../../../api/types';
|
||||
|
||||
import { isChatChannel } from '../../../global/helpers';
|
||||
import { selectChat, selectTabState } from '../../../global/selectors';
|
||||
import { copyTextToClipboard } from '../../../util/clipboard';
|
||||
import { formatFullDate, formatMediaDateTime, formatTime } from '../../../util/dates/dateFormat';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
import LinkField from '../../common/LinkField';
|
||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||
import Button from '../../ui/Button';
|
||||
import ListItem from '../../ui/ListItem';
|
||||
import Spinner from '../../ui/Spinner';
|
||||
|
||||
@ -44,7 +43,6 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
onClose,
|
||||
}) => {
|
||||
const {
|
||||
showNotification,
|
||||
loadChatInviteImporters,
|
||||
loadChatInviteRequesters,
|
||||
openChat,
|
||||
@ -64,13 +62,6 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [chatId, link, loadChatInviteImporters, loadChatInviteRequesters]);
|
||||
|
||||
const handleCopyClicked = useCallback(() => {
|
||||
copyTextToClipboard(invite!.link);
|
||||
showNotification({
|
||||
message: lang('LinkCopied'),
|
||||
});
|
||||
}, [invite, lang, showNotification]);
|
||||
|
||||
useHistoryBack({
|
||||
isActive,
|
||||
onBack: onClose,
|
||||
@ -81,7 +72,7 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
if (!importers) return <Spinner />;
|
||||
return (
|
||||
<div className="section">
|
||||
<p>{importers.length ? lang('PeopleJoined', usage) : lang('NoOneJoined')}</p>
|
||||
<p className="section-heading">{importers.length ? lang('PeopleJoined', usage) : lang('NoOneJoined')}</p>
|
||||
<p className="section-help">
|
||||
{!importers.length && (
|
||||
usageLimit ? lang('PeopleCanJoinViaLinkCount', usageLimit - usage) : lang('NoOneJoinedYet')
|
||||
@ -114,7 +105,7 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
if (!requesters?.length) return undefined;
|
||||
return (
|
||||
<div className="section">
|
||||
<p>{isChannel ? lang('SubscribeRequests') : lang('MemberRequests')}</p>
|
||||
<p className="section-heading">{isChannel ? lang('SubscribeRequests') : lang('MemberRequests')}</p>
|
||||
<p className="section-help">
|
||||
{requesters.map((requester) => (
|
||||
<ListItem
|
||||
@ -136,21 +127,14 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management ManageInviteInfo">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
{!invite && (
|
||||
<p className="section-help">{lang('Loading')}</p>
|
||||
)}
|
||||
{invite && (
|
||||
<>
|
||||
<div className="section">
|
||||
<h3 className="link-title">{invite.title || invite.link}</h3>
|
||||
<input
|
||||
className="form-control"
|
||||
value={invite.link}
|
||||
readOnly
|
||||
onClick={handleCopyClicked}
|
||||
/>
|
||||
<Button className="copy-link" onClick={handleCopyClicked}>{lang('CopyLink')}</Button>
|
||||
<LinkField title={invite.title} link={invite.link} className="invite-link" />
|
||||
{Boolean(expireDate) && (
|
||||
<p className="section-help">
|
||||
{isExpired
|
||||
@ -161,7 +145,7 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
{adminId && (
|
||||
<div className="section">
|
||||
<p>{lang('LinkCreatedeBy')}</p>
|
||||
<p className="section-heading">{lang('LinkCreatedeBy')}</p>
|
||||
<ListItem
|
||||
className="chat-item-clickable scroll-item small-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
|
||||
@ -26,7 +26,6 @@ import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||
import Icon from '../../common/icons/Icon';
|
||||
import LinkField from '../../common/LinkField';
|
||||
import NothingFound from '../../common/NothingFound';
|
||||
import Button from '../../ui/Button';
|
||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||
import ListItem, { type MenuItemContextAction } from '../../ui/ListItem';
|
||||
|
||||
@ -274,7 +273,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management ManageInvites">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="section">
|
||||
<AnimatedIconWithPreview
|
||||
tgsUrl={LOCAL_TGS_URLS.Invite}
|
||||
@ -295,9 +294,9 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
)}
|
||||
<div className="section" teactFastList>
|
||||
<Button isText key="create" className="create-link" onClick={handleCreateNewClick}>
|
||||
<ListItem icon="add" withPrimaryColor key="create" className="create-link" onClick={handleCreateNewClick}>
|
||||
{oldLang('CreateNewLink')}
|
||||
</Button>
|
||||
</ListItem>
|
||||
{(!temporalInvites || !temporalInvites.length) && <NothingFound text="No links found" key="nothing" />}
|
||||
{temporalInvites?.map((invite) => (
|
||||
<ListItem
|
||||
|
||||
@ -191,7 +191,7 @@ const ManageReactions: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="panel-content custom-scroll">
|
||||
{ localReactionsLimit && shouldShowReactionsLimit && (
|
||||
<div className="section">
|
||||
<h3 className="section-heading">
|
||||
|
||||
@ -103,6 +103,10 @@
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.invite-link {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.section-info_push {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
@ -124,6 +128,7 @@
|
||||
.RangeSlider {
|
||||
margin-top: 2rem;
|
||||
margin-inline-start: 1rem;
|
||||
margin-inline-end: 1rem;
|
||||
}
|
||||
|
||||
.button-position {
|
||||
@ -170,6 +175,10 @@
|
||||
.ManageInvites {
|
||||
.create-link {
|
||||
margin-bottom: 0.5rem;
|
||||
.icon-add {
|
||||
margin-inline-start: 0.1875rem;
|
||||
margin-inline-end: 1.1875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ListItem-button {
|
||||
@ -209,6 +218,7 @@
|
||||
|
||||
.ManageInvite {
|
||||
.link-name {
|
||||
padding: 0 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
||||
@ -13,12 +13,13 @@
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 0.625rem;
|
||||
padding: 1.5rem;
|
||||
padding-inline-end: calc(1.5rem - var(--scrollbar-width));
|
||||
|
||||
@include mixins.side-panel-section;
|
||||
}
|
||||
@ -110,10 +111,23 @@
|
||||
}
|
||||
|
||||
.giveawayButton {
|
||||
margin-bottom: 0.5rem;
|
||||
margin: 0 -1rem 0.5rem;
|
||||
}
|
||||
|
||||
.giveawayIcon {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
}
|
||||
|
||||
.primaryLink {
|
||||
position: relative;
|
||||
margin: 0 1rem 0.5rem;
|
||||
}
|
||||
|
||||
.copy {
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ const BoostStatistics = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={buildClassName(styles.root, 'custom-scroll')}>
|
||||
<div className={buildClassName(styles.root, 'panel-content custom-scroll')}>
|
||||
{!isLoaded && <Loading />}
|
||||
{isLoaded && statsOverview && (
|
||||
<>
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
|
||||
border-top: 1px solid transparent;
|
||||
transition: border-top-color 0.3s ease-in-out;
|
||||
|
||||
:global(.lovely-chart--container) {
|
||||
font: inherit !important;
|
||||
font-size: 13px !important;
|
||||
|
||||
@ -189,17 +189,15 @@ const Statistics: FC<OwnProps & StateProps> = ({
|
||||
graphs, graphTitles, isReady, statistics, lang, chatId, loadStatisticsAsyncGraph, dcId, forceUpdate,
|
||||
]);
|
||||
|
||||
if (!isReady || !statistics) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={buildClassName(styles.root, 'custom-scroll', isReady && styles.ready)}>
|
||||
<StatisticsOverview
|
||||
statistics={statistics}
|
||||
type={isGroup ? 'group' : 'channel'}
|
||||
title={lang('StatisticOverview')}
|
||||
/>
|
||||
<div className={buildClassName(styles.root, 'panel-content custom-scroll', isReady && styles.ready)}>
|
||||
{statistics && (
|
||||
<StatisticsOverview
|
||||
statistics={statistics}
|
||||
type={isGroup ? 'group' : 'channel'}
|
||||
title={lang('StatisticOverview')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!loadedCharts.current.length && <Loading />}
|
||||
|
||||
@ -209,7 +207,7 @@ const Statistics: FC<OwnProps & StateProps> = ({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{Boolean((statistics as ApiChannelStatistics).recentPosts?.length) && (
|
||||
{Boolean((statistics as ApiChannelStatistics)?.recentPosts?.length) && (
|
||||
<div className={styles.messages}>
|
||||
<h2 className={styles.messagesTitle}>{lang('ChannelStats.Recent.Header')}</h2>
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
.root {
|
||||
padding: 1rem 0.75rem;
|
||||
padding: 1rem 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 0.0625rem solid var(--color-borders);
|
||||
}
|
||||
|
||||
@ -185,6 +185,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.primary {
|
||||
.ListItem-button {
|
||||
color: var(--color-primary);
|
||||
|
||||
.ListItem-main-icon {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-context-menu {
|
||||
position: absolute;
|
||||
|
||||
|
||||
@ -60,6 +60,7 @@ interface OwnProps {
|
||||
inactive?: boolean;
|
||||
focus?: boolean;
|
||||
destructive?: boolean;
|
||||
withPrimaryColor?: boolean;
|
||||
multiline?: boolean;
|
||||
isStatic?: boolean;
|
||||
allowSelection?: boolean;
|
||||
@ -98,6 +99,7 @@ const ListItem: FC<OwnProps> = ({
|
||||
inactive,
|
||||
focus,
|
||||
destructive,
|
||||
withPrimaryColor,
|
||||
multiline,
|
||||
isStatic,
|
||||
allowSelection,
|
||||
@ -209,6 +211,7 @@ const ListItem: FC<OwnProps> = ({
|
||||
contextMenuAnchor && 'has-menu-open',
|
||||
focus && 'focus',
|
||||
destructive && 'destructive',
|
||||
withPrimaryColor && 'primary',
|
||||
multiline && 'multiline',
|
||||
isStatic && 'is-static',
|
||||
withColorTransition && 'with-color-transition',
|
||||
|
||||
38
src/hooks/useMarkScrolled/useMarkScrolled.ts
Normal file
38
src/hooks/useMarkScrolled/useMarkScrolled.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import type { RefObject } from '../../lib/teact/teact';
|
||||
import { useEffect } from '../../lib/teact/teact';
|
||||
|
||||
import { requestMutation } from '../../lib/fasterdom/fasterdom';
|
||||
import { throttle } from '../../util/schedulers';
|
||||
|
||||
const THROTTLE_DELAY = 100;
|
||||
|
||||
const useMarkScrolled = ({
|
||||
containerRef, selector,
|
||||
}: {
|
||||
containerRef: RefObject<HTMLDivElement | null>;
|
||||
selector: string;
|
||||
}, deps: unknown[]) => {
|
||||
useEffect(() => {
|
||||
const elements = containerRef?.current?.querySelectorAll(selector);
|
||||
if (!elements?.length) return undefined;
|
||||
|
||||
const handleScroll = throttle((event: Event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
const isScrolled = target.scrollTop > 0;
|
||||
requestMutation(() => {
|
||||
target.classList.toggle('scrolled', isScrolled);
|
||||
});
|
||||
}, THROTTLE_DELAY);
|
||||
|
||||
elements.forEach((el) => el.addEventListener('scroll', handleScroll, { passive: true }));
|
||||
// Trigger the scroll handler immediately to apply the current state
|
||||
elements.forEach((el) => el.dispatchEvent(new Event('scroll', { bubbles: false })));
|
||||
|
||||
return () => {
|
||||
elements.forEach((el) => el.removeEventListener('scroll', handleScroll));
|
||||
};
|
||||
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps
|
||||
}, [containerRef, selector, ...deps]);
|
||||
};
|
||||
|
||||
export default useMarkScrolled;
|
||||
@ -17,7 +17,7 @@
|
||||
&-title {
|
||||
font-size: 16px;
|
||||
float: left;
|
||||
margin-right: 2em;
|
||||
margin: 0 1rem;
|
||||
text-transform: lowercase;
|
||||
|
||||
&:first-letter {
|
||||
|
||||
@ -211,7 +211,7 @@ $color-message-story-mention-to: #74bcff;
|
||||
--border-radius-forum-avatar: 33.3333%;
|
||||
--messages-container-width: 45.5rem;
|
||||
--right-column-width: 26.5rem;
|
||||
--header-height: 3.5rem;
|
||||
--header-height: 3.4375rem;
|
||||
--custom-emoji-size: 1.25rem;
|
||||
--emoji-size: 1.25rem;
|
||||
--custom-emoji-border-radius: 0;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user