feat: Initial clean release of CHM
一些检查失败了
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
一些检查失败了
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
这个提交包含在:
@@ -0,0 +1,129 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { FlatList, View } from 'react-native';
|
||||
import NoteItem from '../../NoteItem';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import useQueuedAsyncEffect from '@joplin/lib/hooks/useQueuedAsyncEffect';
|
||||
import { NoteEntity } from '@joplin/lib/services/database/types';
|
||||
import SearchEngineUtils from '@joplin/lib/services/search/SearchEngineUtils';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import SearchEngine, { ComplexTerm } from '@joplin/lib/services/search/SearchEngine';
|
||||
import { ProgressBar } from 'react-native-paper';
|
||||
import shim from '@joplin/lib/shim';
|
||||
|
||||
interface Props {
|
||||
query: string;
|
||||
paused: boolean;
|
||||
onHighlightedWordsChange: (highlightedWords: (ComplexTerm | string)[])=> void;
|
||||
|
||||
ftsEnabled: number;
|
||||
}
|
||||
|
||||
const useResults = (props: Props) => {
|
||||
const [notes, setNotes] = useState<NoteEntity[]>([]);
|
||||
const [highlightedWord, setHighlightedWord] = useState('');
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const query = props.query;
|
||||
const ftsEnabled = props.ftsEnabled;
|
||||
|
||||
useQueuedAsyncEffect(async (event) => {
|
||||
let notes: NoteEntity[] = [];
|
||||
setIsProcessing(true);
|
||||
try {
|
||||
if (query && !props.paused) {
|
||||
if (ftsEnabled) {
|
||||
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('*')}*`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
const parsedQuery = await SearchEngine.instance().parseQuery(query);
|
||||
const searchTerms = SearchEngine.instance().allParsedQueryTerms(parsedQuery);
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
props.onHighlightedWordsChange(searchTerms);
|
||||
setHighlightedWord(SearchEngine.instance().createQueryFromTerms(searchTerms));
|
||||
setNotes(notes);
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
}
|
||||
}, [query, props.paused, ftsEnabled], { interval: 200 });
|
||||
|
||||
return {
|
||||
notes,
|
||||
highlightedWord,
|
||||
isPending: isProcessing,
|
||||
};
|
||||
};
|
||||
|
||||
const useIsLongRunning = (isPending: boolean) => {
|
||||
const [isLongRunning, setIsLongRunning] = useState(false);
|
||||
const isPendingRef = useRef(isPending);
|
||||
isPendingRef.current = isPending;
|
||||
|
||||
type TimeoutType = ReturnType<typeof shim.setTimeout>;
|
||||
const timeoutRef = useRef<TimeoutType|null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (timeoutRef.current !== null) {
|
||||
shim.clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
|
||||
if (isPending) {
|
||||
const longRunningTimeout = 1000;
|
||||
timeoutRef.current = shim.setTimeout(() => {
|
||||
timeoutRef.current = null;
|
||||
setIsLongRunning(isPendingRef.current);
|
||||
}, longRunningTimeout);
|
||||
} else {
|
||||
setIsLongRunning(false);
|
||||
}
|
||||
}, [isPending]);
|
||||
|
||||
return isLongRunning;
|
||||
};
|
||||
|
||||
const containerStyle = { flex: 1 };
|
||||
|
||||
const SearchResults: React.FC<Props> = props => {
|
||||
const { notes, highlightedWord, isPending } = useResults(props);
|
||||
// Don't show the progress bar immediately, only show if the search
|
||||
// is taking some time.
|
||||
const longRunning = useIsLongRunning(isPending);
|
||||
const progressVisible = longRunning;
|
||||
|
||||
// To have the correct height on web, the progress bar needs to be wrapped:
|
||||
const progressBar = <View aria-hidden={!progressVisible}>
|
||||
<ProgressBar indeterminate={true} visible={progressVisible}/>
|
||||
</View>;
|
||||
|
||||
return (
|
||||
<View style={containerStyle}>
|
||||
{progressBar}
|
||||
<FlatList
|
||||
data={notes}
|
||||
extraData={highlightedWord}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={event => <NoteItem note={event.item} highlightedWord={highlightedWord} />}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchResults;
|
||||
在新工单中引用
屏蔽一个用户