1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Mobile: Add support for realtime search

This commit is contained in:
Laurent Cozic 2022-12-30 14:12:07 +00:00
parent a189b2eff0
commit 767213cdc1
7 changed files with 72 additions and 52 deletions

View File

@ -1005,6 +1005,9 @@ packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js.map
packages/app-mobile/components/screens/encryption-config.d.ts
packages/app-mobile/components/screens/encryption-config.js
packages/app-mobile/components/screens/encryption-config.js.map
packages/app-mobile/components/screens/search.d.ts
packages/app-mobile/components/screens/search.js
packages/app-mobile/components/screens/search.js.map
packages/app-mobile/components/side-menu-content.d.ts
packages/app-mobile/components/side-menu-content.js
packages/app-mobile/components/side-menu-content.js.map
@ -1878,6 +1881,9 @@ packages/lib/services/searchengine/filterParser.js.map
packages/lib/services/searchengine/filterParser.test.d.ts
packages/lib/services/searchengine/filterParser.test.js
packages/lib/services/searchengine/filterParser.test.js.map
packages/lib/services/searchengine/gotoAnythingStyleQuery.d.ts
packages/lib/services/searchengine/gotoAnythingStyleQuery.js
packages/lib/services/searchengine/gotoAnythingStyleQuery.js.map
packages/lib/services/searchengine/queryBuilder.d.ts
packages/lib/services/searchengine/queryBuilder.js
packages/lib/services/searchengine/queryBuilder.js.map

6
.gitignore vendored
View File

@ -993,6 +993,9 @@ packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js.map
packages/app-mobile/components/screens/encryption-config.d.ts
packages/app-mobile/components/screens/encryption-config.js
packages/app-mobile/components/screens/encryption-config.js.map
packages/app-mobile/components/screens/search.d.ts
packages/app-mobile/components/screens/search.js
packages/app-mobile/components/screens/search.js.map
packages/app-mobile/components/side-menu-content.d.ts
packages/app-mobile/components/side-menu-content.js
packages/app-mobile/components/side-menu-content.js.map
@ -1866,6 +1869,9 @@ packages/lib/services/searchengine/filterParser.js.map
packages/lib/services/searchengine/filterParser.test.d.ts
packages/lib/services/searchengine/filterParser.test.js
packages/lib/services/searchengine/filterParser.test.js.map
packages/lib/services/searchengine/gotoAnythingStyleQuery.d.ts
packages/lib/services/searchengine/gotoAnythingStyleQuery.js
packages/lib/services/searchengine/gotoAnythingStyleQuery.js.map
packages/lib/services/searchengine/queryBuilder.d.ts
packages/lib/services/searchengine/queryBuilder.js
packages/lib/services/searchengine/queryBuilder.js.map

View File

@ -8,6 +8,7 @@ const { connect } = require('react-redux');
const { _ } = require('@joplin/lib/locale');
const { themeStyle } = require('@joplin/lib/theme');
import SearchEngine from '@joplin/lib/services/searchengine/SearchEngine';
import gotoAnythingStyleQuery from '@joplin/lib/services/searchengine/gotoAnythingStyleQuery';
import BaseModel from '@joplin/lib/BaseModel';
import Tag from '@joplin/lib/models/Tag';
import Folder from '@joplin/lib/models/Folder';
@ -242,19 +243,6 @@ class Dialog extends React.PureComponent<Props, State> {
}, 100);
}
makeSearchQuery(query: string) {
const output = [];
const splitted = query.split(' ');
for (let i = 0; i < splitted.length; i++) {
const s = splitted[i].trim();
if (!s) continue;
output.push(`${s}*`);
}
return output.join(' ');
}
async keywords(searchQuery: string) {
const parsedQuery = await SearchEngine.instance().parseQuery(searchQuery);
return SearchEngine.instance().allParsedQueryTerms(parsedQuery);
@ -321,7 +309,7 @@ class Dialog extends React.PureComponent<Props, State> {
}
} else { // Note TITLE or BODY
listType = BaseModel.TYPE_NOTE;
searchQuery = this.makeSearchQuery(this.state.query);
searchQuery = gotoAnythingStyleQuery(this.state.query);
results = await SearchEngine.instance().search(searchQuery);
resultsInBody = !!results.find((row: any) => row.fields.includes('body'));

View File

@ -1,7 +1,7 @@
const React = require('react');
import { connect } from 'react-redux';
import { PureComponent, Component } from 'react';
import { PureComponent } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image, ScrollView, Dimensions, ViewStyle } from 'react-native';
const Icon = require('react-native-vector-icons/Ionicons').default;
const { BackButtonService } = require('../services/back-button.js');
@ -46,7 +46,7 @@ type DispatchCommandType=(event: { type: string })=> void;
interface ScreenHeaderProps {
selectedNoteIds: string[];
noteSelectionEnabled: boolean;
parentComponent: Component;
parentComponent: any;
showUndoButton: boolean;
undoButtonDisabled?: boolean;
showRedoButton: boolean;
@ -55,8 +55,8 @@ interface ScreenHeaderProps {
folders: FolderEntity[];
folderPickerOptions?: {
enabled: boolean;
selectedFolderId: string;
onValueChange: OnValueChangedListener;
selectedFolderId?: string;
onValueChange?: OnValueChangedListener;
mustSelect?: boolean;
};

View File

@ -1,23 +1,31 @@
const React = require('react');
const { StyleSheet, View, TextInput, FlatList, TouchableHighlight } = require('react-native');
import { StyleSheet, View, TextInput, FlatList, TouchableHighlight } from 'react-native';
const { connect } = require('react-redux');
const { ScreenHeader } = require('../ScreenHeader');
import ScreenHeader from '../ScreenHeader';
const Icon = require('react-native-vector-icons/Ionicons').default;
const { _ } = require('@joplin/lib/locale');
const Note = require('@joplin/lib/models/Note').default;
import { _ } from '@joplin/lib/locale';
import Note from '@joplin/lib/models/Note';
import gotoAnythingStyleQuery from '@joplin/lib/services/searchengine/gotoAnythingStyleQuery';
const { NoteItem } = require('../note-item.js');
const { BaseScreenComponent } = require('../base-screen.js');
const { themeStyle } = require('../global-style.js');
const DialogBox = require('react-native-dialogbox').default;
const SearchEngineUtils = require('@joplin/lib/services/searchengine/SearchEngineUtils').default;
const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine').default;
import SearchEngineUtils from '@joplin/lib/services/searchengine/SearchEngineUtils';
import SearchEngine from '@joplin/lib/services/searchengine/SearchEngine';
import { AppState } from '../../utils/types';
Icon.loadFont();
class SearchScreenComponent extends BaseScreenComponent {
private state: any = null;
private isMounted_ = false;
private styles_: any = {};
private scheduleSearchTimer_: any = null;
static navigationOptions() {
return { header: null };
return { header: null } as any;
}
constructor() {
@ -26,8 +34,6 @@ class SearchScreenComponent extends BaseScreenComponent {
query: '',
notes: [],
};
this.isMounted_ = false;
this.styles_ = {};
}
styles() {
@ -36,7 +42,7 @@ class SearchScreenComponent extends BaseScreenComponent {
if (this.styles_[this.props.themeId]) return this.styles_[this.props.themeId];
this.styles_ = {};
const styles = {
const styles: any = {
body: {
flex: 1,
},
@ -65,7 +71,7 @@ class SearchScreenComponent extends BaseScreenComponent {
componentDidMount() {
this.setState({ query: this.props.query });
this.refreshSearch(this.props.query);
void this.refreshSearch(this.props.query);
this.isMounted_ = true;
}
@ -73,19 +79,6 @@ class SearchScreenComponent extends BaseScreenComponent {
this.isMounted_ = false;
}
searchTextInput_submit() {
const query = this.state.query.trim();
if (!query) return;
this.props.dispatch({
type: 'SEARCH_QUERY',
query: query,
});
this.setState({ query: query });
this.refreshSearch(query);
}
clearButton_press() {
this.props.dispatch({
type: 'SEARCH_QUERY',
@ -93,13 +86,13 @@ class SearchScreenComponent extends BaseScreenComponent {
});
this.setState({ query: '' });
this.refreshSearch('');
void this.refreshSearch('');
}
async refreshSearch(query = null) {
async refreshSearch(query: string = null) {
if (!this.props.visible) return;
query = query === null ? this.state.query.trim : query.trim();
query = gotoAnythingStyleQuery(query);
let notes = [];
@ -134,8 +127,24 @@ class SearchScreenComponent extends BaseScreenComponent {
this.setState({ notes: notes });
}
searchTextInput_changeText(text) {
scheduleSearch() {
if (this.scheduleSearchTimer_) clearTimeout(this.scheduleSearchTimer_);
this.scheduleSearchTimer_ = setTimeout(() => {
this.scheduleSearchTimer_ = null;
void this.refreshSearch(this.state.query);
}, 200);
}
searchTextInput_changeText(text: string) {
this.setState({ query: text });
this.props.dispatch({
type: 'SEARCH_QUERY',
query: text,
});
this.scheduleSearch();
}
render() {
@ -172,9 +181,6 @@ class SearchScreenComponent extends BaseScreenComponent {
style={this.styles().searchTextInput}
autoFocus={this.props.visible}
underlineColorAndroid="#ffffff00"
onSubmitEditing={() => {
this.searchTextInput_submit();
}}
onChangeText={text => this.searchTextInput_changeText(text)}
value={this.state.query}
selectionColor={theme.textSelectionColor}
@ -188,7 +194,7 @@ class SearchScreenComponent extends BaseScreenComponent {
<FlatList data={this.state.notes} keyExtractor={(item) => item.id} renderItem={event => <NoteItem note={event.item} />} />
</View>
<DialogBox
ref={dialogbox => {
ref={(dialogbox: any) => {
this.dialogbox = dialogbox;
}}
/>
@ -197,7 +203,7 @@ class SearchScreenComponent extends BaseScreenComponent {
}
}
const SearchScreen = connect(state => {
const SearchScreen = connect((state: AppState) => {
return {
query: state.searchQuery,
themeId: state.settings.theme,

View File

@ -3,7 +3,7 @@ import Note from '../../models/Note';
import Setting from '../../models/Setting';
export default class SearchEngineUtils {
static async notesForQuery(query: string, applyUserSettings: boolean, options: any = null, searchEngine: SearchEngine = null) {
public static async notesForQuery(query: string, applyUserSettings: boolean, options: any = null, searchEngine: SearchEngine = null) {
if (!options) options = {};
if (!searchEngine) {

View File

@ -0,0 +1,14 @@
export default (query: string) => {
if (!query) return '';
const output = [];
const splitted = query.split(' ');
for (let i = 0; i < splitted.length; i++) {
const s = splitted[i].trim();
if (!s) continue;
output.push(`${s}*`);
}
return output.join(' ');
};