Settings / Folders: Fix layout in Settings Folders Chats Picker component (#4668)
This commit is contained in:
parent
0f2c670e0f
commit
8faa652eb6
@ -37,6 +37,7 @@ type OwnProps = {
|
|||||||
lockedUnselectedSubtitle?: string;
|
lockedUnselectedSubtitle?: string;
|
||||||
filterValue?: string;
|
filterValue?: string;
|
||||||
filterPlaceholder?: string;
|
filterPlaceholder?: string;
|
||||||
|
categoryPlaceholderKey?: string;
|
||||||
notFoundText?: string;
|
notFoundText?: string;
|
||||||
searchInputId?: string;
|
searchInputId?: string;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
@ -65,6 +66,7 @@ const Picker: FC<OwnProps> = ({
|
|||||||
categories,
|
categories,
|
||||||
itemIds,
|
itemIds,
|
||||||
selectedCategories,
|
selectedCategories,
|
||||||
|
categoryPlaceholderKey,
|
||||||
selectedIds,
|
selectedIds,
|
||||||
filterValue,
|
filterValue,
|
||||||
filterPlaceholder,
|
filterPlaceholder,
|
||||||
@ -171,7 +173,9 @@ const Picker: FC<OwnProps> = ({
|
|||||||
onFilterChange?.(value);
|
onFilterChange?.(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const [viewportIds, getMore] = useInfiniteScroll(onLoadMore, sortedItemIds, Boolean(filterValue));
|
const [viewportIds, getMore] = useInfiniteScroll(
|
||||||
|
onLoadMore, sortedItemIds, Boolean(filterValue),
|
||||||
|
);
|
||||||
|
|
||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
|
|
||||||
@ -247,13 +251,15 @@ const Picker: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div key="categories">
|
<div key="categories">
|
||||||
{Boolean(categories?.length) && (
|
{Boolean(categories?.length) && (
|
||||||
<div className="picker-category-title">{lang('PrivacyUserTypes')}</div>
|
<>
|
||||||
|
{categoryPlaceholderKey && <div className="picker-category-title">{lang(categoryPlaceholderKey)}</div>}
|
||||||
|
{categories?.map((category) => renderItem(category.type, true))}
|
||||||
|
<div className="picker-category-title">{lang('FilterChats')}</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{categories?.map((category) => renderItem(category.type, true))}
|
|
||||||
<div className="picker-category-title">{lang('FilterChats')}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [categories, lang, renderItem]);
|
}, [categories, categoryPlaceholderKey, lang, renderItem]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName('Picker', className)}>
|
<div className={buildClassName('Picker', className)}>
|
||||||
|
|||||||
@ -146,6 +146,7 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
|||||||
selectedCategories={newSelectedCategoryTypes}
|
selectedCategories={newSelectedCategoryTypes}
|
||||||
filterValue={searchQuery}
|
filterValue={searchQuery}
|
||||||
filterPlaceholder={isAllowList ? lang('AlwaysAllowPlaceholder') : lang('NeverAllowPlaceholder')}
|
filterPlaceholder={isAllowList ? lang('AlwaysAllowPlaceholder') : lang('NeverAllowPlaceholder')}
|
||||||
|
categoryPlaceholderKey="PrivacyUserTypes"
|
||||||
searchInputId="new-group-picker-search"
|
searchInputId="new-group-picker-search"
|
||||||
isSearchable
|
isSearchable
|
||||||
onSelectedIdsChange={handleSelectedContactIdsChange}
|
onSelectedIdsChange={handleSelectedContactIdsChange}
|
||||||
|
|||||||
@ -94,3 +94,16 @@
|
|||||||
.settings-sortable-item .multiline-item {
|
.settings-sortable-item .multiline-item {
|
||||||
padding-inline-end: 3rem;
|
padding-inline-end: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-folders-chat-list {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-category-title {
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.down {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,25 +1,31 @@
|
|||||||
import type { FC } from '../../../../lib/teact/teact';
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
import React, { memo, useCallback, useMemo } from '../../../../lib/teact/teact';
|
import React, {
|
||||||
import { getGlobal } from '../../../../global';
|
memo, useEffect, useMemo, useState,
|
||||||
|
} from '../../../../lib/teact/teact';
|
||||||
|
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import type {
|
import type { FolderEditDispatch, FoldersState } from '../../../../hooks/reducers/useFoldersReducer';
|
||||||
FolderEditDispatch,
|
|
||||||
FoldersState,
|
|
||||||
} from '../../../../hooks/reducers/useFoldersReducer';
|
|
||||||
|
|
||||||
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID } from '../../../../config';
|
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID } from '../../../../config';
|
||||||
import { filterChatsByName } from '../../../../global/helpers';
|
import { filterChatsByName } from '../../../../global/helpers';
|
||||||
|
import { selectCurrentLimit } from '../../../../global/selectors/limits';
|
||||||
import { unique } from '../../../../util/iteratees';
|
import { unique } from '../../../../util/iteratees';
|
||||||
|
import { CUSTOM_PEER_EXCLUDED_CHAT_TYPES, CUSTOM_PEER_INCLUDED_CHAT_TYPES } from '../../../../util/objects/customPeer';
|
||||||
|
|
||||||
import {
|
import { selectChatFilters } from '../../../../hooks/reducers/useFoldersReducer';
|
||||||
selectChatFilters,
|
|
||||||
} from '../../../../hooks/reducers/useFoldersReducer';
|
|
||||||
import { useFolderManagerForOrderedIds } from '../../../../hooks/useFolderManager';
|
import { useFolderManagerForOrderedIds } from '../../../../hooks/useFolderManager';
|
||||||
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../../hooks/useOldLang';
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Icon from '../../../common/icons/Icon';
|
||||||
|
import Picker from '../../../common/Picker';
|
||||||
|
import FloatingActionButton from '../../../ui/FloatingActionButton';
|
||||||
import Loading from '../../../ui/Loading';
|
import Loading from '../../../ui/Loading';
|
||||||
import SettingsFoldersChatsPicker from './SettingsFoldersChatsPicker';
|
|
||||||
|
type StateProps = {
|
||||||
|
maxChats: number;
|
||||||
|
};
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
mode: 'included' | 'excluded';
|
mode: 'included' | 'excluded';
|
||||||
@ -30,44 +36,62 @@ type OwnProps = {
|
|||||||
onSaveFilter: VoidFunction;
|
onSaveFilter: VoidFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
const SettingsFoldersChatFilters: FC<OwnProps & StateProps> = ({
|
||||||
mode,
|
mode,
|
||||||
state,
|
state,
|
||||||
dispatch,
|
dispatch,
|
||||||
isActive,
|
isActive,
|
||||||
onReset,
|
onReset,
|
||||||
onSaveFilter,
|
onSaveFilter,
|
||||||
|
maxChats,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useOldLang();
|
||||||
|
|
||||||
|
const { openLimitReachedModal } = getActions();
|
||||||
|
|
||||||
const { chatFilter } = state;
|
const { chatFilter } = state;
|
||||||
const { selectedChatIds, selectedChatTypes } = selectChatFilters(state, mode, true);
|
const { selectedChatIds, selectedChatTypes } = selectChatFilters(state, mode, true);
|
||||||
|
const chatTypes = mode === 'included' ? CUSTOM_PEER_INCLUDED_CHAT_TYPES : CUSTOM_PEER_EXCLUDED_CHAT_TYPES;
|
||||||
|
|
||||||
const lang = useOldLang();
|
const [isTouched, setIsTouched] = useState(false);
|
||||||
|
|
||||||
const folderAllOrderedIds = useFolderManagerForOrderedIds(ALL_FOLDER_ID);
|
const folderAllOrderedIds = useFolderManagerForOrderedIds(ALL_FOLDER_ID);
|
||||||
const folderArchivedOrderedIds = useFolderManagerForOrderedIds(ARCHIVED_FOLDER_ID);
|
const folderArchivedOrderedIds = useFolderManagerForOrderedIds(ARCHIVED_FOLDER_ID);
|
||||||
|
|
||||||
const shouldHideChatTypes = state.folder.isChatList;
|
const shouldHideChatTypes = state.folder.isChatList;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isActive) {
|
||||||
|
setIsTouched(false);
|
||||||
|
}
|
||||||
|
}, [isActive]);
|
||||||
|
|
||||||
const displayedIds = useMemo(() => {
|
const displayedIds = useMemo(() => {
|
||||||
// No need for expensive global updates on chats, so we avoid them
|
// No need for expensive global updates on chats, so we avoid them
|
||||||
const chatsById = getGlobal().chats.byId;
|
const chatsById = getGlobal().chats.byId;
|
||||||
|
|
||||||
const chatIds = [...folderAllOrderedIds || [], ...folderArchivedOrderedIds || []];
|
const chatIds = [...folderAllOrderedIds || [], ...folderArchivedOrderedIds || []];
|
||||||
return unique([
|
return unique([
|
||||||
...selectedChatIds,
|
|
||||||
...filterChatsByName(lang, chatIds, chatsById, chatFilter),
|
...filterChatsByName(lang, chatIds, chatsById, chatFilter),
|
||||||
]);
|
]);
|
||||||
}, [folderAllOrderedIds, folderArchivedOrderedIds, selectedChatIds, lang, chatFilter]);
|
}, [folderAllOrderedIds, folderArchivedOrderedIds, lang, chatFilter]);
|
||||||
|
|
||||||
const handleFilterChange = useCallback((newFilter: string) => {
|
const handleFilterChange = useLastCallback((newFilter: string) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'setChatFilter',
|
type: 'setChatFilter',
|
||||||
payload: newFilter,
|
payload: newFilter,
|
||||||
});
|
});
|
||||||
}, [dispatch]);
|
setIsTouched(true);
|
||||||
|
});
|
||||||
|
|
||||||
const handleSelectedIdsChange = useCallback((ids: string[]) => {
|
const handleSelectedIdsChange = useLastCallback((ids: string[]) => {
|
||||||
if (mode === 'included') {
|
if (mode === 'included') {
|
||||||
|
if (ids.length >= maxChats) {
|
||||||
|
openLimitReachedModal({
|
||||||
|
limit: 'dialogFiltersChats',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'setIncludeFilters',
|
type: 'setIncludeFilters',
|
||||||
payload: { ...state.includeFilters, includedChatIds: ids },
|
payload: { ...state.includeFilters, includedChatIds: ids },
|
||||||
@ -78,9 +102,10 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
|||||||
payload: { ...state.excludeFilters, excludedChatIds: ids },
|
payload: { ...state.excludeFilters, excludedChatIds: ids },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [mode, state, dispatch]);
|
setIsTouched(true);
|
||||||
|
});
|
||||||
|
|
||||||
const handleSelectedChatTypesChange = useCallback((keys: string[]) => {
|
const handleSelectedChatTypesChange = useLastCallback((keys: string[]) => {
|
||||||
const newFilters: Record<string, boolean> = {};
|
const newFilters: Record<string, boolean> = {};
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
newFilters[key] = true;
|
newFilters[key] = true;
|
||||||
@ -103,7 +128,7 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [mode, selectedChatIds, dispatch]);
|
});
|
||||||
|
|
||||||
useHistoryBack({
|
useHistoryBack({
|
||||||
isActive,
|
isActive,
|
||||||
@ -115,20 +140,38 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsFoldersChatsPicker
|
<div className="Picker settings-folders-chat-list">
|
||||||
mode={mode}
|
<Picker
|
||||||
chatIds={displayedIds}
|
categories={shouldHideChatTypes ? undefined : chatTypes}
|
||||||
selectedIds={selectedChatIds}
|
itemIds={displayedIds}
|
||||||
selectedChatTypes={selectedChatTypes}
|
selectedIds={selectedChatIds}
|
||||||
filterValue={chatFilter}
|
selectedCategories={selectedChatTypes}
|
||||||
shouldHideChatTypes={shouldHideChatTypes}
|
filterValue={chatFilter}
|
||||||
onSelectedIdsChange={handleSelectedIdsChange}
|
filterPlaceholder={lang('Search')}
|
||||||
onSelectedChatTypesChange={handleSelectedChatTypesChange}
|
categoryPlaceholderKey="FilterChatTypes"
|
||||||
onFilterChange={handleFilterChange}
|
searchInputId="new-group-picker-search"
|
||||||
onSaveFilter={onSaveFilter}
|
isSearchable
|
||||||
isActive={isActive}
|
isRoundCheckbox
|
||||||
/>
|
onSelectedIdsChange={handleSelectedIdsChange}
|
||||||
|
onSelectedCategoriesChange={handleSelectedChatTypesChange}
|
||||||
|
onFilterChange={handleFilterChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FloatingActionButton
|
||||||
|
isShown={isTouched}
|
||||||
|
onClick={onSaveFilter}
|
||||||
|
ariaLabel={lang('Save')}
|
||||||
|
>
|
||||||
|
<Icon name="check" />
|
||||||
|
</FloatingActionButton>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(SettingsFoldersChatFilters);
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global): StateProps => {
|
||||||
|
return {
|
||||||
|
maxChats: selectCurrentLimit(global, 'dialogFiltersChats'),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(SettingsFoldersChatFilters));
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
.SettingsFoldersChatsPicker {
|
|
||||||
height: calc(100% - var(--header-height));
|
|
||||||
|
|
||||||
.picker-header {
|
|
||||||
box-shadow: 0 0 2px var(--color-default-shadow);
|
|
||||||
|
|
||||||
.max-items-reached {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
flex-grow: 1;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-list {
|
|
||||||
padding: 0 0.5rem 0.5rem;
|
|
||||||
|
|
||||||
.no-results {
|
|
||||||
height: 10rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ListItem.picker-list-item {
|
|
||||||
&.chat-type-item .ListItem-button {
|
|
||||||
padding: 0.875rem 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.chat-item .ListItem-button {
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Checkbox {
|
|
||||||
margin-left: auto;
|
|
||||||
padding-left: 3.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-type {
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 400;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[dir="rtl"] {
|
|
||||||
.Checkbox {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: auto;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 3.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-item-header {
|
|
||||||
margin-left: 0.75rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-list-divider {
|
|
||||||
margin: 0.5rem -0.5rem 0;
|
|
||||||
border-bottom: 1px solid var(--color-borders);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,262 +0,0 @@
|
|||||||
import type { FC } from '../../../../lib/teact/teact';
|
|
||||||
import React, {
|
|
||||||
memo, useCallback, useEffect, useRef, useState,
|
|
||||||
} from '../../../../lib/teact/teact';
|
|
||||||
import { getActions, withGlobal } from '../../../../global';
|
|
||||||
|
|
||||||
import type { FolderChatType } from '../../../../hooks/reducers/useFoldersReducer';
|
|
||||||
|
|
||||||
import { requestMutation } from '../../../../lib/fasterdom/fasterdom';
|
|
||||||
import { isUserId } from '../../../../global/helpers';
|
|
||||||
import { selectCurrentLimit } from '../../../../global/selectors/limits';
|
|
||||||
import buildClassName from '../../../../util/buildClassName';
|
|
||||||
|
|
||||||
import {
|
|
||||||
EXCLUDED_CHAT_TYPES,
|
|
||||||
INCLUDED_CHAT_TYPES,
|
|
||||||
} from '../../../../hooks/reducers/useFoldersReducer';
|
|
||||||
import useInfiniteScroll from '../../../../hooks/useInfiniteScroll';
|
|
||||||
import useOldLang from '../../../../hooks/useOldLang';
|
|
||||||
|
|
||||||
import GroupChatInfo from '../../../common/GroupChatInfo';
|
|
||||||
import PickerSelectedItem from '../../../common/PickerSelectedItem';
|
|
||||||
import PrivateChatInfo from '../../../common/PrivateChatInfo';
|
|
||||||
import Checkbox from '../../../ui/Checkbox';
|
|
||||||
import FloatingActionButton from '../../../ui/FloatingActionButton';
|
|
||||||
import InfiniteScroll from '../../../ui/InfiniteScroll';
|
|
||||||
import InputText from '../../../ui/InputText';
|
|
||||||
import ListItem from '../../../ui/ListItem';
|
|
||||||
import Loading from '../../../ui/Loading';
|
|
||||||
|
|
||||||
import '../../../common/Picker.scss';
|
|
||||||
import './SettingsFoldersChatsPicker.scss';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
mode: 'included' | 'excluded';
|
|
||||||
chatIds: string[];
|
|
||||||
selectedIds: string[];
|
|
||||||
selectedChatTypes: string[];
|
|
||||||
filterValue?: string;
|
|
||||||
shouldHideChatTypes?: boolean;
|
|
||||||
onSelectedIdsChange: (ids: string[]) => void;
|
|
||||||
onSelectedChatTypesChange: (types: string[]) => void;
|
|
||||||
onFilterChange: (value: string) => void;
|
|
||||||
onSaveFilter: VoidFunction;
|
|
||||||
isActive?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Focus slows down animation, also it breaks transition layout in Chrome
|
|
||||||
const FOCUS_DELAY_MS = 500;
|
|
||||||
|
|
||||||
const MAX_FULL_ITEMS = 10;
|
|
||||||
const ALWAYS_FULL_ITEMS_COUNT = 5;
|
|
||||||
|
|
||||||
type StateProps = {
|
|
||||||
maxChats: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SettingsFoldersChatsPicker: FC<OwnProps & StateProps> = ({
|
|
||||||
mode,
|
|
||||||
chatIds,
|
|
||||||
selectedIds,
|
|
||||||
selectedChatTypes,
|
|
||||||
filterValue,
|
|
||||||
shouldHideChatTypes,
|
|
||||||
onSelectedIdsChange,
|
|
||||||
onSelectedChatTypesChange,
|
|
||||||
onFilterChange,
|
|
||||||
maxChats,
|
|
||||||
onSaveFilter,
|
|
||||||
isActive,
|
|
||||||
}) => {
|
|
||||||
const { openLimitReachedModal } = getActions();
|
|
||||||
// eslint-disable-next-line no-null/no-null
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
|
||||||
const chatTypes = mode === 'included' ? INCLUDED_CHAT_TYPES : EXCLUDED_CHAT_TYPES;
|
|
||||||
const shouldMinimize = selectedIds.length + selectedChatTypes.length > MAX_FULL_ITEMS;
|
|
||||||
const [isTouched, setIsTouched] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isActive) {
|
|
||||||
setIsTouched(false);
|
|
||||||
}
|
|
||||||
}, [isActive]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
requestMutation(() => {
|
|
||||||
inputRef.current!.focus();
|
|
||||||
});
|
|
||||||
}, FOCUS_DELAY_MS);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleItemClick = useCallback((id: string) => {
|
|
||||||
const newSelectedIds = [...selectedIds];
|
|
||||||
if (newSelectedIds.includes(id)) {
|
|
||||||
newSelectedIds.splice(newSelectedIds.indexOf(id), 1);
|
|
||||||
} else {
|
|
||||||
if (selectedIds.length >= maxChats && mode === 'included') {
|
|
||||||
openLimitReachedModal({
|
|
||||||
limit: 'dialogFiltersChats',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
newSelectedIds.push(id);
|
|
||||||
}
|
|
||||||
setIsTouched(true);
|
|
||||||
onSelectedIdsChange(newSelectedIds);
|
|
||||||
}, [selectedIds, onSelectedIdsChange, maxChats, mode, openLimitReachedModal]);
|
|
||||||
|
|
||||||
const handleChatTypeClick = useCallback((key: FolderChatType['key']) => {
|
|
||||||
const newSelectedChatTypes = [...selectedChatTypes];
|
|
||||||
if (newSelectedChatTypes.includes(key)) {
|
|
||||||
newSelectedChatTypes.splice(newSelectedChatTypes.indexOf(key), 1);
|
|
||||||
} else {
|
|
||||||
newSelectedChatTypes.push(key);
|
|
||||||
}
|
|
||||||
setIsTouched(true);
|
|
||||||
onSelectedChatTypesChange(newSelectedChatTypes);
|
|
||||||
}, [selectedChatTypes, onSelectedChatTypesChange]);
|
|
||||||
|
|
||||||
const handleFilterChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const { value } = e.currentTarget;
|
|
||||||
onFilterChange(value);
|
|
||||||
}, [onFilterChange]);
|
|
||||||
|
|
||||||
const lang = useOldLang();
|
|
||||||
|
|
||||||
function renderSelectedChatType(key: string) {
|
|
||||||
const selectedType = chatTypes.find(({ key: typeKey }) => key === typeKey);
|
|
||||||
if (!selectedType) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PickerSelectedItem
|
|
||||||
icon={selectedType.icon}
|
|
||||||
title={lang(selectedType.title)}
|
|
||||||
isMinimized={shouldMinimize}
|
|
||||||
canClose
|
|
||||||
onClick={handleChatTypeClick}
|
|
||||||
clickArg={selectedType.key}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderChatType(type: FolderChatType) {
|
|
||||||
return (
|
|
||||||
<ListItem
|
|
||||||
key={type.key}
|
|
||||||
className="chat-item-clickable picker-list-item chat-type-item"
|
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
|
||||||
onClick={() => handleChatTypeClick(type.key)}
|
|
||||||
ripple
|
|
||||||
>
|
|
||||||
<i className={buildClassName('icon', `icon-${type.icon}`)} />
|
|
||||||
<h3 className="chat-type" dir="auto">{lang(type.title)}</h3>
|
|
||||||
<Checkbox
|
|
||||||
label=""
|
|
||||||
checked={selectedChatTypes.includes(type.key)}
|
|
||||||
round
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderItem(id: string) {
|
|
||||||
const isSelected = selectedIds.includes(id);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ListItem
|
|
||||||
key={id}
|
|
||||||
className="chat-item-clickable picker-list-item chat-item"
|
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
|
||||||
onClick={() => handleItemClick(id)}
|
|
||||||
ripple
|
|
||||||
>
|
|
||||||
{isUserId(id) ? (
|
|
||||||
<PrivateChatInfo userId={id} />
|
|
||||||
) : (
|
|
||||||
<GroupChatInfo chatId={id} withChatType />
|
|
||||||
)}
|
|
||||||
<Checkbox
|
|
||||||
label=""
|
|
||||||
checked={isSelected}
|
|
||||||
round
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [viewportIds, getMore] = useInfiniteScroll(undefined, chatIds, Boolean(filterValue));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="Picker SettingsFoldersChatsPicker">
|
|
||||||
<div className="picker-header custom-scroll">
|
|
||||||
{selectedChatTypes.map(renderSelectedChatType)}
|
|
||||||
{selectedIds.map((id, i) => (
|
|
||||||
<PickerSelectedItem
|
|
||||||
peerId={id}
|
|
||||||
isMinimized={shouldMinimize && i < selectedIds.length - ALWAYS_FULL_ITEMS_COUNT}
|
|
||||||
canClose
|
|
||||||
onClick={handleItemClick}
|
|
||||||
clickArg={id}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<InputText
|
|
||||||
ref={inputRef}
|
|
||||||
value={filterValue}
|
|
||||||
onChange={handleFilterChange}
|
|
||||||
placeholder={lang('Search')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<InfiniteScroll
|
|
||||||
className="picker-list custom-scroll fab-padding-bottom"
|
|
||||||
itemSelector=".chat-item"
|
|
||||||
items={viewportIds}
|
|
||||||
onLoadMore={getMore}
|
|
||||||
>
|
|
||||||
{(!viewportIds || !viewportIds.length || viewportIds.includes(chatIds[0])) && (
|
|
||||||
<div key="header">
|
|
||||||
{!shouldHideChatTypes && (
|
|
||||||
<>
|
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
|
||||||
{lang('FilterChatTypes')}
|
|
||||||
</h4>
|
|
||||||
{chatTypes.map(renderChatType)}
|
|
||||||
<div className="picker-list-divider" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
|
||||||
{lang('FilterChats')}
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{viewportIds?.length ? (
|
|
||||||
viewportIds.map(renderItem)
|
|
||||||
) : viewportIds && !viewportIds.length ? (
|
|
||||||
<p className="no-results" key="no-results">Sorry, nothing found.</p>
|
|
||||||
) : (
|
|
||||||
<Loading key="loading" />
|
|
||||||
)}
|
|
||||||
</InfiniteScroll>
|
|
||||||
|
|
||||||
<FloatingActionButton
|
|
||||||
isShown={isTouched}
|
|
||||||
onClick={onSaveFilter}
|
|
||||||
ariaLabel={lang('Save')}
|
|
||||||
>
|
|
||||||
<i className="icon icon-check" />
|
|
||||||
</FloatingActionButton>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
|
||||||
(global): StateProps => {
|
|
||||||
return {
|
|
||||||
maxChats: selectCurrentLimit(global, 'dialogFiltersChats'),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
)(SettingsFoldersChatsPicker));
|
|
||||||
@ -28,11 +28,11 @@ import useOldLang from '../../../../hooks/useOldLang';
|
|||||||
|
|
||||||
import AnimatedIcon from '../../../common/AnimatedIcon';
|
import AnimatedIcon from '../../../common/AnimatedIcon';
|
||||||
import GroupChatInfo from '../../../common/GroupChatInfo';
|
import GroupChatInfo from '../../../common/GroupChatInfo';
|
||||||
|
import Icon from '../../../common/icons/Icon';
|
||||||
import PrivateChatInfo from '../../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../../common/PrivateChatInfo';
|
||||||
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';
|
||||||
import ShowMoreButton from '../../../ui/ShowMoreButton';
|
|
||||||
import Spinner from '../../../ui/Spinner';
|
import Spinner from '../../../ui/Spinner';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -265,12 +265,14 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
{(!isExpanded && leftChatsCount > 0) && (
|
{(!isExpanded && leftChatsCount > 0) && (
|
||||||
<ShowMoreButton
|
<ListItem
|
||||||
count={leftChatsCount}
|
key="load-more"
|
||||||
itemName="chat"
|
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
onClick={clickHandler}
|
onClick={clickHandler}
|
||||||
/>
|
>
|
||||||
|
<Icon name="down" className="down" />
|
||||||
|
{lang('FilterShowMoreChats', leftChatsCount, 'i')}
|
||||||
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -483,7 +483,8 @@ export type InlineBotSettings = {
|
|||||||
cacheTime: number;
|
cacheTime: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CustomPeerType = 'premium' | 'toBeDistributed';
|
export type CustomPeerType = 'premium' | 'toBeDistributed' | 'contacts' | 'nonContacts'
|
||||||
|
| 'groups' | 'channels' | 'bots' | 'excludeMuted' | 'excludeArchived' | 'excludeRead';
|
||||||
|
|
||||||
export interface CustomPeer {
|
export interface CustomPeer {
|
||||||
isCustomPeer: true;
|
isCustomPeer: true;
|
||||||
|
|||||||
@ -17,3 +17,73 @@ export const CUSTOM_PEER_TO_BE_DISTRIBUTED: UniqueCustomPeer = {
|
|||||||
avatarIcon: 'user',
|
avatarIcon: 'user',
|
||||||
withPremiumGradient: true,
|
withPremiumGradient: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const CUSTOM_PEER_INCLUDED_CHAT_TYPES: UniqueCustomPeer[] = [
|
||||||
|
{
|
||||||
|
isCustomPeer: true,
|
||||||
|
type: 'contacts',
|
||||||
|
titleKey: 'FilterContacts',
|
||||||
|
avatarIcon: 'user',
|
||||||
|
isAvatarSquare: true,
|
||||||
|
peerColorId: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCustomPeer: true,
|
||||||
|
type: 'nonContacts',
|
||||||
|
titleKey: 'FilterNonContacts',
|
||||||
|
avatarIcon: 'non-contacts',
|
||||||
|
isAvatarSquare: true,
|
||||||
|
peerColorId: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCustomPeer: true,
|
||||||
|
type: 'groups',
|
||||||
|
titleKey: 'FilterGroups',
|
||||||
|
avatarIcon: 'group',
|
||||||
|
isAvatarSquare: true,
|
||||||
|
peerColorId: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCustomPeer: true,
|
||||||
|
type: 'channels',
|
||||||
|
titleKey: 'FilterChannels',
|
||||||
|
avatarIcon: 'channel',
|
||||||
|
isAvatarSquare: true,
|
||||||
|
peerColorId: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCustomPeer: true,
|
||||||
|
type: 'bots',
|
||||||
|
titleKey: 'FilterBots',
|
||||||
|
avatarIcon: 'bots',
|
||||||
|
isAvatarSquare: true,
|
||||||
|
peerColorId: 6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const CUSTOM_PEER_EXCLUDED_CHAT_TYPES: UniqueCustomPeer[] = [
|
||||||
|
{
|
||||||
|
isCustomPeer: true,
|
||||||
|
type: 'excludeMuted',
|
||||||
|
titleKey: 'FilterMuted',
|
||||||
|
avatarIcon: 'mute',
|
||||||
|
isAvatarSquare: true,
|
||||||
|
peerColorId: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCustomPeer: true,
|
||||||
|
type: 'excludeRead',
|
||||||
|
titleKey: 'FilterRead',
|
||||||
|
avatarIcon: 'readchats',
|
||||||
|
isAvatarSquare: true,
|
||||||
|
peerColorId: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCustomPeer: true,
|
||||||
|
type: 'excludeArchived',
|
||||||
|
titleKey: 'FilterArchived',
|
||||||
|
avatarIcon: 'archive',
|
||||||
|
isAvatarSquare: true,
|
||||||
|
peerColorId: 5,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user