Settings: Redesign (#6922)
Co-authored-by: Alexander Zinchuk <alx.zinchuk@gmail.com>
This commit is contained in:
parent
c4b1bad481
commit
60f3995e82
@ -22,6 +22,7 @@ type OwnProps = {
|
|||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
withShare?: boolean;
|
withShare?: boolean;
|
||||||
|
noTitle?: boolean;
|
||||||
onRevoke?: VoidFunction;
|
onRevoke?: VoidFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ const InviteLink: FC<OwnProps> = ({
|
|||||||
isDisabled,
|
isDisabled,
|
||||||
className,
|
className,
|
||||||
withShare,
|
withShare,
|
||||||
|
noTitle,
|
||||||
onRevoke,
|
onRevoke,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -76,9 +78,11 @@ const InviteLink: FC<OwnProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<p className={styles.title}>
|
{!noTitle && (
|
||||||
{oldLang(title || 'InviteLink.InviteLink')}
|
<p className={styles.title}>
|
||||||
</p>
|
{oldLang(title || 'InviteLink.InviteLink')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<div className={styles.primaryLink}>
|
<div className={styles.primaryLink}>
|
||||||
<input
|
<input
|
||||||
className={buildClassName('form-control', styles.input)}
|
className={buildClassName('form-control', styles.input)}
|
||||||
|
|||||||
@ -1,29 +1,5 @@
|
|||||||
@use "../../styles/mixins";
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
margin-bottom: 0.625rem;
|
padding-inline: 1.5rem;
|
||||||
padding: 1.5rem 1.5rem 0;
|
|
||||||
|
|
||||||
@include mixins.side-panel-section;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
position: relative;
|
|
||||||
font-size: 1rem;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
|
|
||||||
&[dir="rtl"] {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
padding-bottom: 1.5rem;
|
|
||||||
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sortableContainer {
|
.sortableContainer {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import useLang from '../../hooks/useLang';
|
|||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
import usePreviousDeprecated from '../../hooks/usePreviousDeprecated';
|
import usePreviousDeprecated from '../../hooks/usePreviousDeprecated';
|
||||||
|
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../gili/layout/Island';
|
||||||
import ConfirmDialog from '../ui/ConfirmDialog';
|
import ConfirmDialog from '../ui/ConfirmDialog';
|
||||||
import Draggable from '../ui/Draggable';
|
import Draggable from '../ui/Draggable';
|
||||||
import ListItem from '../ui/ListItem';
|
import ListItem from '../ui/ListItem';
|
||||||
@ -147,10 +148,10 @@ const ManageUsernames: FC<OwnProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.container}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<h4 className={styles.header} dir={lang.isRtl ? 'rtl' : undefined}>
|
{oldLang('lng_usernames_subtitle')}
|
||||||
{oldLang('lng_usernames_subtitle')}
|
</IslandTitle>
|
||||||
</h4>
|
<Island className={styles.container}>
|
||||||
<div className={styles.sortableContainer} style={`height: ${(usernames.length) * USERNAME_HEIGHT_PX}px`}>
|
<div className={styles.sortableContainer} style={`height: ${(usernames.length) * USERNAME_HEIGHT_PX}px`}>
|
||||||
{usernames.map((usernameData, i) => {
|
{usernames.map((usernameData, i) => {
|
||||||
const isDragged = state.draggedIndex === i;
|
const isDragged = state.draggedIndex === i;
|
||||||
@ -201,10 +202,10 @@ const ManageUsernames: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<p className={styles.description} dir={lang.isRtl ? 'rtl' : undefined}>
|
</Island>
|
||||||
{oldLang('lng_usernames_description')}
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
</p>
|
{oldLang('lng_usernames_description')}
|
||||||
</div>
|
</IslandDescription>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={Boolean(usernameForConfirm)}
|
isOpen={Boolean(usernameForConfirm)}
|
||||||
onClose={closeConfirmUsernameDialog}
|
onClose={closeConfirmUsernameDialog}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
@ -93,6 +94,7 @@ type OwnProps<CategoryType extends string> = {
|
|||||||
withPeerTypes?: boolean;
|
withPeerTypes?: boolean;
|
||||||
withPeerUsernames?: boolean;
|
withPeerUsernames?: boolean;
|
||||||
withDefaultPadding?: boolean;
|
withDefaultPadding?: boolean;
|
||||||
|
withIslands?: boolean;
|
||||||
onFilterChange?: (value: string) => void;
|
onFilterChange?: (value: string) => void;
|
||||||
onDisabledClick?: (id: string, isSelected: boolean) => void;
|
onDisabledClick?: (id: string, isSelected: boolean) => void;
|
||||||
onLoadMore?: () => void;
|
onLoadMore?: () => void;
|
||||||
@ -125,6 +127,7 @@ const PeerPicker = <CategoryType extends string = CustomPeerType>({
|
|||||||
withPeerTypes,
|
withPeerTypes,
|
||||||
withPeerUsernames,
|
withPeerUsernames,
|
||||||
withDefaultPadding,
|
withDefaultPadding,
|
||||||
|
withIslands,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
onDisabledClick,
|
onDisabledClick,
|
||||||
onLoadMore,
|
onLoadMore,
|
||||||
@ -250,6 +253,19 @@ const PeerPicker = <CategoryType extends string = CustomPeerType>({
|
|||||||
onFilterChange?.(value);
|
onFilterChange?.(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function renderSearchInput() {
|
||||||
|
return (
|
||||||
|
<InputText
|
||||||
|
id={searchInputId}
|
||||||
|
ref={inputRef}
|
||||||
|
value={filterValue}
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
placeholder={filterPlaceholder || oldLang('SelectChat')}
|
||||||
|
noMargin={withIslands}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const [viewportIds, getMore] = useInfiniteScroll(
|
const [viewportIds, getMore] = useInfiniteScroll(
|
||||||
onLoadMore, sortedItemIds, Boolean(filterValue),
|
onLoadMore, sortedItemIds, Boolean(filterValue),
|
||||||
);
|
);
|
||||||
@ -387,62 +403,78 @@ const PeerPicker = <CategoryType extends string = CustomPeerType>({
|
|||||||
? sections.some((s) => s.ids.length > 0)
|
? sections.some((s) => s.ids.length > 0)
|
||||||
: Boolean(viewportIds?.length);
|
: Boolean(viewportIds?.length);
|
||||||
|
|
||||||
|
const SearchWrapper = withIslands ? Island : 'div';
|
||||||
|
const ListWrapper = withIslands ? Island : 'div';
|
||||||
|
|
||||||
|
function renderListWrapper(content: TeactNode) {
|
||||||
|
return withIslands
|
||||||
|
? <ListWrapper className={styles.islandList}>{content}</ListWrapper>
|
||||||
|
: content;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.container, className)}>
|
<div className={buildClassName(styles.container, className)}>
|
||||||
{isSearchable && (
|
{isSearchable && (
|
||||||
<div className={buildClassName(styles.header, 'custom-scroll')} dir={oldLang.isRtl ? 'rtl' : undefined}>
|
<>
|
||||||
{selectedCategories?.map((category) => (
|
{(!withIslands || selectedIds.length > 0 || (selectedCategories && selectedCategories.length > 0)) && (
|
||||||
<PeerChip
|
<SearchWrapper
|
||||||
className={styles.peerChip}
|
className={buildClassName(withIslands ? styles.islandHeader : styles.header, 'custom-scroll')}
|
||||||
customPeer={categoriesByType[category]}
|
dir={oldLang.isRtl ? 'rtl' : undefined}
|
||||||
onClick={handleItemClick}
|
>
|
||||||
clickArg={category}
|
{selectedCategories?.map((category) => (
|
||||||
canClose
|
<PeerChip
|
||||||
/>
|
className={styles.peerChip}
|
||||||
))}
|
customPeer={categoriesByType[category]}
|
||||||
{lockedSelectedIds?.map((id, i) => (
|
onClick={handleItemClick}
|
||||||
<PeerChip
|
clickArg={category}
|
||||||
className={styles.peerChip}
|
canClose
|
||||||
peerId={id}
|
/>
|
||||||
isMinimized={shouldMinimize && i < selectedIds.length - ALWAYS_FULL_ITEMS_COUNT}
|
))}
|
||||||
forceShowSelf={forceShowSelf}
|
{lockedSelectedIds?.map((id, i) => (
|
||||||
onClick={handleItemClick}
|
<PeerChip
|
||||||
clickArg={id}
|
className={styles.peerChip}
|
||||||
/>
|
peerId={id}
|
||||||
))}
|
isMinimized={shouldMinimize && i < selectedIds.length - ALWAYS_FULL_ITEMS_COUNT}
|
||||||
{unlockedSelectedIds.map((id, i) => (
|
forceShowSelf={forceShowSelf}
|
||||||
<PeerChip
|
onClick={handleItemClick}
|
||||||
className={styles.peerChip}
|
clickArg={id}
|
||||||
peerId={id}
|
/>
|
||||||
isMinimized={
|
))}
|
||||||
shouldMinimize && i + (lockedSelectedIds?.length || 0) < selectedIds.length - ALWAYS_FULL_ITEMS_COUNT
|
{unlockedSelectedIds.map((id, i) => (
|
||||||
}
|
<PeerChip
|
||||||
canClose
|
className={styles.peerChip}
|
||||||
onClick={handleItemClick}
|
peerId={id}
|
||||||
clickArg={id}
|
isMinimized={
|
||||||
/>
|
shouldMinimize
|
||||||
))}
|
&& i + (lockedSelectedIds?.length || 0) < selectedIds.length - ALWAYS_FULL_ITEMS_COUNT
|
||||||
<InputText
|
}
|
||||||
id={searchInputId}
|
canClose
|
||||||
ref={inputRef}
|
onClick={handleItemClick}
|
||||||
value={filterValue}
|
clickArg={id}
|
||||||
onChange={handleFilterChange}
|
/>
|
||||||
placeholder={filterPlaceholder || oldLang('SelectChat')}
|
))}
|
||||||
/>
|
{!withIslands && renderSearchInput()}
|
||||||
</div>
|
</SearchWrapper>
|
||||||
|
)}
|
||||||
|
{withIslands && (
|
||||||
|
<Island>{renderSearchInput()}</Island>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasContent ? (
|
{hasContent ? (
|
||||||
<InfiniteScroll
|
renderListWrapper(
|
||||||
className={buildClassName(styles.pickerList, withDefaultPadding && styles.padded, 'custom-scroll')}
|
<InfiniteScroll
|
||||||
items={viewportIds}
|
className={buildClassName(styles.pickerList, withDefaultPadding && styles.padded, 'custom-scroll')}
|
||||||
itemSelector={`.${ITEM_CLASS_NAME}`}
|
items={viewportIds}
|
||||||
beforeChildren={beforeChildren}
|
itemSelector={`.${ITEM_CLASS_NAME}`}
|
||||||
onLoadMore={getMore}
|
beforeChildren={beforeChildren}
|
||||||
noScrollRestore={noScrollRestore}
|
onLoadMore={getMore}
|
||||||
>
|
noScrollRestore={noScrollRestore}
|
||||||
{renderItems()}
|
>
|
||||||
</InfiniteScroll>
|
{renderItems()}
|
||||||
|
</InfiniteScroll>,
|
||||||
|
)
|
||||||
) : !isLoading && viewportIds && !viewportIds.length ? (
|
) : !isLoading && viewportIds && !viewportIds.length ? (
|
||||||
<p className={styles.noResults}>{notFoundText || 'Sorry, nothing found.'}</p>
|
<p className={styles.noResults}>{notFoundText || 'Sorry, nothing found.'}</p>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -32,6 +32,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.islandHeader {
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
max-height: 16.5rem;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.pickerCategoryTitle {
|
.pickerCategoryTitle {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
padding-inline: 0.5rem;
|
padding-inline: 0.5rem;
|
||||||
@ -77,6 +87,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.islandList {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
min-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.noResults {
|
.noResults {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -17,12 +17,28 @@
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left, .bottom {
|
.left {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
max-height: 25rem;
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
||||||
|
transition: max-height 0.25s ease-in-out, opacity 0.25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed {
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--color-error);
|
color: var(--color-error);
|
||||||
@ -108,31 +124,3 @@
|
|||||||
animation-name: none;
|
animation-name: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include mixins.on-active-vt('profileBusinessHoursExpand') {
|
|
||||||
&::view-transition-old(.expandArrow) {
|
|
||||||
animation-name: vt-expand-icon-spin;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::view-transition-new(.expandArrow) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::view-transition-old(.businessHours) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include mixins.on-active-vt('profileBusinessHoursCollapse') {
|
|
||||||
&::view-transition-old(.expandArrow) {
|
|
||||||
animation-name: vt-collapse-icon-spin;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::view-transition-new(.expandArrow) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::view-transition-new(.businessHours) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -7,8 +7,6 @@ import type { ApiBusinessWorkHours } from '../../../api/types';
|
|||||||
import { selectTimezones } from '../../../global/selectors';
|
import { selectTimezones } from '../../../global/selectors';
|
||||||
import {
|
import {
|
||||||
VTT_PROFILE_BUSINESS_HOURS,
|
VTT_PROFILE_BUSINESS_HOURS,
|
||||||
VTT_PROFILE_BUSINESS_HOURS_COLLAPSE,
|
|
||||||
VTT_PROFILE_BUSINESS_HOURS_EXPAND,
|
|
||||||
} from '../../../util/animations/viewTransitionTypes';
|
} from '../../../util/animations/viewTransitionTypes';
|
||||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
@ -105,13 +103,9 @@ const BusinessHours = ({
|
|||||||
|
|
||||||
const handleClick = useLastCallback(() => {
|
const handleClick = useLastCallback(() => {
|
||||||
if (isExpanded) {
|
if (isExpanded) {
|
||||||
startViewTransition(VTT_PROFILE_BUSINESS_HOURS_COLLAPSE, () => {
|
collapse();
|
||||||
collapse();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
startViewTransition(VTT_PROFILE_BUSINESS_HOURS_EXPAND, () => {
|
expand();
|
||||||
expand();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -151,36 +145,34 @@ const BusinessHours = ({
|
|||||||
</div>
|
</div>
|
||||||
<Icon className={styles.arrow} style={createVtnStyle('expandArrow', true)} name={isExpanded ? 'up' : 'down'} />
|
<Icon className={styles.arrow} style={createVtnStyle('expandArrow', true)} name={isExpanded ? 'up' : 'down'} />
|
||||||
</div>
|
</div>
|
||||||
{isExpanded && (
|
<div className={buildClassName(styles.bottom, !isExpanded && styles.collapsed)} aria-hidden={!isExpanded}>
|
||||||
<div className={styles.bottom}>
|
{Boolean(timezoneMinuteDifference) && (
|
||||||
{Boolean(timezoneMinuteDifference) && (
|
<div
|
||||||
<div
|
className={styles.offsetTrigger}
|
||||||
className={styles.offsetTrigger}
|
style={createVtnStyle('offsetTrigger')}
|
||||||
style={createVtnStyle('offsetTrigger')}
|
role="button"
|
||||||
role="button"
|
tabIndex={isExpanded ? 0 : -1}
|
||||||
tabIndex={0}
|
onMouseDown={!IS_TOUCH_ENV ? handleTriggerOffset : undefined}
|
||||||
onMouseDown={!IS_TOUCH_ENV ? handleTriggerOffset : undefined}
|
onClick={IS_TOUCH_ENV ? handleTriggerOffset : undefined}
|
||||||
onClick={IS_TOUCH_ENV ? handleTriggerOffset : undefined}
|
>
|
||||||
>
|
{oldLang(isMyTime ? 'BusinessHoursProfileSwitchMy' : 'BusinessHoursProfileSwitchLocal')}
|
||||||
{oldLang(isMyTime ? 'BusinessHoursProfileSwitchMy' : 'BusinessHoursProfileSwitchLocal')}
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
<dl className={styles.timetable}>
|
||||||
<dl className={styles.timetable}>
|
{DAYS.map((day) => (
|
||||||
{DAYS.map((day) => (
|
<>
|
||||||
<>
|
<dt className={buildClassName(styles.weekday, day === currentDay && styles.currentDay)}>
|
||||||
<dt className={buildClassName(styles.weekday, day === currentDay && styles.currentDay)}>
|
{formatWeekday(oldLang, day === 6 ? 0 : day + 1)}
|
||||||
{formatWeekday(oldLang, day === 6 ? 0 : day + 1)}
|
</dt>
|
||||||
</dt>
|
<dd className={styles.schedule}>
|
||||||
<dd className={styles.schedule}>
|
{workHours[day].map((segment) => (
|
||||||
{workHours[day].map((segment) => (
|
<div>{segment}</div>
|
||||||
<div>{segment}</div>
|
))}
|
||||||
))}
|
</dd>
|
||||||
</dd>
|
</>
|
||||||
</>
|
))}
|
||||||
))}
|
</dl>
|
||||||
</dl>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -76,7 +76,6 @@ type OwnProps = {
|
|||||||
isOwnProfile?: boolean;
|
isOwnProfile?: boolean;
|
||||||
isSavedDialog?: boolean;
|
isSavedDialog?: boolean;
|
||||||
isInSettings?: boolean;
|
isInSettings?: boolean;
|
||||||
withIslands?: boolean;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: string;
|
style?: string;
|
||||||
};
|
};
|
||||||
@ -132,7 +131,6 @@ const ChatExtra = ({
|
|||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
isInSettings,
|
isInSettings,
|
||||||
withIslands,
|
|
||||||
canViewSubscribers,
|
canViewSubscribers,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
@ -392,17 +390,15 @@ const ChatExtra = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = withIslands ? Island : 'div';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName('ChatExtra', className)} style={style || createVtnStyle('chatExtra')}>
|
<div className={buildClassName('ChatExtra', className)} style={style || createVtnStyle('chatExtra')}>
|
||||||
{user && userFullInfo?.isUnofficialSecurityRisk && (
|
{user && userFullInfo?.isUnofficialSecurityRisk && (
|
||||||
<Wrapper className={withIslands ? styles.securityRiskIsland : undefined}>
|
<Island className={styles.securityRiskIsland}>
|
||||||
<div className={styles.unofficialSecurityRisk}>
|
<div className={styles.unofficialSecurityRisk}>
|
||||||
<Icon className={buildClassName(styles.riskIcon, 'in-text-icon')} name="info-filled" />
|
<Icon className={buildClassName(styles.riskIcon, 'in-text-icon')} name="info-filled" />
|
||||||
{lang('UnofficialSecurityRisk', { peer: getPeerTitle(lang, user) })}
|
{lang('UnofficialSecurityRisk', { peer: getPeerTitle(lang, user) })}
|
||||||
</div>
|
</div>
|
||||||
</Wrapper>
|
</Island>
|
||||||
)}
|
)}
|
||||||
{personalChannel && (
|
{personalChannel && (
|
||||||
<div className={styles.personalChannel} style={createVtnStyle('personalChannel')}>
|
<div className={styles.personalChannel} style={createVtnStyle('personalChannel')}>
|
||||||
@ -410,7 +406,7 @@ const ChatExtra = ({
|
|||||||
<span className={styles.personalChannelSubscribers}>
|
<span className={styles.personalChannelSubscribers}>
|
||||||
{oldLang('Subscribers', personalChannel.membersCount, 'i')}
|
{oldLang('Subscribers', personalChannel.membersCount, 'i')}
|
||||||
</span>
|
</span>
|
||||||
<Wrapper className={styles.personalChannelItem}>
|
<Island className={styles.personalChannelItem}>
|
||||||
<Chat
|
<Chat
|
||||||
chatId={personalChannel.id}
|
chatId={personalChannel.id}
|
||||||
orderDiff={0}
|
orderDiff={0}
|
||||||
@ -419,10 +415,10 @@ const ChatExtra = ({
|
|||||||
isPreview
|
isPreview
|
||||||
previewMessageId={personalChannelMessageId}
|
previewMessageId={personalChannelMessageId}
|
||||||
/>
|
/>
|
||||||
</Wrapper>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Wrapper>
|
<Island>
|
||||||
{Boolean(formattedNumber?.length) && (
|
{Boolean(formattedNumber?.length) && (
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="phone"
|
icon="phone"
|
||||||
@ -647,7 +643,7 @@ const ChatExtra = ({
|
|||||||
{botVerification.description}
|
{botVerification.description}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Wrapper>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
@use "../../../styles/mixins";
|
@use "../../../styles/mixins";
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
--profile-info-bg: var(--color-background);
|
--profile-info-bg: var(--color-background-secondary);
|
||||||
--rating-outline-color: #000000;
|
--rating-outline-color: #000000;
|
||||||
--rating-text-color: #000000;
|
--rating-text-color: #000000;
|
||||||
|
|
||||||
|
|||||||
@ -57,3 +57,8 @@
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.outside {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
padding-inline: 0.5rem;
|
||||||
|
}
|
||||||
|
|||||||
@ -43,6 +43,24 @@ const IslandTitle = ({ className, children, ...otherProps }: OwnProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const IslandOutside = ({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...otherProps
|
||||||
|
}: OwnProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={buildClassName(
|
||||||
|
styles.outside,
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...otherProps}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const IslandText = ({ className, children, ...otherProps }: OwnProps) => {
|
const IslandText = ({ className, children, ...otherProps }: OwnProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -56,6 +74,7 @@ const IslandText = ({ className, children, ...otherProps }: OwnProps) => {
|
|||||||
|
|
||||||
export default Island;
|
export default Island;
|
||||||
export {
|
export {
|
||||||
|
IslandOutside,
|
||||||
IslandDescription,
|
IslandDescription,
|
||||||
IslandTitle,
|
IslandTitle,
|
||||||
IslandText,
|
IslandText,
|
||||||
|
|||||||
@ -12,6 +12,12 @@
|
|||||||
|
|
||||||
background-color: var(--color-background);
|
background-color: var(--color-background);
|
||||||
|
|
||||||
|
transition: background-color 150ms;
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,8 @@ import type { ReducerAction } from '../../hooks/useReducer';
|
|||||||
import { type AnimationLevel, LeftColumnContent, SettingsScreens } from '../../types';
|
import { type AnimationLevel, LeftColumnContent, SettingsScreens } from '../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
selectCurrentChat, selectIsCurrentUserFrozen, selectIsForumPanelOpen, selectTabState,
|
selectCurrentChat, selectIsCurrentUserFrozen, selectIsForumPanelOpen,
|
||||||
|
selectPeerHasProfileBackground, selectTabState,
|
||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
||||||
import {
|
import {
|
||||||
@ -61,6 +62,7 @@ type StateProps = {
|
|||||||
archiveSettings: GlobalState['archiveSettings'];
|
archiveSettings: GlobalState['archiveSettings'];
|
||||||
isArchivedStoryRibbonShown?: boolean;
|
isArchivedStoryRibbonShown?: boolean;
|
||||||
isAccountFrozen?: boolean;
|
isAccountFrozen?: boolean;
|
||||||
|
hasProfileBackground?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ContentType {
|
enum ContentType {
|
||||||
@ -98,6 +100,7 @@ function LeftColumn({
|
|||||||
archiveSettings,
|
archiveSettings,
|
||||||
isArchivedStoryRibbonShown,
|
isArchivedStoryRibbonShown,
|
||||||
isAccountFrozen,
|
isAccountFrozen,
|
||||||
|
hasProfileBackground,
|
||||||
isFoldersSidebarShown,
|
isFoldersSidebarShown,
|
||||||
}: OwnProps & StateProps) {
|
}: OwnProps & StateProps) {
|
||||||
const {
|
const {
|
||||||
@ -514,6 +517,7 @@ function LeftColumn({
|
|||||||
foldersDispatch={foldersDispatch}
|
foldersDispatch={foldersDispatch}
|
||||||
animationLevel={animationLevel}
|
animationLevel={animationLevel}
|
||||||
shouldSkipTransition={shouldSkipHistoryAnimations}
|
shouldSkipTransition={shouldSkipHistoryAnimations}
|
||||||
|
hasProfileBackground={hasProfileBackground}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -627,6 +631,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
archiveSettings,
|
archiveSettings,
|
||||||
isArchivedStoryRibbonShown: isArchivedRibbonShown,
|
isArchivedStoryRibbonShown: isArchivedRibbonShown,
|
||||||
isAccountFrozen,
|
isAccountFrozen,
|
||||||
|
hasProfileBackground: currentUserId
|
||||||
|
? selectPeerHasProfileBackground(global, currentUserId) : undefined,
|
||||||
contentKey: leftColumn.contentKey,
|
contentKey: leftColumn.contentKey,
|
||||||
settingsScreen: leftColumn.settingsScreen,
|
settingsScreen: leftColumn.settingsScreen,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import StarIcon from '../../common/icons/StarIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -19,20 +20,19 @@ function PremiumStatusItem({ premiumSection }: OwnProps) {
|
|||||||
const handleOpenPremiumModal = useLastCallback(() => openPremiumModal({ initialSection: premiumSection }));
|
const handleOpenPremiumModal = useLastCallback(() => openPremiumModal({ initialSection: premiumSection }));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<ListItem
|
<Island>
|
||||||
leftElement={<StarIcon className="icon ListItem-main-icon" type="premium" size="big" />}
|
<ListItem
|
||||||
onClick={handleOpenPremiumModal}
|
leftElement={<StarIcon className="icon ListItem-main-icon" type="premium" size="big" />}
|
||||||
>
|
onClick={handleOpenPremiumModal}
|
||||||
{lang('PrivacyLastSeenPremium')}
|
>
|
||||||
</ListItem>
|
{lang('PrivacyLastSeenPremium')}
|
||||||
<p
|
</ListItem>
|
||||||
className="settings-item-description-larger premium-info"
|
</Island>
|
||||||
dir={lang.isRtl ? 'rtl' : undefined}
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
>
|
|
||||||
{lang('lng_messages_privacy_premium_about')}
|
{lang('lng_messages_privacy_premium_about')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import PaidMessagePrice from '../../common/paidMessage/PaidMessagePrice';
|
import PaidMessagePrice from '../../common/paidMessage/PaidMessagePrice';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
import PremiumStatusItem from './PremiumStatusItem';
|
import PremiumStatusItem from './PremiumStatusItem';
|
||||||
@ -159,28 +160,27 @@ function PrivacyMessages({
|
|||||||
: oldLang('Users', noPaidReactionsForUsersCount, 'i');
|
: oldLang('Users', noPaidReactionsForUsersCount, 'i');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('RemoveFeeTitle')}
|
{lang('RemoveFeeTitle')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
<ListItem
|
<Island>
|
||||||
narrow
|
<ListItem
|
||||||
icon="delete-user"
|
narrow
|
||||||
|
icon="delete-user"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
openSettingsScreen({ screen: SettingsScreens.PrivacyNoPaidMessages });
|
openSettingsScreen({ screen: SettingsScreens.PrivacyNoPaidMessages });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="multiline-item full-size">
|
<div className="multiline-item full-size">
|
||||||
<span className="title">{lang('ExceptionTitlePrivacyChargeForMessages')}</span>
|
<span className="title">{lang('ExceptionTitlePrivacyChargeForMessages')}</span>
|
||||||
<span className="subtitle">
|
<span className="subtitle">
|
||||||
{
|
{itemSubtitle}
|
||||||
itemSubtitle
|
</span>
|
||||||
}
|
</div>
|
||||||
</span>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</ListItem>
|
</>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,34 +195,34 @@ function PrivacyMessages({
|
|||||||
}, [shouldChargeForMessages, lang]);
|
}, [shouldChargeForMessages, lang]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
{oldLang('PrivacyMessagesTitle')}
|
||||||
{oldLang('PrivacyMessagesTitle')}
|
</IslandTitle>
|
||||||
</h4>
|
<Island>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="privacy-messages"
|
name="privacy-messages"
|
||||||
options={options}
|
options={options}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
selected={selectedValue}
|
selected={selectedValue}
|
||||||
/>
|
/>
|
||||||
<p className="settings-item-description-larger" dir={lang.isRtl ? 'rtl' : undefined}>
|
</Island>
|
||||||
{privacyDescription}
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
</p>
|
{privacyDescription}
|
||||||
</div>
|
</IslandDescription>
|
||||||
{selectedValue === 'charge_for_messages' && (
|
{selectedValue === 'charge_for_messages' && (
|
||||||
<div className="settings-item fluid-container">
|
<Island>
|
||||||
<PaidMessagePrice
|
<PaidMessagePrice
|
||||||
canChangeChargeForMessages={canChangeChargeForMessages}
|
canChangeChargeForMessages={canChangeChargeForMessages}
|
||||||
chargeForMessages={chargeForMessages}
|
chargeForMessages={chargeForMessages}
|
||||||
onChange={handleChargeForMessagesChange}
|
onChange={handleChargeForMessagesChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
)}
|
)}
|
||||||
{canChangeChargeForMessages && selectedValue === 'charge_for_messages' && renderSectionNoPaidMessagesForUsers()}
|
{canChangeChargeForMessages && selectedValue === 'charge_for_messages' && renderSectionNoPaidMessagesForUsers()}
|
||||||
{!isCurrentUserPremium && selectedValue !== 'charge_for_messages'
|
{!isCurrentUserPremium && selectedValue !== 'charge_for_messages'
|
||||||
&& <PremiumStatusItem premiumSection="message_privacy" />}
|
&& <PremiumStatusItem premiumSection="message_privacy" />}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,14 @@
|
|||||||
#Settings {
|
#Settings {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
.chat-list {
|
||||||
|
padding-bottom: 0;
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
padding-block: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.with-notch::before {
|
.with-notch::before {
|
||||||
top: var(--header-height);
|
top: var(--header-height);
|
||||||
}
|
}
|
||||||
@ -15,11 +23,11 @@
|
|||||||
|
|
||||||
.left-header {
|
.left-header {
|
||||||
padding-right: 0.8125rem;
|
padding-right: 0.8125rem;
|
||||||
}
|
|
||||||
|
|
||||||
.self-profile .ProfileInfo {
|
&:has(~ .scrolled),
|
||||||
margin: -0.5rem 0 0.75rem -0.5rem;
|
&:has(~ .settings-fab-wrapper .scrolled) {
|
||||||
margin-inline-end: calc(min(var(--scrollbar-width) - 0.5rem, 0px));
|
background-color: var(--color-background);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +51,14 @@
|
|||||||
|
|
||||||
.settings-content {
|
.settings-content {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
|
||||||
height: calc(100% - var(--header-height));
|
height: calc(100% - var(--header-height));
|
||||||
|
padding: 1rem;
|
||||||
|
padding-top: 0;
|
||||||
|
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
|
||||||
|
@include mixins.adapt-padding-to-scrollbar(1rem);
|
||||||
|
|
||||||
&.password-form .input-group.error label::first-letter {
|
&.password-form .input-group.error label::first-letter {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@ -82,7 +97,9 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
padding: 0 1.5rem;
|
margin-bottom: 1rem;
|
||||||
|
padding-block: 0;
|
||||||
|
padding-inline: 1.5rem;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
@ -105,21 +122,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-main-menu {
|
|
||||||
padding: 0.5rem;
|
|
||||||
|
|
||||||
@include mixins.adapt-padding-to-scrollbar(0.5rem);
|
|
||||||
@include mixins.side-panel-section;
|
|
||||||
|
|
||||||
.ListItem.narrow:not(.multiline) {
|
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
.ListItem-button {
|
|
||||||
min-height: 3.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-range-value {
|
.settings-range-value {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -137,8 +139,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-unlock-button {
|
.settings-unlock-button {
|
||||||
margin-top: 1rem;
|
margin-top: 0.5rem;
|
||||||
margin-inline: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fluid-container {
|
.fluid-container {
|
||||||
@ -246,39 +247,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.blocked-list-item {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
.ListItem-button {
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.5rem;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Avatar {
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-info {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-name {
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: var(--font-weight-medium);
|
|
||||||
line-height: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-phone {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 1rem;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[dir="rtl"] {
|
&[dir="rtl"] {
|
||||||
.multiline-item .date {
|
.multiline-item .date {
|
||||||
float: left;
|
float: left;
|
||||||
@ -314,18 +282,63 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-language-transition {
|
||||||
|
height: auto;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-picker {
|
.settings-picker {
|
||||||
padding-block: 0;
|
padding-block: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-picker-islands {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.settings-input {
|
.settings-input {
|
||||||
padding: 0.5rem 1rem 0 1rem;
|
padding: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-input > :last-child {
|
||||||
|
--input-group-margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-group {
|
.settings-group {
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ListItem.blocked-list-item {
|
||||||
|
.ListItem-button {
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Avatar {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-name {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
line-height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-phone {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.settings-fab-wrapper {
|
.settings-fab-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: calc(100% - var(--header-height));
|
height: calc(100% - var(--header-height));
|
||||||
|
|||||||
@ -155,6 +155,7 @@ export type OwnProps = {
|
|||||||
foldersDispatch: FolderEditDispatch;
|
foldersDispatch: FolderEditDispatch;
|
||||||
animationLevel: AnimationLevel;
|
animationLevel: AnimationLevel;
|
||||||
shouldSkipTransition?: boolean;
|
shouldSkipTransition?: boolean;
|
||||||
|
hasProfileBackground?: boolean;
|
||||||
onReset: (forceReturnToChatList?: true | Event) => void;
|
onReset: (forceReturnToChatList?: true | Event) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -166,6 +167,7 @@ const Settings: FC<OwnProps> = ({
|
|||||||
onReset,
|
onReset,
|
||||||
animationLevel,
|
animationLevel,
|
||||||
shouldSkipTransition,
|
shouldSkipTransition,
|
||||||
|
hasProfileBackground,
|
||||||
}) => {
|
}) => {
|
||||||
const { closeShareChatFolderModal, openSettingsScreen } = getActions();
|
const { closeShareChatFolderModal, openSettingsScreen } = getActions();
|
||||||
|
|
||||||
@ -176,7 +178,8 @@ const Settings: FC<OwnProps> = ({
|
|||||||
|
|
||||||
useScrollNotch({
|
useScrollNotch({
|
||||||
containerRef,
|
containerRef,
|
||||||
selector: '.settings-content',
|
selector: '.Transition_slide-active .settings-content,'
|
||||||
|
+ ' .Transition_slide-active .settings-main-scroll',
|
||||||
}, [currentScreen]);
|
}, [currentScreen]);
|
||||||
|
|
||||||
const handleReset = useLastCallback((forceReturnToChatList?: true | Event) => {
|
const handleReset = useLastCallback((forceReturnToChatList?: true | Event) => {
|
||||||
@ -513,6 +516,7 @@ const Settings: FC<OwnProps> = ({
|
|||||||
currentScreen={currentScreen}
|
currentScreen={currentScreen}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
editedFolderId={foldersState.folderId}
|
editedFolderId={foldersState.folderId}
|
||||||
|
hasProfileBackground={hasProfileBackground}
|
||||||
/>
|
/>
|
||||||
{renderCurrentSectionContent(isScreenActive, activeKey)}
|
{renderCurrentSectionContent(isScreenActive, activeKey)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { selectIsCurrentUserPremium } from '../../../global/selectors';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Switcher from '../../ui/Switcher';
|
import Switcher from '../../ui/Switcher';
|
||||||
|
|
||||||
@ -89,54 +90,56 @@ const SettingsAcceptedGift = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('PrivacyAcceptedGiftTitle')}
|
{lang('PrivacyAcceptedGiftTitle')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
<ListItem onClick={handleLimitedEditionChange}>
|
<Island>
|
||||||
<span>{lang('PrivacyGiftLimitedEdition')}</span>
|
<ListItem onClick={handleLimitedEditionChange}>
|
||||||
<Switcher
|
<span>{lang('PrivacyGiftLimitedEdition')}</span>
|
||||||
id="limited_edition"
|
<Switcher
|
||||||
label={disallowedGifts?.shouldDisallowLimitedStarGifts ? lang('PrivacyDisableLimitedEditionStarGifts')
|
id="limited_edition"
|
||||||
: lang('PrivacyEnableLimitedEditionStarGifts')}
|
label={disallowedGifts?.shouldDisallowLimitedStarGifts ? lang('PrivacyDisableLimitedEditionStarGifts')
|
||||||
disabled={!isCurrentUserPremium}
|
: lang('PrivacyEnableLimitedEditionStarGifts')}
|
||||||
checked={!isCurrentUserPremium ? true : !disallowedGifts?.shouldDisallowLimitedStarGifts}
|
disabled={!isCurrentUserPremium}
|
||||||
/>
|
checked={!isCurrentUserPremium ? true : !disallowedGifts?.shouldDisallowLimitedStarGifts}
|
||||||
</ListItem>
|
/>
|
||||||
<ListItem onClick={handleUnlimitedEditionChange}>
|
</ListItem>
|
||||||
<span>{lang('PrivacyGiftUnlimited')}</span>
|
<ListItem onClick={handleUnlimitedEditionChange}>
|
||||||
<Switcher
|
<span>{lang('PrivacyGiftUnlimited')}</span>
|
||||||
id="unlimited"
|
<Switcher
|
||||||
label={disallowedGifts?.shouldDisallowUnlimitedStarGifts ? lang('PrivacyDisableUnlimitedStarGifts')
|
id="unlimited"
|
||||||
: lang('PrivacyEnableUnlimitedStarGifts')}
|
label={disallowedGifts?.shouldDisallowUnlimitedStarGifts ? lang('PrivacyDisableUnlimitedStarGifts')
|
||||||
disabled={!isCurrentUserPremium}
|
: lang('PrivacyEnableUnlimitedStarGifts')}
|
||||||
checked={!isCurrentUserPremium ? true : !disallowedGifts?.shouldDisallowUnlimitedStarGifts}
|
disabled={!isCurrentUserPremium}
|
||||||
/>
|
checked={!isCurrentUserPremium ? true : !disallowedGifts?.shouldDisallowUnlimitedStarGifts}
|
||||||
</ListItem>
|
/>
|
||||||
<ListItem onClick={handleUniqueChange}>
|
</ListItem>
|
||||||
<span>{lang('PrivacyGiftUnique')}</span>
|
<ListItem onClick={handleUniqueChange}>
|
||||||
<Switcher
|
<span>{lang('PrivacyGiftUnique')}</span>
|
||||||
id="unique"
|
<Switcher
|
||||||
label={disallowedGifts?.shouldDisallowUniqueStarGifts ? lang('PrivacyDisableUniqueStarGifts')
|
id="unique"
|
||||||
: lang('PrivacyEnableUniqueStarGifts')}
|
label={disallowedGifts?.shouldDisallowUniqueStarGifts ? lang('PrivacyDisableUniqueStarGifts')
|
||||||
disabled={!isCurrentUserPremium}
|
: lang('PrivacyEnableUniqueStarGifts')}
|
||||||
checked={!isCurrentUserPremium ? true : !disallowedGifts?.shouldDisallowUniqueStarGifts}
|
disabled={!isCurrentUserPremium}
|
||||||
/>
|
checked={!isCurrentUserPremium ? true : !disallowedGifts?.shouldDisallowUniqueStarGifts}
|
||||||
</ListItem>
|
/>
|
||||||
<ListItem onClick={handlePremiumSubscriptionChange}>
|
</ListItem>
|
||||||
<span>{lang('PrivacyGiftPremiumSubscription')}</span>
|
<ListItem onClick={handlePremiumSubscriptionChange}>
|
||||||
<Switcher
|
<span>{lang('PrivacyGiftPremiumSubscription')}</span>
|
||||||
id="premium_subscription"
|
<Switcher
|
||||||
label={disallowedGifts?.shouldDisallowPremiumGifts ? lang('PrivacyDisablePremiumGifts')
|
id="premium_subscription"
|
||||||
: lang('PrivacyEnablePremiumGifts')}
|
label={disallowedGifts?.shouldDisallowPremiumGifts ? lang('PrivacyDisablePremiumGifts')
|
||||||
disabled={!isCurrentUserPremium}
|
: lang('PrivacyEnablePremiumGifts')}
|
||||||
checked={!isCurrentUserPremium ? true : !disallowedGifts?.shouldDisallowPremiumGifts}
|
disabled={!isCurrentUserPremium}
|
||||||
/>
|
checked={!isCurrentUserPremium ? true : !disallowedGifts?.shouldDisallowPremiumGifts}
|
||||||
</ListItem>
|
/>
|
||||||
<p className="settings-item-description-larger" dir={lang.isRtl ? 'rtl' : undefined}>
|
</ListItem>
|
||||||
|
</Island>
|
||||||
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('PrivacyAcceptedGiftInfo')}
|
{lang('PrivacyAcceptedGiftInfo')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import useHistoryBack from '../../../hooks/useHistoryBack';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Island, { IslandTitle } from '../../gili/layout/Island';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
@ -141,76 +142,79 @@ const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
function renderCurrentSession(session: ApiSession) {
|
function renderCurrentSession(session: ApiSession) {
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('AuthSessionsCurrentSession')}
|
{lang('AuthSessionsCurrentSession')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
|
<Island>
|
||||||
<ListItem narrow inactive icon={`device-${getSessionIcon(session)}`} iconClassName="icon-device">
|
<ListItem narrow inactive icon={`device-${getSessionIcon(session)}`} iconClassName="icon-device">
|
||||||
<div className="multiline-item full-size" dir="auto">
|
<div className="multiline-item full-size" dir="auto">
|
||||||
<span className="title" dir="auto">{session.deviceModel}</span>
|
<span className="title" dir="auto">{session.deviceModel}</span>
|
||||||
<span className="subtitle black tight">
|
<span className="subtitle black tight">
|
||||||
{session.appName}
|
{session.appName}
|
||||||
{' '}
|
{' '}
|
||||||
{session.appVersion}
|
{session.appVersion}
|
||||||
,
|
,
|
||||||
{' '}
|
{' '}
|
||||||
{session.platform}
|
{session.platform}
|
||||||
{' '}
|
{' '}
|
||||||
{session.systemVersion}
|
{session.systemVersion}
|
||||||
</span>
|
</span>
|
||||||
<span className="subtitle">
|
<span className="subtitle">
|
||||||
{session.ip}
|
{session.ip}
|
||||||
{' '}
|
{' '}
|
||||||
-
|
-
|
||||||
{' '}
|
{' '}
|
||||||
{getLocation(session)}
|
{getLocation(session)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
{hasOtherSessions && (
|
|
||||||
<ListItem
|
|
||||||
className="destructive mb-0 no-icon"
|
|
||||||
icon="stop"
|
|
||||||
ripple
|
|
||||||
narrow
|
|
||||||
onClick={openConfirmTerminateAllDialog}
|
|
||||||
>
|
|
||||||
{lang('TerminateAllSessions')}
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
|
||||||
</div>
|
{hasOtherSessions && (
|
||||||
|
<ListItem
|
||||||
|
className="destructive mb-0 no-icon"
|
||||||
|
icon="stop"
|
||||||
|
ripple
|
||||||
|
narrow
|
||||||
|
onClick={openConfirmTerminateAllDialog}
|
||||||
|
>
|
||||||
|
{lang('TerminateAllSessions')}
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
</Island>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderOtherSessions(sessionHashes: string[]) {
|
function renderOtherSessions(sessionHashes: string[]) {
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('OtherSessions')}
|
{lang('OtherSessions')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
|
<Island>
|
||||||
{sessionHashes.map(renderSession)}
|
{sessionHashes.map(renderSession)}
|
||||||
</div>
|
</Island>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAutoTerminate() {
|
function renderAutoTerminate() {
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('TerminateOldSessionHeader')}
|
{lang('TerminateOldSessionHeader')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
|
<Island>
|
||||||
<p className="settings-item-description-larger">{lang('IfInactiveFor')}</p>
|
<p className="settings-item-description-larger">{lang('IfInactiveFor')}</p>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="session_ttl"
|
name="session_ttl"
|
||||||
options={AUTO_TERMINATE_OPTIONS}
|
options={AUTO_TERMINATE_OPTIONS}
|
||||||
selected={autoTerminateValue}
|
selected={autoTerminateValue}
|
||||||
onChange={handleChangeSessionTtl}
|
onChange={handleChangeSessionTtl}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import FullNameTitle from '../../common/FullNameTitle';
|
import FullNameTitle from '../../common/FullNameTitle';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import SettingsActiveWebsite from './SettingsActiveWebsite';
|
import SettingsActiveWebsite from './SettingsActiveWebsite';
|
||||||
@ -80,13 +81,14 @@ const SettingsActiveWebsites: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
function renderSessions(sessionHashes: string[]) {
|
function renderSessions(sessionHashes: string[]) {
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('WebSessionsTitle')}
|
{lang('WebSessionsTitle')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
|
<Island>
|
||||||
{sessionHashes.map(renderSession)}
|
{sessionHashes.map(renderSession)}
|
||||||
</div>
|
</Island>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +137,7 @@ const SettingsActiveWebsites: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
className="destructive mb-0 no-icon"
|
className="destructive mb-0 no-icon"
|
||||||
icon="stop"
|
icon="stop"
|
||||||
@ -145,10 +147,10 @@ const SettingsActiveWebsites: FC<OwnProps & StateProps> = ({
|
|||||||
>
|
>
|
||||||
{lang('AuthSessions.LogOutApplications')}
|
{lang('AuthSessions.LogOutApplications')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<p className={buildClassName('settings-item-description', styles.clearHelp)}>
|
</Island>
|
||||||
{lang('ClearOtherWebSessionsHelp')}
|
<IslandDescription className={styles.clearHelp}>
|
||||||
</p>
|
{lang('ClearOtherWebSessionsHelp')}
|
||||||
</div>
|
</IslandDescription>
|
||||||
{renderSessions(orderedHashes)}
|
{renderSessions(orderedHashes)}
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isConfirmTerminateAllDialogOpen}
|
isOpen={isConfirmTerminateAllDialogOpen}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver'
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import StickerSetCard from '../../common/StickerSetCard';
|
import StickerSetCard from '../../common/StickerSetCard';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -67,27 +68,31 @@ const SettingsCustomEmoji: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
{customEmojiSets && (
|
{customEmojiSets && (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<Checkbox
|
<Island>
|
||||||
label={lang('SuggestAnimatedEmoji')}
|
<Checkbox
|
||||||
checked={shouldSuggestCustomEmoji}
|
label={lang('SuggestAnimatedEmoji')}
|
||||||
onCheck={handleSuggestCustomEmojiChange}
|
checked={shouldSuggestCustomEmoji}
|
||||||
/>
|
onCheck={handleSuggestCustomEmojiChange}
|
||||||
<div className="mt-4" ref={stickerSettingsRef}>
|
/>
|
||||||
{customEmojiSets.map((stickerSet: ApiStickerSet) => (
|
</Island>
|
||||||
<StickerSetCard
|
<Island>
|
||||||
key={stickerSet.id}
|
<div ref={stickerSettingsRef}>
|
||||||
stickerSet={stickerSet}
|
{customEmojiSets.map((stickerSet: ApiStickerSet) => (
|
||||||
observeIntersection={observeIntersectionForCovers}
|
<StickerSetCard
|
||||||
onClick={handleStickerSetClick}
|
key={stickerSet.id}
|
||||||
noPlay={!canPlayAnimatedEmojis}
|
stickerSet={stickerSet}
|
||||||
/>
|
observeIntersection={observeIntersectionForCovers}
|
||||||
))}
|
onClick={handleStickerSetClick}
|
||||||
</div>
|
noPlay={!canPlayAnimatedEmojis}
|
||||||
<p className="settings-item-description mt-3" dir="auto">
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Island>
|
||||||
|
<IslandDescription dir="auto">
|
||||||
{renderText(lang('EmojiBotInfo'), ['links'])}
|
{renderText(lang('EmojiBotInfo'), ['links'])}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import useHistoryBack from '../../../hooks/useHistoryBack';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Island, { IslandTitle } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import RangeSlider from '../../ui/RangeSlider';
|
import RangeSlider from '../../ui/RangeSlider';
|
||||||
@ -84,7 +85,7 @@ const SettingsDataStorage = ({
|
|||||||
const value = AUTODOWNLOAD_FILESIZE_MB_LIMITS.indexOf(autoLoadFileMaxSizeMb);
|
const value = AUTODOWNLOAD_FILESIZE_MB_LIMITS.indexOf(autoLoadFileMaxSizeMb);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pt-5">
|
<div>
|
||||||
<RangeSlider
|
<RangeSlider
|
||||||
label={lang('AutoDownloadMaxFileSize')}
|
label={lang('AutoDownloadMaxFileSize')}
|
||||||
min={0}
|
min={0}
|
||||||
@ -106,37 +107,33 @@ const SettingsDataStorage = ({
|
|||||||
canAutoLoadInChannels: boolean,
|
canAutoLoadInChannels: boolean,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{title}</h4>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{title}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('AutoDownloadSettingsContacts')}
|
label={lang('AutoDownloadSettingsContacts')}
|
||||||
checked={canAutoLoadFromContacts}
|
checked={canAutoLoadFromContacts}
|
||||||
// TODO rewrite to support `useCallback`
|
// TODO rewrite to support `useCallback`
|
||||||
|
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}FromContacts`]: isChecked })}
|
||||||
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}FromContacts`]: isChecked })}
|
/>
|
||||||
/>
|
<Checkbox
|
||||||
<Checkbox
|
label={lang('AutoDownloadSettingsPrivateChats')}
|
||||||
label={lang('AutoDownloadSettingsPrivateChats')}
|
checked={canAutoLoadInPrivateChats}
|
||||||
checked={canAutoLoadInPrivateChats}
|
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InPrivateChats`]: isChecked })}
|
||||||
|
/>
|
||||||
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InPrivateChats`]: isChecked })}
|
<Checkbox
|
||||||
/>
|
label={lang('AutoDownloadSettingsGroupChats')}
|
||||||
<Checkbox
|
checked={canAutoLoadInGroups}
|
||||||
label={lang('AutoDownloadSettingsGroupChats')}
|
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InGroups`]: isChecked })}
|
||||||
checked={canAutoLoadInGroups}
|
/>
|
||||||
|
<Checkbox
|
||||||
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InGroups`]: isChecked })}
|
label={lang('AutoDownloadSettingsChannels')}
|
||||||
/>
|
checked={canAutoLoadInChannels}
|
||||||
<Checkbox
|
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InChannels`]: isChecked })}
|
||||||
label={lang('AutoDownloadSettingsChannels')}
|
/>
|
||||||
checked={canAutoLoadInChannels}
|
{key === 'File' && renderContentSizeSlider()}
|
||||||
|
</Island>
|
||||||
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InChannels`]: isChecked })}
|
</>
|
||||||
/>
|
|
||||||
|
|
||||||
{key === 'File' && renderContentSizeSlider()}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +163,7 @@ const SettingsDataStorage = ({
|
|||||||
canAutoLoadFileInGroups,
|
canAutoLoadFileInGroups,
|
||||||
canAutoLoadFileInChannels,
|
canAutoLoadFileInChannels,
|
||||||
)}
|
)}
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
onClick={handlePurge}
|
onClick={handlePurge}
|
||||||
icon="delete"
|
icon="delete"
|
||||||
@ -179,7 +176,7 @@ const SettingsDataStorage = ({
|
|||||||
{lang('SettingsDataClearMediaCacheDescription')}
|
{lang('SettingsDataClearMediaCacheDescription')}
|
||||||
</span>
|
</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,5 +5,6 @@
|
|||||||
|
|
||||||
.item {
|
.item {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-height: 25rem;
|
flex: 1 1 auto;
|
||||||
|
padding-block: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import ItemPicker, { type ItemPickerOption } from '../../common/pickers/ItemPicker';
|
import ItemPicker, { type ItemPickerOption } from '../../common/pickers/ItemPicker';
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
|
import InputText from '../../ui/InputText';
|
||||||
|
|
||||||
import styles from './SettingsDoNotTranslate.module.scss';
|
import styles from './SettingsDoNotTranslate.module.scss';
|
||||||
|
|
||||||
@ -79,7 +81,16 @@ const SettingsDoNotTranslate = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.root, 'settings-content infinite-scroll')}>
|
<div className={buildClassName(styles.root, 'settings-content infinite-scroll')}>
|
||||||
<div className={buildClassName(styles.item)}>
|
<Island>
|
||||||
|
<InputText
|
||||||
|
id="lang-picker-search"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.currentTarget.value)}
|
||||||
|
placeholder={lang('Search')}
|
||||||
|
noMargin
|
||||||
|
/>
|
||||||
|
</Island>
|
||||||
|
<Island className={styles.item}>
|
||||||
<ItemPicker
|
<ItemPicker
|
||||||
className={styles.picker}
|
className={styles.picker}
|
||||||
items={displayedOptionList}
|
items={displayedOptionList}
|
||||||
@ -87,13 +98,11 @@ const SettingsDoNotTranslate = ({
|
|||||||
onSelectedValuesChange={handleChange}
|
onSelectedValuesChange={handleChange}
|
||||||
filterValue={searchQuery}
|
filterValue={searchQuery}
|
||||||
onFilterChange={setSearchQuery}
|
onFilterChange={setSearchQuery}
|
||||||
isSearchable
|
|
||||||
allowMultiple
|
allowMultiple
|
||||||
withDefaultPadding
|
withDefaultPadding
|
||||||
itemInputType="checkbox"
|
itemInputType="checkbox"
|
||||||
searchInputId="lang-picker-search"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -26,6 +26,8 @@ import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated';
|
|||||||
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';
|
||||||
|
import Island, { IslandDescription, IslandOutside, IslandTitle } from '../../gili/layout/Island';
|
||||||
|
import Surface from '../../gili/layout/Surface';
|
||||||
import AvatarEditable from '../../ui/AvatarEditable';
|
import AvatarEditable from '../../ui/AvatarEditable';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
@ -218,28 +220,30 @@ const SettingsEditProfile = ({
|
|||||||
const purchaseInfoLink = `${TME_LINK_PREFIX}${PURCHASE_USERNAME}`;
|
const purchaseInfoLink = `${TME_LINK_PREFIX}${PURCHASE_USERNAME}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p className="settings-item-description" dir={oldLang.isRtl ? 'rtl' : undefined}>
|
<IslandDescription dir={oldLang.isRtl ? 'rtl' : undefined}>
|
||||||
{(oldLang('lng_username_purchase_available'))
|
{(oldLang('lng_username_purchase_available'))
|
||||||
.replace('{link}', '%PURCHASE_LINK%')
|
.replace('{link}', '%PURCHASE_LINK%')
|
||||||
.split('%')
|
.split('%')
|
||||||
.map((s) => {
|
.map((s) => {
|
||||||
return (s === 'PURCHASE_LINK' ? <SafeLink url={purchaseInfoLink} text={`@${PURCHASE_USERNAME}`} /> : s);
|
return (s === 'PURCHASE_LINK' ? <SafeLink url={purchaseInfoLink} text={`@${PURCHASE_USERNAME}`} /> : s);
|
||||||
})}
|
})}
|
||||||
</p>
|
</IslandDescription>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-fab-wrapper">
|
<div className="settings-fab-wrapper">
|
||||||
<div className="settings-content no-border custom-scroll">
|
<Surface scrollable className="settings-content no-border">
|
||||||
<div className="settings-item">
|
<IslandOutside className="settings-content-header">
|
||||||
|
<AvatarEditable
|
||||||
|
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
||||||
|
onChange={handlePhotoChange}
|
||||||
|
title={lang('AriaSettingsEditProfilePhoto')}
|
||||||
|
disabled={isLoading}
|
||||||
|
/>
|
||||||
|
</IslandOutside>
|
||||||
|
<Island>
|
||||||
<div className="settings-input">
|
<div className="settings-input">
|
||||||
<AvatarEditable
|
|
||||||
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
|
||||||
onChange={handlePhotoChange}
|
|
||||||
title={lang('AriaSettingsEditProfilePhoto')}
|
|
||||||
disabled={isLoading}
|
|
||||||
/>
|
|
||||||
<InputText
|
<InputText
|
||||||
value={firstName}
|
value={firstName}
|
||||||
onChange={handleFirstNameChange}
|
onChange={handleFirstNameChange}
|
||||||
@ -262,13 +266,12 @@ const SettingsEditProfile = ({
|
|||||||
maxLengthIndicator={maxBioLength ? (maxBioLength - bio.length).toString() : undefined}
|
maxLengthIndicator={maxBioLength ? (maxBioLength - bio.length).toString() : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</Island>
|
||||||
|
<IslandDescription dir={oldLang.isRtl ? 'rtl' : undefined}>
|
||||||
|
{renderText(oldLang('lng_settings_about_bio'), ['br', 'simple_markdown'])}
|
||||||
|
</IslandDescription>
|
||||||
|
|
||||||
<p className="settings-item-description" dir={oldLang.isRtl ? 'rtl' : undefined}>
|
<Island>
|
||||||
{renderText(oldLang('lng_settings_about_bio'), ['br', 'simple_markdown'])}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="settings-item">
|
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="gift"
|
icon="gift"
|
||||||
narrow
|
narrow
|
||||||
@ -279,21 +282,20 @@ const SettingsEditProfile = ({
|
|||||||
>
|
>
|
||||||
<span className="flex-grow">{lang('SettingsBirthday')}</span>
|
<span className="flex-grow">{lang('SettingsBirthday')}</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<p className="settings-item-description" dir={oldLang.isRtl ? 'rtl' : undefined}>
|
</Island>
|
||||||
{lang('BirthdayPrivacySuggestion', {
|
<IslandDescription dir={oldLang.isRtl ? 'rtl' : undefined}>
|
||||||
link: (
|
{lang('BirthdayPrivacySuggestion', {
|
||||||
<Link isPrimary onClick={handleBirthdayPrivacyClick}>
|
link: (
|
||||||
{lang('BirthdayPrivacySuggestionLink',
|
<Link isPrimary onClick={handleBirthdayPrivacyClick}>
|
||||||
undefined, { withNodes: true, specialReplacement: NEXT_ARROW_REPLACEMENT })}
|
{lang('BirthdayPrivacySuggestionLink',
|
||||||
</Link>
|
undefined, { withNodes: true, specialReplacement: NEXT_ARROW_REPLACEMENT })}
|
||||||
),
|
</Link>
|
||||||
}, { withNodes: true })}
|
),
|
||||||
</p>
|
}, { withNodes: true })}
|
||||||
</div>
|
</IslandDescription>
|
||||||
|
|
||||||
<div className="settings-item">
|
|
||||||
<h4 className="settings-item-header" dir={oldLang.isRtl ? 'rtl' : undefined}>{oldLang('Username')}</h4>
|
|
||||||
|
|
||||||
|
<IslandTitle dir={oldLang.isRtl ? 'rtl' : undefined}>{oldLang('Username')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<div className="settings-input">
|
<div className="settings-input">
|
||||||
<UsernameInput
|
<UsernameInput
|
||||||
currentUsername={currentUsername}
|
currentUsername={currentUsername}
|
||||||
@ -303,22 +305,21 @@ const SettingsEditProfile = ({
|
|||||||
onChange={handleUsernameChange}
|
onChange={handleUsernameChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</Island>
|
||||||
{editUsernameError === USERNAME_PURCHASE_ERROR && renderPurchaseLink()}
|
{editUsernameError === USERNAME_PURCHASE_ERROR && renderPurchaseLink()}
|
||||||
<p className="settings-item-description" dir={oldLang.isRtl ? 'rtl' : undefined}>
|
<IslandDescription dir={oldLang.isRtl ? 'rtl' : undefined}>
|
||||||
{renderText(oldLang('UsernameHelp'), ['br', 'simple_markdown'])}
|
{renderText(oldLang('UsernameHelp'), ['br', 'simple_markdown'])}
|
||||||
</p>
|
</IslandDescription>
|
||||||
{editableUsername && (
|
{editableUsername && (
|
||||||
<p className="settings-item-description" dir={oldLang.isRtl ? 'rtl' : undefined}>
|
<IslandDescription dir={oldLang.isRtl ? 'rtl' : undefined}>
|
||||||
{oldLang('lng_username_link')}
|
{oldLang('lng_username_link')}
|
||||||
<br />
|
<br />
|
||||||
<span className="username-link">
|
<span className="username-link">
|
||||||
{TME_LINK_PREFIX}
|
{TME_LINK_PREFIX}
|
||||||
{editableUsername}
|
{editableUsername}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</IslandDescription>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
{shouldRenderUsernamesManage && (
|
{shouldRenderUsernamesManage && (
|
||||||
<ManageUsernames
|
<ManageUsernames
|
||||||
@ -326,7 +327,7 @@ const SettingsEditProfile = ({
|
|||||||
onEditUsername={setEditableUsername}
|
onEditUsername={setEditableUsername}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Surface>
|
||||||
|
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={isSaveButtonShown}
|
isShown={isSaveButtonShown}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import useMultiaccountInfo from '../../../hooks/useMultiaccountInfo';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
import { animateSnap } from '../../main/visualEffects/SnapEffectContainer';
|
import { animateSnap } from '../../main/visualEffects/SnapEffectContainer';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -110,15 +111,15 @@ const SettingsExperimental = ({
|
|||||||
/>
|
/>
|
||||||
<p className="settings-item-description pt-3" dir="auto">{lang('lng_settings_experimental_about')}</p>
|
<p className="settings-item-description pt-3" dir="auto">{lang('lng_settings_experimental_about')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
href={newAccountUrl}
|
href={newAccountUrl}
|
||||||
icon="add-user"
|
icon="add-user"
|
||||||
>
|
>
|
||||||
<div className="title">Login on Test Server</div>
|
<div className="title">Login on Test Server</div>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
onClick={handleRequestConfetti}
|
onClick={handleRequestConfetti}
|
||||||
icon="animations"
|
icon="animations"
|
||||||
@ -141,45 +142,38 @@ const SettingsExperimental = ({
|
|||||||
>
|
>
|
||||||
<div className="title">Vaporize this button</div>
|
<div className="title">Vaporize this button</div>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label="Allow HTTP Transport"
|
label="Allow HTTP Transport"
|
||||||
checked={Boolean(shouldAllowHttpTransport)}
|
checked={Boolean(shouldAllowHttpTransport)}
|
||||||
|
|
||||||
onCheck={() => setSharedSettingOption({ shouldAllowHttpTransport: !shouldAllowHttpTransport })}
|
onCheck={() => setSharedSettingOption({ shouldAllowHttpTransport: !shouldAllowHttpTransport })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label="Force HTTP Transport"
|
label="Force HTTP Transport"
|
||||||
disabled={!shouldAllowHttpTransport}
|
disabled={!shouldAllowHttpTransport}
|
||||||
checked={Boolean(shouldForceHttpTransport)}
|
checked={Boolean(shouldForceHttpTransport)}
|
||||||
|
|
||||||
onCheck={() => setSharedSettingOption({ shouldForceHttpTransport: !shouldForceHttpTransport })}
|
onCheck={() => setSharedSettingOption({ shouldForceHttpTransport: !shouldForceHttpTransport })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('DebugMenuEnableLogs')}
|
label={lang('DebugMenuEnableLogs')}
|
||||||
checked={Boolean(shouldCollectDebugLogs)}
|
checked={Boolean(shouldCollectDebugLogs)}
|
||||||
|
|
||||||
onCheck={() => setSharedSettingOption({ shouldCollectDebugLogs: !shouldCollectDebugLogs })}
|
onCheck={() => setSharedSettingOption({ shouldCollectDebugLogs: !shouldCollectDebugLogs })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label="Enable exported senders debug"
|
label="Enable exported senders debug"
|
||||||
checked={Boolean(shouldDebugExportedSenders)}
|
checked={Boolean(shouldDebugExportedSenders)}
|
||||||
|
|
||||||
onCheck={() => setSharedSettingOption({ shouldDebugExportedSenders: !shouldDebugExportedSenders })}
|
onCheck={() => setSharedSettingOption({ shouldDebugExportedSenders: !shouldDebugExportedSenders })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListItem
|
<ListItem
|
||||||
onClick={handleDownloadLog}
|
onClick={handleDownloadLog}
|
||||||
icon="bug"
|
icon="bug"
|
||||||
>
|
>
|
||||||
<div className="title">Download log</div>
|
<div className="title">Download log</div>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import useAppLayout from '../../../hooks/useAppLayout';
|
|||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
|
import Island, { IslandTitle } from '../../gili/layout/Island';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
import RangeSlider from '../../ui/RangeSlider';
|
import RangeSlider from '../../ui/RangeSlider';
|
||||||
@ -117,9 +118,8 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('Settings')}</IslandTitle>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('Settings')}</h4>
|
<Island>
|
||||||
|
|
||||||
<RangeSlider
|
<RangeSlider
|
||||||
label={lang('TextSize')}
|
label={lang('TextSize')}
|
||||||
min={12}
|
min={12}
|
||||||
@ -127,52 +127,47 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
|||||||
value={messageTextSize}
|
value={messageTextSize}
|
||||||
onChange={handleMessageTextSizeChange}
|
onChange={handleMessageTextSizeChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="photo"
|
icon="photo"
|
||||||
narrow
|
narrow
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.GeneralChatBackground })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.GeneralChatBackground })}
|
||||||
>
|
>
|
||||||
{lang('ChatBackground')}
|
{lang('ChatBackground')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('Theme')}</IslandTitle>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<Island>
|
||||||
{lang('Theme')}
|
|
||||||
</h4>
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="theme"
|
name="theme"
|
||||||
options={appearanceThemeOptions}
|
options={appearanceThemeOptions}
|
||||||
selected={shouldUseSystemTheme ? 'auto' : theme}
|
selected={shouldUseSystemTheme ? 'auto' : theme}
|
||||||
onChange={handleAppearanceThemeChange}
|
onChange={handleAppearanceThemeChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('SettingsTimeFormat')}</IslandTitle>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<Island>
|
||||||
{lang('SettingsTimeFormat')}
|
|
||||||
</h4>
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="timeformat"
|
name="timeformat"
|
||||||
options={timeFormatOptions}
|
options={timeFormatOptions}
|
||||||
selected={timeFormat}
|
selected={timeFormat}
|
||||||
onChange={handleTimeFormatChange}
|
onChange={handleTimeFormatChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
{keyboardSendOptions && (
|
{keyboardSendOptions && (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('SettingsKeyboard')}</h4>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('SettingsKeyboard')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="keyboard-send-settings"
|
name="keyboard-send-settings"
|
||||||
options={keyboardSendOptions}
|
options={keyboardSendOptions}
|
||||||
onChange={handleMessageSendComboChange}
|
onChange={handleMessageSendComboChange}
|
||||||
selected={messageSendKeyCombo}
|
selected={messageSendKeyCombo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,13 +1,29 @@
|
|||||||
@use "../../../styles/mixins";
|
@use "../../../styles/mixins";
|
||||||
|
|
||||||
.SettingsGeneralBackground {
|
.SettingsGeneralBackground {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
|
||||||
.settings-wallpapers {
|
.settings-wallpapers {
|
||||||
|
overflow: clip;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-rows: 1fr;
|
grid-auto-rows: 1fr;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: 0.0625rem;
|
gap: 0.0625rem;
|
||||||
|
|
||||||
@include mixins.side-panel-section;
|
margin: 1rem -1rem 0;
|
||||||
|
margin-inline: calc(min(var(--scrollbar-width) - 1rem, 0px));
|
||||||
|
border-top-left-radius: var(--border-radius-island);
|
||||||
|
border-top-right-radius: var(--border-radius-island);
|
||||||
|
|
||||||
|
> :first-child .media-inner,
|
||||||
|
> :first-child::after {
|
||||||
|
border-top-left-radius: var(--border-radius-island);
|
||||||
|
}
|
||||||
|
|
||||||
|
> :nth-child(3) .media-inner,
|
||||||
|
> :nth-child(3)::after {
|
||||||
|
border-top-right-radius: var(--border-radius-island);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Loading {
|
.Loading {
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { openSystemFilesDialog } from '../../../util/systemFilesDialog';
|
|||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
@ -124,7 +125,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">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="camera-add"
|
icon="camera-add"
|
||||||
className="mb-0"
|
className="mb-0"
|
||||||
@ -151,7 +152,7 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
|||||||
checked={Boolean(isBlurred)}
|
checked={Boolean(isBlurred)}
|
||||||
onChange={handleWallPaperBlurChange}
|
onChange={handleWallPaperBlurChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
{loadedWallpapers ? (
|
{loadedWallpapers ? (
|
||||||
<div className="settings-wallpapers">
|
<div className="settings-wallpapers">
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
@use "../../../styles/mixins";
|
@use "../../../styles/mixins";
|
||||||
|
|
||||||
.SettingsGeneralBackgroundColor {
|
.SettingsGeneralBackgroundColor {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
|
||||||
&:not(.is-dragging) .handle {
|
&:not(.is-dragging) .handle {
|
||||||
transition: transform 300ms ease;
|
transition: transform 300ms ease;
|
||||||
}
|
}
|
||||||
@ -71,12 +73,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.predefined-colors {
|
.predefined-colors {
|
||||||
|
overflow: clip;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-rows: 1fr;
|
grid-auto-rows: 1fr;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: 0.0625rem;
|
gap: 0.0625rem;
|
||||||
|
|
||||||
@include mixins.side-panel-section;
|
margin: 1rem -1rem 0;
|
||||||
|
margin-inline: calc(min(var(--scrollbar-width) - 1rem, 0px));
|
||||||
|
border-top-left-radius: var(--border-radius-island);
|
||||||
|
border-top-right-radius: var(--border-radius-island);
|
||||||
}
|
}
|
||||||
|
|
||||||
.predefined-color {
|
.predefined-color {
|
||||||
@ -84,6 +90,14 @@
|
|||||||
box-shadow: inset 0 0 0 0 var(--color-background);
|
box-shadow: inset 0 0 0 0 var(--color-background);
|
||||||
transition: box-shadow 300ms ease;
|
transition: box-shadow 300ms ease;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
border-top-left-radius: var(--border-radius-island);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(3) {
|
||||||
|
border-top-right-radius: var(--border-radius-island);
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
border: 0.125rem solid var(--color-primary);
|
border: 0.125rem solid var(--color-primary);
|
||||||
box-shadow: inset 0 0 0 0.3125rem var(--color-background);
|
box-shadow: inset 0 0 0 0.3125rem var(--color-background);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import { pick } from '../../../util/iteratees';
|
|||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
|
|
||||||
import './SettingsGeneralBackgroundColor.scss';
|
import './SettingsGeneralBackgroundColor.scss';
|
||||||
@ -211,7 +212,7 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className={className}>
|
<div ref={containerRef} className={className}>
|
||||||
<div className="settings-item pt-3">
|
<Island>
|
||||||
<div ref={colorPickerRef} className="color-picker">
|
<div ref={colorPickerRef} className="color-picker">
|
||||||
<canvas />
|
<canvas />
|
||||||
<div
|
<div
|
||||||
@ -230,7 +231,7 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
|||||||
<InputText value={hexInput} label="HEX" onChange={handleHexChange} />
|
<InputText value={hexInput} label="HEX" onChange={handleHexChange} />
|
||||||
<InputText value={rgbInput} label="RGB" onChange={handleRgbChange} />
|
<InputText value={rgbInput} label="RGB" onChange={handleRgbChange} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Island>
|
||||||
<div className="predefined-colors">
|
<div className="predefined-colors">
|
||||||
{PREDEFINED_COLORS.map((color) => (
|
{PREDEFINED_COLORS.map((color) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -19,12 +19,14 @@ import MenuItem from '../../ui/MenuItem';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
currentScreen: SettingsScreens;
|
currentScreen: SettingsScreens;
|
||||||
editedFolderId?: number;
|
editedFolderId?: number;
|
||||||
|
hasProfileBackground?: boolean;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsHeader: FC<OwnProps> = ({
|
const SettingsHeader: FC<OwnProps> = ({
|
||||||
currentScreen,
|
currentScreen,
|
||||||
editedFolderId,
|
editedFolderId,
|
||||||
|
hasProfileBackground,
|
||||||
onReset,
|
onReset,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
@ -286,7 +288,9 @@ const SettingsHeader: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="left-header">
|
<div className={hasProfileBackground && currentScreen === SettingsScreens.Main
|
||||||
|
? 'left-header' : 'left-header secondary'}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
round
|
round
|
||||||
size="smaller"
|
size="smaller"
|
||||||
|
|||||||
@ -19,9 +19,11 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import ItemPicker, { type ItemPickerOption } from '../../common/pickers/ItemPicker';
|
import ItemPicker, { type ItemPickerOption } from '../../common/pickers/ItemPicker';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
@ -131,51 +133,56 @@ const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="settings-content settings-language custom-scroll">
|
<div className="settings-content settings-language custom-scroll">
|
||||||
{IS_TRANSLATION_SUPPORTED && (
|
{IS_TRANSLATION_SUPPORTED && (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<Checkbox
|
<Island>
|
||||||
label={lang('ShowTranslateButton')}
|
<Checkbox
|
||||||
checked={canTranslate}
|
label={lang('ShowTranslateButton')}
|
||||||
onCheck={handleShouldTranslateChange}
|
checked={canTranslate}
|
||||||
/>
|
onCheck={handleShouldTranslateChange}
|
||||||
<Checkbox
|
/>
|
||||||
label={lang('ShowTranslateChatButton')}
|
<Checkbox
|
||||||
checked={canTranslateChatsEnabled}
|
label={lang('ShowTranslateChatButton')}
|
||||||
disabled={!isCurrentUserPremium}
|
checked={canTranslateChatsEnabled}
|
||||||
rightIcon={!isCurrentUserPremium ? 'lock' : undefined}
|
disabled={!isCurrentUserPremium}
|
||||||
onClickLabel={handleShouldTranslateChatsClick}
|
rightIcon={!isCurrentUserPremium ? 'lock' : undefined}
|
||||||
onCheck={handleShouldTranslateChatsChange}
|
onClickLabel={handleShouldTranslateChatsClick}
|
||||||
/>
|
onCheck={handleShouldTranslateChatsChange}
|
||||||
{(canTranslate || canTranslateChatsEnabled) && (
|
/>
|
||||||
<ListItem
|
{(canTranslate || canTranslateChatsEnabled) && (
|
||||||
narrow
|
<ListItem
|
||||||
onClick={handleDoNotSelectOpen}
|
narrow
|
||||||
>
|
onClick={handleDoNotSelectOpen}
|
||||||
{lang('DoNotTranslate')}
|
>
|
||||||
<span className="settings-item__current-value">{doNotTranslateText}</span>
|
{lang('DoNotTranslate')}
|
||||||
</ListItem>
|
<span className="settings-item__current-value">{doNotTranslateText}</span>
|
||||||
)}
|
</ListItem>
|
||||||
<p className="settings-item-description mb-0 mt-1">
|
)}
|
||||||
|
</Island>
|
||||||
|
<IslandDescription>
|
||||||
{lang('lng_translate_settings_about')}
|
{lang('lng_translate_settings_about')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="settings-item settings-item-picker">
|
|
||||||
<h4 className="settings-item-header">
|
<Transition activeKey={options ? 1 : 0} name="fade" className="settings-language-transition">
|
||||||
{lang('Localization.InterfaceLanguage')}
|
|
||||||
</h4>
|
|
||||||
{options ? (
|
{options ? (
|
||||||
<ItemPicker
|
<>
|
||||||
items={options}
|
<IslandTitle>{lang('Localization.InterfaceLanguage')}</IslandTitle>
|
||||||
selectedValue={selectedLanguage}
|
<Island>
|
||||||
forceRenderAllItems
|
<ItemPicker
|
||||||
onSelectedValueChange={handleChange}
|
items={options}
|
||||||
itemInputType="radio"
|
selectedValue={selectedLanguage}
|
||||||
className="settings-picker"
|
forceRenderAllItems
|
||||||
/>
|
onSelectedValueChange={handleChange}
|
||||||
|
itemInputType="radio"
|
||||||
|
className="settings-picker"
|
||||||
|
/>
|
||||||
|
</Island>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Loading />
|
<Loading />
|
||||||
)}
|
)}
|
||||||
</div>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
22
src/components/left/settings/SettingsMain.module.scss
Normal file
22
src/components/left/settings/SettingsMain.module.scss
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@use "../../../styles/mixins";
|
||||||
|
|
||||||
|
.root {
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: calc(100% - var(--header-height));
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuSection,
|
||||||
|
.selfProfile {
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
@include mixins.adapt-padding-to-scrollbar(1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selfProfile {
|
||||||
|
padding-bottom: 0;
|
||||||
|
:global(.ProfileInfo) {
|
||||||
|
margin: -1rem 0 0.75rem -1rem;
|
||||||
|
margin-inline-end: calc(min(var(--scrollbar-width) - 1rem, 0px));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import {
|
|||||||
selectIsGiveawayGiftsPurchaseAvailable,
|
selectIsGiveawayGiftsPurchaseAvailable,
|
||||||
selectIsPremiumPurchaseBlocked,
|
selectIsPremiumPurchaseBlocked,
|
||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { convertCurrencyFromBaseUnit } from '../../../util/formatCurrency';
|
import { convertCurrencyFromBaseUnit } from '../../../util/formatCurrency';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
@ -22,9 +23,12 @@ import Icon from '../../common/icons/Icon';
|
|||||||
import StarIcon from '../../common/icons/StarIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import ChatExtra from '../../common/profile/ChatExtra';
|
import ChatExtra from '../../common/profile/ChatExtra';
|
||||||
import ProfileInfo from '../../common/profile/ProfileInfo';
|
import ProfileInfo from '../../common/profile/ProfileInfo';
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
|
import styles from './SettingsMain.module.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
@ -80,8 +84,8 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className={buildClassName(styles.root, 'settings-main-scroll', 'custom-scroll')}>
|
||||||
<div className="settings-main-menu self-profile">
|
<div className={styles.selfProfile}>
|
||||||
{currentUserId && (
|
{currentUserId && (
|
||||||
<ProfileInfo
|
<ProfileInfo
|
||||||
peerId={currentUserId}
|
peerId={currentUserId}
|
||||||
@ -96,153 +100,141 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="settings-main-menu">
|
<div className={styles.menuSection}>
|
||||||
<ListItem
|
<Island>
|
||||||
icon="settings"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.General })}
|
|
||||||
>
|
|
||||||
{lang('TelegramGeneralSettingsViewController')}
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
icon="animations"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.Performance })}
|
|
||||||
>
|
|
||||||
{lang('MenuAnimations')}
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
icon="unmute"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.Notifications })}
|
|
||||||
>
|
|
||||||
{lang('Notifications')}
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
icon="data"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.DataStorage })}
|
|
||||||
>
|
|
||||||
{lang('DataSettings')}
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
icon="lock"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.Privacy })}
|
|
||||||
>
|
|
||||||
{lang('PrivacySettings')}
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
icon="folder"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.Folders })}
|
|
||||||
>
|
|
||||||
{lang('Filters')}
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
icon="active-sessions"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.ActiveSessions })}
|
|
||||||
>
|
|
||||||
{lang('SessionsTitle')}
|
|
||||||
{sessionCount > 0 && (<span className="settings-item__current-value">{sessionCount}</span>)}
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
icon="language"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.Language })}
|
|
||||||
>
|
|
||||||
{lang('Language')}
|
|
||||||
<span className="settings-item__current-value">{lang.languageInfo.nativeName}</span>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
icon="stickers"
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.Stickers })}
|
|
||||||
>
|
|
||||||
{lang('MenuStickers')}
|
|
||||||
</ListItem>
|
|
||||||
</div>
|
|
||||||
<div className="settings-main-menu">
|
|
||||||
{canBuyPremium && (
|
|
||||||
<ListItem
|
<ListItem
|
||||||
leftElement={<StarIcon className="icon ListItem-main-icon" type="premium" size="big" />}
|
icon="settings"
|
||||||
narrow
|
narrow
|
||||||
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.General })}
|
||||||
onClick={() => openPremiumModal()}
|
|
||||||
>
|
>
|
||||||
{lang('TelegramPremium')}
|
{lang('TelegramGeneralSettingsViewController')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
|
||||||
<ListItem
|
|
||||||
leftElement={<StarIcon className="icon ListItem-main-icon" type="gold" size="big" />}
|
|
||||||
narrow
|
|
||||||
|
|
||||||
onClick={() => openStarsBalanceModal({})}
|
|
||||||
>
|
|
||||||
{lang('MenuStars')}
|
|
||||||
{Boolean(starsBalance) && (
|
|
||||||
<span className="settings-item__current-value">
|
|
||||||
{formatStarsAmount(lang, starsBalance)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
leftElement={<Icon className="icon ListItem-main-icon" name="toncoin" />}
|
|
||||||
narrow
|
|
||||||
onClick={() => openStarsBalanceModal({ currency: TON_CURRENCY_CODE })}
|
|
||||||
>
|
|
||||||
{lang('MenuTon')}
|
|
||||||
{Boolean(tonBalance) && (
|
|
||||||
<span className="settings-item__current-value">
|
|
||||||
{convertCurrencyFromBaseUnit(tonBalance.amount, tonBalance.currency)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</ListItem>
|
|
||||||
{isGiveawayAvailable && (
|
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="gift"
|
icon="animations"
|
||||||
narrow
|
narrow
|
||||||
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.Performance })}
|
||||||
onClick={() => openGiftRecipientPicker()}
|
|
||||||
>
|
>
|
||||||
{lang('MenuSendGift')}
|
{lang('MenuAnimations')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
<ListItem
|
||||||
</div>
|
icon="unmute"
|
||||||
<div className="settings-main-menu">
|
narrow
|
||||||
<ListItem
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.Notifications })}
|
||||||
icon="ask-support"
|
>
|
||||||
narrow
|
{lang('Notifications')}
|
||||||
onClick={openSupportDialog}
|
</ListItem>
|
||||||
>
|
<ListItem
|
||||||
{lang('AskAQuestion')}
|
icon="data"
|
||||||
</ListItem>
|
narrow
|
||||||
<ListItem
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.DataStorage })}
|
||||||
icon="help"
|
>
|
||||||
narrow
|
{lang('DataSettings')}
|
||||||
|
</ListItem>
|
||||||
onClick={() => openUrl({ url: FAQ_URL })}
|
<ListItem
|
||||||
>
|
icon="lock"
|
||||||
{lang('MenuTelegramFaq')}
|
narrow
|
||||||
</ListItem>
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.Privacy })}
|
||||||
<ListItem
|
>
|
||||||
icon="privacy-policy"
|
{lang('PrivacySettings')}
|
||||||
narrow
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
onClick={() => openUrl({ url: PRIVACY_URL })}
|
icon="folder"
|
||||||
>
|
narrow
|
||||||
{lang('MenuPrivacyPolicy')}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.Folders })}
|
||||||
</ListItem>
|
>
|
||||||
|
{lang('Filters')}
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
icon="active-sessions"
|
||||||
|
narrow
|
||||||
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.ActiveSessions })}
|
||||||
|
>
|
||||||
|
{lang('SessionsTitle')}
|
||||||
|
{sessionCount > 0 && (<span className="settings-item__current-value">{sessionCount}</span>)}
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
icon="language"
|
||||||
|
narrow
|
||||||
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.Language })}
|
||||||
|
>
|
||||||
|
{lang('Language')}
|
||||||
|
<span className="settings-item__current-value">{lang.languageInfo.nativeName}</span>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
icon="stickers"
|
||||||
|
narrow
|
||||||
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.Stickers })}
|
||||||
|
>
|
||||||
|
{lang('MenuStickers')}
|
||||||
|
</ListItem>
|
||||||
|
</Island>
|
||||||
|
<Island>
|
||||||
|
{canBuyPremium && (
|
||||||
|
<ListItem
|
||||||
|
leftElement={<StarIcon className="icon ListItem-main-icon" type="premium" size="big" />}
|
||||||
|
narrow
|
||||||
|
onClick={() => openPremiumModal()}
|
||||||
|
>
|
||||||
|
{lang('TelegramPremium')}
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
<ListItem
|
||||||
|
leftElement={<StarIcon className="icon ListItem-main-icon" type="gold" size="big" />}
|
||||||
|
narrow
|
||||||
|
onClick={() => openStarsBalanceModal({})}
|
||||||
|
>
|
||||||
|
{lang('MenuStars')}
|
||||||
|
{Boolean(starsBalance) && (
|
||||||
|
<span className="settings-item__current-value">
|
||||||
|
{formatStarsAmount(lang, starsBalance)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
leftElement={<Icon className="icon ListItem-main-icon" name="toncoin" />}
|
||||||
|
narrow
|
||||||
|
onClick={() => openStarsBalanceModal({ currency: TON_CURRENCY_CODE })}
|
||||||
|
>
|
||||||
|
{lang('MenuTon')}
|
||||||
|
{Boolean(tonBalance) && (
|
||||||
|
<span className="settings-item__current-value">
|
||||||
|
{convertCurrencyFromBaseUnit(tonBalance.amount, tonBalance.currency)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
|
{isGiveawayAvailable && (
|
||||||
|
<ListItem
|
||||||
|
icon="gift"
|
||||||
|
narrow
|
||||||
|
onClick={() => openGiftRecipientPicker()}
|
||||||
|
>
|
||||||
|
{lang('MenuSendGift')}
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
</Island>
|
||||||
|
<Island>
|
||||||
|
<ListItem
|
||||||
|
icon="ask-support"
|
||||||
|
narrow
|
||||||
|
onClick={openSupportDialog}
|
||||||
|
>
|
||||||
|
{lang('AskAQuestion')}
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
icon="help"
|
||||||
|
narrow
|
||||||
|
onClick={() => openUrl({ url: FAQ_URL })}
|
||||||
|
>
|
||||||
|
{lang('MenuTelegramFaq')}
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
icon="privacy-policy"
|
||||||
|
narrow
|
||||||
|
onClick={() => openUrl({ url: PRIVACY_URL })}
|
||||||
|
>
|
||||||
|
{lang('MenuPrivacyPolicy')}
|
||||||
|
</ListItem>
|
||||||
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isSupportDialogOpen}
|
isOpen={isSupportDialogOpen}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import useHistoryBack from '../../../hooks/useHistoryBack';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useRunDebounced from '../../../hooks/useRunDebounced';
|
import useRunDebounced from '../../../hooks/useRunDebounced';
|
||||||
|
|
||||||
|
import Island, { IslandTitle } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import RangeSlider from '../../ui/RangeSlider';
|
import RangeSlider from '../../ui/RangeSlider';
|
||||||
|
|
||||||
@ -140,10 +141,10 @@ const SettingsNotifications = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
{lang('NotificationsWeb')}
|
||||||
{lang('NotificationsWeb')}
|
</IslandTitle>
|
||||||
</h4>
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('NotificationsWeb')}
|
label={lang('NotificationsWeb')}
|
||||||
subLabel={lang(hasWebNotifications ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
subLabel={lang(hasWebNotifications ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||||
@ -160,22 +161,20 @@ const SettingsNotifications = ({
|
|||||||
checked={hasPushNotifications}
|
checked={hasPushNotifications}
|
||||||
onChange={handlePushNotificationsChange}
|
onChange={handlePushNotificationsChange}
|
||||||
/>
|
/>
|
||||||
<div className="settings-item-slider">
|
<RangeSlider
|
||||||
<RangeSlider
|
label={lang('NotificationsSound')}
|
||||||
label={lang('NotificationsSound')}
|
min={0}
|
||||||
min={0}
|
max={10}
|
||||||
max={10}
|
disabled={!areNotificationsSupported}
|
||||||
disabled={!areNotificationsSupported}
|
value={notificationSoundVolume}
|
||||||
value={notificationSoundVolume}
|
onChange={handleVolumeChange}
|
||||||
onChange={handleVolumeChange}
|
/>
|
||||||
/>
|
</Island>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="settings-item">
|
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
|
||||||
{lang('AutodownloadPrivateChats')}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
|
{lang('AutodownloadPrivateChats')}
|
||||||
|
</IslandTitle>
|
||||||
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('NotificationsForPrivateChats')}
|
label={lang('NotificationsForPrivateChats')}
|
||||||
subLabel={lang(!areUsersMuted ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
subLabel={lang(!areUsersMuted ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||||
@ -190,11 +189,10 @@ const SettingsNotifications = ({
|
|||||||
checked={Boolean(notifyDefaults?.users?.shouldShowPreviews)}
|
checked={Boolean(notifyDefaults?.users?.shouldShowPreviews)}
|
||||||
onChange={handlePrivateChatsPreviewChange}
|
onChange={handlePrivateChatsPreviewChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div className="settings-item">
|
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('FilterGroups')}</h4>
|
|
||||||
|
|
||||||
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('FilterGroups')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('NotificationsForGroups')}
|
label={lang('NotificationsForGroups')}
|
||||||
subLabel={lang(!areGroupsMuted ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
subLabel={lang(!areGroupsMuted ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||||
@ -209,11 +207,10 @@ const SettingsNotifications = ({
|
|||||||
checked={Boolean(notifyDefaults?.groups?.shouldShowPreviews)}
|
checked={Boolean(notifyDefaults?.groups?.shouldShowPreviews)}
|
||||||
onChange={handleGroupsPreviewChange}
|
onChange={handleGroupsPreviewChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div className="settings-item">
|
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('FilterChannels')}</h4>
|
|
||||||
|
|
||||||
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('FilterChannels')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('NotificationsForChannels')}
|
label={lang('NotificationsForChannels')}
|
||||||
subLabel={lang(!areChannelsMuted ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
subLabel={lang(!areChannelsMuted ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||||
@ -228,11 +225,10 @@ const SettingsNotifications = ({
|
|||||||
checked={Boolean(notifyDefaults?.channels?.shouldShowPreviews)}
|
checked={Boolean(notifyDefaults?.channels?.shouldShowPreviews)}
|
||||||
onChange={handleChannelsPreviewChange}
|
onChange={handleChannelsPreviewChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div className="settings-item">
|
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('PhoneOther')}</h4>
|
|
||||||
|
|
||||||
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('PhoneOther')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('ContactJoined')}
|
label={lang('ContactJoined')}
|
||||||
checked={hasContactJoinedNotifications}
|
checked={hasContactJoinedNotifications}
|
||||||
@ -243,7 +239,7 @@ const SettingsNotifications = ({
|
|||||||
checked={shouldNotifyAboutPinnedMessages}
|
checked={shouldNotifyAboutPinnedMessages}
|
||||||
onChange={handlePinnedMessagesNotificationChange}
|
onChange={handlePinnedMessagesNotificationChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||||
import CustomEmoji from '../../common/CustomEmoji';
|
import CustomEmoji from '../../common/CustomEmoji';
|
||||||
import Icon from '../../common/icons/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import Link from '../../ui/Link';
|
import Link from '../../ui/Link';
|
||||||
@ -139,7 +140,7 @@ const SettingsPasskeys = ({
|
|||||||
{lang('SettingsPasskeyInfo')}
|
{lang('SettingsPasskeyInfo')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="settings-item">
|
<Island className="settings-item">
|
||||||
{passkeys?.map(renderPasskey)}
|
{passkeys?.map(renderPasskey)}
|
||||||
{canAddPasskey && (
|
{canAddPasskey && (
|
||||||
<Button
|
<Button
|
||||||
@ -153,17 +154,17 @@ const SettingsPasskeys = ({
|
|||||||
{lang('SettingsPasskeysCreate')}
|
{lang('SettingsPasskeysCreate')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<p className="settings-item-description mt-3" dir="auto">
|
</Island>
|
||||||
{lang('SettingsPasskeysFooter', {
|
<IslandDescription dir="auto">
|
||||||
link: (
|
{lang('SettingsPasskeysFooter', {
|
||||||
<Link isPrimary onClick={handleOpenPasskeyModal}>
|
link: (
|
||||||
{lang('SettingsPasskeysFooterLink', undefined,
|
<Link isPrimary onClick={handleOpenPasskeyModal}>
|
||||||
{ withNodes: true, specialReplacement: NEXT_ARROW_REPLACEMENT })}
|
{lang('SettingsPasskeysFooterLink', undefined,
|
||||||
</Link>
|
{ withNodes: true, specialReplacement: NEXT_ARROW_REPLACEMENT })}
|
||||||
),
|
</Link>
|
||||||
}, { withNodes: true })}
|
),
|
||||||
</p>
|
}, { withNodes: true })}
|
||||||
</div>
|
</IslandDescription>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={Boolean(deleteModalId)}
|
isOpen={Boolean(deleteModalId)}
|
||||||
title={lang('PasskeyDeleteTitle')}
|
title={lang('PasskeyDeleteTitle')}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
|
|
||||||
import PasswordForm from '../../common/PasswordForm';
|
import PasswordForm from '../../common/PasswordForm';
|
||||||
import PasswordMonkey from '../../common/PasswordMonkey';
|
import PasswordMonkey from '../../common/PasswordMonkey';
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
error?: string;
|
error?: string;
|
||||||
@ -65,21 +66,23 @@ const SettingsPasswordForm: FC<OwnProps> = ({
|
|||||||
<PasswordMonkey isBig isPasswordVisible={shouldShowPassword} />
|
<PasswordMonkey isBig isPasswordVisible={shouldShowPassword} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item settings-group">
|
<Island>
|
||||||
<PasswordForm
|
<div className="settings-input">
|
||||||
error={validationError || error}
|
<PasswordForm
|
||||||
hint={hint}
|
error={validationError || error}
|
||||||
placeholder={placeholder || lang('CurrentPasswordPlaceholder')}
|
hint={hint}
|
||||||
shouldDisablePasswordManager={shouldDisablePasswordManager}
|
placeholder={placeholder || lang('CurrentPasswordPlaceholder')}
|
||||||
submitLabel={submitLabel}
|
shouldDisablePasswordManager={shouldDisablePasswordManager}
|
||||||
onClearError={handleClearError}
|
submitLabel={submitLabel}
|
||||||
isLoading={isLoading}
|
onClearError={handleClearError}
|
||||||
isPasswordVisible={shouldShowPassword}
|
isLoading={isLoading}
|
||||||
shouldResetValue={isActive}
|
isPasswordVisible={shouldShowPassword}
|
||||||
onChangePasswordVisibility={setShouldShowPassword}
|
shouldResetValue={isActive}
|
||||||
onSubmit={handleSubmit}
|
onChangePasswordVisibility={setShouldShowPassword}
|
||||||
/>
|
onSubmit={handleSubmit}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { IS_BACKDROP_BLUR_SUPPORTED, IS_SNAP_EFFECT_SUPPORTED } from '../../../u
|
|||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import RangeSlider from '../../ui/RangeSlider';
|
import RangeSlider from '../../ui/RangeSlider';
|
||||||
|
|
||||||
@ -168,24 +169,22 @@ function SettingsPerformance({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
{lang('SettingsPerformanceSliderTitle')}
|
||||||
{lang('SettingsPerformanceSliderTitle')}
|
</IslandTitle>
|
||||||
</h4>
|
<Island>
|
||||||
<p className="settings-item-description" dir={lang.isRtl ? 'rtl' : undefined}>
|
|
||||||
{lang('SettingsPerformanceSliderSubtitle')}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<RangeSlider
|
<RangeSlider
|
||||||
options={animationLevelOptions}
|
options={animationLevelOptions}
|
||||||
value={animationLevelState === ANIMATION_LEVEL_CUSTOM ? ANIMATION_LEVEL_MED : animationLevelState}
|
value={animationLevelState === ANIMATION_LEVEL_CUSTOM ? ANIMATION_LEVEL_MED : animationLevelState}
|
||||||
onChange={handleAnimationLevelChange}
|
onChange={handleAnimationLevelChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<div className="settings-item-simple settings-item__with-shifted-dropdown">
|
{lang('SettingsPerformanceSliderSubtitle')}
|
||||||
<h3 className="settings-item-header" dir="auto">{lang('SettingsPerformanceGranularTitle')}</h3>
|
</IslandDescription>
|
||||||
|
|
||||||
|
<IslandTitle dir="auto">{lang('SettingsPerformanceGranularTitle')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
{PERFORMANCE_OPTIONS.map(([sectionName, options], index) => {
|
{PERFORMANCE_OPTIONS.map(([sectionName, options], index) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -223,7 +222,7 @@ function SettingsPerformance({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import StarIcon from '../../common/icons/StarIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
|
import Island, { IslandTitle } from '../../gili/layout/Island';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -196,11 +197,10 @@ const SettingsPrivacy = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="delete-user"
|
icon="delete-user"
|
||||||
narrow
|
narrow
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyBlockedUsers })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyBlockedUsers })}
|
||||||
>
|
>
|
||||||
{oldLang('BlockedUsers')}
|
{oldLang('BlockedUsers')}
|
||||||
@ -210,7 +210,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
icon="lock"
|
icon="lock"
|
||||||
narrow
|
narrow
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({
|
onClick={() => openSettingsScreen({
|
||||||
screen: hasPasscode ? SettingsScreens.PasscodeEnabled : SettingsScreens.PasscodeDisabled,
|
screen: hasPasscode ? SettingsScreens.PasscodeEnabled : SettingsScreens.PasscodeDisabled,
|
||||||
})}
|
})}
|
||||||
@ -226,7 +225,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
icon="admin"
|
icon="admin"
|
||||||
narrow
|
narrow
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({
|
onClick={() => openSettingsScreen({
|
||||||
screen: hasPassword ? SettingsScreens.TwoFaEnabled : SettingsScreens.TwoFaDisabled,
|
screen: hasPassword ? SettingsScreens.TwoFaEnabled : SettingsScreens.TwoFaDisabled,
|
||||||
})}
|
})}
|
||||||
@ -257,22 +255,19 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
icon="web"
|
icon="web"
|
||||||
narrow
|
narrow
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.ActiveWebsites })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.ActiveWebsites })}
|
||||||
>
|
>
|
||||||
{oldLang('PrivacySettings.WebSessions')}
|
{oldLang('PrivacySettings.WebSessions')}
|
||||||
<span className="settings-item__current-value">{webAuthCount}</span>
|
<span className="settings-item__current-value">{webAuthCount}</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div className="settings-item">
|
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{oldLang('PrivacyTitle')}</h4>
|
|
||||||
|
|
||||||
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{oldLang('PrivacyTitle')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyPhoneNumber })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyPhoneNumber })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -285,7 +280,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyLastSeen })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyLastSeen })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -298,7 +292,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyProfilePhoto })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyProfilePhoto })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -311,7 +304,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyBio })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyBio })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -324,7 +316,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyBirthday })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyBirthday })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -337,7 +328,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyGifts })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyGifts })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -350,7 +340,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyForwarding })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyForwarding })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -363,7 +352,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyPhoneCall })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyPhoneCall })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -378,7 +366,6 @@ const SettingsPrivacy = ({
|
|||||||
allowDisabledClick
|
allowDisabledClick
|
||||||
rightElement={isCurrentUserPremium && <StarIcon size="big" type="premium" />}
|
rightElement={isCurrentUserPremium && <StarIcon size="big" type="premium" />}
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyVoiceMessages })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyVoiceMessages })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -392,7 +379,6 @@ const SettingsPrivacy = ({
|
|||||||
narrow
|
narrow
|
||||||
rightElement={isCurrentUserPremium && <StarIcon size="big" type="premium" />}
|
rightElement={isCurrentUserPremium && <StarIcon size="big" type="premium" />}
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyMessages })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyMessages })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -408,7 +394,6 @@ const SettingsPrivacy = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyGroupChats })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.PrivacyGroupChats })}
|
||||||
>
|
>
|
||||||
<div className="multiline-item">
|
<div className="multiline-item">
|
||||||
@ -418,65 +403,68 @@ const SettingsPrivacy = ({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
{canChangeSensitive && (
|
{canChangeSensitive && (
|
||||||
<div className="settings-item fluid-container">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{oldLang('lng_settings_sensitive_title')}
|
{oldLang('lng_settings_sensitive_title')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
<Checkbox
|
<Island>
|
||||||
label={oldLang('lng_settings_sensitive_disable_filtering')}
|
<Checkbox
|
||||||
subLabel={oldLang('lng_settings_sensitive_about')}
|
label={oldLang('lng_settings_sensitive_disable_filtering')}
|
||||||
checked={Boolean(isSensitiveEnabled)}
|
subLabel={oldLang('lng_settings_sensitive_about')}
|
||||||
disabled={!canChangeSensitive || (!isSensitiveEnabled && needAgeVideoVerification)}
|
checked={Boolean(isSensitiveEnabled)}
|
||||||
onCheck={handleUpdateContentSettings}
|
disabled={!canChangeSensitive || (!isSensitiveEnabled && needAgeVideoVerification)}
|
||||||
/>
|
onCheck={handleUpdateContentSettings}
|
||||||
{!isSensitiveEnabled && needAgeVideoVerification && (
|
/>
|
||||||
<Button
|
{!isSensitiveEnabled && needAgeVideoVerification && (
|
||||||
color="primary"
|
<Button
|
||||||
fluid
|
color="primary"
|
||||||
noForcedUpperCase
|
noForcedUpperCase
|
||||||
className="settings-unlock-button"
|
className="settings-unlock-button"
|
||||||
onClick={handleAgeVerification}
|
onClick={handleAgeVerification}
|
||||||
>
|
>
|
||||||
<span className="settings-unlock-button-title">
|
<span className="settings-unlock-button-title">
|
||||||
{lang('ButtonAgeVerification')}
|
{lang('ButtonAgeVerification')}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{canDisplayAutoarchiveSetting && (
|
{canDisplayAutoarchiveSetting && (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{oldLang('NewChatsFromNonContacts')}
|
{oldLang('NewChatsFromNonContacts')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
<Checkbox
|
<Island>
|
||||||
label={oldLang('ArchiveAndMute')}
|
<Checkbox
|
||||||
subLabel={oldLang('ArchiveAndMuteInfo')}
|
label={oldLang('ArchiveAndMute')}
|
||||||
checked={Boolean(shouldArchiveAndMuteNewNonContact)}
|
subLabel={oldLang('ArchiveAndMuteInfo')}
|
||||||
onCheck={handleArchiveAndMuteChange}
|
checked={Boolean(shouldArchiveAndMuteNewNonContact)}
|
||||||
/>
|
onCheck={handleArchiveAndMuteChange}
|
||||||
</div>
|
/>
|
||||||
|
</Island>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
{oldLang('lng_settings_window_system')}
|
||||||
{oldLang('lng_settings_window_system')}
|
</IslandTitle>
|
||||||
</h4>
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={oldLang('lng_settings_title_chat_name')}
|
label={oldLang('lng_settings_title_chat_name')}
|
||||||
checked={Boolean(canDisplayChatInTitle)}
|
checked={Boolean(canDisplayChatInTitle)}
|
||||||
onCheck={handleChatInTitleChange}
|
onCheck={handleChatInTitleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
{lang('DeleteMyAccount')}
|
||||||
{lang('DeleteMyAccount')}
|
</IslandTitle>
|
||||||
</h4>
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
onClick={handleOpenDeleteAccountModal}
|
onClick={handleOpenDeleteAccountModal}
|
||||||
@ -486,7 +474,7 @@ const SettingsPrivacy = ({
|
|||||||
{lang('Months', { count: dayOption }, { pluralValue: 1 })}
|
{lang('Months', { count: dayOption }, { pluralValue: 1 })}
|
||||||
</span>
|
</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
.surface {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listIsland {
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
|
||||||
|
min-height: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
@ -16,11 +16,15 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import FullNameTitle from '../../common/FullNameTitle';
|
import FullNameTitle from '../../common/FullNameTitle';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
|
import Surface from '../../gili/layout/Surface';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import BlockUserModal from './BlockUserModal';
|
import BlockUserModal from './BlockUserModal';
|
||||||
|
|
||||||
|
import styles from './SettingsPrivacyBlockedUsers.module.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
@ -118,25 +122,28 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-fab-wrapper">
|
<div className="settings-fab-wrapper">
|
||||||
<div className="settings-content infinite-scroll">
|
<Surface className={styles.surface}>
|
||||||
<div className="settings-item no-border">
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<p className="settings-item-description-larger mt-0 mb-2" dir={lang.isRtl ? 'rtl' : undefined}>
|
{lang('BlockedUsersInfo')}
|
||||||
{lang('BlockedUsersInfo')}
|
</IslandDescription>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="chat-list custom-scroll">
|
<Island className={styles.listIsland}>
|
||||||
{blockedIds?.length ? (
|
<div className="chat-list custom-scroll">
|
||||||
<div className="scroll-container settings-item">
|
{blockedIds?.length ? (
|
||||||
{blockedIds.map((contactId, i) => renderContact(contactId, i, 0))}
|
<div
|
||||||
</div>
|
className="scroll-container"
|
||||||
) : blockedIds && !blockedIds.length ? (
|
style={`height: ${blockedIds.length * CHAT_HEIGHT_PX}px`}
|
||||||
<div className="no-results" dir="auto">{lang('NoBlocked')}</div>
|
>
|
||||||
) : (
|
{blockedIds.map((contactId, i) => renderContact(contactId, i, 0))}
|
||||||
<Loading key="loading" />
|
</div>
|
||||||
)}
|
) : blockedIds && !blockedIds.length ? (
|
||||||
</div>
|
<div className="no-results" dir="auto">{lang('NoBlocked')}</div>
|
||||||
</div>
|
) : (
|
||||||
|
<Loading key="loading" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Island>
|
||||||
|
</Surface>
|
||||||
|
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown
|
isShown
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import StarIcon from '../../common/icons/StarIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
@ -42,33 +43,32 @@ const SettingsPrivacyLastSeen = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{canShowHideReadTime && (
|
{canShowHideReadTime && (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<Checkbox
|
<Island>
|
||||||
label={lang('HideReadTime')}
|
<Checkbox
|
||||||
checked={shouldHideReadMarks}
|
label={lang('HideReadTime')}
|
||||||
onCheck={handleChangeShouldHideReadMarks}
|
checked={shouldHideReadMarks}
|
||||||
/>
|
onCheck={handleChangeShouldHideReadMarks}
|
||||||
<p className="settings-item-description-larger" dir={lang.isRtl ? 'rtl' : undefined}>
|
/>
|
||||||
|
</Island>
|
||||||
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{renderText(lang('HideReadTimeInfo'), ['br'])}
|
{renderText(lang('HideReadTimeInfo'), ['br'])}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
leftElement={<StarIcon className="icon ListItem-main-icon" type="premium" size="big" />}
|
leftElement={<StarIcon className="icon ListItem-main-icon" type="premium" size="big" />}
|
||||||
onClick={handleOpenPremiumModal}
|
onClick={handleOpenPremiumModal}
|
||||||
>
|
>
|
||||||
{isCurrentUserPremium ? lang('PrivacyLastSeenPremiumForPremium') : lang('PrivacyLastSeenPremium')}
|
{isCurrentUserPremium ? lang('PrivacyLastSeenPremiumForPremium') : lang('PrivacyLastSeenPremium')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<p
|
</Island>
|
||||||
className="settings-item-description-larger premium-info"
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
dir={lang.isRtl ? 'rtl' : undefined}
|
{isCurrentUserPremium
|
||||||
>
|
? lang('PrivacyLastSeenPremiumInfoForPremium')
|
||||||
{isCurrentUserPremium
|
: lang('PrivacyLastSeenPremiumInfo')}
|
||||||
? lang('PrivacyLastSeenPremiumInfoForPremium')
|
</IslandDescription>
|
||||||
: lang('PrivacyLastSeenPremiumInfo')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import useFlag from '../../../hooks/useFlag';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import SelectAvatar from '../../ui/SelectAvatar';
|
import SelectAvatar from '../../ui/SelectAvatar';
|
||||||
@ -63,44 +64,46 @@ const SettingsPrivacyPublicProfilePhoto: FC<OwnProps> = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<ListItem
|
<Island>
|
||||||
narrow
|
|
||||||
icon="camera-add"
|
|
||||||
onClick={handleOpenFileSelector}
|
|
||||||
>
|
|
||||||
<SelectAvatar
|
|
||||||
onChange={handleSelectFile}
|
|
||||||
inputRef={inputRef}
|
|
||||||
/>
|
|
||||||
{lang(currentUserFallbackPhoto
|
|
||||||
? 'Privacy.ProfilePhoto.UpdatePublicPhoto'
|
|
||||||
: 'Privacy.ProfilePhoto.SetPublicPhoto')}
|
|
||||||
</ListItem>
|
|
||||||
{currentUserFallbackPhoto && (
|
|
||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
leftElement={<Avatar photo={currentUserFallbackPhoto} size="mini" className={styles.fallbackPhoto} />}
|
icon="camera-add"
|
||||||
onClick={openDeleteFallbackPhotoModal}
|
onClick={handleOpenFileSelector}
|
||||||
destructive
|
|
||||||
>
|
>
|
||||||
{lang(currentUserFallbackPhoto.isVideo
|
<SelectAvatar
|
||||||
? 'Privacy.ProfilePhoto.RemovePublicVideo'
|
onChange={handleSelectFile}
|
||||||
: 'Privacy.ProfilePhoto.RemovePublicPhoto')}
|
inputRef={inputRef}
|
||||||
<ConfirmDialog
|
|
||||||
isOpen={isDeleteFallbackPhotoModalOpen}
|
|
||||||
onClose={closeDeleteFallbackPhotoModal}
|
|
||||||
text={lang('Privacy.ResetPhoto.Confirm')}
|
|
||||||
confirmLabel={lang('Delete')}
|
|
||||||
confirmHandler={handleConfirmDelete}
|
|
||||||
confirmIsDestructive
|
|
||||||
/>
|
/>
|
||||||
|
{lang(currentUserFallbackPhoto
|
||||||
|
? 'Privacy.ProfilePhoto.UpdatePublicPhoto'
|
||||||
|
: 'Privacy.ProfilePhoto.SetPublicPhoto')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
{currentUserFallbackPhoto && (
|
||||||
<p className="settings-item-description-larger" dir={lang.isRtl ? 'rtl' : undefined}>
|
<ListItem
|
||||||
|
narrow
|
||||||
|
leftElement={<Avatar photo={currentUserFallbackPhoto} size="mini" className={styles.fallbackPhoto} />}
|
||||||
|
onClick={openDeleteFallbackPhotoModal}
|
||||||
|
destructive
|
||||||
|
>
|
||||||
|
{lang(currentUserFallbackPhoto.isVideo
|
||||||
|
? 'Privacy.ProfilePhoto.RemovePublicVideo'
|
||||||
|
: 'Privacy.ProfilePhoto.RemovePublicPhoto')}
|
||||||
|
<ConfirmDialog
|
||||||
|
isOpen={isDeleteFallbackPhotoModalOpen}
|
||||||
|
onClose={closeDeleteFallbackPhotoModal}
|
||||||
|
text={lang('Privacy.ResetPhoto.Confirm')}
|
||||||
|
confirmLabel={lang('Delete')}
|
||||||
|
confirmHandler={handleConfirmDelete}
|
||||||
|
confirmIsDestructive
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
</Island>
|
||||||
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('Privacy.ProfilePhoto.PublicPhotoInfo')}
|
{lang('Privacy.ProfilePhoto.PublicPhotoInfo')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import Icon from '../../common/icons/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
import Switcher from '../../ui/Switcher';
|
import Switcher from '../../ui/Switcher';
|
||||||
@ -95,25 +96,27 @@ const SettingsPrivacyVisibility: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
{screen === SettingsScreens.PrivacyGifts && (
|
{screen === SettingsScreens.PrivacyGifts && (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<ListItem onClick={handleShowGiftIconInChats}>
|
<Island>
|
||||||
<span>{lang('PrivacyDisplayGiftsButton')}</span>
|
<ListItem onClick={handleShowGiftIconInChats}>
|
||||||
<Switcher
|
<span>{lang('PrivacyDisplayGiftsButton')}</span>
|
||||||
id="gift"
|
<Switcher
|
||||||
disabled={!isCurrentUserPremium}
|
id="gift"
|
||||||
label={shouldDisplayGiftsButton ? lang('HideGiftsButton') : lang('DisplayGiftsButton')}
|
disabled={!isCurrentUserPremium}
|
||||||
checked={shouldDisplayGiftsButton}
|
label={shouldDisplayGiftsButton ? lang('HideGiftsButton') : lang('DisplayGiftsButton')}
|
||||||
/>
|
checked={shouldDisplayGiftsButton}
|
||||||
</ListItem>
|
/>
|
||||||
<p className="settings-item-description-larger" dir={lang.isRtl ? 'rtl' : undefined}>
|
</ListItem>
|
||||||
|
</Island>
|
||||||
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('PrivacyDisplayGiftIconInChats', {
|
{lang('PrivacyDisplayGiftIconInChats', {
|
||||||
icon: <Icon name="gift" className="gift-icon" />,
|
icon: <Icon name="gift" className="gift-icon" />,
|
||||||
gift: lang('PrivacyDisplayGift'),
|
gift: lang('PrivacyDisplayGift'),
|
||||||
}, {
|
}, {
|
||||||
withNodes: true,
|
withNodes: true,
|
||||||
})}
|
})}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
<PrivacySubsection
|
<PrivacySubsection
|
||||||
screen={screen}
|
screen={screen}
|
||||||
@ -347,54 +350,54 @@ function PrivacySubsection({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="settings-item">
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{headerText}</IslandTitle>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{headerText}</h4>
|
<Island>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={`visibility-${privacyKey}`}
|
name={`visibility-${privacyKey}`}
|
||||||
options={visibilityOptions}
|
options={visibilityOptions}
|
||||||
onChange={handleVisibilityChange}
|
onChange={handleVisibilityChange}
|
||||||
selected={privacy?.visibility}
|
selected={privacy?.visibility}
|
||||||
/>
|
/>
|
||||||
{descriptionText && (
|
</Island>
|
||||||
<p className="settings-item-description-larger" dir={lang.isRtl ? 'rtl' : undefined}>{descriptionText}</p>
|
{descriptionText && (
|
||||||
)}
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>{descriptionText}</IslandDescription>
|
||||||
</div>
|
)}
|
||||||
{!isPremiumRequired && (primaryExceptionLists.shouldShowAllowed || primaryExceptionLists.shouldShowDenied) && (
|
{!isPremiumRequired && (primaryExceptionLists.shouldShowAllowed || primaryExceptionLists.shouldShowDenied) && (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{oldLang('PrivacyExceptions')}
|
{oldLang('PrivacyExceptions')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
{primaryExceptionLists.shouldShowAllowed && (
|
<Island>
|
||||||
<ListItem
|
{primaryExceptionLists.shouldShowAllowed && (
|
||||||
narrow
|
<ListItem
|
||||||
icon="add-user"
|
narrow
|
||||||
|
icon="add-user"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
openSettingsScreen({ screen: allowedContactsScreen });
|
openSettingsScreen({ screen: allowedContactsScreen });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="multiline-item full-size">
|
<div className="multiline-item full-size">
|
||||||
<span className="title">{oldLang('AlwaysAllow')}</span>
|
<span className="title">{oldLang('AlwaysAllow')}</span>
|
||||||
<span className="subtitle">{allowedString}</span>
|
<span className="subtitle">{allowedString}</span>
|
||||||
</div>
|
</div>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
{primaryExceptionLists.shouldShowDenied && (
|
{primaryExceptionLists.shouldShowDenied && (
|
||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
icon="delete-user"
|
icon="delete-user"
|
||||||
|
onClick={() => {
|
||||||
onClick={() => {
|
openSettingsScreen({ screen: deniedContactsScreen });
|
||||||
openSettingsScreen({ screen: deniedContactsScreen });
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<div className="multiline-item full-size">
|
||||||
<div className="multiline-item full-size">
|
<span className="title">{oldLang('NeverAllow')}</span>
|
||||||
<span className="title">{oldLang('NeverAllow')}</span>
|
<span className="subtitle">{blockString}</span>
|
||||||
<span className="subtitle">{blockString}</span>
|
</div>
|
||||||
</div>
|
</ListItem>
|
||||||
</ListItem>
|
)}
|
||||||
)}
|
</Island>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
{isPremiumRequired && <PremiumStatusItem />}
|
{isPremiumRequired && <PremiumStatusItem />}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -183,7 +183,7 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="NewChat-inner step-1">
|
<div className="Picker settings-picker-islands">
|
||||||
<PeerPicker
|
<PeerPicker
|
||||||
categories={getCustomCategory()}
|
categories={getCustomCategory()}
|
||||||
itemIds={displayedIds || []}
|
itemIds={displayedIds || []}
|
||||||
@ -201,6 +201,7 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
|||||||
itemInputType="checkbox"
|
itemInputType="checkbox"
|
||||||
withDefaultPadding
|
withDefaultPadding
|
||||||
withStatus
|
withStatus
|
||||||
|
withIslands
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import type { ApiAvailableReaction, ApiReaction } from '../../../api/types';
|
|||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
|
import Surface from '../../gili/layout/Surface';
|
||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -52,15 +54,17 @@ const SettingsQuickReaction: FC<OwnProps & StateProps> = ({
|
|||||||
}, [setDefaultReaction]);
|
}, [setDefaultReaction]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content settings-item custom-scroll settings-quick-reaction">
|
<Surface scrollable className="settings-content settings-quick-reaction">
|
||||||
<RadioGroup
|
<Island>
|
||||||
name="quick-reaction-settings"
|
<RadioGroup
|
||||||
options={options}
|
name="quick-reaction-settings"
|
||||||
selected={selectedReaction?.type === 'emoji' ? selectedReaction.emoticon : undefined}
|
options={options}
|
||||||
onChange={handleChange}
|
selected={selectedReaction?.type === 'emoji' ? selectedReaction.emoticon : undefined}
|
||||||
withIcon
|
onChange={handleChange}
|
||||||
/>
|
withIcon
|
||||||
</div>
|
/>
|
||||||
|
</Island>
|
||||||
|
</Surface>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
|
|
||||||
import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
||||||
import StickerSetCard from '../../common/StickerSetCard';
|
import StickerSetCard from '../../common/StickerSetCard';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ const SettingsStickers: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('SuggestStickers')}
|
label={lang('SuggestStickers')}
|
||||||
checked={shouldSuggestStickers}
|
checked={shouldSuggestStickers}
|
||||||
@ -101,7 +102,6 @@ const SettingsStickers: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.CustomEmoji })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.CustomEmoji })}
|
||||||
icon="smile"
|
icon="smile"
|
||||||
>
|
>
|
||||||
@ -112,7 +112,6 @@ const SettingsStickers: FC<OwnProps & StateProps> = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
className="SettingsDefaultReaction"
|
className="SettingsDefaultReaction"
|
||||||
narrow
|
narrow
|
||||||
|
|
||||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.QuickReaction })}
|
onClick={() => openSettingsScreen({ screen: SettingsScreens.QuickReaction })}
|
||||||
>
|
>
|
||||||
<ReactionStaticEmoji
|
<ReactionStaticEmoji
|
||||||
@ -124,40 +123,44 @@ const SettingsStickers: FC<OwnProps & StateProps> = ({
|
|||||||
<div className="title">{lang('DoubleTapSetting')}</div>
|
<div className="title">{lang('DoubleTapSetting')}</div>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
<div className="settings-item">
|
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('InstalledStickers.DynamicPackOrder')}
|
{lang('InstalledStickers.DynamicPackOrder')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('InstalledStickers.DynamicPackOrder')}
|
label={lang('InstalledStickers.DynamicPackOrder')}
|
||||||
checked={shouldUpdateStickerSetOrder}
|
checked={shouldUpdateStickerSetOrder}
|
||||||
onCheck={handleSuggestStickerSetOrderChange}
|
onCheck={handleSuggestStickerSetOrderChange}
|
||||||
/>
|
/>
|
||||||
<p className="settings-item-description mt-3" dir="auto">
|
</Island>
|
||||||
{lang('InstalledStickers.DynamicPackOrderInfo')}
|
<IslandDescription dir="auto">
|
||||||
</p>
|
{lang('InstalledStickers.DynamicPackOrderInfo')}
|
||||||
</div>
|
</IslandDescription>
|
||||||
|
|
||||||
{stickerSets && (
|
{stickerSets && (
|
||||||
<div className="settings-item">
|
<>
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('ChooseStickerMyStickerSets')}
|
{lang('ChooseStickerMyStickerSets')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
<div ref={stickerSettingsRef}>
|
<Island>
|
||||||
{stickerSets.map((stickerSet: ApiStickerSet) => (
|
<div ref={stickerSettingsRef}>
|
||||||
<StickerSetCard
|
{stickerSets.map((stickerSet: ApiStickerSet) => (
|
||||||
key={stickerSet.id}
|
<StickerSetCard
|
||||||
stickerSet={stickerSet}
|
key={stickerSet.id}
|
||||||
observeIntersection={observeIntersectionForCovers}
|
stickerSet={stickerSet}
|
||||||
onClick={handleStickerSetClick}
|
observeIntersection={observeIntersectionForCovers}
|
||||||
noPlay={!canPlayAnimatedEmojis}
|
onClick={handleStickerSetClick}
|
||||||
/>
|
noPlay={!canPlayAnimatedEmojis}
|
||||||
))}
|
/>
|
||||||
</div>
|
))}
|
||||||
<p className="settings-item-description mt-3" dir="auto">
|
</div>
|
||||||
|
</Island>
|
||||||
|
<IslandDescription dir="auto">
|
||||||
{renderText(lang('StickersBotInfo'), ['links'])}
|
{renderText(lang('StickersBotInfo'), ['links'])}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -123,7 +123,6 @@
|
|||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
margin-inline: 1rem;
|
|
||||||
padding: 0.125rem;
|
padding: 0.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,11 +223,19 @@
|
|||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-folders-header.settings-content-header {
|
||||||
|
padding-inline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-folders-input-container {
|
.settings-folders-input-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-folders-input-with-icon .form-control {
|
.settings-folders-input-with-icon .form-control {
|
||||||
@ -236,11 +243,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-folders-icon-picker {
|
.settings-folders-icon-picker {
|
||||||
--custom-emoji-size: 2rem;
|
--custom-emoji-size: 1.5rem;
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset-inline-end: 0.5rem;
|
inset-inline-end: 1.5rem;
|
||||||
font-size: 2rem;
|
font-size: 1.5rem;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -137,7 +137,7 @@ const SettingsFoldersChatFilters: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Picker settings-folders-chat-list">
|
<div className="Picker settings-folders-chat-list settings-picker-islands">
|
||||||
<PeerPicker
|
<PeerPicker
|
||||||
categories={shouldHideChatTypes ? undefined : chatTypes}
|
categories={shouldHideChatTypes ? undefined : chatTypes}
|
||||||
itemIds={displayedIds}
|
itemIds={displayedIds}
|
||||||
@ -150,6 +150,7 @@ const SettingsFoldersChatFilters: FC<OwnProps & StateProps> = ({
|
|||||||
isSearchable
|
isSearchable
|
||||||
withDefaultPadding
|
withDefaultPadding
|
||||||
withPeerTypes
|
withPeerTypes
|
||||||
|
withIslands
|
||||||
allowMultiple
|
allowMultiple
|
||||||
itemInputType="checkbox"
|
itemInputType="checkbox"
|
||||||
onSelectedIdsChange={handleSelectedIdsChange}
|
onSelectedIdsChange={handleSelectedIdsChange}
|
||||||
|
|||||||
@ -40,6 +40,8 @@ import FolderIcon from '../../../common/FolderIcon';
|
|||||||
import GroupChatInfo from '../../../common/GroupChatInfo';
|
import GroupChatInfo from '../../../common/GroupChatInfo';
|
||||||
import Icon from '../../../common/icons/Icon';
|
import Icon from '../../../common/icons/Icon';
|
||||||
import PrivateChatInfo from '../../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription, IslandOutside, IslandTitle } from '../../../gili/layout/Island';
|
||||||
|
import Surface from '../../../gili/layout/Surface';
|
||||||
import FloatingActionButton from '../../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../../ui/FloatingActionButton';
|
||||||
import InputText from '../../../ui/InputText';
|
import InputText from '../../../ui/InputText';
|
||||||
import ListItem from '../../../ui/ListItem';
|
import ListItem from '../../../ui/ListItem';
|
||||||
@ -381,8 +383,8 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-fab-wrapper">
|
<div className="settings-fab-wrapper">
|
||||||
<div className="settings-content no-border custom-scroll">
|
<Surface scrollable className="settings-content no-border">
|
||||||
<div className="settings-content-header">
|
<IslandOutside className="settings-content-header settings-folders-header">
|
||||||
<AnimatedIconWithPreview
|
<AnimatedIconWithPreview
|
||||||
size={STICKER_SIZE_FOLDER_SETTINGS}
|
size={STICKER_SIZE_FOLDER_SETTINGS}
|
||||||
tgsUrl={LOCAL_TGS_URLS.FoldersNew}
|
tgsUrl={LOCAL_TGS_URLS.FoldersNew}
|
||||||
@ -395,9 +397,9 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
|||||||
{lang('FilterIncludeInfo')}
|
{lang('FilterIncludeInfo')}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="settings-folders-input-container">
|
<Island className="settings-folders-input-container">
|
||||||
<InputText
|
<InputText
|
||||||
className={buildClassName('mb-0', !isMobile && 'settings-folders-input-with-icon')}
|
className={buildClassName(!isMobile && 'settings-folders-input-with-icon')}
|
||||||
label={lang('FilterNameHint')}
|
label={lang('FilterNameHint')}
|
||||||
value={titleText}
|
value={titleText}
|
||||||
maxLength={folderTitleMaxLength}
|
maxLength={folderTitleMaxLength}
|
||||||
@ -424,67 +426,69 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</IslandOutside>
|
||||||
|
|
||||||
{!isOnlyInvites && (
|
{!isOnlyInvites && (
|
||||||
<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}>
|
<IslandDescription className="color-danger" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{oldLang(state.error)}
|
{oldLang(state.error)}
|
||||||
</p>
|
</IslandDescription>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<h4 className="settings-item-header mb-3" dir={lang.isRtl ? 'rtl' : undefined}>{lang('FilterInclude')}</h4>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('FilterInclude')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
|
<ListItem
|
||||||
|
className="settings-folders-list-item color-primary"
|
||||||
|
icon="add"
|
||||||
|
narrow
|
||||||
|
onClick={onAddIncludedChats}
|
||||||
|
>
|
||||||
|
{lang('FilterAddChats')}
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
<ListItem
|
{renderChats('included')}
|
||||||
className="settings-folders-list-item color-primary"
|
</Island>
|
||||||
icon="add"
|
</>
|
||||||
narrow
|
|
||||||
onClick={onAddIncludedChats}
|
|
||||||
>
|
|
||||||
{lang('FilterAddChats')}
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
{renderChats('included')}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isOnlyInvites && !isEditingChatList && (
|
{!isOnlyInvites && !isEditingChatList && (
|
||||||
<div className="settings-item pt-3">
|
<>
|
||||||
<h4 className="settings-item-header mb-3" dir={lang.isRtl ? 'rtl' : undefined}>{lang('FilterExclude')}</h4>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('FilterExclude')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
|
<ListItem
|
||||||
|
className="settings-folders-list-item color-primary"
|
||||||
|
icon="add"
|
||||||
|
narrow
|
||||||
|
onClick={onAddExcludedChats}
|
||||||
|
>
|
||||||
|
{lang('FilterAddChats')}
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
<ListItem
|
{renderChats('excluded')}
|
||||||
className="settings-folders-list-item color-primary"
|
</Island>
|
||||||
icon="add"
|
</>
|
||||||
narrow
|
|
||||||
onClick={onAddExcludedChats}
|
|
||||||
>
|
|
||||||
{lang('FilterAddChats')}
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
{renderChats('excluded')}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="settings-item pt-3">
|
<IslandTitle className="color-picker-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<h4 className="settings-item-header mb-3 color-picker-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
<span className="color-picker-title-text">{lang('FilterColorTitle')}</span>
|
||||||
<span className="color-picker-title-text">{lang('FilterColorTitle')}</span>
|
<div className={buildClassName(
|
||||||
<div className={buildClassName(
|
'color-picker-title',
|
||||||
'color-picker-title',
|
'color-picker-selected-color',
|
||||||
'color-picker-selected-color',
|
isCurrentUserPremium && state.folder.color !== undefined && state.folder.color !== -1
|
||||||
isCurrentUserPremium && state.folder.color !== undefined && state.folder.color !== -1
|
? getPeerColorClass(state.folder.color)
|
||||||
? getPeerColorClass(state.folder.color)
|
: 'color-picker-item-disabled',
|
||||||
: 'color-picker-item-disabled',
|
)}
|
||||||
)}
|
>
|
||||||
>
|
{renderTextWithEntities({
|
||||||
{renderTextWithEntities({
|
text: state.folder.title.text,
|
||||||
text: state.folder.title.text,
|
entities: state.folder.title.entities,
|
||||||
entities: state.folder.title.entities,
|
noCustomEmojiPlayback: state.folder.noTitleAnimations,
|
||||||
noCustomEmojiPlayback: state.folder.noTitleAnimations,
|
})}
|
||||||
})}
|
</div>
|
||||||
</div>
|
</IslandTitle>
|
||||||
</h4>
|
<Island>
|
||||||
<div className="color-picker custom-scroll-x">
|
<div className="color-picker custom-scroll-x">
|
||||||
{FOLDER_COLORS.map((color) => (
|
{FOLDER_COLORS.map((color) => (
|
||||||
<button
|
<button
|
||||||
@ -530,16 +534,15 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="settings-item-description mb-0 mt-3" dir={lang.isRtl ? 'rtl' : undefined}>
|
</Island>
|
||||||
{lang('FilterColorHint')}
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
</p>
|
{lang('FilterColorHint')}
|
||||||
</div>
|
</IslandDescription>
|
||||||
|
|
||||||
<div className="settings-item pt-3">
|
|
||||||
<h4 className="settings-item-header mb-3" dir={lang.isRtl ? 'rtl' : undefined}>
|
|
||||||
{oldLang('FolderLinkScreen.Title')}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
|
{oldLang('FolderLinkScreen.Title')}
|
||||||
|
</IslandTitle>
|
||||||
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
className="settings-folders-list-item color-primary"
|
className="settings-folders-list-item color-primary"
|
||||||
icon="add"
|
icon="add"
|
||||||
@ -564,9 +567,8 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
|
</Island>
|
||||||
</div>
|
</Surface>
|
||||||
</div>
|
|
||||||
|
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={Boolean(state.isTouched)}
|
isShown={Boolean(state.isTouched)}
|
||||||
|
|||||||
@ -28,6 +28,8 @@ import usePreviousDeprecated from '../../../../hooks/usePreviousDeprecated';
|
|||||||
|
|
||||||
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
|
||||||
import Icon from '../../../common/icons/Icon';
|
import Icon from '../../../common/icons/Icon';
|
||||||
|
import Island, { IslandOutside, IslandTitle } from '../../../gili/layout/Island';
|
||||||
|
import Surface from '../../../gili/layout/Surface';
|
||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
import Checkbox from '../../../ui/Checkbox';
|
import Checkbox from '../../../ui/Checkbox';
|
||||||
import Draggable from '../../../ui/Draggable';
|
import Draggable from '../../../ui/Draggable';
|
||||||
@ -225,8 +227,8 @@ const SettingsFoldersMain = ({
|
|||||||
}, [foldersById, isPremium, maxFolders]);
|
}, [foldersById, isPremium, maxFolders]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content no-border custom-scroll">
|
<Surface scrollable className="settings-content no-border">
|
||||||
<div className="settings-content-header">
|
<IslandOutside className="settings-content-header">
|
||||||
<AnimatedIconWithPreview
|
<AnimatedIconWithPreview
|
||||||
size={STICKER_SIZE_FOLDER_SETTINGS}
|
size={STICKER_SIZE_FOLDER_SETTINGS}
|
||||||
tgsUrl={LOCAL_TGS_URLS.FoldersAll}
|
tgsUrl={LOCAL_TGS_URLS.FoldersAll}
|
||||||
@ -250,11 +252,10 @@ const SettingsFoldersMain = ({
|
|||||||
{lang('CreateNewFilter')}
|
{lang('CreateNewFilter')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</IslandOutside>
|
||||||
|
|
||||||
<div className="settings-item pt-3">
|
|
||||||
<h4 className="settings-item-header mb-3" dir={lang.isRtl ? 'rtl' : undefined}>{lang('Filters')}</h4>
|
|
||||||
|
|
||||||
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('Filters')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<div className="settings-sortable-container" style={`height: ${(folderIds?.length || 0) * FOLDER_HEIGHT_PX}px`}>
|
<div className="settings-sortable-container" style={`height: ${(folderIds?.length || 0) * FOLDER_HEIGHT_PX}px`}>
|
||||||
{userFolders?.length ? userFolders.map((folder, i) => {
|
{userFolders?.length ? userFolders.map((folder, i) => {
|
||||||
const isBlocked = i > maxFolders - 1;
|
const isBlocked = i > maxFolders - 1;
|
||||||
@ -365,48 +366,49 @@ const SettingsFoldersMain = ({
|
|||||||
</p>
|
</p>
|
||||||
) : <Loading />}
|
) : <Loading />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
{(recommendedChatFolders && Boolean(recommendedChatFolders.length)) && (
|
{(recommendedChatFolders && Boolean(recommendedChatFolders.length)) && (
|
||||||
<div className="settings-item pt-3">
|
<>
|
||||||
<h4 className="settings-item-header mb-3" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('FilterRecommended')}
|
{lang('FilterRecommended')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
|
<Island>
|
||||||
|
{recommendedChatFolders.map((folder) => (
|
||||||
|
<ListItem
|
||||||
|
key={folder.id}
|
||||||
|
narrow
|
||||||
|
onClick={() => handleCreateFolderFromRecommended(folder)}
|
||||||
|
>
|
||||||
|
<div className="settings-folders-recommended-item">
|
||||||
|
<div className="multiline-item">
|
||||||
|
<span className="title">
|
||||||
|
{renderTextWithEntities({
|
||||||
|
text: folder.title.text,
|
||||||
|
entities: folder.title.entities,
|
||||||
|
noCustomEmojiPlayback: folder.noTitleAnimations,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
<span className="subtitle">{folder.description}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
{recommendedChatFolders.map((folder) => (
|
<Button
|
||||||
<ListItem
|
className="px-3"
|
||||||
narrow
|
color="primary"
|
||||||
|
size="tiny"
|
||||||
onClick={() => handleCreateFolderFromRecommended(folder)}
|
pill
|
||||||
>
|
fluid
|
||||||
<div className="settings-folders-recommended-item">
|
isRtl={lang.isRtl}
|
||||||
<div className="multiline-item">
|
>
|
||||||
<span className="title">
|
{lang('Add')}
|
||||||
{renderTextWithEntities({
|
</Button>
|
||||||
text: folder.title.text,
|
|
||||||
entities: folder.title.entities,
|
|
||||||
noCustomEmojiPlayback: folder.noTitleAnimations,
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
<span className="subtitle">{folder.description}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
</ListItem>
|
||||||
<Button
|
))}
|
||||||
className="px-3"
|
</Island>
|
||||||
color="primary"
|
</>
|
||||||
size="tiny"
|
|
||||||
pill
|
|
||||||
fluid
|
|
||||||
isRtl={lang.isRtl}
|
|
||||||
>
|
|
||||||
{lang('Add')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
<div className="settings-item pt-3">
|
<Island>
|
||||||
<div className="settings-item-relative">
|
<div className="settings-item-relative">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('ShowFolderTags')}
|
label={lang('ShowFolderTags')}
|
||||||
@ -422,26 +424,27 @@ const SettingsFoldersMain = ({
|
|||||||
/>
|
/>
|
||||||
{!isPremium && <Icon name="lock-badge" className="settings-folders-lock-icon" />}
|
{!isPremium && <Icon name="lock-badge" className="settings-folders-lock-icon" />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Island>
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<div className="settings-item pt-3">
|
<>
|
||||||
<h4 className="settings-item-header mb-3" dir={lang.isRtl ? 'rtl' : undefined}>{lang('TabsPosition')}</h4>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang('TabsPosition')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="tabsPosition"
|
name="tabsPosition"
|
||||||
options={[{
|
options={[{
|
||||||
label: lang('TabsPositionLeft'),
|
label: lang('TabsPositionLeft'),
|
||||||
value: FOLDERS_POSITION_LEFT,
|
value: FOLDERS_POSITION_LEFT,
|
||||||
}, {
|
}, {
|
||||||
label: lang('TabsPositionTop'),
|
label: lang('TabsPositionTop'),
|
||||||
value: FOLDERS_POSITION_TOP,
|
value: FOLDERS_POSITION_TOP,
|
||||||
}]}
|
}]}
|
||||||
selected={foldersPosition}
|
selected={foldersPosition}
|
||||||
onChange={handleFoldersPositionChange}
|
onChange={handleFoldersPositionChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Surface>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import useOldLang from '../../../../hooks/useOldLang';
|
|||||||
import AnimatedIcon from '../../../common/AnimatedIcon';
|
import AnimatedIcon from '../../../common/AnimatedIcon';
|
||||||
import LinkField from '../../../common/LinkField';
|
import LinkField from '../../../common/LinkField';
|
||||||
import PeerPicker from '../../../common/pickers/PeerPicker';
|
import PeerPicker from '../../../common/pickers/PeerPicker';
|
||||||
|
import Island, { IslandTitle } from '../../../gili/layout/Island';
|
||||||
import FloatingActionButton from '../../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../../ui/FloatingActionButton';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -170,15 +171,18 @@ const SettingsShareChatlist: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LinkField
|
<IslandTitle>{oldLang('InviteLink.InviteLink')}</IslandTitle>
|
||||||
className="settings-item"
|
<Island>
|
||||||
link={!url ? oldLang('Loading') : url}
|
<LinkField
|
||||||
withShare
|
link={!url ? oldLang('Loading') : url}
|
||||||
onRevoke={handleRevoke}
|
withShare
|
||||||
isDisabled={!chatsCount || isTouched}
|
noTitle
|
||||||
/>
|
onRevoke={handleRevoke}
|
||||||
|
isDisabled={!chatsCount || isTouched}
|
||||||
|
/>
|
||||||
|
</Island>
|
||||||
|
|
||||||
<div className="settings-item settings-item-picker">
|
<Island>
|
||||||
<PeerPicker
|
<PeerPicker
|
||||||
itemIds={itemIds}
|
itemIds={itemIds}
|
||||||
lockedUnselectedIds={lockedIds}
|
lockedUnselectedIds={lockedIds}
|
||||||
@ -189,7 +193,7 @@ const SettingsShareChatlist: FC<OwnProps & StateProps> = ({
|
|||||||
withStatus
|
withStatus
|
||||||
itemInputType="checkbox"
|
itemInputType="checkbox"
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={isLoading || isTouched}
|
isShown={isLoading || isTouched}
|
||||||
|
|||||||
@ -43,9 +43,7 @@ const SettingsPasscodeCongratulations: FC<OwnProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item settings-group">
|
<Button onClick={fullReset}>{lang('Back')}</Button>
|
||||||
<Button onClick={fullReset}>{lang('Back')}</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import useHistoryBack from '../../../../hooks/useHistoryBack';
|
|||||||
import useOldLang from '../../../../hooks/useOldLang';
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
|
||||||
|
import Island from '../../../gili/layout/Island';
|
||||||
import ListItem from '../../../ui/ListItem';
|
import ListItem from '../../../ui/ListItem';
|
||||||
|
|
||||||
import lockPreviewUrl from '../../../../assets/lock.png';
|
import lockPreviewUrl from '../../../../assets/lock.png';
|
||||||
@ -42,7 +43,7 @@ const SettingsPasscodeEnabled: FC<OwnProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="edit"
|
icon="edit"
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ const SettingsPasscodeEnabled: FC<OwnProps> = ({
|
|||||||
>
|
>
|
||||||
{lang('Passcode.TurnOff')}
|
{lang('Passcode.TurnOff')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -43,9 +43,7 @@ const SettingsPasscodeStart: FC<OwnProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item settings-group">
|
<Button onClick={onStart}>{lang('EnablePasscode')}</Button>
|
||||||
<Button onClick={onStart}>{lang('EnablePasscode')}</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -48,9 +48,7 @@ const SettingsTwoFaCongratulations: FC<OwnProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item settings-group">
|
<Button onClick={handleClick}>{lang('TwoStepVerificationPasswordReturnSettings')}</Button>
|
||||||
<Button onClick={handleClick}>{lang('TwoStepVerificationPasswordReturnSettings')}</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import useHistoryBack from '../../../../hooks/useHistoryBack';
|
|||||||
import useOldLang from '../../../../hooks/useOldLang';
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
import AnimatedIconFromSticker from '../../../common/AnimatedIconFromSticker';
|
import AnimatedIconFromSticker from '../../../common/AnimatedIconFromSticker';
|
||||||
|
import Island from '../../../gili/layout/Island';
|
||||||
import InputText from '../../../ui/InputText';
|
import InputText from '../../../ui/InputText';
|
||||||
import Loading from '../../../ui/Loading';
|
import Loading from '../../../ui/Loading';
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item settings-group">
|
<Island>
|
||||||
<InputText
|
<InputText
|
||||||
value={value}
|
value={value}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
@ -98,7 +99,7 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
|||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
{isLoading && <Loading />}
|
{isLoading && <Loading />}
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import useHistoryBack from '../../../../hooks/useHistoryBack';
|
|||||||
import useOldLang from '../../../../hooks/useOldLang';
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
|
||||||
|
import Island from '../../../gili/layout/Island';
|
||||||
import ListItem from '../../../ui/ListItem';
|
import ListItem from '../../../ui/ListItem';
|
||||||
|
|
||||||
import lockPreviewUrl from '../../../../assets/lock.png';
|
import lockPreviewUrl from '../../../../assets/lock.png';
|
||||||
@ -46,7 +47,7 @@ const SettingsTwoFaEnabled: FC<OwnProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="edit"
|
icon="edit"
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ const SettingsTwoFaEnabled: FC<OwnProps> = ({
|
|||||||
>
|
>
|
||||||
{lang('SetRecoveryEmail')}
|
{lang('SetRecoveryEmail')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import useHistoryBack from '../../../../hooks/useHistoryBack';
|
|||||||
import useOldLang from '../../../../hooks/useOldLang';
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
import AnimatedIconFromSticker from '../../../common/AnimatedIconFromSticker';
|
import AnimatedIconFromSticker from '../../../common/AnimatedIconFromSticker';
|
||||||
|
import Island from '../../../gili/layout/Island';
|
||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
import InputText from '../../../ui/InputText';
|
import InputText from '../../../ui/InputText';
|
||||||
import Modal from '../../../ui/Modal';
|
import Modal from '../../../ui/Modal';
|
||||||
@ -107,8 +108,8 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item settings-group">
|
<Island>
|
||||||
<form action="" onSubmit={handleSubmit}>
|
<form className="settings-input" action="" onSubmit={handleSubmit}>
|
||||||
<InputText
|
<InputText
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={value}
|
value={value}
|
||||||
@ -161,7 +162,7 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -42,9 +42,7 @@ const SettingsTwoFaStart: FC<OwnProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item settings-group">
|
<Button onClick={onStart}>{lang('EditAdminTransferSetPassword')}</Button>
|
||||||
<Button onClick={onStart}>{lang('EditAdminTransferSetPassword')}</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
.AddChatMembers {
|
.AddChatMembers {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--color-background);
|
padding: 1rem;
|
||||||
|
|
||||||
|
background: var(--color-background-secondary);
|
||||||
|
|
||||||
&-inner {
|
&-inner {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -131,6 +131,7 @@ const AddChatMembers: FC<OwnProps & StateProps> = ({
|
|||||||
onFilterChange={handleFilterChange}
|
onFilterChange={handleFilterChange}
|
||||||
isSearchable
|
isSearchable
|
||||||
withDefaultPadding
|
withDefaultPadding
|
||||||
|
withIslands
|
||||||
noScrollRestore={noPickerScrollRestore}
|
noScrollRestore={noPickerScrollRestore}
|
||||||
allowMultiple
|
allowMultiple
|
||||||
withStatus
|
withStatus
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import useOldLang from '../../hooks/useOldLang';
|
|||||||
|
|
||||||
import CustomEmojiPicker from '../common/CustomEmojiPicker';
|
import CustomEmojiPicker from '../common/CustomEmojiPicker';
|
||||||
import TopicIcon from '../common/TopicIcon';
|
import TopicIcon from '../common/TopicIcon';
|
||||||
|
import Island from '../gili/layout/Island';
|
||||||
import FloatingActionButton from '../ui/FloatingActionButton';
|
import FloatingActionButton from '../ui/FloatingActionButton';
|
||||||
import InputText from '../ui/InputText';
|
import InputText from '../ui/InputText';
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
@ -114,7 +115,7 @@ const CreateTopic: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.root}>
|
<div className={styles.root}>
|
||||||
<div className={buildClassName(styles.content, 'custom-scroll')}>
|
<div className={buildClassName(styles.content, 'custom-scroll')}>
|
||||||
<div className={buildClassName(styles.section, styles.top)}>
|
<Island className={styles.top}>
|
||||||
<span className={styles.heading}>{lang('CreateTopicTitle')}</span>
|
<span className={styles.heading}>{lang('CreateTopicTitle')}</span>
|
||||||
<Transition
|
<Transition
|
||||||
name="zoomFade"
|
name="zoomFade"
|
||||||
@ -138,8 +139,8 @@ const CreateTopic: FC<OwnProps & StateProps> = ({
|
|||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
teactExperimentControlled
|
teactExperimentControlled
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
<div className={buildClassName(styles.section, styles.bottom)}>
|
<Island className={styles.bottom}>
|
||||||
<CustomEmojiPicker
|
<CustomEmojiPicker
|
||||||
idPrefix="create-topic-icons-set-"
|
idPrefix="create-topic-icons-set-"
|
||||||
isHidden={!isActive}
|
isHidden={!isActive}
|
||||||
@ -149,7 +150,7 @@ const CreateTopic: FC<OwnProps & StateProps> = ({
|
|||||||
pickerListClassName="fab-padding-bottom"
|
pickerListClassName="fab-padding-bottom"
|
||||||
withDefaultTopicIcons
|
withDefaultTopicIcons
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={isTouched}
|
isShown={isTouched}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import useOldLang from '../../hooks/useOldLang';
|
|||||||
|
|
||||||
import CustomEmojiPicker from '../common/CustomEmojiPicker';
|
import CustomEmojiPicker from '../common/CustomEmojiPicker';
|
||||||
import TopicIcon from '../common/TopicIcon';
|
import TopicIcon from '../common/TopicIcon';
|
||||||
|
import Island from '../gili/layout/Island';
|
||||||
import FloatingActionButton from '../ui/FloatingActionButton';
|
import FloatingActionButton from '../ui/FloatingActionButton';
|
||||||
import InputText from '../ui/InputText';
|
import InputText from '../ui/InputText';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
@ -136,7 +137,7 @@ const EditTopic: FC<OwnProps & StateProps> = ({
|
|||||||
{!topic && <Loading />}
|
{!topic && <Loading />}
|
||||||
{topic && (
|
{topic && (
|
||||||
<>
|
<>
|
||||||
<div className={buildClassName(styles.section, styles.top, isGeneral && styles.general)}>
|
<Island className={buildClassName(styles.top, isGeneral && styles.general)}>
|
||||||
<span className={styles.heading}>{lang(isGeneral ? 'CreateGeneralTopicTitle' : 'CreateTopicTitle')}</span>
|
<span className={styles.heading}>{lang(isGeneral ? 'CreateGeneralTopicTitle' : 'CreateTopicTitle')}</span>
|
||||||
<Transition
|
<Transition
|
||||||
name="zoomFade"
|
name="zoomFade"
|
||||||
@ -160,9 +161,9 @@ const EditTopic: FC<OwnProps & StateProps> = ({
|
|||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
teactExperimentControlled
|
teactExperimentControlled
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
{!isGeneral && (
|
{!isGeneral && (
|
||||||
<div className={buildClassName(styles.section, styles.bottom)}>
|
<Island className={styles.bottom}>
|
||||||
<CustomEmojiPicker
|
<CustomEmojiPicker
|
||||||
idPrefix="edit-topic-icons-set-"
|
idPrefix="edit-topic-icons-set-"
|
||||||
isHidden={!isActive}
|
isHidden={!isActive}
|
||||||
@ -172,7 +173,7 @@ const EditTopic: FC<OwnProps & StateProps> = ({
|
|||||||
pickerListClassName="fab-padding-bottom"
|
pickerListClassName="fab-padding-bottom"
|
||||||
withDefaultTopicIcons
|
withDefaultTopicIcons
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -5,37 +5,35 @@
|
|||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: var(--color-background);
|
background-color: var(--color-background-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
padding: 1rem;
|
||||||
|
|
||||||
.section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
@include mixins.side-panel-section;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.general {
|
.general {
|
||||||
border-bottom: 0;
|
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top {
|
.top {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
min-height: 30rem;
|
min-height: 30rem;
|
||||||
margin-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconWrapper {
|
.iconWrapper {
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
:global(.FloatingActionButton) {
|
:global(.FloatingActionButton) {
|
||||||
z-index: 1;
|
z-index: 3;
|
||||||
|
|
||||||
@include mixins.with-vt-type('rightColumn');
|
@include mixins.with-vt-type('rightColumn');
|
||||||
|
|
||||||
|
|||||||
@ -1267,7 +1267,6 @@ const Profile = ({
|
|||||||
chatOrUserId={profileId}
|
chatOrUserId={profileId}
|
||||||
isSavedDialog={isSavedDialog}
|
isSavedDialog={isSavedDialog}
|
||||||
isOwnProfile={isOwnProfile}
|
isOwnProfile={isOwnProfile}
|
||||||
withIslands
|
|
||||||
className={styles.chatExtraBlock}
|
className={styles.chatExtraBlock}
|
||||||
style={createVtnStyle('chatExtraBlock', true)}
|
style={createVtnStyle('chatExtraBlock', true)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -20,6 +20,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .Transition {
|
> .Transition {
|
||||||
|
--slide-background-color: var(--color-background-secondary);
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: calc(100% - var(--header-height));
|
height: calc(100% - var(--header-height));
|
||||||
}
|
}
|
||||||
@ -33,7 +35,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Management .section > .ChatInfo {
|
.Management .section > .ChatInfo,
|
||||||
|
.Management > .custom-scroll > .ChatInfo,
|
||||||
|
.Management > .panel-content > .ChatInfo {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
padding: 0 1.5rem;
|
padding: 0 1.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import type { FC } from '@teact';
|
import type { FC } from '@teact';
|
||||||
import { memo, useEffect, useRef, useState } from '@teact';
|
import { memo, useEffect, useLayoutEffect, useRef, useState } from '@teact';
|
||||||
import { getActions, withGlobal } from '../../global';
|
import { getActions, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type { AnimationLevel, ThreadId } from '../../types';
|
import type { AnimationLevel, ThreadId } from '../../types';
|
||||||
import { ManagementScreens, NewChatMembersProgress, ProfileState, RightColumnContent } from '../../types';
|
import { ManagementScreens, NewChatMembersProgress, ProfileState, RightColumnContent } from '../../types';
|
||||||
|
|
||||||
import { ANIMATION_END_DELAY, MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN } from '../../config';
|
import { MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN } from '../../config';
|
||||||
import { getIsSavedDialog } from '../../global/helpers';
|
import { getIsSavedDialog } from '../../global/helpers';
|
||||||
import {
|
import {
|
||||||
selectAreActiveChatsLoaded,
|
selectAreActiveChatsLoaded,
|
||||||
@ -20,10 +20,12 @@ import captureEscKeyListener from '../../util/captureEscKeyListener';
|
|||||||
import { resolveTransitionName } from '../../util/resolveTransitionName.ts';
|
import { resolveTransitionName } from '../../util/resolveTransitionName.ts';
|
||||||
|
|
||||||
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||||
|
import useFlag from '../../hooks/useFlag';
|
||||||
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 useScrollNotch from '../../hooks/useScrollNotch.ts';
|
import useScrollNotch from '../../hooks/useScrollNotch.ts';
|
||||||
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
import useWindowSize from '../../hooks/window/useWindowSize';
|
import useWindowSize from '../../hooks/window/useWindowSize';
|
||||||
|
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
@ -63,7 +65,6 @@ type StateProps = {
|
|||||||
hasProfileBackground?: boolean;
|
hasProfileBackground?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ANIMATION_DURATION = 450 + ANIMATION_END_DELAY;
|
|
||||||
const MAIN_SCREENS_COUNT = Object.keys(RightColumnContent).length / 2;
|
const MAIN_SCREENS_COUNT = Object.keys(RightColumnContent).length / 2;
|
||||||
const MANAGEMENT_SCREENS_COUNT = Object.keys(ManagementScreens).length / 2;
|
const MANAGEMENT_SCREENS_COUNT = Object.keys(ManagementScreens).length / 2;
|
||||||
|
|
||||||
@ -120,7 +121,6 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
|||||||
const [selectedChatMemberId, setSelectedChatMemberId] = useState<string | undefined>();
|
const [selectedChatMemberId, setSelectedChatMemberId] = useState<string | undefined>();
|
||||||
const [isPromotedByCurrentUser, setIsPromotedByCurrentUser] = useState<boolean | undefined>();
|
const [isPromotedByCurrentUser, setIsPromotedByCurrentUser] = useState<boolean | undefined>();
|
||||||
const [isProfileExpanded, setIsProfileExpanded] = useState(false);
|
const [isProfileExpanded, setIsProfileExpanded] = useState(false);
|
||||||
const [isProfileScrolled, setIsProfileScrolled] = useState(false);
|
|
||||||
const isScrolledDown = profileState !== ProfileState.Profile;
|
const isScrolledDown = profileState !== ProfileState.Profile;
|
||||||
|
|
||||||
const isOpen = contentKey !== undefined;
|
const isOpen = contentKey !== undefined;
|
||||||
@ -138,43 +138,57 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
|||||||
const isCreatingTopic = contentKey === RightColumnContent.CreateTopic;
|
const isCreatingTopic = contentKey === RightColumnContent.CreateTopic;
|
||||||
const isEditingTopic = contentKey === RightColumnContent.EditTopic;
|
const isEditingTopic = contentKey === RightColumnContent.EditTopic;
|
||||||
const isOverlaying = windowWidth <= MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN;
|
const isOverlaying = windowWidth <= MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN;
|
||||||
|
const isContentScrolledRef = useRef(false);
|
||||||
|
|
||||||
const headerBackground: 'regular' | 'secondary' = (() => {
|
const hasStaticHeader = isSavedMessages || isAddingChatMembers || isCreatingTopic || isEditingTopic
|
||||||
if (isSavedMessages) return 'secondary';
|
|| (isManagement && (
|
||||||
if (!isProfile) return 'regular';
|
managementScreen === ManagementScreens.GroupMembers
|
||||||
|
|| managementScreen === ManagementScreens.ChatAdministrators
|
||||||
|
|| managementScreen === ManagementScreens.GroupAddAdmins
|
||||||
|
));
|
||||||
|
|
||||||
|
const getHeaderBackground = useLastCallback((): 'regular' | 'secondary' => {
|
||||||
|
const isScrolled = isContentScrolledRef.current;
|
||||||
|
if (hasStaticHeader) return 'secondary';
|
||||||
|
if (!isProfile) return isScrolled ? 'regular' : 'secondary';
|
||||||
if (isScrolledDown) return 'secondary';
|
if (isScrolledDown) return 'secondary';
|
||||||
if (!isProfileScrolled && !isProfileExpanded && !hasProfileBackground) return 'secondary';
|
if (!isScrolled && !isProfileExpanded && !hasProfileBackground) return 'secondary';
|
||||||
return 'regular';
|
return 'regular';
|
||||||
})();
|
});
|
||||||
|
|
||||||
const [shouldSkipTransition, setShouldSkipTransition] = useState(!isOpen);
|
const [headerBackground, setHeaderBackground] = useState<'regular' | 'secondary'>(getHeaderBackground);
|
||||||
|
|
||||||
|
const handleContentScrolled = useLastCallback((isScrolled: boolean) => {
|
||||||
|
isContentScrolledRef.current = isScrolled;
|
||||||
|
setHeaderBackground(getHeaderBackground());
|
||||||
|
});
|
||||||
|
|
||||||
|
const [isAnimating, startAnimating, stopAnimating] = useFlag();
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const elements = containerRef.current?.querySelectorAll<HTMLElement>(
|
||||||
|
':scope .custom-scroll, :scope .panel-content',
|
||||||
|
);
|
||||||
|
elements?.forEach((el) => {
|
||||||
|
el.scrollTop = 0;
|
||||||
|
});
|
||||||
|
isContentScrolledRef.current = false;
|
||||||
|
setHeaderBackground(getHeaderBackground());
|
||||||
|
}, [contentKey, managementScreen]);
|
||||||
|
|
||||||
|
useSyncEffect(() => {
|
||||||
|
setHeaderBackground(getHeaderBackground());
|
||||||
|
}, [isScrolledDown, isProfileExpanded, hasProfileBackground]);
|
||||||
|
|
||||||
const renderingContentKey = useCurrentOrPrev(contentKey, true, !isChatSelected) ?? -1;
|
const renderingContentKey = useCurrentOrPrev(contentKey, true, !isChatSelected) ?? -1;
|
||||||
|
|
||||||
useScrollNotch({
|
useScrollNotch({
|
||||||
containerRef,
|
containerRef,
|
||||||
selector: ':scope .custom-scroll, :scope .panel-content',
|
selector: ':scope .custom-scroll, :scope .panel-content',
|
||||||
shouldHideTopNotch: isSavedMessages || (isProfile && isScrolledDown),
|
shouldHideTopNotch: isAnimating || isSavedMessages || hasStaticHeader || (isProfile && isScrolledDown),
|
||||||
|
onScrolled: handleContentScrolled,
|
||||||
}, [contentKey, managementScreen, chatId, threadId]);
|
}, [contentKey, managementScreen, chatId, threadId]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isProfile || isScrolledDown || isSavedMessages) {
|
|
||||||
setIsProfileScrolled(false);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scrollEl = containerRef.current?.querySelector<HTMLElement>('.custom-scroll');
|
|
||||||
if (!scrollEl) return undefined;
|
|
||||||
|
|
||||||
const handleProfileScroll = () => {
|
|
||||||
setIsProfileScrolled(scrollEl.scrollTop > 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleProfileScroll();
|
|
||||||
scrollEl.addEventListener('scroll', handleProfileScroll, { passive: true });
|
|
||||||
return () => scrollEl.removeEventListener('scroll', handleProfileScroll);
|
|
||||||
}, [chatId, threadId, isProfile, isScrolledDown, isSavedMessages]);
|
|
||||||
|
|
||||||
const close = useLastCallback((shouldScrollUp = true) => {
|
const close = useLastCallback((shouldScrollUp = true) => {
|
||||||
switch (contentKey) {
|
switch (contentKey) {
|
||||||
case RightColumnContent.AddingMembers:
|
case RightColumnContent.AddingMembers:
|
||||||
@ -278,12 +292,6 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
useEffect(() => (isOpen && chatId ? captureEscKeyListener(close) : undefined), [isOpen, close, chatId]);
|
useEffect(() => (isOpen && chatId ? captureEscKeyListener(close) : undefined), [isOpen, close, chatId]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
setShouldSkipTransition(!isOpen);
|
|
||||||
}, ANIMATION_DURATION);
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (nextManagementScreen) {
|
if (nextManagementScreen) {
|
||||||
setManagementScreen(nextManagementScreen);
|
setManagementScreen(nextManagementScreen);
|
||||||
@ -429,10 +437,12 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
<Transition
|
<Transition
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
name={resolveTransitionName('layers', animationLevel, shouldSkipTransition || shouldSkipHistoryAnimations)}
|
name={resolveTransitionName('layers', animationLevel, !isOpen || shouldSkipHistoryAnimations)}
|
||||||
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}
|
||||||
shouldCleanup
|
shouldCleanup
|
||||||
|
onStart={startAnimating}
|
||||||
|
onStop={stopAnimating}
|
||||||
cleanupExceptionKey={
|
cleanupExceptionKey={
|
||||||
(renderingContentKey === RightColumnContent.MessageStatistics
|
(renderingContentKey === RightColumnContent.MessageStatistics
|
||||||
|| renderingContentKey === RightColumnContent.StoryStatistics)
|
|| renderingContentKey === RightColumnContent.StoryStatistics)
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useMedia from '../../../hooks/useMedia';
|
import useMedia from '../../../hooks/useMedia';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import AvatarEditable from '../../ui/AvatarEditable';
|
import AvatarEditable from '../../ui/AvatarEditable';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
@ -183,7 +184,7 @@ const ManageBot: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="custom-scroll">
|
<div className="custom-scroll">
|
||||||
<div className="section">
|
<Island>
|
||||||
<AvatarEditable
|
<AvatarEditable
|
||||||
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
||||||
onChange={handlePhotoChange}
|
onChange={handlePhotoChange}
|
||||||
@ -206,8 +207,8 @@ const ManageBot: FC<OwnProps & StateProps> = ({
|
|||||||
maxLength={maxBioLength}
|
maxLength={maxBioLength}
|
||||||
maxLengthIndicator={maxBioLength ? (maxBioLength - bio.length).toString() : undefined}
|
maxLengthIndicator={maxBioLength ? (maxBioLength - bio.length).toString() : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
<div className="section">
|
<Island>
|
||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
<ListItem icon="bot-commands-filled" ripple onClick={handleChangeEditIntro}>
|
<ListItem icon="bot-commands-filled" ripple onClick={handleChangeEditIntro}>
|
||||||
<span>{lang('BotEditIntro')}</span>
|
<span>{lang('BotEditIntro')}</span>
|
||||||
@ -218,11 +219,11 @@ const ManageBot: FC<OwnProps & StateProps> = ({
|
|||||||
<ListItem icon="bots" ripple onClick={handleChangeSettings}>
|
<ListItem icon="bots" ripple onClick={handleChangeSettings}>
|
||||||
<span>{lang('BotChangeSettings')}</span>
|
<span>{lang('BotChangeSettings')}</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<div className="section-info section-info_push">
|
<IslandDescription>
|
||||||
{renderText(lang('BotManageInfo'), ['links'])}
|
{renderText(lang('BotManageInfo'), ['links'])}
|
||||||
</div>
|
</IslandDescription>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={isFieldTouched || isAvatarTouched}
|
isShown={isFieldTouched || isAvatarTouched}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useMedia from '../../../hooks/useMedia';
|
import useMedia from '../../../hooks/useMedia';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
import AvatarEditable from '../../ui/AvatarEditable';
|
import AvatarEditable from '../../ui/AvatarEditable';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -225,12 +226,12 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section">
|
<AvatarEditable
|
||||||
<AvatarEditable
|
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
||||||
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
onChange={handleSetPhoto}
|
||||||
onChange={handleSetPhoto}
|
disabled={!canChangeInfo}
|
||||||
disabled={!canChangeInfo}
|
/>
|
||||||
/>
|
<Island>
|
||||||
<div className="settings-edit">
|
<div className="settings-edit">
|
||||||
<InputText
|
<InputText
|
||||||
id="channel-title"
|
id="channel-title"
|
||||||
@ -318,8 +319,8 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
<div className="section">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="admin"
|
icon="admin"
|
||||||
multiline
|
multiline
|
||||||
@ -344,12 +345,12 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
|||||||
<span className="title">{lang('ChannelBlockedUsers')}</span>
|
<span className="title">{lang('ChannelBlockedUsers')}</span>
|
||||||
<span className="subtitle">{removedUsersCount}</span>
|
<span className="subtitle">{removedUsersCount}</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
<div className="section">
|
<Island>
|
||||||
<ListItem icon="delete" ripple destructive onClick={openDeleteDialog}>
|
<ListItem icon="delete" ripple destructive onClick={openDeleteDialog}>
|
||||||
{chat.isCreator ? lang('ChannelDelete') : lang('LeaveChannel')}
|
{chat.isCreator ? lang('ChannelDelete') : lang('LeaveChannel')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={isProfileFieldsTouched}
|
isShown={isProfileFieldsTouched}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -111,7 +112,7 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section">
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="recent"
|
icon="recent"
|
||||||
multiline
|
multiline
|
||||||
@ -120,15 +121,14 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
|||||||
<span className="title">{lang('EventLog')}</span>
|
<span className="title">{lang('EventLog')}</span>
|
||||||
<span className="subtitle">{lang(isChannel ? 'EventLogInfoDetailChannel' : 'EventLogInfoDetail')}</span>
|
<span className="subtitle">{lang(isChannel ? 'EventLogInfoDetailChannel' : 'EventLogInfoDetail')}</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
|
||||||
<p className="section-help" dir="auto">
|
|
||||||
{lang(isChannel
|
|
||||||
? 'Channel.Management.AddModeratorHelp'
|
|
||||||
: 'Group.Management.AddModeratorHelp')}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
<IslandDescription dir="auto">
|
||||||
|
{lang(isChannel
|
||||||
|
? 'Channel.Management.AddModeratorHelp'
|
||||||
|
: 'Group.Management.AddModeratorHelp')}
|
||||||
|
</IslandDescription>
|
||||||
|
<Island dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{adminMembers.map((member) => (
|
{adminMembers.map((member) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={member.userId}
|
key={member.userId}
|
||||||
@ -150,10 +150,10 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
|||||||
ariaLabel={lang('Channel.Management.AddModerator')}
|
ariaLabel={lang('Channel.Management.AddModerator')}
|
||||||
iconName="add-user-filled"
|
iconName="add-user-filled"
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
{canToggleSignatures && (
|
{canToggleSignatures && (
|
||||||
<div className="section">
|
<Island>
|
||||||
<div className="ListItem narrow">
|
<div className="ListItem narrow">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={areSignaturesEnabled}
|
checked={areSignaturesEnabled}
|
||||||
@ -170,12 +170,12 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
|||||||
onChange={handleToggleProfiles}
|
onChange={handleToggleProfiles}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="section-info section-info_push">
|
<IslandDescription>
|
||||||
{lang('ChannelSignProfilesInfo')}
|
{lang('ChannelSignProfilesInfo')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -25,6 +25,7 @@ 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';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -184,71 +185,77 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
|||||||
const purchaseInfoLink = `${TME_LINK_PREFIX}${PURCHASE_USERNAME}`;
|
const purchaseInfoLink = `${TME_LINK_PREFIX}${PURCHASE_USERNAME}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p className="section-info" dir="auto">
|
<IslandDescription dir="auto">
|
||||||
{(lang('lng_username_purchase_available'))
|
{(lang('lng_username_purchase_available'))
|
||||||
.replace('{link}', '%PURCHASE_LINK%')
|
.replace('{link}', '%PURCHASE_LINK%')
|
||||||
.split('%')
|
.split('%')
|
||||||
.map((s) => {
|
.map((s) => {
|
||||||
return (s === 'PURCHASE_LINK' ? <SafeLink url={purchaseInfoLink} text={`@${PURCHASE_USERNAME}`} /> : s);
|
return (s === 'PURCHASE_LINK' ? <SafeLink url={purchaseInfoLink} text={`@${PURCHASE_USERNAME}`} /> : s);
|
||||||
})}
|
})}
|
||||||
</p>
|
</IslandDescription>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>{lang(`${langPrefix2}Type`)}</IslandTitle>
|
||||||
<h3 className="section-heading">{lang(`${langPrefix2}Type`)}</h3>
|
<Island dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
selected={privacyType}
|
selected={privacyType}
|
||||||
name="channel-type"
|
name="channel-type"
|
||||||
options={options}
|
options={options}
|
||||||
onChange={handleOptionChange}
|
onChange={handleOptionChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
{privacyType === 'private' ? (
|
{privacyType === 'private' ? (
|
||||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
<>
|
||||||
{privateInviteLink ? (
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<>
|
{lang('InviteLink.InviteLink')}
|
||||||
<LinkField link={privateInviteLink} className="invite-link" />
|
</IslandTitle>
|
||||||
<p className="section-info" dir={lang.isRtl ? 'rtl' : undefined}>
|
<Island dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang(`${langPrefix1}PrivateLinkHelp`)}
|
{privateInviteLink ? (
|
||||||
</p>
|
<>
|
||||||
|
<LinkField link={privateInviteLink} className="invite-link" noTitle />
|
||||||
<ListItem icon="delete" ripple destructive onClick={openRevokeConfirmDialog}>
|
<ListItem icon="delete" ripple destructive onClick={openRevokeConfirmDialog}>
|
||||||
{lang('RevokeLink')}
|
{lang('RevokeLink')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isRevokeConfirmDialogOpen}
|
isOpen={isRevokeConfirmDialogOpen}
|
||||||
onClose={closeRevokeConfirmDialog}
|
onClose={closeRevokeConfirmDialog}
|
||||||
text={lang('RevokeAlert')}
|
text={lang('RevokeAlert')}
|
||||||
confirmLabel={lang('RevokeButton')}
|
confirmLabel={lang('RevokeButton')}
|
||||||
confirmHandler={handleRevokePrivateLink}
|
confirmHandler={handleRevokePrivateLink}
|
||||||
confirmIsDestructive
|
confirmIsDestructive
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Loading />
|
<Loading />
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
|
{lang(`${langPrefix1}PrivateLinkHelp`)}
|
||||||
|
</IslandDescription>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="section no-border">
|
<>
|
||||||
<div className="settings-input">
|
<Island>
|
||||||
<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>
|
/>
|
||||||
|
</div>
|
||||||
|
</Island>
|
||||||
{error === USERNAME_PURCHASE_ERROR && renderPurchaseLink()}
|
{error === USERNAME_PURCHASE_ERROR && renderPurchaseLink()}
|
||||||
<p className="section-info" dir="auto">
|
<IslandDescription dir="auto">
|
||||||
{lang(`${langPrefix2}.Username.CreatePublicLinkHelp`)}
|
{lang(`${langPrefix2}.Username.CreatePublicLinkHelp`)}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
{shouldRenderUsernamesManage && (
|
{shouldRenderUsernamesManage && (
|
||||||
<ManageUsernames
|
<ManageUsernames
|
||||||
@ -257,22 +264,22 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
|||||||
onEditUsername={handleUsernameChange}
|
onEditUsername={handleUsernameChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<h3 className="section-heading">
|
{lang(isChannel ? 'ChannelVisibility.Forwarding.ChannelTitle' : 'ChannelVisibility.Forwarding.GroupTitle')}
|
||||||
{lang(isChannel ? 'ChannelVisibility.Forwarding.ChannelTitle' : 'ChannelVisibility.Forwarding.GroupTitle')}
|
</IslandTitle>
|
||||||
</h3>
|
<Island dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
selected={isProtected ? 'protected' : 'allowed'}
|
selected={isProtected ? 'protected' : 'allowed'}
|
||||||
name="forwarding-type"
|
name="forwarding-type"
|
||||||
options={forwardingOptions}
|
options={forwardingOptions}
|
||||||
onChange={handleForwardingOptionChange}
|
onChange={handleForwardingOptionChange}
|
||||||
/>
|
/>
|
||||||
<p className="section-info section-info_push">
|
</Island>
|
||||||
{isChannel
|
<IslandDescription>
|
||||||
? lang('ChannelVisibility.Forwarding.ChannelInfo')
|
{isChannel
|
||||||
: lang('ChannelVisibility.Forwarding.GroupInfo')}
|
? lang('ChannelVisibility.Forwarding.ChannelInfo')
|
||||||
</p>
|
: lang('ChannelVisibility.Forwarding.GroupInfo')}
|
||||||
</div>
|
</IslandDescription>
|
||||||
</div>
|
</div>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={canUpdate}
|
isShown={canUpdate}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import useHistoryBack from '../../../hooks/useHistoryBack';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import ListItem, { type MenuItemContextAction } from '../../ui/ListItem';
|
import ListItem, { type MenuItemContextAction } from '../../ui/ListItem';
|
||||||
import RemoveGroupUserModal from './RemoveGroupUserModal';
|
import RemoveGroupUserModal from './RemoveGroupUserModal';
|
||||||
@ -83,40 +84,41 @@ const ManageChatRemovedUsers: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandDescription>{lang(isChannel ? 'NoBlockedChannel2' : 'NoBlockedGroup2')}</IslandDescription>
|
||||||
<p className="section-help">{lang(isChannel ? 'NoBlockedChannel2' : 'NoBlockedGroup2')}</p>
|
{Boolean(removedMembers.length) && (
|
||||||
|
<Island dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{removedMembers.map((member) => (
|
{removedMembers.map((member) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={member.userId}
|
key={member.userId}
|
||||||
className="chat-item-clickable"
|
className="chat-item-clickable"
|
||||||
ripple
|
ripple
|
||||||
contextActions={getContextActions(member)}
|
contextActions={getContextActions(member)}
|
||||||
>
|
>
|
||||||
<PrivateChatInfo
|
<PrivateChatInfo
|
||||||
userId={member.userId}
|
userId={member.userId}
|
||||||
status={getRemovedBy(member)}
|
status={getRemovedBy(member)}
|
||||||
forceShowSelf
|
forceShowSelf
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
{canDeleteMembers && (
|
</Island>
|
||||||
<FloatingActionButton
|
)}
|
||||||
isShown
|
|
||||||
onClick={openRemoveUserModal}
|
|
||||||
ariaLabel={lang('Channel.EditAdmin.Permission.BanUsers')}
|
|
||||||
iconName="add-user-filled"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{chat && canDeleteMembers && (
|
|
||||||
<RemoveGroupUserModal
|
|
||||||
chat={chat}
|
|
||||||
isOpen={isRemoveUserModalOpen}
|
|
||||||
onClose={closeRemoveUserModal}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{canDeleteMembers && (
|
||||||
|
<FloatingActionButton
|
||||||
|
isShown
|
||||||
|
onClick={openRemoveUserModal}
|
||||||
|
ariaLabel={lang('Channel.EditAdmin.Permission.BanUsers')}
|
||||||
|
iconName="add-user-filled"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{chat && canDeleteMembers && (
|
||||||
|
<RemoveGroupUserModal
|
||||||
|
chat={chat}
|
||||||
|
isOpen={isRemoveUserModalOpen}
|
||||||
|
onClose={closeRemoveUserModal}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
|||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import GroupChatInfo from '../../common/GroupChatInfo';
|
import GroupChatInfo from '../../common/GroupChatInfo';
|
||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -208,7 +209,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
|||||||
function renderDiscussionGroups() {
|
function renderDiscussionGroups() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p className="section-help" dir="auto">{lang('DiscussionChannelHelp')}</p>
|
<IslandDescription dir="auto">{lang('DiscussionChannelHelp')}</IslandDescription>
|
||||||
|
|
||||||
<div teactFastList>
|
<div teactFastList>
|
||||||
<ListItem
|
<ListItem
|
||||||
@ -239,7 +240,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
|||||||
<NothingFound key="nothing-found" teactOrderKey={0} text="No discussion groups found" />
|
<NothingFound key="nothing-found" teactOrderKey={0} text="No discussion groups found" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-4 mb-0 section-help" dir="auto">{lang('DiscussionChannelHelp2')}</p>
|
<IslandDescription className="mt-4 mb-0" dir="auto">{lang('DiscussionChannelHelp2')}</IslandDescription>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isConfirmLinkGroupDialogOpen}
|
isOpen={isConfirmLinkGroupDialogOpen}
|
||||||
onClose={closeConfirmLinkGroupDialog}
|
onClose={closeConfirmLinkGroupDialog}
|
||||||
@ -255,7 +256,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section">
|
<Island>
|
||||||
<AnimatedIconWithPreview
|
<AnimatedIconWithPreview
|
||||||
tgsUrl={LOCAL_TGS_URLS.DiscussionGroups}
|
tgsUrl={LOCAL_TGS_URLS.DiscussionGroups}
|
||||||
size={STICKER_SIZE_DISCUSSION_GROUPS}
|
size={STICKER_SIZE_DISCUSSION_GROUPS}
|
||||||
@ -263,30 +264,32 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
{linkedChat && renderLinkedGroup()}
|
{linkedChat && renderLinkedGroup()}
|
||||||
{!linkedChat && renderDiscussionGroups()}
|
{!linkedChat && renderDiscussionGroups()}
|
||||||
</div>
|
</Island>
|
||||||
{linkedChat && (
|
{linkedChat && (
|
||||||
<div className="section">
|
<>
|
||||||
<h3 className="section-heading">{lang('ChannelSettingsJoinTitle')}</h3>
|
<IslandTitle>{lang('ChannelSettingsJoinTitle')}</IslandTitle>
|
||||||
<div className="ListItem narrow">
|
<Island>
|
||||||
<Checkbox
|
|
||||||
checked={isJoinToSend}
|
|
||||||
onCheck={handleJoinToSendCheck}
|
|
||||||
label={lang('ChannelSettingsJoinToSend')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{isJoinToSend && (
|
|
||||||
<div className="ListItem narrow">
|
<div className="ListItem narrow">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isJoinRequest}
|
checked={isJoinToSend}
|
||||||
onCheck={handleJoinRequestCheck}
|
onCheck={handleJoinToSendCheck}
|
||||||
label={lang('ChannelSettingsJoinRequest')}
|
label={lang('ChannelSettingsJoinToSend')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
{isJoinToSend && (
|
||||||
<p className="section-info section-info_push">
|
<div className="ListItem narrow">
|
||||||
|
<Checkbox
|
||||||
|
checked={isJoinRequest}
|
||||||
|
onCheck={handleJoinRequestCheck}
|
||||||
|
label={lang('ChannelSettingsJoinRequest')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Island>
|
||||||
|
<IslandDescription>
|
||||||
{isJoinToSend ? lang('ChannelSettingsJoinRequestInfo') : lang('ChannelSettingsJoinToSendInfo')}
|
{isJoinToSend ? lang('ChannelSettingsJoinRequestInfo') : lang('ChannelSettingsJoinToSendInfo')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import useMedia from '../../../hooks/useMedia';
|
import useMedia from '../../../hooks/useMedia';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import AvatarEditable from '../../ui/AvatarEditable';
|
import AvatarEditable from '../../ui/AvatarEditable';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
@ -320,13 +321,13 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section">
|
<AvatarEditable
|
||||||
<AvatarEditable
|
isForForum={isForumEnabled}
|
||||||
isForForum={isForumEnabled}
|
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
||||||
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
onChange={handleSetPhoto}
|
||||||
onChange={handleSetPhoto}
|
disabled={!canChangeInfo}
|
||||||
disabled={!canChangeInfo}
|
/>
|
||||||
/>
|
<Island>
|
||||||
<div className="settings-edit">
|
<div className="settings-edit">
|
||||||
<InputText
|
<InputText
|
||||||
id="group-title"
|
id="group-title"
|
||||||
@ -431,11 +432,11 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
|||||||
inactive
|
inactive
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<div className="section-info section-info_push">{lang('ForumToggleDescription')}</div>
|
<IslandDescription>{lang('ForumToggleDescription')}</IslandDescription>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
<div className="section">
|
<Island>
|
||||||
<ListItem icon="group" multiline onClick={handleClickMembers}>
|
<ListItem icon="group" multiline onClick={handleClickMembers}>
|
||||||
<span className="title">{lang('GroupMembers')}</span>
|
<span className="title">{lang('GroupMembers')}</span>
|
||||||
<span className="subtitle">{formatInteger(chat.membersCount ?? 0)}</span>
|
<span className="subtitle">{formatInteger(chat.membersCount ?? 0)}</span>
|
||||||
@ -455,12 +456,12 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
<div className="section">
|
<Island>
|
||||||
<ListItem icon="delete" ripple destructive onClick={openDeleteDialog}>
|
<ListItem icon="delete" ripple destructive onClick={openDeleteDialog}>
|
||||||
{lang('DeleteMega')}
|
{lang('DeleteMega')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={isProfileFieldsTouched}
|
isShown={isProfileFieldsTouched}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
|
|
||||||
import PasswordConfirmModal from '../../common/PasswordConfirmModal';
|
import PasswordConfirmModal from '../../common/PasswordConfirmModal';
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -257,7 +258,7 @@ const ManageGroupAdminRights = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section">
|
<Island>
|
||||||
<ListItem inactive className="chat-item-clickable">
|
<ListItem inactive className="chat-item-clickable">
|
||||||
<PrivateChatInfo
|
<PrivateChatInfo
|
||||||
userId={selectedChatMember.userId}
|
userId={selectedChatMember.userId}
|
||||||
@ -266,8 +267,11 @@ const ManageGroupAdminRights = ({
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<h3 className="section-heading mt-4" dir="auto">{lang('EditAdminWhatCanDo')}</h3>
|
</Island>
|
||||||
|
|
||||||
|
<IslandTitle dir="auto">{lang('EditAdminWhatCanDo')}</IslandTitle>
|
||||||
|
|
||||||
|
<Island>
|
||||||
<div className="ListItem">
|
<div className="ListItem">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name="changeInfo"
|
name="changeInfo"
|
||||||
@ -442,9 +446,9 @@ const ManageGroupAdminRights = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{isFormFullyDisabled && (
|
{isFormFullyDisabled && (
|
||||||
<p className="section-info mb-4" dir="auto">
|
<IslandDescription className="mb-4" dir="auto">
|
||||||
{lang('EditAdminUnavailable')}
|
{lang('EditAdminUnavailable')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isChannel && (
|
{!isChannel && (
|
||||||
@ -469,7 +473,7 @@ const ManageGroupAdminRights = ({
|
|||||||
{lang('EditAdminRemoveAdmin')}
|
{lang('EditAdminRemoveAdmin')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
|||||||
@ -21,11 +21,13 @@ import usePeerStoriesPolling from '../../../hooks/polling/usePeerStoriesPolling'
|
|||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||||
import useKeyboardListNavigation from '../../../hooks/useKeyboardListNavigation';
|
import useKeyboardListNavigation from '../../../hooks/useKeyboardListNavigation';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
@ -86,7 +88,8 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
|||||||
openChat, setUserSearchQuery, closeManagement,
|
openChat, setUserSearchQuery, closeManagement,
|
||||||
toggleParticipantsHidden, setNewChatMembersDialogState, toggleManagement,
|
toggleParticipantsHidden, setNewChatMembersDialogState, toggleManagement,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
const lang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
const lang = useLang();
|
||||||
const inputRef = useRef<HTMLInputElement>();
|
const inputRef = useRef<HTMLInputElement>();
|
||||||
const containerRef = useRef<HTMLDivElement>();
|
const containerRef = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
@ -182,7 +185,7 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
function getMemberContextAction(memberId: string): MenuItemContextAction[] | undefined {
|
function getMemberContextAction(memberId: string): MenuItemContextAction[] | undefined {
|
||||||
return memberId === currentUserId || !canDeleteMembers ? undefined : [{
|
return memberId === currentUserId || !canDeleteMembers ? undefined : [{
|
||||||
title: lang('lng_context_remove_from_group'),
|
title: oldLang('lng_context_remove_from_group'),
|
||||||
icon: 'stop',
|
icon: 'stop',
|
||||||
handler: () => {
|
handler: () => {
|
||||||
setDeletingUserId(memberId);
|
setDeletingUserId(memberId);
|
||||||
@ -192,34 +195,37 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
function renderSearchField() {
|
function renderSearchField() {
|
||||||
return (
|
return (
|
||||||
<div className="Management__filter" dir={lang.isRtl ? 'rtl' : undefined}>
|
<Island dir={oldLang.isRtl ? 'rtl' : undefined}>
|
||||||
<InputText
|
<InputText
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={handleFilterChange}
|
onChange={handleFilterChange}
|
||||||
placeholder={lang('Search')}
|
placeholder={oldLang('Search')}
|
||||||
|
noMargin
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management ManageGroupMembers">
|
||||||
{noAdmins && renderSearchField()}
|
<div className="panel-content">
|
||||||
<div className="panel-content custom-scroll">
|
{noAdmins && renderSearchField()}
|
||||||
{canHideParticipants && !isChannel && (
|
{canHideParticipants && !isChannel && (
|
||||||
<div className="section">
|
<>
|
||||||
<ListItem icon="group" ripple onClick={handleToggleParticipantsHidden}>
|
<Island>
|
||||||
<span>{lang('ChannelHideMembers')}</span>
|
<ListItem icon="group" ripple onClick={handleToggleParticipantsHidden}>
|
||||||
<Switcher label={lang('ChannelHideMembers')} checked={areParticipantsHidden} />
|
<span>{oldLang('ChannelHideMembers')}</span>
|
||||||
</ListItem>
|
<Switcher label={oldLang('ChannelHideMembers')} checked={areParticipantsHidden} />
|
||||||
<p className="section-info">
|
</ListItem>
|
||||||
{lang(areParticipantsHidden ? 'GroupMembers.MembersHiddenOn' : 'GroupMembers.MembersHiddenOff')}
|
</Island>
|
||||||
</p>
|
<IslandDescription>
|
||||||
</div>
|
{oldLang(areParticipantsHidden ? 'GroupMembers.MembersHiddenOn' : 'GroupMembers.MembersHiddenOff')}
|
||||||
|
</IslandDescription>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="section">
|
{viewportIds?.length ? (
|
||||||
{viewportIds?.length ? (
|
<Island className="island-list">
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
className="picker-list custom-scroll"
|
className="picker-list custom-scroll"
|
||||||
items={displayedIds}
|
items={displayedIds}
|
||||||
@ -232,7 +238,6 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
key={id}
|
key={id}
|
||||||
className="chat-item-clickable scroll-item"
|
className="chat-item-clickable scroll-item"
|
||||||
|
|
||||||
onClick={() => handleMemberClick(id)}
|
onClick={() => handleMemberClick(id)}
|
||||||
contextActions={getMemberContextAction(id)}
|
contextActions={getMemberContextAction(id)}
|
||||||
withPortalForMenu
|
withPortalForMenu
|
||||||
@ -241,22 +246,22 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
) : !isSearching && viewportIds && !viewportIds.length ? (
|
</Island>
|
||||||
<NothingFound
|
) : !isSearching && viewportIds && !viewportIds.length ? (
|
||||||
teactOrderKey={0}
|
<NothingFound
|
||||||
key="nothing-found"
|
teactOrderKey={0}
|
||||||
text={isChannel ? 'No subscribers found' : 'No members found'}
|
key="nothing-found"
|
||||||
/>
|
text={lang(isChannel ? 'NoSubscribersFound' : 'NoMembersFound')}
|
||||||
) : (
|
/>
|
||||||
<Loading />
|
) : (
|
||||||
)}
|
<Loading />
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
{canAddMembers && (
|
{canAddMembers && (
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown
|
isShown
|
||||||
onClick={handleNewMemberDialogOpen}
|
onClick={handleNewMemberDialogOpen}
|
||||||
ariaLabel={lang('lng_channel_add_users')}
|
ariaLabel={oldLang('lng_channel_add_users')}
|
||||||
iconName="add-user-filled"
|
iconName="add-user-filled"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import useManagePermissions from '../hooks/useManagePermissions';
|
|||||||
|
|
||||||
import PaidMessagePrice from '../../common/paidMessage/PaidMessagePrice';
|
import PaidMessagePrice from '../../common/paidMessage/PaidMessagePrice';
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import PermissionCheckboxList from '../../main/PermissionCheckboxList';
|
import PermissionCheckboxList from '../../main/PermissionCheckboxList';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -222,7 +223,6 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const arePermissionsChanged = isPriceForMessagesChanged || havePermissionChanged;
|
const arePermissionsChanged = isPriceForMessagesChanged || havePermissionChanged;
|
||||||
const arePermissionsLoading = progress === ManagementProgress.InProgress || isLoading;
|
const arePermissionsLoading = progress === ManagementProgress.InProgress || isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="Management with-shifted-dropdown"
|
className="Management with-shifted-dropdown"
|
||||||
@ -230,8 +230,8 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
+ `--before-shift-height: ${BEFORE_ITEMS_COUNT * ITEM_HEIGHT}px;`}
|
+ `--before-shift-height: ${BEFORE_ITEMS_COUNT * ITEM_HEIGHT}px;`}
|
||||||
>
|
>
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section without-bottom-shadow">
|
<IslandTitle dir="auto">{lang('ChannelPermissionsHeader')}</IslandTitle>
|
||||||
<h3 className="section-heading" dir="auto">{lang('ChannelPermissionsHeader')}</h3>
|
<Island className={buildClassName('without-bottom-shadow', isMediaDropdownOpen && 'dropdown-open')}>
|
||||||
<PermissionCheckboxList
|
<PermissionCheckboxList
|
||||||
chatId={chat?.id}
|
chatId={chat?.id}
|
||||||
isMediaDropdownOpen={isMediaDropdownOpen}
|
isMediaDropdownOpen={isMediaDropdownOpen}
|
||||||
@ -243,17 +243,12 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
'DropdownList',
|
'DropdownList',
|
||||||
isMediaDropdownOpen && 'DropdownList--open',
|
isMediaDropdownOpen && 'DropdownList--open',
|
||||||
)}
|
)}
|
||||||
shiftedClassName={buildClassName('part', isMediaDropdownOpen && 'shifted')}
|
shiftedClassName="part"
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
{arePaidMessagesAvailable && (
|
{arePaidMessagesAvailable && (
|
||||||
<div
|
<Island>
|
||||||
className={buildClassName(
|
|
||||||
'section',
|
|
||||||
isMediaDropdownOpen && 'shifted',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ListItem onClick={handleChargeStarsForMessages}>
|
<ListItem onClick={handleChargeStarsForMessages}>
|
||||||
<span>{lang('GroupMessagesChargePrice')}</span>
|
<span>{lang('GroupMessagesChargePrice')}</span>
|
||||||
<Switcher
|
<Switcher
|
||||||
@ -262,34 +257,26 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
checked={isPriceForMessagesOpen}
|
checked={isPriceForMessagesOpen}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<p className="settings-item-description-larger" dir={lang.isRtl ? 'rtl' : undefined}>
|
</Island>
|
||||||
{lang('RightsChargeStarsAbout')}
|
)}
|
||||||
</p>
|
{arePaidMessagesAvailable && (
|
||||||
</div>
|
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
|
{lang('RightsChargeStarsAbout')}
|
||||||
|
</IslandDescription>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isPriceForMessagesOpen && (
|
{isPriceForMessagesOpen && (
|
||||||
<div
|
<Island>
|
||||||
className={buildClassName(
|
|
||||||
'section',
|
|
||||||
isMediaDropdownOpen && 'shifted',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<PaidMessagePrice
|
<PaidMessagePrice
|
||||||
canChangeChargeForMessages
|
canChangeChargeForMessages
|
||||||
isGroupChat
|
isGroupChat
|
||||||
chargeForMessages={chargeForMessages}
|
chargeForMessages={chargeForMessages}
|
||||||
onChange={handleChargeForMessagesChange}
|
onChange={handleChargeForMessagesChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<Island>
|
||||||
className={buildClassName(
|
|
||||||
'section',
|
|
||||||
isMediaDropdownOpen && 'shifted',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="delete-user"
|
icon="delete-user"
|
||||||
multiline
|
multiline
|
||||||
@ -299,16 +286,10 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
<span className="title">{lang('ChannelBlockedUsers')}</span>
|
<span className="title">{lang('ChannelBlockedUsers')}</span>
|
||||||
<span className="subtitle">{removedUsersCount}</span>
|
<span className="subtitle">{removedUsersCount}</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
<div
|
|
||||||
className={buildClassName(
|
|
||||||
'section',
|
|
||||||
isMediaDropdownOpen && 'shifted',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<h3 className="section-heading" dir="auto">{lang('PrivacyExceptions')}</h3>
|
|
||||||
|
|
||||||
|
<IslandTitle dir="auto">{lang('PrivacyExceptions')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="add-user"
|
icon="add-user"
|
||||||
onClick={handleAddExceptionClick}
|
onClick={handleAddExceptionClick}
|
||||||
@ -330,7 +311,7 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
import useManagePermissions from '../hooks/useManagePermissions';
|
import useManagePermissions from '../hooks/useManagePermissions';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandTitle } from '../../gili/layout/Island';
|
||||||
import PermissionCheckboxList from '../../main/PermissionCheckboxList';
|
import PermissionCheckboxList from '../../main/PermissionCheckboxList';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -134,12 +135,12 @@ const ManageGroupUserPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
+ `--before-shift-height: ${BEFORE_ITEMS_COUNT * ITEM_HEIGHT + BEFORE_USER_INFO_HEIGHT}px;`}
|
+ `--before-shift-height: ${BEFORE_ITEMS_COUNT * ITEM_HEIGHT + BEFORE_USER_INFO_HEIGHT}px;`}
|
||||||
>
|
>
|
||||||
<div className="custom-scroll">
|
<div className="custom-scroll">
|
||||||
<div className="section without-bottom-shadow">
|
<Island className={buildClassName('without-bottom-shadow', isMediaDropdownOpen && 'dropdown-open')}>
|
||||||
<ListItem inactive className="chat-item-clickable">
|
<ListItem inactive className="chat-item-clickable">
|
||||||
<PrivateChatInfo userId={selectedChatMember.userId} forceShowSelf />
|
<PrivateChatInfo userId={selectedChatMember.userId} forceShowSelf />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<h3 className="section-heading mt-4" dir="auto">{oldLang('UserRestrictionsCanDo')}</h3>
|
<IslandTitle className="mt-4" dir="auto">{oldLang('UserRestrictionsCanDo')}</IslandTitle>
|
||||||
<PermissionCheckboxList
|
<PermissionCheckboxList
|
||||||
chatId={chat?.id}
|
chatId={chat?.id}
|
||||||
isMediaDropdownOpen={isMediaDropdownOpen}
|
isMediaDropdownOpen={isMediaDropdownOpen}
|
||||||
@ -151,22 +152,22 @@ const ManageGroupUserPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
isMediaDropdownOpen && 'DropdownList--open',
|
isMediaDropdownOpen && 'DropdownList--open',
|
||||||
)}
|
)}
|
||||||
dropdownClassName="DropdownListTrap"
|
dropdownClassName="DropdownListTrap"
|
||||||
shiftedClassName={buildClassName('part', isMediaDropdownOpen && 'shifted')}
|
shiftedClassName="part"
|
||||||
getControlIsDisabled={getControlIsDisabled}
|
getControlIsDisabled={getControlIsDisabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
|
|
||||||
{!isFormFullyDisabled && (
|
{!isFormFullyDisabled && (
|
||||||
<div
|
<Island
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
'section',
|
'part',
|
||||||
isMediaDropdownOpen && 'shifted',
|
isMediaDropdownOpen && 'shifted',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListItem icon="delete-user" ripple destructive onClick={openBanConfirmationDialog}>
|
<ListItem icon="delete-user" ripple destructive onClick={openBanConfirmationDialog}>
|
||||||
{oldLang('UserRestrictionsBlock')}
|
{oldLang('UserRestrictionsBlock')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -9,9 +9,11 @@ import { isChatChannel, sortUserIds } from '../../../global/helpers';
|
|||||||
import { selectChat, selectChatFullInfo } from '../../../global/selectors';
|
import { selectChat, selectChatFullInfo } from '../../../global/selectors';
|
||||||
|
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -39,6 +41,8 @@ const ManageGroupUserPermissionsCreate: FC<OwnProps & StateProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
isActive,
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack({
|
useHistoryBack({
|
||||||
isActive,
|
isActive,
|
||||||
onBack: onClose,
|
onBack: onClose,
|
||||||
@ -64,9 +68,9 @@ const ManageGroupUserPermissionsCreate: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="custom-scroll">
|
<div className="custom-scroll">
|
||||||
<div className="section" teactFastList>
|
{memberIds?.length ? (
|
||||||
{memberIds ? (
|
<Island teactFastList>
|
||||||
memberIds.map((id, i) => (
|
{memberIds.map((id, i) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={id}
|
key={id}
|
||||||
teactOrderKey={i}
|
teactOrderKey={i}
|
||||||
@ -76,15 +80,13 @@ const ManageGroupUserPermissionsCreate: FC<OwnProps & StateProps> = ({
|
|||||||
>
|
>
|
||||||
<PrivateChatInfo userId={id} forceShowSelf />
|
<PrivateChatInfo userId={id} forceShowSelf />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))
|
))}
|
||||||
) : (
|
</Island>
|
||||||
<NothingFound
|
) : (
|
||||||
teactOrderKey={0}
|
<NothingFound
|
||||||
key="nothing-found"
|
text={lang(isChannel ? 'NoSubscribersFound' : 'NoMembersFound')}
|
||||||
text={isChannel ? 'No subscribers found' : 'No members found'}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
|
|
||||||
import CalendarModal from '../../common/CalendarModal';
|
import CalendarModal from '../../common/CalendarModal';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -158,25 +159,25 @@ const ManageInvite: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management ManageInvite">
|
<div className="Management ManageInvite">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section">
|
<Island>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={lang('ApproveNewMembers')}
|
label={lang('ApproveNewMembers')}
|
||||||
subLabel={lang('ApproveNewMembersDescription')}
|
subLabel={lang('ApproveNewMembersDescription')}
|
||||||
checked={isRequestNeeded}
|
checked={isRequestNeeded}
|
||||||
onChange={handleIsRequestChange}
|
onChange={handleIsRequestChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Island>
|
||||||
<div className="section">
|
<Island>
|
||||||
<InputText
|
<InputText
|
||||||
className="link-name"
|
className="link-name"
|
||||||
placeholder={lang('LinkNameHint')}
|
placeholder={lang('LinkNameHint')}
|
||||||
value={title}
|
value={title}
|
||||||
onChange={handleTitleChange}
|
onChange={handleTitleChange}
|
||||||
/>
|
/>
|
||||||
<p className="section-help hint">{lang('LinkNameHelp')}</p>
|
<IslandDescription>{lang('LinkNameHelp')}</IslandDescription>
|
||||||
</div>
|
</Island>
|
||||||
<div className="section">
|
<IslandTitle>{lang('LimitByPeriod')}</IslandTitle>
|
||||||
<div className="section-heading">{lang('LimitByPeriod')}</div>
|
<Island>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="expireOptions"
|
name="expireOptions"
|
||||||
options={[
|
options={[
|
||||||
@ -211,39 +212,41 @@ const ManageInvite: FC<OwnProps & StateProps> = ({
|
|||||||
{formatTime(lang, customExpireDate)}
|
{formatTime(lang, customExpireDate)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<p className="section-help hint">{lang('TimeLimitHelp')}</p>
|
<IslandDescription>{lang('TimeLimitHelp')}</IslandDescription>
|
||||||
</div>
|
</Island>
|
||||||
{!isRequestNeeded && (
|
{!isRequestNeeded && (
|
||||||
<div className="section">
|
<>
|
||||||
<div className="section-heading">{lang('LimitNumberOfUses')}</div>
|
<IslandTitle>{lang('LimitNumberOfUses')}</IslandTitle>
|
||||||
<RadioGroup
|
<Island>
|
||||||
name="usageOptions"
|
<RadioGroup
|
||||||
options={[
|
name="usageOptions"
|
||||||
...DEFAULT_USAGE_LIMITS.map((n) => ({ value: n.toString(), label: n })),
|
options={[
|
||||||
{
|
...DEFAULT_USAGE_LIMITS.map((n) => ({ value: n.toString(), label: n })),
|
||||||
value: '0',
|
{
|
||||||
label: lang('NoLimit'),
|
value: '0',
|
||||||
},
|
label: lang('NoLimit'),
|
||||||
{
|
},
|
||||||
value: 'custom',
|
{
|
||||||
label: lang('lng_group_invite_usage_custom'),
|
value: 'custom',
|
||||||
},
|
label: lang('lng_group_invite_usage_custom'),
|
||||||
]}
|
},
|
||||||
onChange={setSelectedUsageOption}
|
]}
|
||||||
selected={selectedUsageOption}
|
onChange={setSelectedUsageOption}
|
||||||
/>
|
selected={selectedUsageOption}
|
||||||
{selectedUsageOption === 'custom' && (
|
|
||||||
<input
|
|
||||||
className="form-control usage-limit"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="99999"
|
|
||||||
value={customUsageLimit}
|
|
||||||
onChange={handleCustomUsageLimitChange}
|
|
||||||
/>
|
/>
|
||||||
)}
|
{selectedUsageOption === 'custom' && (
|
||||||
<p className="section-help hint">{lang('UsesLimitHelp')}</p>
|
<input
|
||||||
</div>
|
className="form-control usage-limit"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="99999"
|
||||||
|
value={customUsageLimit}
|
||||||
|
onChange={handleCustomUsageLimitChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<IslandDescription>{lang('UsesLimitHelp')}</IslandDescription>
|
||||||
|
</Island>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown
|
isShown
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
|
|
||||||
import LinkField from '../../common/LinkField';
|
import LinkField from '../../common/LinkField';
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Spinner from '../../ui/Spinner';
|
import Spinner from '../../ui/Spinner';
|
||||||
|
|
||||||
@ -70,11 +71,13 @@ const ManageInviteInfo = ({
|
|||||||
if (!importers?.length && requesters?.length) return undefined;
|
if (!importers?.length && requesters?.length) return undefined;
|
||||||
if (!importers) return <Spinner />;
|
if (!importers) return <Spinner />;
|
||||||
return (
|
return (
|
||||||
<div className="section">
|
<>
|
||||||
<p className="section-heading">{importers.length ? lang('PeopleJoined', usage) : lang('NoOneJoined')}</p>
|
<IslandTitle>{importers.length ? lang('PeopleJoined', usage) : lang('NoOneJoined')}</IslandTitle>
|
||||||
<p className="section-help">
|
<Island>
|
||||||
{!importers.length && (
|
{!importers.length && (
|
||||||
usageLimit ? lang('PeopleCanJoinViaLinkCount', usageLimit - usage) : lang('NoOneJoinedYet')
|
<IslandDescription>
|
||||||
|
{usageLimit ? lang('PeopleCanJoinViaLinkCount', usageLimit - usage) : lang('NoOneJoinedYet')}
|
||||||
|
</IslandDescription>
|
||||||
)}
|
)}
|
||||||
{importers.map((importer) => {
|
{importers.map((importer) => {
|
||||||
const joinTime = formatMediaDateTime(lang, importer.date * 1000, true);
|
const joinTime = formatMediaDateTime(lang, importer.date * 1000, true);
|
||||||
@ -93,8 +96,8 @@ const ManageInviteInfo = ({
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</p>
|
</Island>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -103,9 +106,9 @@ const ManageInviteInfo = ({
|
|||||||
if (!requesters && importers) return <Spinner />;
|
if (!requesters && importers) return <Spinner />;
|
||||||
if (!requesters?.length) return undefined;
|
if (!requesters?.length) return undefined;
|
||||||
return (
|
return (
|
||||||
<div className="section">
|
<>
|
||||||
<p className="section-heading">{isChannel ? lang('SubscribeRequests') : lang('MemberRequests')}</p>
|
<IslandTitle>{isChannel ? lang('SubscribeRequests') : lang('MemberRequests')}</IslandTitle>
|
||||||
<p className="section-help">
|
<Island>
|
||||||
{requesters.map((requester) => (
|
{requesters.map((requester) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
className="chat-item-clickable scroll-item small-icon"
|
className="chat-item-clickable scroll-item small-icon"
|
||||||
@ -119,8 +122,8 @@ const ManageInviteInfo = ({
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</p>
|
</Island>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,35 +131,37 @@ const ManageInviteInfo = ({
|
|||||||
<div className="Management ManageInviteInfo">
|
<div className="Management ManageInviteInfo">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
{!invite && (
|
{!invite && (
|
||||||
<p className="section-help">{lang('Loading')}</p>
|
<IslandDescription>{lang('Loading')}</IslandDescription>
|
||||||
)}
|
)}
|
||||||
{invite && (
|
{invite && (
|
||||||
<>
|
<>
|
||||||
<div className="section">
|
<Island>
|
||||||
<LinkField title={invite.title} link={invite.link} className="invite-link" />
|
<LinkField title={invite.title} link={invite.link} className="invite-link" />
|
||||||
{Boolean(expireDate) && (
|
{Boolean(expireDate) && (
|
||||||
<p className="section-help">
|
<IslandDescription>
|
||||||
{isExpired
|
{isExpired
|
||||||
? lang('ExpiredLink')
|
? lang('ExpiredLink')
|
||||||
: lang('LinkExpiresIn', `${formatFullDate(lang, expireDate)} ${formatTime(lang, expireDate)}`)}
|
: lang('LinkExpiresIn', `${formatFullDate(lang, expireDate)} ${formatTime(lang, expireDate)}`)}
|
||||||
</p>
|
</IslandDescription>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
{adminId && (
|
{adminId && (
|
||||||
<div className="section">
|
<>
|
||||||
<p className="section-heading">{lang('LinkCreatedeBy')}</p>
|
<IslandTitle>{lang('LinkCreatedeBy')}</IslandTitle>
|
||||||
<ListItem
|
<Island>
|
||||||
className="chat-item-clickable scroll-item small-icon"
|
<ListItem
|
||||||
|
className="chat-item-clickable scroll-item small-icon"
|
||||||
|
|
||||||
onClick={() => openChat({ id: adminId })}
|
onClick={() => openChat({ id: adminId })}
|
||||||
>
|
>
|
||||||
<PrivateChatInfo
|
<PrivateChatInfo
|
||||||
userId={adminId}
|
userId={adminId}
|
||||||
status={formatMediaDateTime(lang, invite.date * 1000, true)}
|
status={formatMediaDateTime(lang, invite.date * 1000, true)}
|
||||||
forceShowSelf
|
forceShowSelf
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{renderImporters()}
|
{renderImporters()}
|
||||||
{renderRequesters()}
|
{renderRequesters()}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ 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 Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
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,26 +275,33 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management ManageInvites">
|
<div className="Management ManageInvites">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
<div className="section">
|
<Island>
|
||||||
<AnimatedIconWithPreview
|
<AnimatedIconWithPreview
|
||||||
tgsUrl={LOCAL_TGS_URLS.Invite}
|
tgsUrl={LOCAL_TGS_URLS.Invite}
|
||||||
size={STICKER_SIZE_INVITES}
|
size={STICKER_SIZE_INVITES}
|
||||||
className="section-icon"
|
className="section-icon"
|
||||||
/>
|
/>
|
||||||
<p className="section-help">{isChannel ? oldLang('PrimaryLinkHelpChannel') : oldLang('PrimaryLinkHelp')}</p>
|
<IslandDescription>
|
||||||
</div>
|
{isChannel ? oldLang('PrimaryLinkHelpChannel') : oldLang('PrimaryLinkHelp')}
|
||||||
|
</IslandDescription>
|
||||||
|
</Island>
|
||||||
{primaryInviteLink && (
|
{primaryInviteLink && (
|
||||||
<div className="section">
|
<>
|
||||||
<LinkField
|
<IslandTitle>
|
||||||
className="settings-input"
|
{chat?.usernames ? oldLang('PublicLink') : oldLang('lng_create_permanent_link_title')}
|
||||||
link={primaryInviteLink}
|
</IslandTitle>
|
||||||
withShare
|
<Island>
|
||||||
onRevoke={!chat?.usernames ? handlePrimaryRevoke : undefined}
|
<LinkField
|
||||||
title={chat?.usernames ? oldLang('PublicLink') : oldLang('lng_create_permanent_link_title')}
|
className="settings-input"
|
||||||
/>
|
link={primaryInviteLink}
|
||||||
</div>
|
noTitle
|
||||||
|
withShare
|
||||||
|
onRevoke={!chat?.usernames ? handlePrimaryRevoke : undefined}
|
||||||
|
/>
|
||||||
|
</Island>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="section" teactFastList>
|
<Island teactFastList>
|
||||||
<ListItem icon="add" withPrimaryColor key="create" className="create-item" onClick={handleCreateNewClick}>
|
<ListItem icon="add" withPrimaryColor key="create" className="create-item" onClick={handleCreateNewClick}>
|
||||||
{oldLang('CreateNewLink')}
|
{oldLang('CreateNewLink')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
@ -314,11 +322,11 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
<p className="section-help hint" key="links-hint">{oldLang('ManageLinksInfoHelp')}</p>
|
</Island>
|
||||||
</div>
|
<IslandDescription>{oldLang('ManageLinksInfoHelp')}</IslandDescription>
|
||||||
{revokedExportedInvites && Boolean(revokedExportedInvites.length) && (
|
{revokedExportedInvites && Boolean(revokedExportedInvites.length) && (
|
||||||
<div className="section" teactFastList>
|
<Island teactFastList>
|
||||||
<p className="section-help" key="title">{oldLang('RevokedLinks')}</p>
|
<IslandDescription key="title">{oldLang('RevokedLinks')}</IslandDescription>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="delete"
|
icon="delete"
|
||||||
destructive
|
destructive
|
||||||
@ -343,7 +351,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Island>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import useHistoryBack from '../../../hooks/useHistoryBack';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import Spinner from '../../ui/Spinner';
|
import Spinner from '../../ui/Spinner';
|
||||||
@ -68,7 +69,7 @@ const ManageJoinRequests: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management ManageJoinRequests">
|
<div className="Management ManageJoinRequests">
|
||||||
<div className="custom-scroll">
|
<div className="custom-scroll">
|
||||||
<div className="section">
|
<Island>
|
||||||
<AnimatedIconWithPreview
|
<AnimatedIconWithPreview
|
||||||
tgsUrl={LOCAL_TGS_URLS.JoinRequest}
|
tgsUrl={LOCAL_TGS_URLS.JoinRequest}
|
||||||
size={STICKER_SIZE_JOIN_REQUESTS}
|
size={STICKER_SIZE_JOIN_REQUESTS}
|
||||||
@ -80,8 +81,8 @@ const ManageJoinRequests: FC<OwnProps & StateProps> = ({
|
|||||||
<Button className="bulk-action-button" onClick={openRejectAllDialog} isText>Dismiss all</Button>
|
<Button className="bulk-action-button" onClick={openRejectAllDialog} isText>Dismiss all</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Island>
|
||||||
<div className="section" teactFastList>
|
<Island teactFastList>
|
||||||
<p key="title">
|
<p key="title">
|
||||||
{!chat?.joinRequests ? lang('Loading') : chat.joinRequests.length
|
{!chat?.joinRequests ? lang('Loading') : chat.joinRequests.length
|
||||||
? lang('JoinRequests', chat.joinRequests.length) : lang('NoMemberRequests')}
|
? lang('JoinRequests', chat.joinRequests.length) : lang('NoMemberRequests')}
|
||||||
@ -90,9 +91,9 @@ const ManageJoinRequests: FC<OwnProps & StateProps> = ({
|
|||||||
<Spinner key="loading" />
|
<Spinner key="loading" />
|
||||||
)}
|
)}
|
||||||
{chat?.joinRequests?.length === 0 && (
|
{chat?.joinRequests?.length === 0 && (
|
||||||
<p className="section-help" key="empty">
|
<IslandDescription key="empty">
|
||||||
{isChannel ? lang('NoSubscribeRequestsDescription') : lang('NoMemberRequestsDescription')}
|
{isChannel ? lang('NoSubscribeRequestsDescription') : lang('NoMemberRequestsDescription')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
)}
|
)}
|
||||||
{chat?.joinRequests?.map(({ userId, about, date }) => (
|
{chat?.joinRequests?.map(({ userId, about, date }) => (
|
||||||
<JoinRequest
|
<JoinRequest
|
||||||
@ -104,7 +105,7 @@ const ManageJoinRequests: FC<OwnProps & StateProps> = ({
|
|||||||
key={userId}
|
key={userId}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isAcceptAllDialogOpen}
|
isOpen={isAcceptAllDialogOpen}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import useHistoryBack from '../../../hooks/useHistoryBack';
|
|||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
@ -189,61 +190,65 @@ const ManageReactions: FC<OwnProps & StateProps> = ({
|
|||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="panel-content custom-scroll">
|
<div className="panel-content custom-scroll">
|
||||||
{Boolean(localReactionsLimit && shouldShowReactionsLimit) && (
|
{Boolean(localReactionsLimit && shouldShowReactionsLimit) && (
|
||||||
<div className="section">
|
<>
|
||||||
<h3 className="section-heading">
|
<IslandTitle>
|
||||||
{lang('MaximumReactionsHeader')}
|
{lang('MaximumReactionsHeader')}
|
||||||
</h3>
|
</IslandTitle>
|
||||||
<RangeSlider
|
<Island>
|
||||||
min={1}
|
<RangeSlider
|
||||||
max={maxUniqueReactions}
|
min={1}
|
||||||
value={localReactionsLimit!}
|
max={maxUniqueReactions}
|
||||||
onChange={handleReactionsLimitChange}
|
value={localReactionsLimit!}
|
||||||
renderValue={renderReactionsMaxCountValue}
|
onChange={handleReactionsLimitChange}
|
||||||
isCenteredLayout
|
renderValue={renderReactionsMaxCountValue}
|
||||||
/>
|
isCenteredLayout
|
||||||
<p className="section-info section-info_push">
|
/>
|
||||||
|
</Island>
|
||||||
|
<IslandDescription>
|
||||||
{lang('ChannelReactions.MaxCount.Info')}
|
{lang('ChannelReactions.MaxCount.Info')}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="section">
|
<IslandTitle>
|
||||||
<h3 className="section-heading">
|
{lang('AvailableReactions')}
|
||||||
{lang('AvailableReactions')}
|
</IslandTitle>
|
||||||
</h3>
|
<Island>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
selected={localEnabledReactions?.type || 'none'}
|
selected={localEnabledReactions?.type || 'none'}
|
||||||
name="reactions"
|
name="reactions"
|
||||||
options={reactionsOptions}
|
options={reactionsOptions}
|
||||||
onChange={handleReactionsOptionChange}
|
onChange={handleReactionsOptionChange}
|
||||||
/>
|
/>
|
||||||
<p className="section-info section-info_push">
|
</Island>
|
||||||
{localEnabledReactions?.type === 'all' && lang('EnableAllReactionsInfo')}
|
<IslandDescription>
|
||||||
{localEnabledReactions?.type === 'some' && lang('EnableSomeReactionsInfo')}
|
{localEnabledReactions?.type === 'all' && lang('EnableAllReactionsInfo')}
|
||||||
{!localEnabledReactions && lang('DisableReactionsInfo')}
|
{localEnabledReactions?.type === 'some' && lang('EnableSomeReactionsInfo')}
|
||||||
</p>
|
{!localEnabledReactions && lang('DisableReactionsInfo')}
|
||||||
</div>
|
</IslandDescription>
|
||||||
{localEnabledReactions?.type === 'some' && (
|
{localEnabledReactions?.type === 'some' && (
|
||||||
<div className="section section-with-fab">
|
<>
|
||||||
<h3 className="section-heading">
|
<IslandTitle>
|
||||||
{lang('OnlyAllowThisReactions')}
|
{lang('OnlyAllowThisReactions')}
|
||||||
</h3>
|
</IslandTitle>
|
||||||
{availableActiveReactions?.map(({ reaction, title }) => (
|
<Island>
|
||||||
<div className="ListItem">
|
{availableActiveReactions?.map(({ reaction, title }) => (
|
||||||
<Checkbox
|
<div key={reaction.emoticon} className="ListItem">
|
||||||
name={reaction.emoticon}
|
<Checkbox
|
||||||
checked={localEnabledReactions?.allowed.some((r) => isSameReaction(reaction, r))}
|
name={reaction.emoticon}
|
||||||
label={(
|
checked={localEnabledReactions?.allowed.some((r) => isSameReaction(reaction, r))}
|
||||||
<div className="Reaction">
|
label={(
|
||||||
<ReactionStaticEmoji reaction={reaction} availableReactions={availableReactions} />
|
<div className="Reaction">
|
||||||
{title}
|
<ReactionStaticEmoji reaction={reaction} availableReactions={availableReactions} />
|
||||||
</div>
|
{title}
|
||||||
)}
|
</div>
|
||||||
withIcon
|
)}
|
||||||
onChange={handleReactionChange}
|
withIcon
|
||||||
/>
|
onChange={handleReactionChange}
|
||||||
</div>
|
/>
|
||||||
))}
|
</div>
|
||||||
</div>
|
))}
|
||||||
|
</Island>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -227,14 +228,14 @@ const ManageUser: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="custom-scroll">
|
<div className="custom-scroll">
|
||||||
<div className="section">
|
<PrivateChatInfo
|
||||||
<PrivateChatInfo
|
userId={user.id}
|
||||||
userId={user.id}
|
avatarSize="jumbo"
|
||||||
avatarSize="jumbo"
|
noStatusOrTyping
|
||||||
noStatusOrTyping
|
noEmojiStatus
|
||||||
noEmojiStatus
|
withFullInfo
|
||||||
withFullInfo
|
/>
|
||||||
/>
|
<Island>
|
||||||
<div className="settings-edit">
|
<div className="settings-edit">
|
||||||
<InputText
|
<InputText
|
||||||
ref={firstNameRef}
|
ref={firstNameRef}
|
||||||
@ -262,7 +263,9 @@ const ManageUser: FC<OwnProps & StateProps> = ({
|
|||||||
noReplaceNewlines
|
noReplaceNewlines
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="section-edit-info" dir="auto">{lang('EditUserNoteHint')}</p>
|
</Island>
|
||||||
|
<IslandDescription dir="auto">{lang('EditUserNoteHint')}</IslandDescription>
|
||||||
|
<Island>
|
||||||
<div className="ListItem narrow">
|
<div className="ListItem narrow">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isNotificationsEnabled}
|
checked={isNotificationsEnabled}
|
||||||
@ -273,40 +276,42 @@ const ManageUser: FC<OwnProps & StateProps> = ({
|
|||||||
onChange={handleNotificationChange}
|
onChange={handleNotificationChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Island>
|
||||||
{canSetPersonalPhoto && (
|
{canSetPersonalPhoto && (
|
||||||
<div className="section">
|
<>
|
||||||
<ListItem icon="camera-add" ripple onClick={handleSuggestPhoto}>
|
<Island>
|
||||||
<span className="list-item-ellipsis">{oldLang('UserInfo.SuggestPhoto', user.firstName)}</span>
|
<ListItem icon="camera-add" ripple onClick={handleSuggestPhoto}>
|
||||||
</ListItem>
|
<span className="list-item-ellipsis">{oldLang('UserInfo.SuggestPhoto', user.firstName)}</span>
|
||||||
<ListItem icon="camera-add" ripple onClick={handleSetPersonalPhoto}>
|
|
||||||
<span className="list-item-ellipsis">{oldLang('UserInfo.SetCustomPhoto', user.firstName)}</span>
|
|
||||||
</ListItem>
|
|
||||||
{personalPhoto && (
|
|
||||||
<ListItem
|
|
||||||
leftElement={(
|
|
||||||
<Avatar
|
|
||||||
photo={notPersonalPhoto}
|
|
||||||
noPersonalPhoto
|
|
||||||
peer={user}
|
|
||||||
size="mini"
|
|
||||||
className="personal-photo"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
ripple
|
|
||||||
onClick={openResetPersonalPhotoDialog}
|
|
||||||
>
|
|
||||||
{oldLang('UserInfo.ResetCustomPhoto')}
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
<ListItem icon="camera-add" ripple onClick={handleSetPersonalPhoto}>
|
||||||
<p className="section-help" dir="auto">{oldLang('UserInfo.CustomPhotoInfo', user.firstName)}</p>
|
<span className="list-item-ellipsis">{oldLang('UserInfo.SetCustomPhoto', user.firstName)}</span>
|
||||||
</div>
|
</ListItem>
|
||||||
|
{personalPhoto && (
|
||||||
|
<ListItem
|
||||||
|
leftElement={(
|
||||||
|
<Avatar
|
||||||
|
photo={notPersonalPhoto}
|
||||||
|
noPersonalPhoto
|
||||||
|
peer={user}
|
||||||
|
size="mini"
|
||||||
|
className="personal-photo"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
ripple
|
||||||
|
onClick={openResetPersonalPhotoDialog}
|
||||||
|
>
|
||||||
|
{oldLang('UserInfo.ResetCustomPhoto')}
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
</Island>
|
||||||
|
<IslandDescription dir="auto">{oldLang('UserInfo.CustomPhotoInfo', user.firstName)}</IslandDescription>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="section">
|
<Island>
|
||||||
<ListItem icon="delete" ripple destructive onClick={openDeleteDialog}>
|
<ListItem icon="delete" ripple destructive onClick={openDeleteDialog}>
|
||||||
{oldLang('DeleteContact')}
|
{oldLang('DeleteContact')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
isShown={isProfileFieldsTouched}
|
isShown={isProfileFieldsTouched}
|
||||||
|
|||||||
@ -3,10 +3,17 @@
|
|||||||
.Management {
|
.Management {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
& > .custom-scroll {
|
& > .custom-scroll,
|
||||||
|
& > .panel-content {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
|
||||||
|
@include mixins.adapt-padding-to-scrollbar(1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.personal-photo {
|
.personal-photo {
|
||||||
@ -14,116 +21,58 @@
|
|||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section-icon {
|
||||||
padding: 1rem 0.5rem;
|
margin: 0 auto 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
@include mixins.adapt-padding-to-scrollbar(0.5rem);
|
& > .custom-scroll > .ChatInfo,
|
||||||
@include mixins.side-panel-section;
|
& > .panel-content > .ChatInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
&.wide {
|
margin-bottom: 1rem;
|
||||||
padding: 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatInfo .title h3 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ListItem {
|
||||||
|
.Reaction {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-of-type {
|
.ReactionStaticEmoji {
|
||||||
padding-top: 1rem;
|
width: 1.5rem;
|
||||||
|
margin-right: 1.6875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.no-border {
|
&.with-checkbox {
|
||||||
border-top: none;
|
body.is-ios &::after,
|
||||||
}
|
body.is-android &::after {
|
||||||
|
bottom: -1rem;
|
||||||
&.section-with-fab {
|
|
||||||
padding-bottom: 3.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .ChatInfo {
|
|
||||||
margin: 0 0 2rem !important;
|
|
||||||
|
|
||||||
.title h3 {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-icon {
|
&.exceptions-member {
|
||||||
margin: 0 auto 2rem;
|
.ChatInfo .status {
|
||||||
}
|
white-space: pre-wrap;
|
||||||
|
|
||||||
.ListItem {
|
|
||||||
.Reaction {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ReactionStaticEmoji {
|
|
||||||
width: 1.5rem;
|
|
||||||
margin-right: 1.6875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.with-checkbox {
|
|
||||||
body.is-ios &::after,
|
|
||||||
body.is-android &::after {
|
|
||||||
bottom: -1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.exceptions-member {
|
|
||||||
.ChatInfo .status {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.section-heading {
|
.invite-link {
|
||||||
position: relative;
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
padding-inline-start: 1rem;
|
.input-admin-title {
|
||||||
|
margin-top: 0.875rem;
|
||||||
font-size: 1rem;
|
margin-inline: 1rem;
|
||||||
font-weight: var(--font-weight-semibold);
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
|
|
||||||
&[dir="auto"] {
|
|
||||||
text-align: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-help {
|
|
||||||
padding: 0 1rem;
|
|
||||||
line-height: 1.375rem;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
|
|
||||||
&[dir="auto"] {
|
|
||||||
text-align: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-edit-info,
|
|
||||||
.section-info {
|
|
||||||
padding: 0 1rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-edit-info {
|
|
||||||
margin-top: -0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invite-link {
|
|
||||||
padding: 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-info_push {
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-admin-title {
|
|
||||||
margin-top: 0.875rem;
|
|
||||||
margin-inline: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[dir="rtl"] {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea.form-control {
|
textarea.form-control {
|
||||||
@ -183,8 +132,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ManageGroupMembers {
|
.ManageGroupMembers > .panel-content {
|
||||||
padding: 0.5rem 1rem;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.island-list {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
min-height: 0;
|
||||||
|
padding-block: 0;
|
||||||
|
|
||||||
|
.picker-list {
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-block: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ManageInvites {
|
.ManageInvites {
|
||||||
@ -311,6 +279,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
||||||
|
transition: height 0.25s ease-in-out;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
|
|
||||||
@ -333,27 +304,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.without-bottom-shadow {
|
.without-bottom-shadow {
|
||||||
padding-bottom: 0;
|
--before-shift-height: 48px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0.5rem 0.5rem 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
|
&.dropdown-open .DropdownListTrap {
|
||||||
|
height: var(--shift-height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.part {
|
.part {
|
||||||
margin: 0 -1.5rem;
|
padding-bottom: 0.5rem;
|
||||||
padding: 0 1.5rem 1rem;
|
|
||||||
|
|
||||||
@include mixins.side-panel-section;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section, .part {
|
.section, .part {
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: 0.25s ease-in-out transform;
|
|
||||||
|
|
||||||
&.shifted {
|
|
||||||
transform: translateY(var(--shift-height));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-edit {
|
.settings-edit {
|
||||||
padding: 0 1rem !important;
|
padding: 1.125rem !important;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
.root {
|
.root {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.noResults {
|
.noResults {
|
||||||
@ -19,9 +21,6 @@
|
|||||||
|
|
||||||
.section {
|
.section {
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
padding-inline-end: calc(1.5rem - var(--scrollbar-width));
|
|
||||||
|
|
||||||
@include mixins.side-panel-section;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user :global(.status) {
|
.user :global(.status) {
|
||||||
@ -113,10 +112,6 @@
|
|||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.giveawayButton {
|
|
||||||
margin: 0 -1rem 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.giveawayIcon {
|
.giveawayIcon {
|
||||||
width: 2.75rem;
|
width: 2.75rem;
|
||||||
height: 2.75rem;
|
height: 2.75rem;
|
||||||
@ -134,7 +129,3 @@
|
|||||||
right: 0.5rem;
|
right: 0.5rem;
|
||||||
transform: translate(0, -50%);
|
transform: translate(0, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.boostInfo {
|
|
||||||
margin: 0 -1rem;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import Icon from '../../common/icons/Icon';
|
|||||||
import LinkField from '../../common/LinkField';
|
import LinkField from '../../common/LinkField';
|
||||||
import PremiumProgress from '../../common/PremiumProgress';
|
import PremiumProgress from '../../common/PremiumProgress';
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import Spinner from '../../ui/Spinner';
|
import Spinner from '../../ui/Spinner';
|
||||||
@ -222,7 +223,7 @@ const BoostStatistics = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
className={buildClassName(styles.boostInfo, 'chat-item-clickable')}
|
className="chat-item-clickable"
|
||||||
onClick={() => handleBoosterClick(boost.userId)}
|
onClick={() => handleBoosterClick(boost.userId)}
|
||||||
>
|
>
|
||||||
<PrivateChatInfo
|
<PrivateChatInfo
|
||||||
@ -254,6 +255,25 @@ const BoostStatistics = ({
|
|||||||
openGiveawayModal({ chatId, prepaidGiveaway });
|
openGiveawayModal({ chatId, prepaidGiveaway });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function renderLoadMore() {
|
||||||
|
if (!boostersToLoadCount) return undefined;
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
key="load-more"
|
||||||
|
className={styles.showMore}
|
||||||
|
disabled={boostStatistics?.isLoadingBoosters}
|
||||||
|
onClick={handleLoadMore}
|
||||||
|
>
|
||||||
|
{boostStatistics?.isLoadingBoosters ? (
|
||||||
|
<Spinner className={styles.loadMoreSpinner} />
|
||||||
|
) : (
|
||||||
|
<Icon name="down" className={styles.down} />
|
||||||
|
)}
|
||||||
|
{lang('ShowVotes', boostersToLoadCount, 'i')}
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function renderContent() {
|
function renderContent() {
|
||||||
let listToRender;
|
let listToRender;
|
||||||
if (tabType === 'boostList') {
|
if (tabType === 'boostList') {
|
||||||
@ -267,9 +287,10 @@ const BoostStatistics = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<Island>
|
||||||
{listToRender?.map((boost) => renderBoostList(boost))}
|
{listToRender?.map((boost) => renderBoostList(boost))}
|
||||||
</div>
|
{renderLoadMore()}
|
||||||
|
</Island>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +299,7 @@ const BoostStatistics = ({
|
|||||||
{!isLoaded && <Loading />}
|
{!isLoaded && <Loading />}
|
||||||
{isLoaded && statsOverview && (
|
{isLoaded && statsOverview && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.section}>
|
<Island>
|
||||||
<PremiumProgress
|
<PremiumProgress
|
||||||
leftText={lang('BoostsLevel', currentLevel)}
|
leftText={lang('BoostsLevel', currentLevel)}
|
||||||
rightText={hasNextLevel ? lang('BoostsLevel', currentLevel + 1) : undefined}
|
rightText={hasNextLevel ? lang('BoostsLevel', currentLevel + 1) : undefined}
|
||||||
@ -287,73 +308,75 @@ const BoostStatistics = ({
|
|||||||
floatingBadgeIcon="boost"
|
floatingBadgeIcon="boost"
|
||||||
/>
|
/>
|
||||||
<StatisticsOverview className={styles.stats} statistics={statsOverview} type="boost" />
|
<StatisticsOverview className={styles.stats} statistics={statsOverview} type="boost" />
|
||||||
</div>
|
</Island>
|
||||||
{statsOverview.prepaidGiveaways && (
|
{statsOverview.prepaidGiveaways && (
|
||||||
<div className={styles.section}>
|
<>
|
||||||
<h4 className={styles.sectionHeader} dir={lang.isRtl ? 'rtl' : undefined}>
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
{lang('BoostingPreparedGiveaways')}
|
{lang('BoostingPreparedGiveaways')}
|
||||||
</h4>
|
</IslandTitle>
|
||||||
{statsOverview?.prepaidGiveaways?.map((prepaidGiveaway) => {
|
<Island>
|
||||||
const isStarsGiveaway = 'stars' in prepaidGiveaway;
|
{statsOverview?.prepaidGiveaways?.map((prepaidGiveaway) => {
|
||||||
|
const isStarsGiveaway = 'stars' in prepaidGiveaway;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={prepaidGiveaway.id}
|
key={prepaidGiveaway.id}
|
||||||
className="chat-item-clickable"
|
className="chat-item-clickable"
|
||||||
|
|
||||||
onClick={() => launchPrepaidGiveawayHandler(prepaidGiveaway)}
|
onClick={() => launchPrepaidGiveawayHandler(prepaidGiveaway)}
|
||||||
>
|
>
|
||||||
<div className={buildClassName(styles.status, 'status-clickable')}>
|
<div className={buildClassName(styles.status, 'status-clickable')}>
|
||||||
<div>
|
<div>
|
||||||
{isStarsGiveaway
|
|
||||||
? (
|
|
||||||
<img
|
|
||||||
src={GiftStar}
|
|
||||||
className={styles.giveawayIcon}
|
|
||||||
alt={lang('GiftStar')}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<img
|
|
||||||
src={GIVEAWAY_IMG_LIST[prepaidGiveaway.months] || GIVEAWAY_IMG_LIST[3]}
|
|
||||||
className={styles.giveawayIcon}
|
|
||||||
alt={lang('Giveaway')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={styles.info}>
|
|
||||||
<h3>
|
|
||||||
{isStarsGiveaway
|
{isStarsGiveaway
|
||||||
? lang('Giveaway.Stars.Prepaid.Title', prepaidGiveaway.stars)
|
? (
|
||||||
: lang('BoostingTelegramPremiumCountPlural', prepaidGiveaway.quantity)}
|
<img
|
||||||
</h3>
|
src={GiftStar}
|
||||||
<p className={styles.month}>
|
className={styles.giveawayIcon}
|
||||||
{
|
alt={lang('GiftStar')}
|
||||||
isStarsGiveaway ? lang('Giveaway.Stars.Prepaid.Desc', prepaidGiveaway.quantity)
|
/>
|
||||||
: lang('PrepaidGiveawayMonths', prepaidGiveaway.months)
|
) : (
|
||||||
}
|
<img
|
||||||
</p>
|
src={GIVEAWAY_IMG_LIST[prepaidGiveaway.months] || GIVEAWAY_IMG_LIST[3]}
|
||||||
</div>
|
className={styles.giveawayIcon}
|
||||||
<div className={styles.quantity}>
|
alt={lang('Giveaway')}
|
||||||
<div className={buildClassName(styles.floatingBadge,
|
/>
|
||||||
styles.floatingBadgeButtonColor,
|
)}
|
||||||
styles.floatingBadgeButton)}
|
</div>
|
||||||
>
|
<div className={styles.info}>
|
||||||
<Icon name="boost" className={styles.floatingBadgeIcon} />
|
<h3>
|
||||||
<div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}>
|
{isStarsGiveaway
|
||||||
{isStarsGiveaway ? prepaidGiveaway.boosts
|
? lang('Giveaway.Stars.Prepaid.Title', prepaidGiveaway.stars)
|
||||||
: prepaidGiveaway.quantity * (giveawayBoostsPerPremium ?? GIVEAWAY_BOOST_PER_PREMIUM)}
|
: lang('BoostingTelegramPremiumCountPlural', prepaidGiveaway.quantity)}
|
||||||
|
</h3>
|
||||||
|
<p className={styles.month}>
|
||||||
|
{
|
||||||
|
isStarsGiveaway ? lang('Giveaway.Stars.Prepaid.Desc', prepaidGiveaway.quantity)
|
||||||
|
: lang('PrepaidGiveawayMonths', prepaidGiveaway.months)
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className={styles.quantity}>
|
||||||
|
<div className={buildClassName(styles.floatingBadge,
|
||||||
|
styles.floatingBadgeButtonColor,
|
||||||
|
styles.floatingBadgeButton)}
|
||||||
|
>
|
||||||
|
<Icon name="boost" className={styles.floatingBadgeIcon} />
|
||||||
|
<div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
|
{isStarsGiveaway ? prepaidGiveaway.boosts
|
||||||
|
: prepaidGiveaway.quantity * (giveawayBoostsPerPremium ?? GIVEAWAY_BOOST_PER_PREMIUM)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ListItem>
|
||||||
</ListItem>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
</Island>
|
||||||
<p className="text-muted hint" key="links-hint">{lang('BoostingSelectPaidGiveaway')}</p>
|
<IslandDescription>{lang('BoostingSelectPaidGiveaway')}</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className={styles.section}>
|
{shouldDisplayGiftList ? (
|
||||||
{shouldDisplayGiftList ? (
|
<Island>
|
||||||
<div
|
<div
|
||||||
className={buildClassName(styles.boostSection, styles.content)}
|
className={buildClassName(styles.boostSection, styles.content)}
|
||||||
>
|
>
|
||||||
@ -368,52 +391,45 @@ const BoostStatistics = ({
|
|||||||
</Transition>
|
</Transition>
|
||||||
<SquareTabList activeTab={renderingActiveTab} tabs={tabs} onSwitchTab={setActiveTab} />
|
<SquareTabList activeTab={renderingActiveTab} tabs={tabs} onSwitchTab={setActiveTab} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</Island>
|
||||||
<>
|
) : (
|
||||||
<h4 className={styles.sectionHeader} dir={lang.isRtl ? 'rtl' : undefined}>
|
<>
|
||||||
{lang('BoostingBoostsCount', boostStatistics?.boosts?.count)}
|
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
|
||||||
</h4>
|
{lang('BoostingBoostsCount', boostStatistics?.boosts?.count)}
|
||||||
{!boostStatistics?.boosts?.list?.length && (
|
</IslandTitle>
|
||||||
<div className={styles.noResults}>
|
{!boostStatistics?.boosts?.list?.length ? (
|
||||||
{lang(isChannel ? 'NoBoostersHint' : 'NoBoostersGroupHint')}
|
<IslandDescription>
|
||||||
</div>
|
{lang(isChannel ? 'NoBoostersHint' : 'NoBoostersGroupHint')}
|
||||||
)}
|
</IslandDescription>
|
||||||
{boostStatistics?.boosts?.list?.map((boost) => renderBoostList(boost))}
|
) : (
|
||||||
</>
|
<Island>
|
||||||
)}
|
{boostStatistics?.boosts?.list?.map((boost) => renderBoostList(boost))}
|
||||||
{Boolean(boostersToLoadCount) && (
|
{renderLoadMore()}
|
||||||
<ListItem
|
</Island>
|
||||||
key="load-more"
|
)}
|
||||||
className={styles.showMore}
|
</>
|
||||||
disabled={boostStatistics?.isLoadingBoosters}
|
)}
|
||||||
onClick={handleLoadMore}
|
<IslandTitle>{lang('LinkForBoosting')}</IslandTitle>
|
||||||
>
|
<Island>
|
||||||
{boostStatistics?.isLoadingBoosters ? (
|
<LinkField link={status!.boostUrl} withShare noTitle />
|
||||||
<Spinner className={styles.loadMoreSpinner} />
|
</Island>
|
||||||
) : (
|
|
||||||
<Icon name="down" className={styles.down} />
|
|
||||||
)}
|
|
||||||
{lang('ShowVotes', boostersToLoadCount, 'i')}
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<LinkField className={styles.section} link={status!.boostUrl} withShare title={lang('LinkForBoosting')} />
|
|
||||||
{isGiveawayAvailable && (
|
{isGiveawayAvailable && (
|
||||||
<div className={styles.section}>
|
<>
|
||||||
<ListItem
|
<Island>
|
||||||
key="load-more"
|
<ListItem
|
||||||
icon="gift"
|
key="load-more"
|
||||||
onClick={handleGiveawayClick}
|
icon="gift"
|
||||||
className={styles.giveawayButton}
|
onClick={handleGiveawayClick}
|
||||||
>
|
>
|
||||||
{lang('BoostingGetBoostsViaGifts')}
|
{lang('BoostingGetBoostsViaGifts')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<p className="text-muted hint" key="links-hint">
|
</Island>
|
||||||
|
<IslandDescription>
|
||||||
{lang(
|
{lang(
|
||||||
isChannel ? 'BoostingGetMoreBoosts' : 'BoostingGetMoreBoostsGroup',
|
isChannel ? 'BoostingGetMoreBoosts' : 'BoostingGetMoreBoostsGroup',
|
||||||
)}
|
)}
|
||||||
</p>
|
</IslandDescription>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,9 +15,11 @@ import { callApi } from '../../../api/gramjs';
|
|||||||
import { isGraph } from './helpers/isGraph';
|
import { isGraph } from './helpers/isGraph';
|
||||||
|
|
||||||
import useForceUpdate from '../../../hooks/useForceUpdate';
|
import useForceUpdate from '../../../hooks/useForceUpdate';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Island, { IslandTitle } from '../../gili/layout/Island';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import StatisticsMessagePublicForward from './StatisticsMessagePublicForward';
|
import StatisticsMessagePublicForward from './StatisticsMessagePublicForward';
|
||||||
@ -62,7 +64,8 @@ function MessageStatistics({
|
|||||||
dcId,
|
dcId,
|
||||||
messageId,
|
messageId,
|
||||||
}: OwnProps & StateProps) {
|
}: OwnProps & StateProps) {
|
||||||
const lang = useOldLang();
|
const lang = useLang();
|
||||||
|
const oldLang = useOldLang();
|
||||||
const containerRef = useRef<HTMLDivElement>();
|
const containerRef = useRef<HTMLDivElement>();
|
||||||
const [isReady, setIsReady] = useState(false);
|
const [isReady, setIsReady] = useState(false);
|
||||||
const loadedChartsRef = useRef<Set<string>>(new Set());
|
const loadedChartsRef = useRef<Set<string>>(new Set());
|
||||||
@ -141,10 +144,10 @@ function MessageStatistics({
|
|||||||
LovelyChart.create(
|
LovelyChart.create(
|
||||||
containerRef.current!.children[index] as HTMLElement,
|
containerRef.current!.children[index] as HTMLElement,
|
||||||
{
|
{
|
||||||
title: lang((GRAPH_TITLES as Record<string, string>)[name]),
|
title: oldLang((GRAPH_TITLES as Record<string, string>)[name]),
|
||||||
...zoomToken ? {
|
...zoomToken ? {
|
||||||
onZoom: (x: number) => callApi('fetchStatisticsAsyncGraph', { token: zoomToken, x, dcId }),
|
onZoom: (x: number) => callApi('fetchStatisticsAsyncGraph', { token: zoomToken, x, dcId }),
|
||||||
zoomOutLabel: lang('Graph.ZoomOut'),
|
zoomOutLabel: oldLang('Graph.ZoomOut'),
|
||||||
} : {},
|
} : {},
|
||||||
...graph,
|
...graph,
|
||||||
},
|
},
|
||||||
@ -156,7 +159,7 @@ function MessageStatistics({
|
|||||||
forceUpdate();
|
forceUpdate();
|
||||||
})();
|
})();
|
||||||
}, [
|
}, [
|
||||||
isReady, statistics, lang, chatId, messageId, loadStatisticsAsyncGraph, dcId, forceUpdate,
|
isReady, statistics, oldLang, chatId, messageId, loadStatisticsAsyncGraph, dcId, forceUpdate,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleLoadMore = useLastCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
const handleLoadMore = useLastCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||||
@ -174,11 +177,14 @@ function MessageStatistics({
|
|||||||
key={`${chatId}-${messageId}`}
|
key={`${chatId}-${messageId}`}
|
||||||
className={buildClassName(styles.root, 'custom-scroll', isReady && styles.ready)}
|
className={buildClassName(styles.root, 'custom-scroll', isReady && styles.ready)}
|
||||||
>
|
>
|
||||||
<StatisticsOverview statistics={statistics} type="message" title={lang('StatisticOverview')} />
|
<IslandTitle>{lang('StatisticOverview')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
|
<StatisticsOverview statistics={statistics} type="message" />
|
||||||
|
</Island>
|
||||||
|
|
||||||
{(!loadedChartsRef.current.size || !statistics.publicForwardsData) && <Loading />}
|
{(!loadedChartsRef.current.size || !statistics.publicForwardsData) && <Loading />}
|
||||||
|
|
||||||
<div ref={containerRef} data-stricterdom-ignore>
|
<div ref={containerRef} className={styles.graphContainer} data-stricterdom-ignore>
|
||||||
{GRAPHS.map((graph) => {
|
{GRAPHS.map((graph) => {
|
||||||
const isGraphReady = loadedChartsRef.current.has(graph) && !errorChartsRef.current.has(graph);
|
const isGraphReady = loadedChartsRef.current.has(graph) && !errorChartsRef.current.has(graph);
|
||||||
return (
|
return (
|
||||||
@ -189,18 +195,19 @@ function MessageStatistics({
|
|||||||
|
|
||||||
{Boolean(statistics.publicForwards) && (
|
{Boolean(statistics.publicForwards) && (
|
||||||
<div className={styles.publicForwards}>
|
<div className={styles.publicForwards}>
|
||||||
<h2 className={styles.publicForwardsTitle}>{lang('Stats.Message.PublicShares')}</h2>
|
<IslandTitle>{lang('StatsMessagePublicShares')}</IslandTitle>
|
||||||
|
<Island>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
items={statistics.publicForwardsData}
|
items={statistics.publicForwardsData}
|
||||||
itemSelector=".statistic-public-forward"
|
itemSelector=".statistic-public-forward"
|
||||||
onLoadMore={handleLoadMore}
|
onLoadMore={handleLoadMore}
|
||||||
noFastList
|
noFastList
|
||||||
>
|
>
|
||||||
{(statistics.publicForwardsData as ApiMessagePublicForward[]).map((item) => (
|
{(statistics.publicForwardsData as ApiMessagePublicForward[]).map((item) => (
|
||||||
<StatisticsMessagePublicForward key={item.messageId} data={item} />
|
<StatisticsMessagePublicForward key={item.messageId} data={item} />
|
||||||
))}
|
))}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,17 +4,16 @@
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph {
|
.graph {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
border-bottom: 0.0625rem solid var(--color-borders);
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
|
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border-bottom: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hidden {
|
&.hidden {
|
||||||
@ -23,21 +22,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.charts {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.ready {
|
.ready {
|
||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.balanceSection {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
padding: 1rem 0.75rem;
|
|
||||||
border-bottom: 0.0625rem solid var(--color-borders);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.topText {
|
.balanceTitle {
|
||||||
display: block;
|
font-size: 0.9375rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.availableReward {
|
.availableReward {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
import AboutMonetizationModal from '../../common/AboutMonetizationModal.async';
|
import AboutMonetizationModal from '../../common/AboutMonetizationModal.async';
|
||||||
import Icon from '../../common/icons/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import SafeLink from '../../common/SafeLink';
|
import SafeLink from '../../common/SafeLink';
|
||||||
|
import Island, { IslandDescription } from '../../gili/layout/Island';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import Link from '../../ui/Link';
|
import Link from '../../ui/Link';
|
||||||
@ -230,29 +231,31 @@ const MonetizationStatistics = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.root, 'custom-scroll', isReady && styles.ready)}>
|
<div className={buildClassName(styles.root, 'panel-content custom-scroll', isReady && styles.ready)}>
|
||||||
<div className={buildClassName(styles.section, styles.topText)}>{topText}</div>
|
<IslandDescription>{topText}</IslandDescription>
|
||||||
|
|
||||||
<StatisticsOverview
|
<Island>
|
||||||
statistics={statistics}
|
<StatisticsOverview
|
||||||
isToncoin
|
statistics={statistics}
|
||||||
type="monetization"
|
isToncoin
|
||||||
title={oldLang('MonetizationOverview')}
|
type="monetization"
|
||||||
subtitle={
|
title={oldLang('MonetizationOverview')}
|
||||||
<div className={styles.textBottom}>{oldLang('MonetizationProceedsTONInfo')}</div>
|
/>
|
||||||
}
|
</Island>
|
||||||
/>
|
<IslandDescription>{oldLang('MonetizationProceedsTONInfo')}</IslandDescription>
|
||||||
|
|
||||||
{!loadedChartsRef.current.size && <Loading />}
|
{!loadedChartsRef.current.size && <Loading />}
|
||||||
|
|
||||||
<div ref={containerRef} className={styles.section} data-stricterdom-ignore>
|
<Island>
|
||||||
{MONETIZATION_GRAPHS.filter(Boolean).map((graph) => (
|
<div ref={containerRef} data-stricterdom-ignore className={styles.charts}>
|
||||||
<div key={graph} className={buildClassName(styles.graph, styles.hidden)} />
|
{MONETIZATION_GRAPHS.filter(Boolean).map((graph) => (
|
||||||
))}
|
<div key={graph} className={buildClassName(styles.graph, styles.hidden)} />
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
</Island>
|
||||||
|
|
||||||
<div className={styles.section}>
|
<Island className={styles.balanceSection}>
|
||||||
{oldLang('lng_channel_earn_balance_title')}
|
<h4 className={styles.balanceTitle}>{oldLang('lng_channel_earn_balance_title')}</h4>
|
||||||
|
|
||||||
{renderAvailableReward()}
|
{renderAvailableReward()}
|
||||||
|
|
||||||
@ -264,8 +267,8 @@ const MonetizationStatistics = ({
|
|||||||
{oldLang('MonetizationWithdraw')}
|
{oldLang('MonetizationWithdraw')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className={styles.textBottom}>{rewardsText}</div>
|
</Island>
|
||||||
</div>
|
<IslandDescription>{rewardsText}</IslandDescription>
|
||||||
|
|
||||||
<AboutMonetizationModal
|
<AboutMonetizationModal
|
||||||
isOpen={isAboutMonetizationModalOpen}
|
isOpen={isAboutMonetizationModalOpen}
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-top: 1px solid transparent;
|
padding: 1rem;
|
||||||
|
|
||||||
transition: border-top-color 0.2s ease-in-out;
|
background-color: var(--color-background-secondary);
|
||||||
|
|
||||||
:global(.lovely-chart--container) {
|
:global(.lovely-chart--container) {
|
||||||
font: inherit !important;
|
font: inherit !important;
|
||||||
@ -13,7 +13,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:global(.lovely-chart--header) {
|
:global(.lovely-chart--header) {
|
||||||
margin: 0 0.75rem;
|
margin: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.lovely-chart--header),
|
:global(.lovely-chart--header),
|
||||||
@ -35,7 +35,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.messages, .publicForwards {
|
.messages, .publicForwards {
|
||||||
padding: 1rem 0;
|
margin-top: 1rem;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
&-title {
|
&-title {
|
||||||
padding-left: 0.75rem;
|
padding-left: 0.75rem;
|
||||||
@ -55,20 +56,25 @@
|
|||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.graphContainer {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.graph {
|
.graph {
|
||||||
margin-bottom: 1rem;
|
border-radius: var(--border-radius-island);
|
||||||
border-bottom: 1px solid var(--color-borders);
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
background-color: var(--color-background);
|
||||||
|
box-shadow: 0 1px 4px 0 #0000000D;
|
||||||
|
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
|
|
||||||
&:last-of-type {
|
& + & {
|
||||||
margin-bottom: 0;
|
margin-top: 1rem;
|
||||||
border-bottom: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hidden {
|
&.hidden {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-bottom: none;
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import { isGraph } from './helpers/isGraph';
|
|||||||
import useForceUpdate from '../../../hooks/useForceUpdate';
|
import useForceUpdate from '../../../hooks/useForceUpdate';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Island from '../../gili/layout/Island';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import StatisticsOverview from './StatisticsOverview';
|
import StatisticsOverview from './StatisticsOverview';
|
||||||
import StatisticsRecentMessage from './StatisticsRecentMessage';
|
import StatisticsRecentMessage from './StatisticsRecentMessage';
|
||||||
@ -200,16 +201,18 @@ const Statistics = ({
|
|||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.root, 'panel-content custom-scroll', isReady && styles.ready)}>
|
<div className={buildClassName(styles.root, 'panel-content custom-scroll', isReady && styles.ready)}>
|
||||||
{statistics && (
|
{statistics && (
|
||||||
<StatisticsOverview
|
<Island>
|
||||||
statistics={statistics}
|
<StatisticsOverview
|
||||||
type={isGroup ? 'group' : 'channel'}
|
statistics={statistics}
|
||||||
title={lang('StatisticOverview')}
|
type={isGroup ? 'group' : 'channel'}
|
||||||
/>
|
title={lang('StatisticOverview')}
|
||||||
|
/>
|
||||||
|
</Island>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loadedChartsRef.current.size && <Loading />}
|
{!loadedChartsRef.current.size && <Loading />}
|
||||||
|
|
||||||
<div ref={containerRef} data-stricterdom-ignore>
|
<div ref={containerRef} data-stricterdom-ignore className={styles.graphContainer}>
|
||||||
{graphs.map((graph) => {
|
{graphs.map((graph) => {
|
||||||
const isGraphReady = loadedChartsRef.current.has(graph) && !errorChartsRef.current.has(graph);
|
const isGraphReady = loadedChartsRef.current.has(graph) && !errorChartsRef.current.has(graph);
|
||||||
return (
|
return (
|
||||||
@ -219,7 +222,7 @@ const Statistics = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{Boolean((statistics as ApiChannelStatistics)?.recentPosts?.length) && (
|
{Boolean((statistics as ApiChannelStatistics)?.recentPosts?.length) && (
|
||||||
<div className={styles.messages}>
|
<Island className={styles.messages}>
|
||||||
<h2 className={styles.messagesTitle}>{lang('ChannelStats.Recent.Header')}</h2>
|
<h2 className={styles.messagesTitle}>{lang('ChannelStats.Recent.Header')}</h2>
|
||||||
|
|
||||||
{(statistics as ApiChannelStatistics).recentPosts.map((postStatistic) => {
|
{(statistics as ApiChannelStatistics).recentPosts.map((postStatistic) => {
|
||||||
@ -251,7 +254,7 @@ const Statistics = ({
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
})}
|
})}
|
||||||
</div>
|
</Island>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
.root {
|
.root {
|
||||||
margin-bottom: 1rem;
|
padding: 0.5rem;
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
border-bottom: 0.0625rem solid var(--color-borders);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@ -41,12 +39,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tableHeading {
|
.tableHeading {
|
||||||
font-size: 0.9375rem;
|
font-size: 0.875rem;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableValue {
|
.tableValue {
|
||||||
font-size: 1.25rem;
|
font-size: 1rem;
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@ type OwnProps = {
|
|||||||
title?: string;
|
title?: string;
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
teactExperimentControlled?: boolean;
|
teactExperimentControlled?: boolean;
|
||||||
|
noMargin?: boolean;
|
||||||
inputMode?: 'text' | 'none' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
|
inputMode?: 'text' | 'none' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
|
||||||
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
onInput?: (e: FormEvent<HTMLInputElement>) => void;
|
onInput?: (e: FormEvent<HTMLInputElement>) => void;
|
||||||
@ -54,6 +55,7 @@ const InputText = ({
|
|||||||
title,
|
title,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
teactExperimentControlled,
|
teactExperimentControlled,
|
||||||
|
noMargin,
|
||||||
onChange,
|
onChange,
|
||||||
onInput,
|
onInput,
|
||||||
onKeyPress,
|
onKeyPress,
|
||||||
@ -71,6 +73,7 @@ const InputText = ({
|
|||||||
disabled && 'disabled',
|
disabled && 'disabled',
|
||||||
readOnly && 'disabled',
|
readOnly && 'disabled',
|
||||||
labelText && 'with-label',
|
labelText && 'with-label',
|
||||||
|
noMargin && 'no-margin',
|
||||||
className,
|
className,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -163,11 +163,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-slideFadeAndroid {
|
&-slideFadeAndroid {
|
||||||
--background-color: var(--color-background);
|
--slide-background-color: var(--color-background);
|
||||||
|
|
||||||
> .Transition_slide {
|
> .Transition_slide {
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
background: var(--background-color);
|
background: var(--slide-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
> .Transition_slide-to {
|
> .Transition_slide-to {
|
||||||
@ -179,11 +179,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-slideFadeAndroidBackwards {
|
&-slideFadeAndroidBackwards {
|
||||||
--background-color: var(--color-background);
|
--slide-background-color: var(--color-background);
|
||||||
|
|
||||||
> .Transition_slide {
|
> .Transition_slide {
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
background: var(--background-color);
|
background: var(--slide-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
> .Transition_slide-from {
|
> .Transition_slide-from {
|
||||||
@ -277,12 +277,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-slideLayers {
|
&-slideLayers {
|
||||||
--background-color: var(--color-background);
|
--slide-background-color: var(--color-background);
|
||||||
|
|
||||||
background: black !important;
|
background: black !important;
|
||||||
|
|
||||||
> .Transition_slide {
|
> .Transition_slide {
|
||||||
background: var(--background-color);
|
background: var(--slide-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
> .Transition_slide-to {
|
> .Transition_slide-to {
|
||||||
@ -296,12 +296,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-slideLayersBackwards {
|
&-slideLayersBackwards {
|
||||||
--background-color: var(--color-background);
|
--slide-background-color: var(--color-background);
|
||||||
|
|
||||||
background: black !important;
|
background: black !important;
|
||||||
|
|
||||||
> .Transition_slide {
|
> .Transition_slide {
|
||||||
background: var(--background-color);
|
background: var(--slide-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
> .Transition_slide-to {
|
> .Transition_slide-to {
|
||||||
@ -317,8 +317,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-pushSlide {
|
&-pushSlide {
|
||||||
|
--slide-background-color: var(--color-background);
|
||||||
|
|
||||||
> .Transition_slide {
|
> .Transition_slide {
|
||||||
background: var(--color-background);
|
background: var(--slide-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
> .Transition_slide-from {
|
> .Transition_slide-from {
|
||||||
@ -343,8 +345,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-pushSlideBackwards {
|
&-pushSlideBackwards {
|
||||||
|
--slide-background-color: var(--color-background);
|
||||||
|
|
||||||
> .Transition_slide {
|
> .Transition_slide {
|
||||||
background: var(--color-background);
|
background: var(--slide-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
> .Transition_slide-to {
|
> .Transition_slide-to {
|
||||||
|
|||||||
@ -13,11 +13,13 @@ const useScrollNotch = ({
|
|||||||
selector,
|
selector,
|
||||||
isBottomNotch,
|
isBottomNotch,
|
||||||
shouldHideTopNotch,
|
shouldHideTopNotch,
|
||||||
|
onScrolled,
|
||||||
}: {
|
}: {
|
||||||
containerRef: ElementRef<HTMLDivElement>;
|
containerRef: ElementRef<HTMLDivElement>;
|
||||||
selector: string;
|
selector: string;
|
||||||
isBottomNotch?: boolean;
|
isBottomNotch?: boolean;
|
||||||
shouldHideTopNotch?: boolean;
|
shouldHideTopNotch?: boolean;
|
||||||
|
onScrolled?: (isScrolled: boolean) => void;
|
||||||
}, deps: unknown[]) => {
|
}, deps: unknown[]) => {
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const elements = containerRef.current?.querySelectorAll<HTMLElement>(selector);
|
const elements = containerRef.current?.querySelectorAll<HTMLElement>(selector);
|
||||||
@ -29,6 +31,8 @@ const useScrollNotch = ({
|
|||||||
const { scrollHeight, scrollTop, clientHeight } = target;
|
const { scrollHeight, scrollTop, clientHeight } = target;
|
||||||
const isAtEnd = scrollHeight - scrollTop - clientHeight < SCROLL_THRESHOLD;
|
const isAtEnd = scrollHeight - scrollTop - clientHeight < SCROLL_THRESHOLD;
|
||||||
|
|
||||||
|
onScrolled?.(isScrolled);
|
||||||
|
|
||||||
requestMutation(() => {
|
requestMutation(() => {
|
||||||
if (!shouldHideTopNotch) {
|
if (!shouldHideTopNotch) {
|
||||||
toggleExtraClass(target, 'scrolled', isScrolled);
|
toggleExtraClass(target, 'scrolled', isScrolled);
|
||||||
@ -63,13 +67,18 @@ const useScrollNotch = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const elements = containerRef.current?.querySelectorAll<HTMLElement>(selector);
|
const elements = containerRef.current?.querySelectorAll<HTMLElement>(selector);
|
||||||
if (!elements?.length) return undefined;
|
if (!elements?.length) {
|
||||||
|
onScrolled?.(false);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
elements.forEach((el) => {
|
elements.forEach((el) => {
|
||||||
const isScrolled = el.scrollTop > 0;
|
const isScrolled = el.scrollTop > 0;
|
||||||
const { scrollHeight, scrollTop, clientHeight } = el;
|
const { scrollHeight, scrollTop, clientHeight } = el;
|
||||||
const isAtEnd = scrollHeight - scrollTop - clientHeight < SCROLL_THRESHOLD;
|
const isAtEnd = scrollHeight - scrollTop - clientHeight < SCROLL_THRESHOLD;
|
||||||
|
|
||||||
|
onScrolled?.(isScrolled);
|
||||||
|
|
||||||
requestMutation(() => {
|
requestMutation(() => {
|
||||||
if (!shouldHideTopNotch) {
|
if (!shouldHideTopNotch) {
|
||||||
toggleExtraClass(el, 'scrolled', isScrolled);
|
toggleExtraClass(el, 'scrolled', isScrolled);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user