forked from Ivasoft/mattermost-mobile
Compare commits
14 Commits
test1.0.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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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'
|
||||
/>
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user