signal-desktop/ts/components/StoriesPane.tsx

192 lines
5.9 KiB
TypeScript
Raw Normal View History

2022-03-04 21:14:52 +00:00
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import Fuse from 'fuse.js';
import React, { useEffect, useState } from 'react';
import classNames from 'classnames';
2022-06-17 00:48:57 +00:00
2022-07-01 00:52:03 +00:00
import type {
ConversationType,
ShowConversationType,
} from '../state/ducks/conversations';
2022-11-16 22:10:11 +00:00
import type { ConversationStoryType, MyStoryType } from '../types/Stories';
2023-08-09 00:53:06 +00:00
import type { LocalizerType, ThemeType } from '../types/Util';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import type { ShowToastAction } from '../state/ducks/toast';
2022-08-22 17:44:23 +00:00
import type { ViewUserStoriesActionCreatorType } from '../state/ducks/stories';
import { MyStoryButton } from './MyStoryButton';
2022-03-04 21:14:52 +00:00
import { SearchInput } from './SearchInput';
import { StoryListItem } from './StoryListItem';
2022-06-17 00:48:57 +00:00
import { isNotNil } from '../util/isNotNil';
2023-08-09 00:53:06 +00:00
import { NavSidebarSearchHeader } from './NavSidebar';
2022-03-04 21:14:52 +00:00
const FUSE_OPTIONS: Fuse.IFuseOptions<ConversationStoryType> = {
2022-07-06 19:06:20 +00:00
getFn: (story, path) => {
2022-10-19 22:56:55 +00:00
if (path[0] === 'searchNames' || path === 'searchNames') {
2022-07-06 19:06:20 +00:00
return [story.storyView.sender.title, story.storyView.sender.name].filter(
isNotNil
);
2022-03-04 21:14:52 +00:00
}
2022-07-06 19:06:20 +00:00
return story.group?.title ?? '';
2022-03-04 21:14:52 +00:00
},
keys: [
{
name: 'searchNames',
weight: 1,
},
{
name: 'group',
weight: 1,
},
],
threshold: 0.1,
};
function search(
stories: ReadonlyArray<ConversationStoryType>,
searchTerm: string
): Array<ConversationStoryType> {
return new Fuse<ConversationStoryType>(stories, FUSE_OPTIONS)
.search(searchTerm)
.map(result => result.item);
2022-03-04 21:14:52 +00:00
}
export type PropsType = {
getPreferredBadge: PreferredBadgeSelectorType;
2022-03-04 21:14:52 +00:00
hiddenStories: Array<ConversationStoryType>;
i18n: LocalizerType;
maxAttachmentSizeInKb: number;
2022-07-01 00:52:03 +00:00
me: ConversationType;
myStories: Array<MyStoryType>;
2022-08-04 19:23:24 +00:00
onAddStory: (file?: File) => unknown;
2022-07-01 00:52:03 +00:00
onMyStoriesClicked: () => unknown;
onStoriesSettings: () => unknown;
2023-02-24 23:18:57 +00:00
onMediaPlaybackStart: () => void;
2022-03-29 01:10:08 +00:00
queueStoryDownload: (storyId: string) => unknown;
2022-06-16 19:12:50 +00:00
showConversation: ShowConversationType;
showToast: ShowToastAction;
2022-03-04 21:14:52 +00:00
stories: Array<ConversationStoryType>;
2023-08-09 00:53:06 +00:00
theme: ThemeType;
2022-03-04 21:14:52 +00:00
toggleHideStories: (conversationId: string) => unknown;
2022-08-22 17:44:23 +00:00
viewUserStories: ViewUserStoriesActionCreatorType;
2022-03-04 21:14:52 +00:00
};
2022-11-18 00:45:19 +00:00
export function StoriesPane({
getPreferredBadge,
2022-04-15 22:31:18 +00:00
hiddenStories,
2022-03-04 21:14:52 +00:00
i18n,
maxAttachmentSizeInKb,
2022-07-01 00:52:03 +00:00
me,
myStories,
2022-06-17 00:48:57 +00:00
onAddStory,
2022-07-01 00:52:03 +00:00
onMyStoriesClicked,
2023-02-24 23:18:57 +00:00
onMediaPlaybackStart,
2022-03-29 01:10:08 +00:00
queueStoryDownload,
2022-06-16 19:12:50 +00:00
showConversation,
showToast,
2022-03-04 21:14:52 +00:00
stories,
2023-08-09 00:53:06 +00:00
theme,
2022-03-04 21:14:52 +00:00
toggleHideStories,
2022-07-25 18:55:44 +00:00
viewUserStories,
2022-11-18 00:45:19 +00:00
}: PropsType): JSX.Element {
2022-03-04 21:14:52 +00:00
const [searchTerm, setSearchTerm] = useState('');
2022-04-15 22:31:18 +00:00
const [isShowingHiddenStories, setIsShowingHiddenStories] = useState(false);
2022-03-04 21:14:52 +00:00
const [renderedStories, setRenderedStories] =
useState<Array<ConversationStoryType>>(stories);
useEffect(() => {
if (searchTerm) {
setRenderedStories(search(stories, searchTerm));
} else {
setRenderedStories(stories);
}
}, [searchTerm, stories]);
return (
<>
2023-08-09 00:53:06 +00:00
<NavSidebarSearchHeader>
<SearchInput
2022-08-04 19:23:24 +00:00
i18n={i18n}
2023-08-09 00:53:06 +00:00
onChange={event => {
setSearchTerm(event.target.value);
}}
2023-08-09 00:53:06 +00:00
placeholder={i18n('icu:search')}
value={searchTerm}
/>
2023-08-09 00:53:06 +00:00
</NavSidebarSearchHeader>
<div className="Stories__pane__list">
2022-11-18 00:45:19 +00:00
<MyStoryButton
i18n={i18n}
maxAttachmentSizeInKb={maxAttachmentSizeInKb}
2022-11-18 00:45:19 +00:00
me={me}
myStories={myStories}
onAddStory={onAddStory}
onClick={onMyStoriesClicked}
queueStoryDownload={queueStoryDownload}
showToast={showToast}
2023-02-24 23:18:57 +00:00
onMediaPlaybackStart={onMediaPlaybackStart}
2022-11-18 00:45:19 +00:00
/>
{renderedStories.map(story => (
<StoryListItem
conversationId={story.conversationId}
getPreferredBadge={getPreferredBadge}
hasReplies={story.hasReplies}
hasRepliesFromSelf={story.hasRepliesFromSelf}
group={story.group}
i18n={i18n}
2022-11-18 00:45:19 +00:00
key={story.storyView.timestamp}
onGoToConversation={conversationId => {
showConversation({ conversationId });
}}
onHideStory={toggleHideStories}
2023-02-24 23:18:57 +00:00
onMediaPlaybackStart={onMediaPlaybackStart}
queueStoryDownload={queueStoryDownload}
2022-11-18 00:45:19 +00:00
story={story.storyView}
2023-08-09 00:53:06 +00:00
theme={theme}
2022-11-18 00:45:19 +00:00
viewUserStories={viewUserStories}
/>
2022-11-18 00:45:19 +00:00
))}
{Boolean(hiddenStories.length) && (
<>
<button
className={classNames('Stories__hidden-stories', {
2023-08-09 00:53:06 +00:00
'Stories__hidden-stories--collapsed': !isShowingHiddenStories,
2022-11-18 00:45:19 +00:00
'Stories__hidden-stories--expanded': isShowingHiddenStories,
})}
onClick={() => setIsShowingHiddenStories(!isShowingHiddenStories)}
type="button"
>
2023-03-30 00:03:25 +00:00
{i18n('icu:Stories__hidden-stories')}
2022-11-18 00:45:19 +00:00
</button>
{isShowingHiddenStories &&
hiddenStories.map(story => (
<StoryListItem
conversationId={story.conversationId}
getPreferredBadge={getPreferredBadge}
group={story.group}
i18n={i18n}
isHidden
key={story.storyView.timestamp}
onGoToConversation={conversationId => {
showConversation({ conversationId });
}}
onHideStory={toggleHideStories}
2023-02-24 23:18:57 +00:00
onMediaPlaybackStart={onMediaPlaybackStart}
2022-11-18 00:45:19 +00:00
queueStoryDownload={queueStoryDownload}
story={story.storyView}
2023-08-09 00:53:06 +00:00
theme={theme}
2022-11-18 00:45:19 +00:00
viewUserStories={viewUserStories}
/>
))}
</>
)}
{!stories.length && (
<div className="Stories__pane__list--empty">
2023-03-30 00:03:25 +00:00
{i18n('icu:Stories__list-empty')}
2022-11-18 00:45:19 +00:00
</div>
)}
</div>
</>
2022-03-04 21:14:52 +00:00
);
2022-11-18 00:45:19 +00:00
}