diff --git a/web/chat/chat-thread-composer.css b/web/chat/chat-thread-composer.css index 0c542b5f2..4fdfc82d0 100644 --- a/web/chat/chat-thread-composer.css +++ b/web/chat/chat-thread-composer.css @@ -1,78 +1,79 @@ div.threadSearchContainer { background-color: var(--thread-creation-search-container-bg); color: var(--fg); display: flex; flex-direction: column; max-height: 50%; overflow: auto; flex-shrink: 0; } div.fullHeight { flex-grow: 1; max-height: 100%; } div.userSelectedTags { display: flex; flex-wrap: wrap; flex-direction: row; align-items: center; gap: 4px; padding: 4px 12px; margin-bottom: 8px; } div.searchRow { display: flex; flex-direction: row; align-items: center; margin-right: 8px; } div.searchField { flex-grow: 1; + padding: 1rem; } .closeSearch { color: var(--thread-creation-close-search-color); margin: 0 8px; } ul.searchResultsContainer { display: flex; flex-direction: column; overflow: auto; padding: 0 12px 8px; list-style-type: none; } .searchResultsItem { display: flex; } .searchResultsItem:hover { background-color: var(--thread-creation-search-item-bg-hover); } .searchResultsButton { justify-content: space-between; flex: 1; padding: 8px 12px; } div.userName { color: var(--fg); margin-left: 8px; } div.userInfo { font-style: italic; color: var(--thread-creation-search-item-info-color); } div.userContainer { display: flex; flex-direction: row; align-items: center; } diff --git a/web/chat/chat-thread-list-search.react.js b/web/chat/chat-thread-list-search.react.js index 07639a903..3e18677a0 100644 --- a/web/chat/chat-thread-list-search.react.js +++ b/web/chat/chat-thread-list-search.react.js @@ -1,29 +1,32 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; +import css from './chat-thread-list.css'; import { ThreadListContext } from './thread-list-provider.js'; import Search from '../components/search.react.js'; function ChatThreadListSearch(): React.Node { const threadListContext = React.useContext(ThreadListContext); invariant( threadListContext, 'threadListContext should be set in ChatThreadListSearch', ); const { setSearchText, searchText } = threadListContext; return React.useMemo( () => ( - +
+ +
), [searchText, setSearchText], ); } export default ChatThreadListSearch; diff --git a/web/chat/chat-thread-list.css b/web/chat/chat-thread-list.css index e522ea717..323c879b6 100644 --- a/web/chat/chat-thread-list.css +++ b/web/chat/chat-thread-list.css @@ -1,282 +1,286 @@ .thread { display: flex; flex-direction: row; } .sidebarItem { display: flex; flex-direction: row; width: 100%; } .threadListSidebar { display: flex; flex-direction: row; height: 32px; padding-right: 8px; position: relative; cursor: pointer; } .threadListSidebar > svg { position: absolute; top: -7px; left: 30px; } .activeThread, .threadListSidebar:hover { background: var(--thread-active-bg); } .activeThread :is(.title, .lastMessage, .lastMessage *) { color: var(--chat-thread-list-color-active); } .activeThread.thread:hover { background: var(--thread-active-bg); } .thread:hover { background: var(--thread-hover-bg); } div.title { flex: 1; font-size: var(--m-font-16); font-weight: var(--semi-bold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--thread-color-read); line-height: var(--line-height-text); } .threadButton { flex: 1; cursor: pointer; overflow: hidden; padding-left: 12px; } .threadButton + div { display: flex; flex-direction: column; } .threadButtonSidebar { cursor: pointer; overflow: hidden; display: flex; align-items: center; padding-left: 12px; } p.breadCrumbs { display: flex; padding: 8px 0 2px 0; font-size: var(--xs-font-12); font-weight: var(--normal); color: var(--breadcrumb-color); } p.breadCrumbs.unread { color: var(--breadcrumb-color-unread); } span.breadCrumb { display: flex; align-items: center; white-space: nowrap; text-overflow: ellipsis; } span.breadCrumb svg { margin-left: 4px; margin-right: 4px; } div.colorContainer { display: flex; padding-top: 8px; } div.spacer { width: 42px; border-radius: 1.68px; } div.avatarContainer { height: 42px; display: flex; } div.lastActivity { font-size: var(--xxs-font-10); color: var(--fg); line-height: 1.5; padding-right: 16px; font-weight: var(--semi-bold); white-space: nowrap; flex-grow: 1; padding-bottom: 12px; align-items: flex-end; display: flex; } div.lastMessage { font-size: var(--s-font-14); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: var(--line-height-text); padding-bottom: 8px; } div.unread { color: var(--fg); font-weight: var(--semi-bold); } div.dark { color: var(--thread-color-read); padding-right: 16px; } .messagePreviewPrimary { color: var(--thread-color-read); } .messagePreviewSecondary { color: var(--thread-preview-secondary); } div.dotContainer { display: flex; align-items: center; justify-content: center; width: 16px; } div.unreadDot { height: 4px; width: 4px; background: var(--fg); border-radius: 15px; align-self: center; } div.italic { font-style: italic; } div.sidebarTitle { flex: 1; font-size: var(--s-font-14); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--thread-color-read); align-self: flex-start; } .threadListSidebar > div.dotContainer { width: 16px; } div.sidebarTitle.unread { color: var(--fg); } div.seeMoreButton { display: flex; align-items: center; padding-left: 22px; } div.seeMoreText { padding-left: 14px; } div.sidebar .menu > button svg { font-size: 16px; color: var(--thread-color-read); } div.sidebar .menu { opacity: 0; } div.sidebar:hover .menu { display: flex; align-self: flex-end; opacity: 1; } .menu { position: relative; display: flex; justify-content: flex-end; } .menu > button { background-color: transparent; color: var(--thread-color-read); border: none; cursor: pointer; display: flex; align-items: center; } .menu > button:focus { outline: none; } .menuContent { display: none; position: absolute; top: calc(100% + 1px); right: 0; z-index: 1; width: max-content; overflow: hidden; background-color: #eeeeee; border-radius: 5px; box-shadow: 1px 1px 5px 2px #00000022; } .menuContentVisible { display: block; } button.menuContent { border: none; cursor: pointer; padding: 10px; font-size: 16px; } button.menuContent:hover { background-color: #dddddd; } ul.list { margin: 5px 3px 10px 0; overflow: auto; } div.spacer { height: 6px; } div.emptyItemContainer { display: flex; flex-direction: column; align-items: center; } div.emptyItemText { padding: 16px; font-size: 16px; text-align: center; white-space: pre-wrap; color: var(--fg); } div.threadListContainer { display: flex; flex-direction: column; overflow: hidden; flex: 1; } div.createNewThread { display: flex; flex-direction: column; align-items: stretch; padding: 8px; } img.longArrow { height: 40px; width: 25px; position: absolute; left: 28.5px; top: -18px; } img.arrow { position: absolute; left: 28px; top: -10px; } + +.searchBarContainer { + padding: 1rem; +} diff --git a/web/components/search.css b/web/components/search.css index 1303f25f8..725f2fd74 100644 --- a/web/components/search.css +++ b/web/components/search.css @@ -1,42 +1,41 @@ div.searchContainer { background-color: var(--inputField-background-secondary-default); display: flex; align-items: center; - margin: 1rem; border-radius: 8px; padding: 8px; flex: 1; } div.searchIcon { display: flex; color: var(--inputField-label-secondary-default); } input.searchInput, #searchInputID { background-color: transparent; font-size: var(--m-font-16); line-height: 1.5; padding: 4px 12px; flex: 1; border: none; color: var(--inputField-label-primary-default); outline: none; } input.searchInput::placeholder { color: var(--inputField-label-secondary-default); } button.clearSearch { color: var(--inputField-clearIcon-primary-default); transition: ease-in-out 0.15s; background: var(--inputField-clearBackground-primary-default); border-radius: 50%; padding: 6px; } button.clearSearchDisabled { opacity: 0; } diff --git a/web/modals/search/message-search-modal.css b/web/modals/search/message-search-modal.css index 43c7a588d..10c3d15cd 100644 --- a/web/modals/search/message-search-modal.css +++ b/web/modals/search/message-search-modal.css @@ -1,40 +1,45 @@ .container { display: flex; flex-direction: column; overflow: hidden; height: 80vh; } .content { overflow-y: scroll; border-top: 2px solid var(--border-color); padding: 16px 0; } .content > * { margin-bottom: 16px; } .loading { text-align: center; margin-bottom: 8px; margin-top: 24px; } .footer { color: var(--modal-secondary-label); size: 14; font-weight: 400; text-align: center; margin-top: 20px; } .header { display: flex; flex-direction: row; margin-bottom: 16px; } .button { margin: 10px 10px 10px 0; } + +.searchBarContainer { + padding: 1rem; + flex-grow: 1; +} diff --git a/web/modals/search/message-search-modal.react.js b/web/modals/search/message-search-modal.react.js index 7de301111..47c5a7d7c 100644 --- a/web/modals/search/message-search-modal.react.js +++ b/web/modals/search/message-search-modal.react.js @@ -1,159 +1,161 @@ // @flow import * as React from 'react'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; import css from './message-search-modal.css'; import { useParseSearchResults } from './message-search-utils.react.js'; import Button from '../../components/button.react.js'; import MessageResult from '../../components/message-result.react.js'; import Search from '../../components/search.react.js'; import LoadingIndicator from '../../loading-indicator.react.js'; import { useMessageSearchContext } from '../../search/message-search-state-provider.react.js'; import { useTooltipContext } from '../../tooltips/tooltip-provider.js'; import Modal from '../modal.react.js'; type ContentProps = { +threadInfo: ThreadInfo, }; function MessageSearchModal(props: ContentProps): React.Node { const { threadInfo } = props; const { getQuery, setQuery, clearQuery, searchMessages, getSearchResults, getEndReached, } = useMessageSearchContext(); const [input, setInput] = React.useState(getQuery(threadInfo.id)); const onPressSearch = React.useCallback(() => { setQuery(input, threadInfo.id); searchMessages(threadInfo.id); }, [setQuery, input, searchMessages, threadInfo.id]); const onKeyDown = React.useCallback( (event: SyntheticKeyboardEvent) => { if (event.key === 'Enter') { onPressSearch(); } }, [onPressSearch], ); const modifiedItems = useParseSearchResults( threadInfo, getSearchResults(threadInfo.id), ); const { clearTooltip } = useTooltipContext(); const messageContainer = React.useRef(null); const possiblyLoadMoreMessages = React.useCallback(() => { if (!messageContainer.current) { return; } const loaderTopOffset = 32; const { scrollTop, scrollHeight, clientHeight } = messageContainer.current; if (Math.abs(scrollTop) + clientHeight + loaderTopOffset < scrollHeight) { return; } searchMessages(threadInfo.id); }, [searchMessages, threadInfo.id]); const onScroll = React.useCallback(() => { clearTooltip(); possiblyLoadMoreMessages(); }, [clearTooltip, possiblyLoadMoreMessages]); const renderItem = React.useCallback( (item: ChatMessageInfoItem) => ( ), [threadInfo], ); const messages = React.useMemo( () => modifiedItems.map(item => renderItem(item)), [modifiedItems, renderItem], ); const endReached = getEndReached(threadInfo.id); const query = getQuery(threadInfo.id); const footer = React.useMemo(() => { if (query === '') { return (
Your search results will appear here
); } if (!endReached) { return (
); } if (modifiedItems.length > 0) { return
End of results
; } return (
No results. Please try using different keywords to refine your search
); }, [query, endReached, modifiedItems.length]); const { uiName } = useResolvedThreadInfo(threadInfo); const searchPlaceholder = `Searching in ${uiName}`; const { popModal } = useModalContext(); const clearQueryWrapper = React.useCallback( () => clearQuery(threadInfo.id), [clearQuery, threadInfo.id], ); return (
- +
+ +
{messages} {footer}
); } export default MessageSearchModal;