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