Filter chats by Unread

This commit is contained in:
yash-signal 2024-11-13 13:33:41 -06:00 committed by GitHub
parent 45e9c07125
commit a56e7d0ade
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 883 additions and 438 deletions

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useEffect, useRef } from 'react';
import classNames from 'classnames';
import type {
ConversationType,
ShowConversationType,
@ -10,17 +11,19 @@ import type { LocalizerType } from '../types/Util';
import { Avatar, AvatarSize } from './Avatar';
import { SearchInput } from './SearchInput';
import { usePrevious } from '../hooks/usePrevious';
import { Tooltip, TooltipPlacement } from './Tooltip';
import { Theme } from '../util/theme';
type PropsType = {
type BasePropsType = {
clearConversationSearch: () => void;
clearSearch: () => void;
clearSearchQuery: () => void;
disabled?: boolean;
endConversationSearch: () => void;
endSearch: () => void;
i18n: LocalizerType;
isSearchingGlobally: boolean;
onEnterKeyDown?: (
clearSearch: () => void,
clearSearchQuery: () => void,
showConversation: ShowConversationType
) => void;
searchConversation?: ConversationType;
@ -30,9 +33,23 @@ type PropsType = {
updateSearchTerm: (searchTerm: string) => void;
};
type NoFilterPropsType = BasePropsType & {
filterButtonEnabled?: false;
filterPressed?: false;
onFilterClick?: () => void;
};
type WithFilterPropsType = BasePropsType & {
filterButtonEnabled: boolean;
filterPressed: boolean;
onFilterClick: (enabled: boolean) => void;
};
type PropsType = NoFilterPropsType | WithFilterPropsType;
export function LeftPaneSearchInput({
clearConversationSearch,
clearSearch,
clearSearchQuery,
disabled,
endConversationSearch,
endSearch,
@ -44,6 +61,9 @@ export function LeftPaneSearchInput({
showConversation,
startSearchCounter,
updateSearchTerm,
filterButtonEnabled = false,
filterPressed = false,
onFilterClick,
}: PropsType): JSX.Element {
const inputRef = useRef<null | HTMLInputElement>(null);
@ -83,7 +103,7 @@ export function LeftPaneSearchInput({
if (searchConversation) {
clearConversationSearch();
} else {
clearSearch();
clearSearchQuery();
}
return;
@ -94,79 +114,109 @@ export function LeftPaneSearchInput({
}
};
const label = searchConversation ? i18n('icu:searchIn') : i18n('icu:search');
let label: string;
if (searchConversation) {
label = i18n('icu:searchIn');
} else if (filterPressed) {
label = i18n('icu:searchUnreadChats');
} else {
label = i18n('icu:search');
}
return (
<SearchInput
disabled={disabled}
label={label}
hasSearchIcon={!searchConversation}
i18n={i18n}
moduleClassName="LeftPaneSearchInput"
onBlur={() => {
if (!searchConversation && !searchTerm) {
endSearch();
}
}}
onKeyDown={event => {
if (onEnterKeyDown && event.key === 'Enter') {
onEnterKeyDown(clearSearch, showConversation);
event.preventDefault();
event.stopPropagation();
}
}}
onChange={event => {
changeValue(event.currentTarget.value);
}}
onClear={() => {
if (searchTerm) {
clearSearch();
inputRef.current?.focus();
} else if (searchConversation) {
endConversationSearch();
inputRef.current?.focus();
} else {
inputRef.current?.blur();
}
}}
ref={inputRef}
placeholder={label}
value={searchTerm}
>
{searchConversation && (
// Clicking the non-X part of the pill should focus the input but have a normal
// cursor. This effectively simulates `pointer-events: none` while still
// letting us change the cursor.
// eslint-disable-next-line max-len
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
className="LeftPaneSearchInput__in-conversation-pill"
onClick={() => {
<>
<SearchInput
disabled={disabled}
label={label}
hasSearchIcon={!searchConversation}
i18n={i18n}
moduleClassName="LeftPaneSearchInput"
onBlur={() => {
if (!searchConversation && !searchTerm) {
endSearch();
}
}}
onKeyDown={event => {
if (onEnterKeyDown && event.key === 'Enter') {
onEnterKeyDown(clearSearchQuery, showConversation);
event.preventDefault();
event.stopPropagation();
}
}}
onChange={event => {
changeValue(event.currentTarget.value);
}}
onClear={() => {
if (searchTerm) {
clearSearchQuery();
inputRef.current?.focus();
}}
} else if (searchConversation) {
endConversationSearch();
inputRef.current?.focus();
} else {
inputRef.current?.blur();
}
}}
ref={inputRef}
placeholder={label}
value={searchTerm}
>
{searchConversation && (
// Clicking the non-X part of the pill should focus the input but have a normal
// cursor. This effectively simulates `pointer-events: none` while still
// letting us change the cursor.
// eslint-disable-next-line max-len
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
className="LeftPaneSearchInput__in-conversation-pill"
onClick={() => {
inputRef.current?.focus();
}}
>
<Avatar
acceptedMessageRequest={searchConversation.acceptedMessageRequest}
avatarUrl={searchConversation.avatarUrl}
badge={undefined}
color={searchConversation.color}
conversationType={searchConversation.type}
i18n={i18n}
isMe={searchConversation.isMe}
noteToSelf={searchConversation.isMe}
sharedGroupNames={searchConversation.sharedGroupNames}
size={AvatarSize.TWENTY}
title={searchConversation.title}
unblurredAvatarUrl={searchConversation.unblurredAvatarUrl}
/>
<button
aria-label={i18n('icu:clearSearch')}
className="LeftPaneSearchInput__in-conversation-pill__x-button"
onClick={endConversationSearch}
type="button"
/>
</div>
)}
</SearchInput>
{filterButtonEnabled && (
<Tooltip
direction={TooltipPlacement.Bottom}
content={i18n('icu:filterByUnreadButtonLabel')}
theme={Theme.Dark}
delay={600}
>
<Avatar
acceptedMessageRequest={searchConversation.acceptedMessageRequest}
avatarUrl={searchConversation.avatarUrl}
badge={undefined}
color={searchConversation.color}
conversationType={searchConversation.type}
i18n={i18n}
isMe={searchConversation.isMe}
noteToSelf={searchConversation.isMe}
sharedGroupNames={searchConversation.sharedGroupNames}
size={AvatarSize.TWENTY}
title={searchConversation.title}
unblurredAvatarUrl={searchConversation.unblurredAvatarUrl}
/>
<button
aria-label={i18n('icu:clearSearch')}
className="LeftPaneSearchInput__in-conversation-pill__x-button"
onClick={endConversationSearch}
className={classNames('LeftPaneSearchInput__FilterButton', {
'LeftPaneSearchInput__FilterButton--pressed': filterPressed,
})}
type="button"
/>
</div>
aria-pressed={filterPressed}
onClick={() => onFilterClick?.(!filterPressed)}
>
<span className="LeftPaneSearchInput__FilterLabel">
{i18n('icu:filterByUnreadButtonLabel')}
</span>
</button>
</Tooltip>
)}
</SearchInput>
</>
);
}