Fix and Improve searching
This commit is contained in:
parent
e8e1c7e3b4
commit
c2a3698015
8 changed files with 44 additions and 19 deletions
|
@ -3,7 +3,7 @@ import { doResolveUris } from 'redux/actions/claims';
|
||||||
import { MAX_LIVESTREAM_COMMENTS } from 'constants/livestream';
|
import { MAX_LIVESTREAM_COMMENTS } from 'constants/livestream';
|
||||||
import { selectChannelMentionData } from 'redux/selectors/comments';
|
import { selectChannelMentionData } from 'redux/selectors/comments';
|
||||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import { doSetSearchResults } from 'redux/actions/search';
|
import { doSetMentionSearchResults } from 'redux/actions/search';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import TextareaWithSuggestions from './view';
|
import TextareaWithSuggestions from './view';
|
||||||
|
|
||||||
|
@ -13,7 +13,14 @@ const select = (state, props) => {
|
||||||
const uri = `lbry:/${pathname.replaceAll(':', '#')}`;
|
const uri = `lbry:/${pathname.replaceAll(':', '#')}`;
|
||||||
|
|
||||||
const data = selectChannelMentionData(state, uri, maxComments);
|
const data = selectChannelMentionData(state, uri, maxComments);
|
||||||
const { canonicalCommentors, canonicalCreatorUri, canonicalSubscriptions, commentorUris, canonicalSearch } = data;
|
const {
|
||||||
|
canonicalCommentors,
|
||||||
|
canonicalCreatorUri,
|
||||||
|
canonicalSearch,
|
||||||
|
canonicalSubscriptions,
|
||||||
|
commentorUris,
|
||||||
|
hasNewResolvedResults,
|
||||||
|
} = data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canonicalCommentors,
|
canonicalCommentors,
|
||||||
|
@ -21,13 +28,14 @@ const select = (state, props) => {
|
||||||
canonicalSearch,
|
canonicalSearch,
|
||||||
canonicalSubscriptions,
|
canonicalSubscriptions,
|
||||||
commentorUris,
|
commentorUris,
|
||||||
|
hasNewResolvedResults,
|
||||||
showMature: selectShowMatureContent(state),
|
showMature: selectShowMatureContent(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
doResolveUris: (uris) => dispatch(doResolveUris(uris, true)),
|
doResolveUris: (uris) => dispatch(doResolveUris(uris, true)),
|
||||||
doSetSearchResults: (uris) => dispatch(doSetSearchResults(uris)),
|
doSetMentionSearchResults: (uris) => dispatch(doSetMentionSearchResults(uris)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRouter(connect(select, perform)(TextareaWithSuggestions));
|
export default withRouter(connect(select, perform)(TextareaWithSuggestions));
|
||||||
|
|
|
@ -4,11 +4,12 @@ import { matchSorter } from 'match-sorter';
|
||||||
import { SEARCH_OPTIONS } from 'constants/search';
|
import { SEARCH_OPTIONS } from 'constants/search';
|
||||||
import * as KEYCODES from 'constants/keycodes';
|
import * as KEYCODES from 'constants/keycodes';
|
||||||
import Autocomplete from '@mui/material/Autocomplete';
|
import Autocomplete from '@mui/material/Autocomplete';
|
||||||
|
import BusyIndicator from 'component/common/busy-indicator';
|
||||||
import EMOJIS from 'emoji-dictionary';
|
import EMOJIS from 'emoji-dictionary';
|
||||||
|
import Popper from '@mui/material/Popper';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import TextareaSuggestionsItem from 'component/textareaSuggestionsItem';
|
import TextareaSuggestionsItem from 'component/textareaSuggestionsItem';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
import Popper from '@mui/material/Popper';
|
|
||||||
import useLighthouse from 'effects/use-lighthouse';
|
import useLighthouse from 'effects/use-lighthouse';
|
||||||
import useThrottle from 'effects/use-throttle';
|
import useThrottle from 'effects/use-throttle';
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ type Props = {
|
||||||
className?: string,
|
className?: string,
|
||||||
commentorUris?: Array<string>,
|
commentorUris?: Array<string>,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
|
hasNewResolvedResults?: boolean,
|
||||||
id: string,
|
id: string,
|
||||||
inputRef: any,
|
inputRef: any,
|
||||||
isLivestream?: boolean,
|
isLivestream?: boolean,
|
||||||
|
@ -52,7 +54,7 @@ type Props = {
|
||||||
uri?: string,
|
uri?: string,
|
||||||
value: any,
|
value: any,
|
||||||
doResolveUris: (Array<string>) => void,
|
doResolveUris: (Array<string>) => void,
|
||||||
doSetSearchResults: (Array<string>) => void,
|
doSetMentionSearchResults: (Array<string>) => void,
|
||||||
onBlur: (any) => any,
|
onBlur: (any) => any,
|
||||||
onChange: (any) => any,
|
onChange: (any) => any,
|
||||||
onFocus: (any) => any,
|
onFocus: (any) => any,
|
||||||
|
@ -67,6 +69,7 @@ export default function TextareaWithSuggestions(props: Props) {
|
||||||
className,
|
className,
|
||||||
commentorUris,
|
commentorUris,
|
||||||
disabled,
|
disabled,
|
||||||
|
hasNewResolvedResults,
|
||||||
id,
|
id,
|
||||||
inputRef,
|
inputRef,
|
||||||
isLivestream,
|
isLivestream,
|
||||||
|
@ -76,7 +79,7 @@ export default function TextareaWithSuggestions(props: Props) {
|
||||||
type,
|
type,
|
||||||
value: messageValue,
|
value: messageValue,
|
||||||
doResolveUris,
|
doResolveUris,
|
||||||
doSetSearchResults,
|
doSetMentionSearchResults,
|
||||||
onBlur,
|
onBlur,
|
||||||
onChange,
|
onChange,
|
||||||
onFocus,
|
onFocus,
|
||||||
|
@ -101,7 +104,8 @@ export default function TextareaWithSuggestions(props: Props) {
|
||||||
|
|
||||||
const hasMinLength = suggestionTerm && isMention && suggestionTerm.length >= LIGHTHOUSE_MIN_CHARACTERS;
|
const hasMinLength = suggestionTerm && isMention && suggestionTerm.length >= LIGHTHOUSE_MIN_CHARACTERS;
|
||||||
const isTyping = isMention && debouncedTerm !== suggestionTerm;
|
const isTyping = isMention && debouncedTerm !== suggestionTerm;
|
||||||
const showPlaceholder = isMention && (isTyping || loading);
|
const showPlaceholder =
|
||||||
|
isMention && (isTyping || loading || (results && results.length > 0 && !hasNewResolvedResults));
|
||||||
|
|
||||||
const shouldFilter = (uri, previous) => uri !== canonicalCreatorUri && (!previous || !previous.includes(uri));
|
const shouldFilter = (uri, previous) => uri !== canonicalCreatorUri && (!previous || !previous.includes(uri));
|
||||||
const filteredCommentors = canonicalCommentors && canonicalCommentors.filter((uri) => shouldFilter(uri));
|
const filteredCommentors = canonicalCommentors && canonicalCommentors.filter((uri) => shouldFilter(uri));
|
||||||
|
@ -271,9 +275,9 @@ export default function TextareaWithSuggestions(props: Props) {
|
||||||
const arrayResults = JSON.parse(stringifiedResults);
|
const arrayResults = JSON.parse(stringifiedResults);
|
||||||
if (arrayResults && arrayResults.length > 0) {
|
if (arrayResults && arrayResults.length > 0) {
|
||||||
doResolveUris(arrayResults);
|
doResolveUris(arrayResults);
|
||||||
doSetSearchResults(arrayResults);
|
doSetMentionSearchResults(arrayResults);
|
||||||
}
|
}
|
||||||
}, [doResolveUris, doSetSearchResults, stringifiedResults]);
|
}, [doResolveUris, doSetMentionSearchResults, stringifiedResults]);
|
||||||
|
|
||||||
// Disable sending on Enter on Livestream chat
|
// Disable sending on Enter on Livestream chat
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
@ -355,7 +359,7 @@ export default function TextareaWithSuggestions(props: Props) {
|
||||||
id={id}
|
id={id}
|
||||||
inputValue={messageValue}
|
inputValue={messageValue}
|
||||||
loading={!allMatches || allMatches.length === 0 || showPlaceholder}
|
loading={!allMatches || allMatches.length === 0 || showPlaceholder}
|
||||||
loadingText={results || showPlaceholder ? __('Searching...') : __('Nothing found')}
|
loadingText={showPlaceholder ? <BusyIndicator message={__('Searching...')} /> : __('Nothing found')}
|
||||||
onBlur={() => onBlur && onBlur()}
|
onBlur={() => onBlur && onBlur()}
|
||||||
/* Different from onInputChange, onChange is only used for the selected value,
|
/* Different from onInputChange, onChange is only used for the selected value,
|
||||||
so here it is acting simply as a selection handler (see it as onSelect) */
|
so here it is acting simply as a selection handler (see it as onSelect) */
|
||||||
|
@ -366,7 +370,7 @@ export default function TextareaWithSuggestions(props: Props) {
|
||||||
onInputChange={(event, value, reason) => reason === 'input' && handleInputChange(value)}
|
onInputChange={(event, value, reason) => reason === 'input' && handleInputChange(value)}
|
||||||
onOpen={() => suggestionTerm && setClose(false)}
|
onOpen={() => suggestionTerm && setClose(false)}
|
||||||
/* 'open' is for the popper box component, set to check for a valid term
|
/* 'open' is for the popper box component, set to check for a valid term
|
||||||
or else it will be displayed all the time as empty */
|
or else it will be displayed all the time as empty (no options) */
|
||||||
open={!!suggestionTerm && !shouldClose}
|
open={!!suggestionTerm && !shouldClose}
|
||||||
options={allOptionsGrouped}
|
options={allOptionsGrouped}
|
||||||
renderGroup={({ group, children }) => renderGroup(group, children)}
|
renderGroup={({ group, children }) => renderGroup(group, children)}
|
||||||
|
|
|
@ -230,7 +230,7 @@ export const SEARCH_SUCCESS = 'SEARCH_SUCCESS';
|
||||||
export const SEARCH_FAIL = 'SEARCH_FAIL';
|
export const SEARCH_FAIL = 'SEARCH_FAIL';
|
||||||
export const UPDATE_SEARCH_OPTIONS = 'UPDATE_SEARCH_OPTIONS';
|
export const UPDATE_SEARCH_OPTIONS = 'UPDATE_SEARCH_OPTIONS';
|
||||||
export const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS';
|
export const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS';
|
||||||
export const SET_SEARCH_RESULTS = 'SET_SEARCH_RESULTS';
|
export const SET_MENTION_SEARCH_RESULTS = 'SET_MENTION_SEARCH_RESULTS';
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
||||||
|
|
|
@ -147,9 +147,9 @@ export const doUpdateSearchOptions = (newOptions: SearchOptions, additionalOptio
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const doSetSearchResults = (uris: Array<string>) => (dispatch: Dispatch) => {
|
export const doSetMentionSearchResults = (uris: Array<string>) => (dispatch: Dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.SET_SEARCH_RESULTS,
|
type: ACTIONS.SET_MENTION_SEARCH_RESULTS,
|
||||||
data: { uris },
|
data: { uris },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -68,7 +68,7 @@ export default handleActions(
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
[ACTIONS.SET_SEARCH_RESULTS]: (state: SearchState, action: SearchSuccess): SearchState => ({
|
[ACTIONS.SET_MENTION_SEARCH_RESULTS]: (state: SearchState, action: SearchSuccess): SearchState => ({
|
||||||
...state,
|
...state,
|
||||||
results: action.data.uris,
|
results: action.data.uris,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { createSelector } from 'reselect';
|
||||||
import { createCachedSelector } from 're-reselect';
|
import { createCachedSelector } from 're-reselect';
|
||||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import { selectSearchResults } from 'redux/selectors/search';
|
import { selectMentionSearchResults } from 'redux/selectors/search';
|
||||||
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
|
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
|
||||||
import {
|
import {
|
||||||
selectClaimsById,
|
selectClaimsById,
|
||||||
|
@ -398,7 +398,7 @@ export const selectChannelMentionData = createCachedSelector(
|
||||||
selectClaimsById,
|
selectClaimsById,
|
||||||
selectTopLevelCommentsForUri,
|
selectTopLevelCommentsForUri,
|
||||||
selectSubscriptionUris,
|
selectSubscriptionUris,
|
||||||
selectSearchResults,
|
selectMentionSearchResults,
|
||||||
(uri, claimIdsByUri, claimsById, topLevelComments, subscriptionUris, searchUris) => {
|
(uri, claimIdsByUri, claimsById, topLevelComments, subscriptionUris, searchUris) => {
|
||||||
let canonicalCreatorUri;
|
let canonicalCreatorUri;
|
||||||
const commentorUris = [];
|
const commentorUris = [];
|
||||||
|
@ -437,6 +437,7 @@ export const selectChannelMentionData = createCachedSelector(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let hasNewResolvedResults = false;
|
||||||
if (searchUris && searchUris.length > 0) {
|
if (searchUris && searchUris.length > 0) {
|
||||||
searchUris.forEach((uri) => {
|
searchUris.forEach((uri) => {
|
||||||
// Update: canonicalSubscriptions
|
// Update: canonicalSubscriptions
|
||||||
|
@ -446,8 +447,16 @@ export const selectChannelMentionData = createCachedSelector(
|
||||||
canonicalSearch.push(claim.canonical_url);
|
canonicalSearch.push(claim.canonical_url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
hasNewResolvedResults = canonicalSearch.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { canonicalCommentors, canonicalCreatorUri, canonicalSubscriptions, commentorUris, canonicalSearch };
|
return {
|
||||||
|
canonicalCommentors,
|
||||||
|
canonicalCreatorUri,
|
||||||
|
canonicalSubscriptions,
|
||||||
|
commentorUris,
|
||||||
|
hasNewResolvedResults,
|
||||||
|
canonicalSearch,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
)((state, uri, maxCount) => `${String(uri)}:${maxCount}`);
|
)((state, uri, maxCount) => `${String(uri)}:${maxCount}`);
|
||||||
|
|
|
@ -32,7 +32,7 @@ export const selectSearchResultByQuery: (state: State) => { [string]: Array<stri
|
||||||
selectState(state).resultsByQuery;
|
selectState(state).resultsByQuery;
|
||||||
export const selectHasReachedMaxResultsLength: (state: State) => { [boolean]: Array<boolean> } = (state) =>
|
export const selectHasReachedMaxResultsLength: (state: State) => { [boolean]: Array<boolean> } = (state) =>
|
||||||
selectState(state).hasReachedMaxResultsLength;
|
selectState(state).hasReachedMaxResultsLength;
|
||||||
export const selectSearchResults: (state: State) => Array<string> = (state) => selectState(state).results;
|
export const selectMentionSearchResults: (state: State) => Array<string> = (state) => selectState(state).results;
|
||||||
|
|
||||||
export const makeSelectSearchUrisForQuery = (query: string): ((state: State) => Array<string>) =>
|
export const makeSelectSearchUrisForQuery = (query: string): ((state: State) => Array<string>) =>
|
||||||
createSelector(selectSearchResultByQuery, (byQuery) => {
|
createSelector(selectSearchResultByQuery, (byQuery) => {
|
||||||
|
|
|
@ -86,3 +86,7 @@
|
||||||
.textareaSuggestions__topSeparator {
|
.textareaSuggestions__topSeparator {
|
||||||
@extend .wunderbar__top-separator;
|
@extend .wunderbar__top-separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.MuiAutocomplete-loading {
|
||||||
|
color: var(--color-text) !important;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue