1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00
joplin/packages/app-mobile/components/screens/search.tsx

221 lines
5.8 KiB
TypeScript
Raw Normal View History

2019-07-29 15:43:53 +02:00
const React = require('react');
2019-07-29 15:58:33 +02:00
import { StyleSheet, View, TextInput, FlatList, TouchableHighlight } from 'react-native';
const { connect } = require('react-redux');
import ScreenHeader from '../ScreenHeader';
const Icon = require('react-native-vector-icons/Ionicons').default;
import { _ } from '@joplin/lib/locale';
import Note from '@joplin/lib/models/Note';
2020-11-05 18:58:23 +02:00
const { NoteItem } = require('../note-item.js');
const { BaseScreenComponent } = require('../base-screen');
2020-11-05 18:58:23 +02:00
const { themeStyle } = require('../global-style.js');
const DialogBox = require('react-native-dialogbox').default;
import SearchEngineUtils from '@joplin/lib/services/searchengine/SearchEngineUtils';
import SearchEngine from '@joplin/lib/services/searchengine/SearchEngine';
import { AppState } from '../../utils/types';
2023-12-13 21:24:58 +02:00
import { NoteEntity } from '@joplin/lib/services/database/types';
2017-07-23 00:52:24 +02:00
// We need this to suppress the useless warning
// https://github.com/oblador/react-native-vector-icons/issues/1465
// eslint-disable-next-line no-console
2023-01-04 22:25:09 +02:00
Icon.loadFont().catch((error: any) => { console.info(error); });
2020-02-09 16:51:12 +02:00
2017-07-23 00:52:24 +02:00
class SearchScreenComponent extends BaseScreenComponent {
private state: any = null;
private isMounted_ = false;
private styles_: any = {};
private scheduleSearchTimer_: any = null;
public static navigationOptions() {
return { header: null } as any;
2017-07-23 00:52:24 +02:00
}
public constructor() {
2017-07-23 00:52:24 +02:00
super();
this.state = {
query: '',
notes: [],
};
2017-08-01 19:59:01 +02:00
}
public styles() {
2020-09-15 15:01:07 +02:00
const theme = themeStyle(this.props.themeId);
2017-08-01 19:59:01 +02:00
2020-09-15 15:01:07 +02:00
if (this.styles_[this.props.themeId]) return this.styles_[this.props.themeId];
2017-08-01 19:59:01 +02:00
this.styles_ = {};
const styles: any = {
2017-08-01 19:59:01 +02:00
body: {
flex: 1,
},
searchContainer: {
flexDirection: 'row',
alignItems: 'center',
borderWidth: 1,
borderColor: theme.dividerColor,
2019-07-29 15:43:53 +02:00
},
};
2017-08-01 19:59:01 +02:00
styles.searchTextInput = { ...theme.lineInput };
2017-08-01 19:59:01 +02:00
styles.searchTextInput.paddingLeft = theme.marginLeft;
styles.searchTextInput.flex = 1;
styles.searchTextInput.backgroundColor = theme.backgroundColor;
styles.searchTextInput.color = theme.color;
styles.clearIcon = { ...theme.icon };
2017-08-01 19:59:01 +02:00
styles.clearIcon.color = theme.colorFaded;
styles.clearIcon.paddingRight = theme.marginRight;
styles.clearIcon.backgroundColor = theme.backgroundColor;
2020-09-15 15:01:07 +02:00
this.styles_[this.props.themeId] = StyleSheet.create(styles);
return this.styles_[this.props.themeId];
2017-07-23 00:52:24 +02:00
}
public componentDidMount() {
2017-07-23 00:52:24 +02:00
this.setState({ query: this.props.query });
void this.refreshSearch(this.props.query);
2017-07-23 00:52:24 +02:00
this.isMounted_ = true;
}
public componentWillUnmount() {
2017-07-23 00:52:24 +02:00
this.isMounted_ = false;
}
private clearButton_press() {
2017-07-23 00:52:24 +02:00
this.props.dispatch({
type: 'SEARCH_QUERY',
query: '',
});
2019-02-03 18:57:29 +02:00
this.setState({ query: '' });
void this.refreshSearch('');
2017-07-23 00:52:24 +02:00
}
public async refreshSearch(query: string = null) {
if (!this.props.visible) return;
2023-12-13 21:24:58 +02:00
let notes: NoteEntity[] = [];
if (query) {
if (this.props.settings['db.ftsEnabled']) {
2023-12-13 21:24:58 +02:00
const r = await SearchEngineUtils.notesForQuery(query, true, { appendWildCards: true });
notes = r.notes;
} else {
const p = query.split(' ');
const temp = [];
for (let i = 0; i < p.length; i++) {
const t = p[i].trim();
if (!t) continue;
temp.push(t);
}
notes = await Note.previews(null, {
anywherePattern: `*${temp.join('*')}*`,
});
}
}
2017-07-23 00:52:24 +02:00
if (!this.isMounted_) return;
const parsedQuery = await SearchEngine.instance().parseQuery(query);
const highlightedWords = SearchEngine.instance().allParsedQueryTerms(parsedQuery);
this.props.dispatch({
type: 'SET_HIGHLIGHTED',
words: highlightedWords,
});
2017-07-23 00:52:24 +02:00
this.setState({ notes: notes });
}
public scheduleSearch() {
if (this.scheduleSearchTimer_) clearTimeout(this.scheduleSearchTimer_);
this.scheduleSearchTimer_ = setTimeout(() => {
this.scheduleSearchTimer_ = null;
void this.refreshSearch(this.state.query);
}, 200);
}
private searchTextInput_changeText(text: string) {
2017-07-23 00:52:24 +02:00
this.setState({ query: text });
this.props.dispatch({
type: 'SEARCH_QUERY',
query: text,
});
this.scheduleSearch();
2017-07-23 00:52:24 +02:00
}
public render() {
2017-07-25 22:24:30 +02:00
if (!this.isMounted_) return null;
2020-09-15 15:01:07 +02:00
const theme = themeStyle(this.props.themeId);
const rootStyle = {
flex: 1,
backgroundColor: theme.backgroundColor,
2019-07-29 15:43:53 +02:00
};
if (!this.props.visible) {
rootStyle.flex = 0.001; // This is a bit of a hack but it seems to work fine - it makes the component invisible but without unmounting it
}
const thisComponent = this;
2017-07-23 00:52:24 +02:00
return (
<View style={rootStyle}>
<ScreenHeader
title={_('Search')}
parentComponent={thisComponent}
folderPickerOptions={{
enabled: this.props.noteSelectionEnabled,
mustSelect: true,
}}
showSideMenuButton={false}
showSearchButton={false}
/>
2017-08-01 19:59:01 +02:00
<View style={this.styles().body}>
<View style={this.styles().searchContainer}>
2017-07-23 00:52:24 +02:00
<TextInput
2017-08-01 19:59:01 +02:00
style={this.styles().searchTextInput}
autoFocus={this.props.visible}
2019-07-29 15:43:53 +02:00
underlineColorAndroid="#ffffff00"
onChangeText={text => this.searchTextInput_changeText(text)}
2017-07-23 00:52:24 +02:00
value={this.state.query}
selectionColor={theme.textSelectionColor}
keyboardAppearance={theme.keyboardAppearance}
2017-07-23 00:52:24 +02:00
/>
<TouchableHighlight
onPress={() => this.clearButton_press()}
accessibilityLabel={_('Clear')}
>
<Icon name="close-circle" style={this.styles().clearIcon} />
2017-07-23 00:52:24 +02:00
</TouchableHighlight>
</View>
<FlatList data={this.state.notes} keyExtractor={(item) => item.id} renderItem={event => <NoteItem note={event.item} />} />
2017-07-23 00:52:24 +02:00
</View>
2019-07-29 15:43:53 +02:00
<DialogBox
ref={(dialogbox: any) => {
2019-07-29 15:43:53 +02:00
this.dialogbox = dialogbox;
}}
/>
2017-07-23 00:52:24 +02:00
</View>
);
}
}
const SearchScreen = connect((state: AppState) => {
2019-07-29 15:43:53 +02:00
return {
query: state.searchQuery,
2020-09-15 15:01:07 +02:00
themeId: state.settings.theme,
2019-07-29 15:43:53 +02:00
settings: state.settings,
noteSelectionEnabled: state.noteSelectionEnabled,
};
})(SearchScreenComponent);
2017-07-23 00:52:24 +02:00
2019-07-29 15:43:53 +02:00
module.exports = { SearchScreen };