forked from Ivasoft/mattermost-mobile
Compare commits
14 Commits
release-2.
...
MM-44517-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edbac66554 | ||
|
|
fda124b52b | ||
|
|
4d293707eb | ||
|
|
c12803d740 | ||
|
|
113e2a1721 | ||
|
|
eac2477239 | ||
|
|
39b6a6d65c | ||
|
|
eb82d6f450 | ||
|
|
780a0d8b25 | ||
|
|
c48af776a2 | ||
|
|
508e7b1b1c | ||
|
|
dd78baddc6 | ||
|
|
af3a287730 | ||
|
|
19020ec615 |
@@ -160,6 +160,103 @@ export function highlightMentions(ast: Node, mentionKeys: UserMentionKey[]) {
|
|||||||
return ast;
|
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[]) {
|
export function mentionKeysToPatterns(mentionKeys: UserMentionKey[]) {
|
||||||
return mentionKeys.filter((mention) => mention.key.trim() !== '').map((mention) => {
|
return mentionKeys.filter((mention) => mention.key.trim() !== '').map((mention) => {
|
||||||
const flags = mention.caseSensitive ? '' : 'i';
|
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
|
// Resume processing on the next node after the match node which may include any remaining text
|
||||||
// that was part of this one
|
// that was part of this one
|
||||||
walker.resumeAt(matchNode, false);
|
walker.resumeAt(matchNode, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import Post from '@components/post_list/post';
|
|||||||
import ChannelInfo from './channel_info';
|
import ChannelInfo from './channel_info';
|
||||||
|
|
||||||
import type PostModel from '@typings/database/models/servers/post';
|
import type PostModel from '@typings/database/models/servers/post';
|
||||||
|
import type {SearchPattern} from '@typings/global/markdown';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isCRTEnabled: boolean;
|
isCRTEnabled: boolean;
|
||||||
post: PostModel;
|
post: PostModel;
|
||||||
|
searchPatterns?: SearchPattern[];
|
||||||
location: string;
|
location: string;
|
||||||
testID?: 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 (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ChannelInfo
|
<ChannelInfo
|
||||||
@@ -41,6 +43,7 @@ function PostWithChannelInfo({isCRTEnabled, post, location, testID}: Props) {
|
|||||||
post={post}
|
post={post}
|
||||||
location={location}
|
location={location}
|
||||||
highlightPinnedOrSaved={false}
|
highlightPinnedOrSaved={false}
|
||||||
|
searchPatterns={searchPatterns}
|
||||||
skipPinnedHeader={true}
|
skipPinnedHeader={true}
|
||||||
skipSavedHeader={true}
|
skipSavedHeader={true}
|
||||||
shouldRenderReplyButton={false}
|
shouldRenderReplyButton={false}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import React, {useCallback, useMemo} from 'react';
|
import React, {useCallback, useMemo} from 'react';
|
||||||
import {FlatList, ListRenderItemInfo, StyleProp, ViewStyle} from 'react-native';
|
import {FlatList, ListRenderItemInfo, StyleProp, ViewStyle} from 'react-native';
|
||||||
|
|
||||||
|
import {searchTermsToPatterns} from '@components/markdown/transform';
|
||||||
import NoResultsWithTerm from '@components/no_results_with_term';
|
import NoResultsWithTerm from '@components/no_results_with_term';
|
||||||
import DateSeparator from '@components/post_list/date_separator';
|
import DateSeparator from '@components/post_list/date_separator';
|
||||||
import PostWithChannelInfo from '@components/post_with_channel_info';
|
import PostWithChannelInfo from '@components/post_with_channel_info';
|
||||||
@@ -48,6 +49,7 @@ const PostResults = ({
|
|||||||
return (
|
return (
|
||||||
<PostWithChannelInfo
|
<PostWithChannelInfo
|
||||||
location={Screens.SEARCH}
|
location={Screens.SEARCH}
|
||||||
|
searchPatterns={searchTermsToPatterns(searchValue)}
|
||||||
post={item}
|
post={item}
|
||||||
testID='search_results.post_list'
|
testID='search_results.post_list'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const getSearchParams = (terms: string, filterValue?: FileFilter) => {
|
|||||||
const extensionTerms = fileExtensions ? ' ' + fileExtensions : '';
|
const extensionTerms = fileExtensions ? ' ' + fileExtensions : '';
|
||||||
return {
|
return {
|
||||||
terms: terms + extensionTerms,
|
terms: terms + extensionTerms,
|
||||||
is_or_search: true,
|
is_or_search: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user