Navigation: Support quick chat picker (#6635)
This commit is contained in:
parent
184c2a88e8
commit
1140242b0e
@ -107,3 +107,4 @@ export { default as FrozenAccountModal } from '../components/modals/frozenAccoun
|
||||
export { default as ProfileRatingModal } from '../components/modals/profileRating/ProfileRatingModal';
|
||||
export { default as QuickPreviewModal } from '../components/modals/quickPreview/QuickPreviewModal';
|
||||
export { default as StealthModeModal } from '../components/modals/storyStealthMode/StealthModeModal';
|
||||
export { default as QuickChatPickerModal } from '../components/modals/quickChatPicker/QuickChatPickerModal';
|
||||
|
||||
@ -67,7 +67,9 @@
|
||||
.picker-list {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
height: 100%;
|
||||
padding-block: 0.125rem;
|
||||
padding-inline: 0.5rem;
|
||||
|
||||
@include mixins.adapt-padding-to-scrollbar(0.5rem);
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
.root {
|
||||
scroll-margin-block: 0.25rem;
|
||||
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
@ -9,7 +11,7 @@
|
||||
|
||||
min-height: 2.5rem;
|
||||
padding: 0.25rem;
|
||||
border-radius: var(--border-radius-default);
|
||||
border-radius: 1.25rem;
|
||||
|
||||
line-height: 1.25;
|
||||
color: var(--color-text);
|
||||
@ -40,10 +42,14 @@
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
&:hover {
|
||||
background-color: var(--color-item-hover);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
z-index: 1;
|
||||
outline: 2px solid var(--color-borders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ import {
|
||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||
import { resolveTransitionName } from '../../util/resolveTransitionName';
|
||||
import { captureControlledSwipe } from '../../util/swipeController';
|
||||
import { isComposerHasSelection } from '../middle/composer/helpers/selection';
|
||||
|
||||
import useFoldersReducer from '../../hooks/reducers/useFoldersReducer';
|
||||
import { useHotkeys } from '../../hooks/useHotkeys';
|
||||
@ -110,6 +111,7 @@ function LeftColumn({
|
||||
openChat,
|
||||
openLeftColumnContent,
|
||||
openSettingsScreen,
|
||||
openQuickChatPicker,
|
||||
} = getActions();
|
||||
|
||||
const [contactsFilter, setContactsFilter] = useState<string>('');
|
||||
@ -436,8 +438,16 @@ function LeftColumn({
|
||||
openLeftColumnContent({ contentKey: LeftColumnContent.Settings });
|
||||
});
|
||||
|
||||
const handleQuickChatPicker = useLastCallback((e: KeyboardEvent) => {
|
||||
if (isComposerHasSelection()) return;
|
||||
|
||||
e.preventDefault();
|
||||
openQuickChatPicker();
|
||||
});
|
||||
|
||||
useHotkeys(useMemo(() => ({
|
||||
'Mod+Shift+F': handleHotkeySearch,
|
||||
'Mod+K': handleQuickChatPicker,
|
||||
// https://support.mozilla.org/en-US/kb/take-screenshots-firefox
|
||||
...(!IS_FIREFOX && {
|
||||
'Mod+Shift+S': handleHotkeySavedMessages,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { EDITABLE_INPUT_ID, EDITABLE_INPUT_MODAL_ID } from '../../../../config';
|
||||
|
||||
const MAX_NESTING_PARENTS = 5;
|
||||
|
||||
export function isSelectionInsideInput(selectionRange: Range, inputId: string) {
|
||||
@ -11,3 +13,14 @@ export function isSelectionInsideInput(selectionRange: Range, inputId: string) {
|
||||
|
||||
return Boolean(parentNode && parentNode.id === inputId);
|
||||
}
|
||||
|
||||
export function isComposerHasSelection() {
|
||||
const activeElement = document.activeElement;
|
||||
const isComposerFocused = activeElement?.id === EDITABLE_INPUT_ID
|
||||
|| activeElement?.id === EDITABLE_INPUT_MODAL_ID;
|
||||
|
||||
if (!isComposerFocused) return false;
|
||||
|
||||
const selection = window.getSelection();
|
||||
return Boolean(selection && !selection.isCollapsed);
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ import PasskeyModal from './passkey/PasskeyModal.async';
|
||||
import PreparedMessageModal from './preparedMessage/PreparedMessageModal.async';
|
||||
import PriceConfirmModal from './priceConfirm/PriceConfirmModal.async';
|
||||
import ProfileRatingModal from './profileRating/ProfileRatingModal.async';
|
||||
import QuickChatPickerModal from './quickChatPicker/QuickChatPickerModal.async';
|
||||
import QuickPreviewModal from './quickPreview/QuickPreviewModal.async';
|
||||
import ReportAdModal from './reportAd/ReportAdModal.async';
|
||||
import ReportModal from './reportModal/ReportModal.async';
|
||||
@ -126,7 +127,8 @@ type ModalKey = keyof Pick<TabState,
|
||||
'quickPreview' |
|
||||
'storyStealthModal' |
|
||||
'isPasskeyModalOpen' |
|
||||
'birthdaySetupModal'
|
||||
'birthdaySetupModal' |
|
||||
'isQuickChatPickerOpen'
|
||||
>;
|
||||
|
||||
type StateProps = {
|
||||
@ -201,6 +203,7 @@ const MODALS: ModalRegistry = {
|
||||
storyStealthModal: StealthModeModal,
|
||||
isPasskeyModalOpen: PasskeyModal,
|
||||
birthdaySetupModal: BirthdaySetupModal,
|
||||
isQuickChatPickerOpen: QuickChatPickerModal,
|
||||
};
|
||||
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
||||
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import type { OwnProps } from './QuickChatPickerModal';
|
||||
|
||||
import { Bundles } from '../../../util/moduleLoader';
|
||||
|
||||
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||
|
||||
const QuickChatPickerModalAsync = (props: OwnProps) => {
|
||||
const { modal } = props;
|
||||
const QuickChatPickerModal = useModuleLoader(Bundles.Extra, 'QuickChatPickerModal', !modal);
|
||||
|
||||
return QuickChatPickerModal ? <QuickChatPickerModal {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default QuickChatPickerModalAsync;
|
||||
@ -0,0 +1,36 @@
|
||||
import { memo } from '../../../lib/teact/teact';
|
||||
import { getActions } from '../../../global';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
import RecipientPicker from '../../common/RecipientPicker';
|
||||
|
||||
export type OwnProps = {
|
||||
modal?: boolean;
|
||||
};
|
||||
|
||||
const QuickChatPickerModal = ({
|
||||
modal,
|
||||
}: OwnProps) => {
|
||||
const { closeQuickChatPicker, openChat } = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
const isOpen = Boolean(modal);
|
||||
|
||||
const handleSelectRecipient = useLastCallback((peerId: string) => {
|
||||
openChat({ id: peerId });
|
||||
closeQuickChatPicker();
|
||||
});
|
||||
|
||||
return (
|
||||
<RecipientPicker
|
||||
isOpen={isOpen}
|
||||
searchPlaceholder={lang('Search')}
|
||||
onSelectRecipient={handleSelectRecipient}
|
||||
onClose={closeQuickChatPicker}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(QuickChatPickerModal);
|
||||
@ -951,3 +951,13 @@ addCallback((global: GlobalState) => {
|
||||
prevIsScreenLocked = global.passcode.isScreenLocked;
|
||||
prevBlurredTabsCount = blurredTabsCount;
|
||||
});
|
||||
|
||||
addActionHandler('openQuickChatPicker', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
|
||||
return updateTabState(global, {
|
||||
isQuickChatPickerOpen: true,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addTabStateResetterAction('closeQuickChatPicker', 'isQuickChatPickerOpen');
|
||||
|
||||
@ -2556,6 +2556,9 @@ export interface ActionPayloads {
|
||||
openGiftRecipientPicker: WithTabId | undefined;
|
||||
closeGiftRecipientPicker: WithTabId | undefined;
|
||||
|
||||
openQuickChatPicker: WithTabId | undefined;
|
||||
closeQuickChatPicker: WithTabId | undefined;
|
||||
|
||||
openWebAppsCloseConfirmationModal: WithTabId | undefined;
|
||||
|
||||
closeWebAppsCloseConfirmationModal: ({
|
||||
|
||||
@ -681,6 +681,8 @@ export type TabState = {
|
||||
|
||||
isGiftRecipientPickerOpen?: boolean;
|
||||
|
||||
isQuickChatPickerOpen?: boolean;
|
||||
|
||||
isFrozenAccountModalOpen?: boolean;
|
||||
|
||||
starsGiftingPickerModal?: {
|
||||
|
||||
@ -41,6 +41,8 @@ const useKeyboardListNavigation = (
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const focusedElement = document.activeElement;
|
||||
const elementChildren = Array.from(itemSelector ? element.querySelectorAll(itemSelector) : element.children);
|
||||
|
||||
@ -59,7 +61,8 @@ const useKeyboardListNavigation = (
|
||||
const item = elementChildren[newIndex] as HTMLElement;
|
||||
if (item) {
|
||||
setFocusedIndex(newIndex);
|
||||
item.focus();
|
||||
item.focus({ preventScroll: true });
|
||||
item.scrollIntoView({ behavior: 'instant', block: 'nearest' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user