一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
133 行
3.6 KiB
TypeScript
133 行
3.6 KiB
TypeScript
import * as React from 'react';
|
|
|
|
import { StyleSheet, View } from 'react-native';
|
|
import { connect } from 'react-redux';
|
|
import ScreenHeader from '../../ScreenHeader';
|
|
import { _ } from '@joplin/lib/locale';
|
|
import { ThemeStyle, themeStyle } from '../../global-style';
|
|
import { AppState } from '../../../utils/types';
|
|
import { Dispatch } from 'redux';
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
import SearchResults from './SearchResults';
|
|
import AccessibleView from '../../accessibility/AccessibleView';
|
|
import { ComplexTerm } from '@joplin/lib/services/search/SearchEngine';
|
|
import SearchBar from './SearchBar';
|
|
|
|
interface Props {
|
|
themeId: number;
|
|
query: string;
|
|
visible: boolean;
|
|
dispatch: Dispatch;
|
|
|
|
noteSelectionEnabled: boolean;
|
|
ftsEnabled: number;
|
|
}
|
|
|
|
const useStyles = (theme: ThemeStyle, visible: boolean) => {
|
|
return useMemo(() => {
|
|
return StyleSheet.create({
|
|
body: {
|
|
flex: 1,
|
|
},
|
|
rootStyle: visible ? theme.rootStyle : theme.hiddenRootStyle,
|
|
});
|
|
}, [theme, visible]);
|
|
};
|
|
|
|
// Workaround for https://github.com/laurent22/joplin/issues/12823:
|
|
// Disable search-as-you-type for short 0-2 character searches that
|
|
// are likely to match the start of a large number of words.
|
|
const useSearchPaused = (query: string) => {
|
|
const [pauseDisabled, setPauseDisabled] = useState(false);
|
|
// Only disable search-as-you-type for a subset of all characters.
|
|
// This is, for example, to ensure that search-as-you-type remains
|
|
// enabled for CJK characters (e.g. U+6570 has length 1).
|
|
const paused = query.match(/^[a-z0-9]{0,2}$/i);
|
|
|
|
const onOverridePause = useCallback(() => {
|
|
setPauseDisabled(true);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
setPauseDisabled(false);
|
|
}, [query]);
|
|
|
|
return {
|
|
paused: paused && !pauseDisabled,
|
|
onOverridePause,
|
|
};
|
|
};
|
|
|
|
const SearchScreenComponent: React.FC<Props> = props => {
|
|
const theme = themeStyle(props.themeId);
|
|
const styles = useStyles(theme, props.visible);
|
|
|
|
const [query, setQuery] = useState(props.query);
|
|
const { paused, onOverridePause } = useSearchPaused(query);
|
|
|
|
const globalQueryRef = useRef(props.query);
|
|
globalQueryRef.current = props.query;
|
|
useEffect(() => {
|
|
if (globalQueryRef.current !== query) {
|
|
props.dispatch({
|
|
type: 'SEARCH_QUERY',
|
|
query,
|
|
});
|
|
}
|
|
}, [props.dispatch, query]);
|
|
|
|
const clearButton_press = useCallback(() => {
|
|
setQuery('');
|
|
}, []);
|
|
|
|
const onHighlightedWordsChange = useCallback((words: (ComplexTerm | string)[]) => {
|
|
props.dispatch({
|
|
type: 'SET_HIGHLIGHTED',
|
|
words,
|
|
});
|
|
}, [props.dispatch]);
|
|
|
|
return (
|
|
<AccessibleView style={styles.rootStyle} inert={!props.visible}>
|
|
<ScreenHeader
|
|
title={_('Search')}
|
|
folderPickerOptions={{
|
|
visible: props.noteSelectionEnabled,
|
|
mustSelect: true,
|
|
}}
|
|
showSideMenuButton={false}
|
|
showSearchButton={false}
|
|
/>
|
|
<View style={styles.body}>
|
|
<SearchBar
|
|
themeId={props.themeId}
|
|
autoFocus={props.visible}
|
|
value={query}
|
|
onChangeText={setQuery}
|
|
onSubmitEditing={onOverridePause}
|
|
onClearButtonPress={clearButton_press}
|
|
placeholder={_('Search notes, notebooks...')}
|
|
/>
|
|
<SearchResults
|
|
query={query}
|
|
paused={paused}
|
|
ftsEnabled={props.ftsEnabled}
|
|
onHighlightedWordsChange={onHighlightedWordsChange}
|
|
/>
|
|
</View>
|
|
</AccessibleView>
|
|
);
|
|
};
|
|
|
|
const SearchScreen = connect((state: AppState) => {
|
|
return {
|
|
query: state.searchQuery,
|
|
themeId: state.settings.theme,
|
|
settings: state.settings,
|
|
noteSelectionEnabled: state.noteSelectionEnabled,
|
|
ftsEnabled: state.settings['db.ftsEnabled'],
|
|
};
|
|
})(SearchScreenComponent);
|
|
|
|
export default SearchScreen;
|