Compare commits

...

14 Commits

Author SHA1 Message Date
Mattermod
edbac66554 Merge branch 'main' into MM-44517-commonmark 2022-12-23 14:43:19 +02:00
Jason Frerich
fda124b52b add searchPatterns prop 2022-11-07 10:18:38 -06:00
Jason Frerich
4d293707eb fix post merge 2022-11-07 09:43:20 -06:00
Jason Frerich
c12803d740 Merge branch 'gekidou' into MM-44517-commonmark 2022-11-07 09:36:18 -06:00
Jason Frerich
113e2a1721 Merge branch 'gekidou' into MM-44517-commonmark 2022-08-21 11:43:43 -05:00
Jason Frerich
eac2477239 revert theme to keep the PR scoped 2022-08-19 16:04:43 -05:00
Jason Frerich
39b6a6d65c when resuming, the walker, entering should be true 2022-08-19 16:00:04 -05:00
Jason Frerich
eb82d6f450 Merge branch 'gekidou' into MM-44517-commonmark 2022-08-19 11:39:47 -05:00
Jason Frerich
780a0d8b25 needed to match the results from the webapp 2022-07-27 17:57:47 -05:00
Jason Frerich
c48af776a2 remove type 2022-07-27 17:44:14 -05:00
Jason Frerich
508e7b1b1c call the function directly. no useEffect needed 2022-07-27 17:42:51 -05:00
Jason Frerich
dd78baddc6 get theme from useTheme 2022-07-27 17:38:04 -05:00
Jason Frerich
af3a287730 wip 2022-07-27 17:25:45 -05:00
Jason Frerich
19020ec615 wip 2022-07-27 17:24:01 -05:00
4 changed files with 105 additions and 3 deletions

View File

@@ -160,6 +160,103 @@ export function highlightMentions(ast: Node, mentionKeys: UserMentionKey[]) {
return ast;
}
const puncStart = /^[^\p{L}\d\s#]+/u;
const puncEnd = /[^\p{L}\d\s]+$/u;
export function parseSearchTerms(searchTerm: string): string[] {
let terms = [];
let termString = searchTerm;
while (termString) {
let captured;
// check for a quoted string
captured = (/^"([^"]*)"/).exec(termString);
if (captured) {
termString = termString.substring(captured[0].length);
if (captured[1].length > 0) {
terms.push(captured[1]);
}
continue;
}
// check for a search flag (and don't add it to terms)
captured = (/^-?(?:in|from|channel|on|before|after): ?\S+/).exec(termString);
if (captured) {
termString = termString.substring(captured[0].length);
continue;
}
// capture at mentions differently from the server so we can highlight them with the preceeding at sign
captured = (/^@[a-z0-9.-_]+\b/).exec(termString);
if (captured) {
termString = termString.substring(captured[0].length);
terms.push(captured[0]);
continue;
}
// capture any plain text up until the next quote or search flag
captured = (/^.+?(?=(?:\b|\B-)(?:in:|from:|channel:|on:|before:|after:)|"|$)/).exec(termString);
if (captured) {
termString = termString.substring(captured[0].length);
// break the text up into words based on how the server splits them in SqlPostStore.SearchPosts and then discard empty terms
terms.push(
...captured[0].split(/[ <>+()~@]/).filter((term) => Boolean(term)),
);
continue;
}
// we should never reach this point since at least one of the regexes should match something in the remaining text
throw new Error(
'Infinite loop in search term parsing: "' + termString + '"',
);
}
// remove punctuation from each term
terms = terms.map((term) => {
term.replace(puncStart, '');
if (term.charAt(term.length - 1) !== '*') {
term.replace(puncEnd, '');
}
return term;
});
return terms;
}
function convertSearchTermToRegex(term: string): SearchPattern {
let pattern;
if (cjkPattern.test(term)) {
// term contains Chinese, Japanese, or Korean characters so don't mark word boundaries
pattern = '()(' + escapeRegex(term.replace(/\*/g, '')) + ')';
} else if ((/[^\s][*]$/).test(term)) {
pattern = '\\b()(' + escapeRegex(term.substring(0, term.length - 1)) + ')';
} else if (term.startsWith('@') || term.startsWith('#')) {
// needs special handling of the first boundary because a word boundary doesn't work before a symbol
pattern = '(\\W|^)(' + escapeRegex(term) + ')\\b';
} else {
pattern = '\\b()(' + escapeRegex(term) + ')\\b';
}
return {
pattern: new RegExp(pattern, 'gi'),
term,
};
}
export function searchTermsToPatterns(terms: string) {
const searchPatterns = parseSearchTerms(terms || '').map(convertSearchTermToRegex).sort((a, b) => {
return b.term.length - a.term.length;
});
return searchPatterns;
}
export function mentionKeysToPatterns(mentionKeys: UserMentionKey[]) {
return mentionKeys.filter((mention) => mention.key.trim() !== '').map((mention) => {
const flags = mention.caseSensitive ? '' : 'i';
@@ -198,7 +295,7 @@ export function highlightSearchPatterns(ast: Node, searchPatterns: SearchPattern
// Resume processing on the next node after the match node which may include any remaining text
// that was part of this one
walker.resumeAt(matchNode, false);
walker.resumeAt(matchNode, true);
}
}

View File

@@ -9,10 +9,12 @@ import Post from '@components/post_list/post';
import ChannelInfo from './channel_info';
import type PostModel from '@typings/database/models/servers/post';
import type {SearchPattern} from '@typings/global/markdown';
type Props = {
isCRTEnabled: boolean;
post: PostModel;
searchPatterns?: SearchPattern[];
location: string;
testID?: string;
}
@@ -28,7 +30,7 @@ const styles = StyleSheet.create({
},
});
function PostWithChannelInfo({isCRTEnabled, post, location, testID}: Props) {
function PostWithChannelInfo({isCRTEnabled, post, location, searchPatterns, testID}: Props) {
return (
<View style={styles.container}>
<ChannelInfo
@@ -41,6 +43,7 @@ function PostWithChannelInfo({isCRTEnabled, post, location, testID}: Props) {
post={post}
location={location}
highlightPinnedOrSaved={false}
searchPatterns={searchPatterns}
skipPinnedHeader={true}
skipSavedHeader={true}
shouldRenderReplyButton={false}

View File

@@ -4,6 +4,7 @@
import React, {useCallback, useMemo} from 'react';
import {FlatList, ListRenderItemInfo, StyleProp, ViewStyle} from 'react-native';
import {searchTermsToPatterns} from '@components/markdown/transform';
import NoResultsWithTerm from '@components/no_results_with_term';
import DateSeparator from '@components/post_list/date_separator';
import PostWithChannelInfo from '@components/post_with_channel_info';
@@ -48,6 +49,7 @@ const PostResults = ({
return (
<PostWithChannelInfo
location={Screens.SEARCH}
searchPatterns={searchTermsToPatterns(searchValue)}
post={item}
testID='search_results.post_list'
/>

View File

@@ -67,7 +67,7 @@ const getSearchParams = (terms: string, filterValue?: FileFilter) => {
const extensionTerms = fileExtensions ? ' ' + fileExtensions : '';
return {
terms: terms + extensionTerms,
is_or_search: true,
is_or_search: false,
};
};