Merge branch 'main' into gm_conversion_on_reconnect_handler

This commit is contained in:
harshil Sharma 2023-11-24 11:35:07 +05:30
commit d31887bf77
23 changed files with 712 additions and 131 deletions

View file

@ -29,11 +29,11 @@ import MarkdownTable from './markdown_table';
import MarkdownTableCell, {type MarkdownTableCellProps} from './markdown_table_cell';
import MarkdownTableImage from './markdown_table_image';
import MarkdownTableRow, {type MarkdownTableRowProps} from './markdown_table_row';
import {addListItemIndices, combineTextNodes, highlightMentions, highlightSearchPatterns, parseTaskLists, pullOutImages} from './transform';
import {addListItemIndices, combineTextNodes, highlightMentions, highlightWithoutNotification, highlightSearchPatterns, parseTaskLists, pullOutImages} from './transform';
import type {
MarkdownAtMentionRenderer, MarkdownBaseRenderer, MarkdownBlockStyles, MarkdownChannelMentionRenderer,
MarkdownEmojiRenderer, MarkdownImageRenderer, MarkdownLatexRenderer, MarkdownTextStyles, SearchPattern, UserMentionKey,
MarkdownEmojiRenderer, MarkdownImageRenderer, MarkdownLatexRenderer, MarkdownTextStyles, SearchPattern, UserMentionKey, HighlightWithoutNotificationKey,
} from '@typings/global/markdown';
type MarkdownProps = {
@ -55,6 +55,7 @@ type MarkdownProps = {
disableTables?: boolean;
enableLatex: boolean;
enableInlineLatex: boolean;
highlightKeys?: HighlightWithoutNotificationKey[];
imagesMetadata?: Record<string, PostImage | undefined>;
isEdited?: boolean;
isReplyPost?: boolean;
@ -133,7 +134,7 @@ const Markdown = ({
disableCodeBlock, disableGallery, disableHashtags, disableHeading, disableTables,
enableInlineLatex, enableLatex, maxNodes,
imagesMetadata, isEdited, isReplyPost, isSearchResult, layoutHeight, layoutWidth,
location, mentionKeys, minimumHashtagLength = 3, onPostPress, postId, searchPatterns,
location, mentionKeys, highlightKeys, minimumHashtagLength = 3, onPostPress, postId, searchPatterns,
textStyles = {}, theme, value = '', baseParagraphStyle, onLinkLongPress,
}: MarkdownProps) => {
const style = getStyleSheet(theme);
@ -578,6 +579,7 @@ const Markdown = ({
mention_highlight: Renderer.forwardChildren,
search_highlight: Renderer.forwardChildren,
highlight_without_notification: Renderer.forwardChildren,
checkbox: renderCheckbox,
editedIndicator: renderEditedIndicator,
@ -604,10 +606,12 @@ const Markdown = ({
if (mentionKeys) {
ast = highlightMentions(ast, mentionKeys);
}
if (highlightKeys) {
ast = highlightWithoutNotification(ast, highlightKeys);
}
if (searchPatterns) {
ast = highlightSearchPatterns(ast, searchPatterns);
}
if (isEdited) {
const editIndicatorNode = new Node('edited_indicator');
if (ast.lastChild && ['heading', 'paragraph'].includes(ast.lastChild.type)) {

View file

@ -13,6 +13,8 @@ import {
highlightTextNode,
mentionKeysToPatterns,
pullOutImages,
highlightWithoutNotification,
highlightKeysToPatterns,
} from '@components/markdown/transform';
import {logError} from '@utils/log';
@ -2602,7 +2604,7 @@ describe('Components.Markdown.transform', () => {
}
});
describe('getFirstMention', () => {
describe('getFirstMention with mentionKeysToPatterns', () => {
const tests = [{
name: 'no mention keys',
input: 'apple banana orange',
@ -2801,6 +2803,461 @@ describe('Components.Markdown.transform', () => {
});
}
});
describe('highlightWithoutNotification', () => {
const tests = [{
name: 'no highlights',
input: 'Cant put down an anti gravity book',
highlightKeys: [],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [{
type: 'text',
literal: 'Cant put down an anti gravity book',
}],
}],
},
}, {
name: 'key bigger than input',
input: 'incredible',
highlightKeys: [{key: 'incredible and industructable'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [{
type: 'text',
literal: 'incredible',
}],
}],
},
}, {
name: 'key part of the word',
input: 'Sesquipedalian',
highlightKeys: [{key: 'quipedalian'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [{
type: 'text',
literal: 'Sesquipedalian',
}],
}],
},
}, {
name: 'word part of key',
input: 'floccinauc',
highlightKeys: [{key: 'floccinaucinihilipilification'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [{
type: 'text',
literal: 'floccinauc',
}],
}],
},
}, {
name: 'a word highlight',
input: 'Cant put down an anti gravity book',
highlightKeys: [{key: 'anti'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: 'Cant put down an ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'anti',
}],
},
{
type: 'text',
literal: ' gravity book',
},
],
}],
},
}, {
name: 'a sentence highlight',
input: 'Cant put down an anti gravity book',
highlightKeys: [{key: 'anti gravity'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: 'Cant put down an ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'anti gravity',
}],
},
{
type: 'text',
literal: ' book',
},
],
}],
},
}, {
name: 'insensitive keywords',
input: 'Cant put down an anti gravity book',
highlightKeys: [{key: 'dOwN'}, {key: 'Anti'}, {key: 'BOOK'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: 'Cant put ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'down',
}],
},
{
type: 'text',
literal: ' an ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'anti',
}],
},
{
type: 'text',
literal: ' gravity ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'book',
}],
},
],
}],
},
}, {
name: 'insensitive keywords',
input: 'Cant put down an anti gravity book',
highlightKeys: [{key: 'dOwN'}, {key: 'Anti'}, {key: 'BOOK'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: 'Cant put ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'down',
}],
},
{
type: 'text',
literal: ' an ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'anti',
}],
},
{
type: 'text',
literal: ' gravity ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'book',
}],
},
],
}],
},
}, {
name: 'words with characters surrounding them',
input: 'peace& ^peace -peace-',
highlightKeys: [{key: 'PEACE'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'peace',
}],
},
{
type: 'text',
literal: '& ^',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'peace',
}],
},
{
type: 'text',
literal: ' -',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'peace',
}],
},
{
type: 'text',
literal: '-',
},
],
}],
},
}, {
name: 'input in code block',
input: '```\nTurning it off and\non\n```',
highlightKeys: [{key: 'words'}],
expected: {
type: 'document',
children: [{
type: 'code_block',
literal: 'Turning it off and\non\n',
}],
},
}, {
name: 'key in bold',
input: 'Actions speak **louder** than words',
highlightKeys: [{key: 'louder'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [{
type: 'text',
literal: 'Actions speak ',
}, {
type: 'strong',
children: [{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'louder',
}],
}],
}, {
type: 'text',
literal: ' than words',
}],
}],
},
}, {
name: 'key in italic',
input: 'Actions speak *louder* than words',
highlightKeys: [{key: 'louder'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [{
type: 'text',
literal: 'Actions speak ',
}, {
type: 'emph',
children: [{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'louder',
}],
}],
}, {
type: 'text',
literal: ' than words',
}],
}],
},
}, {
name: 'key in heading',
input: '### Actions speak louder than words',
highlightKeys: [{key: 'Actions'}],
expected: {
type: 'document',
children: [{
type: 'heading',
level: 3,
children: [
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'Actions',
}],
},
{
type: 'text',
literal: ' speak louder than words',
}],
}],
},
}, {
name: 'Do not mention partial keys',
input: 'Adding more memory wont help @bob',
highlightKeys: [{key: 'bob'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [{
type: 'text',
literal: 'Adding more memory wont help ',
}, {
type: 'at_mention',
_mentionName: 'bob',
}],
}],
},
}, {
name: 'CJK word highlight',
input: '我确实喜欢我的同事。',
highlightKeys: [{key: '喜欢'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: '我确实',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: '喜欢',
}],
},
{
type: 'text',
literal: '我的同事。',
},
],
}],
},
}];
for (const test of tests) {
it(test.name, () => {
const input = combineTextNodes(parser.parse(test.input));
const expected = makeAst(test.expected);
const actual = highlightWithoutNotification(input, test.highlightKeys);
assert.ok(verifyAst(actual));
assert.deepStrictEqual(stripUnusedFields(actual), stripUnusedFields(expected));
});
}
});
describe('getFirstMatch with highlightKeysToPatterns', () => {
const tests = [{
name: 'text with space before',
input: ' lol',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with space afterwards and before',
input: ' Lol ',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with a non word character before (?)',
input: '?Lol',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with a non word character before (.)',
input: '?Lol',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with a non word character before (_)',
input: '?Lol',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with non word character after (.)',
input: 'Lol.',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 0, length: 3},
},
{
name: 'text with non word character after (?)',
input: 'Lol?',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 0, length: 3},
},
{
name: 'text with non word character after (_)',
input: 'Lol_',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 0, length: 3},
},
{
name: 'text with non word character before and after',
input: '?Lol?',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
}];
for (const test of tests) {
it(test.name, () => {
const actual = getFirstMatch(test.input, test.patterns);
assert.deepStrictEqual(actual, test.expected);
});
}
});
});
// Testing and debugging functions

View file

@ -5,7 +5,7 @@ import {Node, type NodeType} from 'commonmark';
import {escapeRegex} from '@utils/markdown';
import type {SearchPattern, UserMentionKey} from '@typings/global/markdown';
import type {HighlightWithoutNotificationKey, SearchPattern, UserMentionKey} from '@typings/global/markdown';
/* eslint-disable no-underscore-dangle */
@ -174,6 +174,55 @@ export function mentionKeysToPatterns(mentionKeys: UserMentionKey[]) {
});
}
export function highlightWithoutNotification(ast: Node, highlightKeys: HighlightWithoutNotificationKey[]) {
const walker = ast.walker();
const patterns = highlightKeysToPatterns(highlightKeys);
let e;
while ((e = walker.next())) {
if (!e.entering) {
continue;
}
const node = e.node;
if (node.type === 'text' && node.literal) {
const {index, length} = getFirstMatch(node.literal, patterns);
// If the text node doesn't match any of the patterns, skip the loop
if (index === -1) {
continue;
}
const matchNode = highlightTextNode(node, index, index + length, 'highlight_without_notification');
// 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);
}
}
return ast;
}
export function highlightKeysToPatterns(highlightKeys: HighlightWithoutNotificationKey[]) {
if (highlightKeys.length === 0) {
return [];
}
return highlightKeys.
filter((highlight) => highlight.key.trim() !== '').
sort((a, b) => b.key.length - a.key.length).
map(({key}) => {
if (cjkPattern.test(key)) {
// If the key contains Chinese, Japanese, Korean or Russian characters, don't mark word boundaries
return new RegExp(`${escapeRegex(key)}`, 'gi');
}
// If the key contains only English characters, mark word boundaries
return new RegExp(`(^|\\b)(${escapeRegex(key)})(?=_*\\b)`, 'gi');
});
}
export function highlightSearchPatterns(ast: Node, searchPatterns: SearchPattern[]) {
const walker = ast.walker();
@ -212,14 +261,22 @@ export function getFirstMatch(str: string, patterns: RegExp[]) {
let firstMatchLength = -1;
for (const pattern of patterns) {
const match = pattern.exec(str);
if (!match || match[0] === '') {
let matchResult;
if (pattern.global || pattern.sticky) {
// Since regex objects are stateful in global or sticky flags, we need to reset
const regex = new RegExp(pattern.source, pattern.flags);
matchResult = regex.exec(str);
} else {
matchResult = pattern.exec(str);
}
if (!matchResult || matchResult[0] === '') {
continue;
}
if (firstMatchIndex === -1 || match.index < firstMatchIndex) {
firstMatchIndex = match.index;
firstMatchLength = match[0].length;
if (firstMatchIndex === -1 || matchResult.index < firstMatchIndex) {
firstMatchIndex = matchResult.index;
firstMatchLength = matchResult[0].length;
}
}

View file

@ -4,6 +4,7 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {observeIfHighlightWithoutNotificationHasLicense} from '@queries/servers/system';
import {observeCurrentUser} from '@queries/servers/user';
import Message from './message';
@ -12,8 +13,10 @@ import type {WithDatabaseArgs} from '@typings/database/database';
const withMessageInput = withObservables([], ({database}: WithDatabaseArgs) => {
const currentUser = observeCurrentUser(database);
const isHighlightWithoutNotificationLicensed = observeIfHighlightWithoutNotificationHasLicense(database);
return {
currentUser,
isHighlightWithoutNotificationLicensed,
};
});

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useMemo, useState} from 'react';
import React, {useCallback, useState} from 'react';
import {type LayoutChangeEvent, ScrollView, useWindowDimensions, View} from 'react-native';
import Animated from 'react-native-reanimated';
@ -16,10 +16,11 @@ import ShowMoreButton from './show_more_button';
import type PostModel from '@typings/database/models/servers/post';
import type UserModel from '@typings/database/models/servers/user';
import type {SearchPattern} from '@typings/global/markdown';
import type {HighlightWithoutNotificationKey, SearchPattern, UserMentionKey} from '@typings/global/markdown';
type MessageProps = {
currentUser?: UserModel;
isHighlightWithoutNotificationLicensed?: boolean;
highlight: boolean;
isEdited: boolean;
isPendingOrFailed: boolean;
@ -33,6 +34,9 @@ type MessageProps = {
const SHOW_MORE_HEIGHT = 54;
const EMPTY_MENTION_KEYS: UserMentionKey[] = [];
const EMPTY_HIGHLIGHT_KEYS: HighlightWithoutNotificationKey[] = [];
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
messageContainer: {
@ -52,7 +56,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
};
});
const Message = ({currentUser, highlight, isEdited, isPendingOrFailed, isReplyPost, layoutWidth, location, post, searchPatterns, theme}: MessageProps) => {
const Message = ({currentUser, isHighlightWithoutNotificationLicensed, highlight, isEdited, isPendingOrFailed, isReplyPost, layoutWidth, location, post, searchPatterns, theme}: MessageProps) => {
const [open, setOpen] = useState(false);
const [height, setHeight] = useState<number|undefined>();
const dimensions = useWindowDimensions();
@ -62,10 +66,6 @@ const Message = ({currentUser, highlight, isEdited, isPendingOrFailed, isReplyPo
const blockStyles = getMarkdownBlockStyles(theme);
const textStyles = getMarkdownTextStyles(theme);
const mentionKeys = useMemo(() => {
return currentUser?.mentionKeys;
}, [currentUser]);
const onLayout = useCallback((event: LayoutChangeEvent) => setHeight(event.nativeEvent.layout.height), []);
const onPress = () => setOpen(!open);
@ -96,7 +96,8 @@ const Message = ({currentUser, highlight, isEdited, isPendingOrFailed, isReplyPo
postId={post.id}
textStyles={textStyles}
value={post.message}
mentionKeys={mentionKeys}
mentionKeys={currentUser?.mentionKeys ?? EMPTY_MENTION_KEYS}
highlightKeys={isHighlightWithoutNotificationLicensed ? (currentUser?.highlightKeys ?? EMPTY_HIGHLIGHT_KEYS) : EMPTY_HIGHLIGHT_KEYS}
searchPatterns={searchPatterns}
theme={theme}
/>

View file

@ -30,6 +30,7 @@ exports[`components/channel_list_row should show no results 1`] = `
"desktop_sound": "true",
"email": "true",
"first_name": "true",
"highlight_keys": "",
"mention_keys": "",
"push": "mention",
"push_status": "away",
@ -317,6 +318,7 @@ exports[`components/channel_list_row should show results and tutorial 1`] = `
"desktop_sound": "true",
"email": "true",
"first_name": "true",
"highlight_keys": "",
"mention_keys": "",
"push": "mention",
"push_status": "away",
@ -613,6 +615,7 @@ exports[`components/channel_list_row should show results no tutorial 1`] = `
"desktop_sound": "true",
"email": "true",
"first_name": "true",
"highlight_keys": "",
"mention_keys": "",
"push": "mention",
"push_status": "away",
@ -900,6 +903,7 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
"desktop_sound": "true",
"email": "true",
"first_name": "true",
"highlight_keys": "",
"mention_keys": "",
"push": "mention",
"push_status": "away",
@ -932,6 +936,7 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
"desktop_sound": "true",
"email": "true",
"first_name": "true",
"highlight_keys": "",
"mention_keys": "",
"push": "mention",
"push_status": "away",

View file

@ -34,6 +34,7 @@ describe('components/channel_list_row', () => {
email: 'true',
first_name: 'true',
mention_keys: '',
highlight_keys: '',
push: 'mention',
push_status: 'away',
},
@ -61,6 +62,7 @@ describe('components/channel_list_row', () => {
email: 'true',
first_name: 'true',
mention_keys: '',
highlight_keys: '',
push: 'mention',
push_status: 'away',
},

View file

@ -9,4 +9,9 @@ export default {
Professional: 'professional',
Enterprise: 'enterprise',
},
SelfHostedProducts: {
STARTER: 'starter',
PROFESSIONAL: 'professional',
ENTERPRISE: 'enterprise',
},
};

View file

@ -16,7 +16,7 @@ import type ReactionModel from '@typings/database/models/servers/reaction';
import type TeamMembershipModel from '@typings/database/models/servers/team_membership';
import type ThreadParticipantsModel from '@typings/database/models/servers/thread_participant';
import type UserModelInterface from '@typings/database/models/servers/user';
import type {UserMentionKey} from '@typings/global/markdown';
import type {UserMentionKey, HighlightWithoutNotificationKey} from '@typings/global/markdown';
const {
CHANNEL,
@ -194,4 +194,24 @@ export default class UserModel extends Model implements UserModelInterface {
m.key !== '@here'
));
}
get highlightKeys() {
if (!this.notifyProps) {
return [];
}
const highlightWithoutNotificationKeys: HighlightWithoutNotificationKey[] = [];
if (this.notifyProps?.highlight_keys?.length) {
this.notifyProps.highlight_keys.
split(',').
forEach((key) => {
if (key.trim().length > 0) {
highlightWithoutNotificationKeys.push({key: key.trim()});
}
});
}
return highlightWithoutNotificationKeys;
}
}

View file

@ -74,6 +74,7 @@ describe('*** Operator: User Handlers tests ***', () => {
first_name: 'true',
mark_unread: 'mention',
mention_keys: '',
highlight_keys: '',
push: 'mention',
channel: 'true',
auto_responder_active: 'false',

View file

@ -57,6 +57,7 @@ describe('*** USER Prepare Records Test ***', () => {
email: 'true',
first_name: 'true',
mention_keys: '',
highlight_keys: '',
mark_unread: 'mention',
push: 'mention',
channel: 'true',

View file

@ -5,7 +5,7 @@ import {Database, Q} from '@nozbe/watermelondb';
import {of as of$, Observable, combineLatest} from 'rxjs';
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
import {Preferences} from '@constants';
import {Preferences, License} from '@constants';
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
import {PUSH_PROXY_STATUS_UNKNOWN} from '@constants/push_proxy';
import {isMinimumServerVersion} from '@utils/helpers';
@ -560,3 +560,60 @@ export const observeLastServerVersionCheck = (database: Database) => {
switchMap((model) => of$(parseInt(model.value, 10))),
);
};
export const observeIfHighlightWithoutNotificationHasLicense = (database: Database) => {
const license = observeLicense(database);
const isCloudStarterFree = checkIsCloudStarterFree(license);
const isStarterSKULicense = checkIsStarterSKULicense(license);
const isSelfHostedStarter = observeIsSelfHosterStarter(database);
const isEnterpriseReady = observeConfigBooleanValue(database, 'BuildEnterpriseReady', false);
return combineLatest([isCloudStarterFree, isStarterSKULicense, isSelfHostedStarter, isEnterpriseReady]).pipe(
switchMap(([isCSF, isSSL, isSHS, isEnt]) => {
// It should have enterprise build AND not have a starter license of any kind
const highlightWithoutNotificationHasLicense = isEnt && !(isCSF || isSSL || isSHS);
return of$(highlightWithoutNotificationHasLicense);
}),
);
};
function checkIsCloudStarterFree(license: Observable<ClientLicense | undefined>) {
return license.pipe(
switchMap((l) => {
const isCloud = l?.Cloud === 'true';
const isStarterSKU = l?.SkuShortName === License.SKU_SHORT_NAME.Starter;
return of$(isCloud && isStarterSKU);
}),
distinctUntilChanged(),
);
}
function checkIsStarterSKULicense(license: Observable<ClientLicense | undefined>) {
return license.pipe(
switchMap((l) => {
const isLicensed = l?.IsLicensed === 'true';
const isSelfHostedStarterProduct = l?.SelfHostedProducts === License.SelfHostedProducts.STARTER;
return of$(isLicensed && isSelfHostedStarterProduct);
}),
distinctUntilChanged(),
);
}
const observeIsSelfHosterStarter = (database: Database) => {
const license = observeLicense(database);
const isEnterpriseReady = observeConfigBooleanValue(database, 'BuildEnterpriseReady', false);
return combineLatest([license, isEnterpriseReady]).pipe(
switchMap(([lic, isEnt]) => {
const isLicensed = lic?.IsLicensed === 'true';
const isSelfHostedStarter = isEnt && !isLicensed;
return of$(isSelfHostedStarter);
}),
);
};

View file

@ -59,6 +59,7 @@ describe('components/integration_selector/selected_option', () => {
email: 'true',
first_name: 'true',
mention_keys: 'false',
highlight_keys: '',
push: 'mention',
push_status: 'ooo',
},

View file

@ -39,6 +39,7 @@ describe('components/integration_selector/selected_options', () => {
email: 'true',
first_name: 'true',
mention_keys: 'false',
highlight_keys: '',
push: 'mention',
push_status: 'ooo',
},

View file

@ -111,6 +111,10 @@ export const getMarkdownTextStyles = makeStyleSheetFromTheme((theme: Theme) => {
mention_highlight: {
color: theme.mentionHighlightLink,
},
highlight_without_notification: {
backgroundColor: theme.mentionHighlightBg,
color: theme.mentionHighlightLink,
},
search_highlight: {
backgroundColor: theme.mentionHighlightBg,
color: theme.mentionHighlightLink,

View file

@ -334,6 +334,7 @@ export function getNotificationProps(user?: UserModel) {
first_name: (!user || !user.firstName) ? 'false' : 'true',
mark_unread: 'all',
mention_keys: user ? `${user.username},@${user.username}` : '',
highlight_keys: '',
push: 'mention',
push_status: 'online',
push_threads: 'all',

166
package-lock.json generated
View file

@ -6,7 +6,7 @@
"packages": {
"": {
"name": "mattermost-mobile",
"version": "2.10.0",
"version": "2.11.0",
"hasInstallScript": true,
"license": "Apache 2.0",
"dependencies": {
@ -42,7 +42,7 @@
"@tsconfig/react-native": "3.0.2",
"@voximplant/react-native-foreground-service": "3.0.2",
"base-64": "1.0.0",
"commonmark": "npm:@mattermost/commonmark@0.30.1-1",
"commonmark": "npm:@mattermost/commonmark@0.30.1-2",
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#81b5d27509652bae50b4b510ede777dd3bd923cf",
"deep-equal": "2.2.2",
"deepmerge": "4.3.1",
@ -9688,9 +9688,9 @@
},
"node_modules/commonmark": {
"name": "@mattermost/commonmark",
"version": "0.30.1-1",
"resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-1.tgz",
"integrity": "sha512-zOiOtPA5LjB1WCUVbaMt5KNuNdq/0NLpBOcQlGrKe+ftuJVhiis7pWEqS4agIBlpjhm7ssywwl7p1eurOjcm5A==",
"version": "0.30.1-2",
"resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-2.tgz",
"integrity": "sha512-r/xhNVt49pEFTjOgmKvjnPNM5RA8OztWsUn3CSQBcXiH2r36QipnR6qxU1hHo3XCteXkYDu9ypn+voA+jaN4Xg==",
"dependencies": {
"entities": "~3.0.1",
"mdurl": "~1.0.1",
@ -23634,8 +23634,7 @@
"version": "7.21.0-placeholder-for-preset-env.2",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"requires": {
}
"requires": {}
},
"@babel/plugin-syntax-async-generators": {
"version": "7.8.4",
@ -25489,8 +25488,7 @@
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@mattermost/react-native-emm/-/react-native-emm-1.3.5.tgz",
"integrity": "sha512-REdUEsm/RA6lI1Rt4b009jvWn28f7H+e27gd4hlNk6zesIh/dlfiHwYfInW/vwbNFBdSPpvHy7Qi2mdcvrNqhg==",
"requires": {
}
"requires": {}
},
"@mattermost/react-native-network-client": {
"version": "1.4.1",
@ -25513,8 +25511,7 @@
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@mattermost/react-native-turbo-log/-/react-native-turbo-log-0.2.3.tgz",
"integrity": "sha512-usWyD8zVAHzrYqgPH1ne5I14gCOkhS2mefK58g5v4DewZfCm0/Uc0w8MRuPS/9jyOPPq1rUZj8U1AqKgEne9tQ==",
"requires": {
}
"requires": {}
},
"@msgpack/msgpack": {
"version": "2.8.0",
@ -25606,15 +25603,13 @@
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@react-native-camera-roll/camera-roll/-/camera-roll-5.7.2.tgz",
"integrity": "sha512-s8VAUG1Kvi+tEJkLHObmOJdXAL/uclnXJ/IdnJtx2fCKiWA3Ho0ln9gDQqCYHHHHu+sXk7wovsH/I2/AYy0brg==",
"requires": {
}
"requires": {}
},
"@react-native-clipboard/clipboard": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.11.2.tgz",
"integrity": "sha512-bHyZVW62TuleiZsXNHS1Pv16fWc0fh8O9WvBzl4h2fykqZRW9a+Pv/RGTH56E3X2PqzHP38K5go8zmCZUoIsoQ==",
"requires": {
}
"requires": {}
},
"@react-native-community/cli": {
"version": "10.2.4",
@ -27323,8 +27318,7 @@
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-9.4.1.tgz",
"integrity": "sha512-dAbY5mfw+6Kas/GJ6QX9AZyY+K+eq9ad4Su6utoph/nxyH3whp5cMSgRNgE2VhGQVRZ/OG0qq3IaD3+wzoqJXw==",
"requires": {
}
"requires": {}
},
"@react-native-cookies/cookies": {
"version": "6.2.1",
@ -27490,8 +27484,7 @@
"version": "1.3.18",
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.18.tgz",
"integrity": "sha512-/0hwnJkrr415yP0Hf4PjUKgGyfshrvNUKFXN85Mrt1gY49hy9IwxZgrrxlh0THXkPeq8q4VWw44eHDfAcQf20Q==",
"requires": {
}
"requires": {}
},
"@react-navigation/native": {
"version": "6.1.7",
@ -27718,72 +27711,63 @@
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/@stream-io/flat-list-mvcp/-/flat-list-mvcp-0.10.3.tgz",
"integrity": "sha512-2ZK8piYlEfKIPZrH8BpZz9uj8HZcUvMCV0X7qSLSAc/vhLOANBfR0SSn0OaWPbqb2mFGAd4FxmLSPp1zKEYuaw==",
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-add-jsx-attribute": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
"integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-remove-jsx-attribute": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
"integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-remove-jsx-empty-expression": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
"integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-replace-jsx-attribute-value": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
"integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-svg-dynamic-title": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
"integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-svg-em-dimensions": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
"integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-transform-react-native-svg": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.0.0.tgz",
"integrity": "sha512-UKrY3860AQICgH7g+6h2zkoxeVEPLYwX/uAjmqo4PIq2FIHppwhIqZstIyTz0ZtlwreKR41O3W3BzsBBiJV2Aw==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-transform-svg-component": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
"integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-preset": {
"version": "8.0.0",
@ -28590,8 +28574,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@voximplant/react-native-foreground-service/-/react-native-foreground-service-3.0.2.tgz",
"integrity": "sha512-ZIyccOAXPqznA1PAVAYlKZ+GI7kXYCuYgH+gmAkhPouyJbkgrSXaCJJzQ+uBkPr4FBa/PuC/yjzK8vf6tJREQA==",
"requires": {
}
"requires": {}
},
"@webassemblyjs/ast": {
"version": "1.11.1",
@ -28759,8 +28742,7 @@
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz",
"integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==",
"dev": true,
"requires": {
}
"requires": {}
},
"@webpack-cli/info": {
"version": "1.4.0",
@ -28776,8 +28758,7 @@
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz",
"integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==",
"dev": true,
"requires": {
}
"requires": {}
},
"@xtuc/ieee754": {
"version": "1.2.0",
@ -28832,15 +28813,13 @@
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
"dev": true,
"peer": true,
"requires": {
}
"requires": {}
},
"acorn-jsx": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"requires": {
}
"requires": {}
},
"agent-base": {
"version": "6.0.2",
@ -28896,8 +28875,7 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true,
"peer": true,
"requires": {
}
"requires": {}
},
"anser": {
"version": "1.4.10",
@ -29175,8 +29153,7 @@
"version": "7.0.0-bridge.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
"integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
"requires": {
}
"requires": {}
},
"babel-jest": {
"version": "29.6.2",
@ -30106,9 +30083,9 @@
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
},
"commonmark": {
"version": "npm:@mattermost/commonmark@0.30.1-1",
"resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-1.tgz",
"integrity": "sha512-zOiOtPA5LjB1WCUVbaMt5KNuNdq/0NLpBOcQlGrKe+ftuJVhiis7pWEqS4agIBlpjhm7ssywwl7p1eurOjcm5A==",
"version": "npm:@mattermost/commonmark@0.30.1-2",
"resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-2.tgz",
"integrity": "sha512-r/xhNVt49pEFTjOgmKvjnPNM5RA8OztWsUn3CSQBcXiH2r36QipnR6qxU1hHo3XCteXkYDu9ypn+voA+jaN4Xg==",
"requires": {
"entities": "~3.0.1",
"mdurl": "~1.0.1",
@ -30377,8 +30354,7 @@
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz",
"integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==",
"dev": true,
"requires": {
}
"requires": {}
},
"deep-equal": {
"version": "2.2.2",
@ -31281,8 +31257,7 @@
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz",
"integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==",
"requires": {
}
"requires": {}
},
"eslint-import-resolver-node": {
"version": "0.3.7",
@ -31367,8 +31342,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz",
"integrity": "sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==",
"dev": true,
"requires": {
}
"requires": {}
},
"eslint-plugin-import": {
"version": "2.28.0",
@ -31502,8 +31476,7 @@
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
"requires": {
}
"requires": {}
},
"eslint-plugin-react-native": {
"version": "4.0.0",
@ -33877,8 +33850,7 @@
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
"integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
"dev": true,
"requires": {
}
"requires": {}
},
"jest-regex-util": {
"version": "29.4.3",
@ -37106,8 +37078,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.3.tgz",
"integrity": "sha512-ZnXwLQnGzrDpHBHiC56TXFXvmolPeMjTn1UOm610M4EXGzbEDR7oOIyS2ZiItgbs6eZc4oU/a0hpk8PrcKvv5g==",
"requires": {
}
"requires": {}
},
"react-intl": {
"version": "6.4.4",
@ -37428,8 +37399,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/react-native-background-timer/-/react-native-background-timer-2.4.1.tgz",
"integrity": "sha512-TE4Kiy7jUyv+hugxDxitzu38sW1NqjCk4uE5IgU2WevLv7sZacaBc6PZKOShNRPGirLl1NWkaG3LDEkdb9Um5g==",
"requires": {
}
"requires": {}
},
"react-native-button": {
"version": "3.1.0",
@ -37482,15 +37452,13 @@
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/react-native-create-thumbnail/-/react-native-create-thumbnail-1.6.4.tgz",
"integrity": "sha512-JWuKXswDXtqUPfuqh6rjCVMvTSSG3kUtwvSK/YdaNU0i+nZKxeqHmt/CO2+TyI/WSUFynGVmWT1xOHhCZAFsRQ==",
"requires": {
}
"requires": {}
},
"react-native-device-info": {
"version": "10.8.0",
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-10.8.0.tgz",
"integrity": "sha512-DE4/X82ZVhdcnR1Y21iTP46WSSJA/rHK3lmeqWfGGq1RKLwXTIdxmfbZZnYwryqJ+esrw2l4ND19qlgxDGby8A==",
"requires": {
}
"requires": {}
},
"react-native-document-picker": {
"version": "9.0.1",
@ -37527,22 +37495,19 @@
"version": "2.10.10",
"resolved": "https://registry.npmjs.org/react-native-exception-handler/-/react-native-exception-handler-2.10.10.tgz",
"integrity": "sha512-otAXGoZDl1689OoUJWN/rXxVbdoZ3xcmyF1uq/CsizdLwwyZqVGd6d+p/vbYvnF996FfEyAEBnHrdFxulTn51w==",
"requires": {
}
"requires": {}
},
"react-native-fast-image": {
"version": "8.6.3",
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz",
"integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==",
"requires": {
}
"requires": {}
},
"react-native-file-viewer": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/react-native-file-viewer/-/react-native-file-viewer-2.1.5.tgz",
"integrity": "sha512-MGC6sx9jsqHdefhVQ6o0akdsPGpkXgiIbpygb2Sg4g4bh7v6K1cardLV1NwGB9A6u1yICOSDT/MOC//9Ez6EUg==",
"requires": {
}
"requires": {}
},
"react-native-fs": {
"version": "2.20.0",
@ -37581,22 +37546,19 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-2.0.3.tgz",
"integrity": "sha512-7+qvcxXZts/hA+HOOIFyM1x9m9fn/TJVSTgXaoQ8uT4gLc97IMvqHQ559tDmnlth+hHMzd3HRMpmRLWoKPL0DA==",
"requires": {
}
"requires": {}
},
"react-native-hw-keyboard-event": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/react-native-hw-keyboard-event/-/react-native-hw-keyboard-event-0.0.4.tgz",
"integrity": "sha512-G8qp0nm17PHigLb/axgdF9xg51BKCG2p1AGeq//J/luLp5zNczIcQJh+nm02R1MeEUE3e53wqO4LMe0MV3raZg==",
"requires": {
}
"requires": {}
},
"react-native-image-picker": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-5.6.1.tgz",
"integrity": "sha512-LPPlgJi97EzCDY4NWp7z0oUWmCbagnB6HSoKcLJHJD/DaFYN/dJPrqjqKaqqw8K/5Ze6DIsNg9PZohjNEYQQWQ==",
"requires": {
}
"requires": {}
},
"react-native-in-app-review": {
"version": "4.3.3",
@ -37607,15 +37569,13 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/react-native-incall-manager/-/react-native-incall-manager-4.1.0.tgz",
"integrity": "sha512-v1c+XOGu5VudY5//E3i5xiaRA9v6RvevMzZ4RumLqI+hte+4XslB2z6HSek2FF0EmAnY1rCn4ckiwgkTI1Tmtw==",
"requires": {
}
"requires": {}
},
"react-native-iphone-x-helper": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
"integrity": "sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==",
"requires": {
}
"requires": {}
},
"react-native-keyboard-aware-scroll-view": {
"version": "0.9.5",
@ -37630,8 +37590,7 @@
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/react-native-keyboard-tracking-view/-/react-native-keyboard-tracking-view-5.7.0.tgz",
"integrity": "sha512-MDeEwAbn9LJDOfHq0QLCGaZirVLk2X/tHqkAqz3y6uxryTRdSl9PwleOVar5Jx2oAPEg4J9BXbUD1wwOOi+5Kg==",
"requires": {
}
"requires": {}
},
"react-native-keychain": {
"version": "8.1.2",
@ -37642,15 +37601,13 @@
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.8.2.tgz",
"integrity": "sha512-hgmCsgzd58WNcDCyPtKrvxsaoETjb/jLGxis/dmU3Aqm2u4ICIduj4ECjbil7B7pm9OnuTkmpwXu08XV2mpg8g==",
"requires": {
}
"requires": {}
},
"react-native-localize": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.0.2.tgz",
"integrity": "sha512-/l/oE1LVNgIRRhLbhmfFMHiWV0xhUn0A0iz1ytLVRYywL7FTp8Rx2vkJS/q/RpExDvV7yLw2493XZBYIM1dnLQ==",
"requires": {
}
"requires": {}
},
"react-native-math-view": {
"version": "3.9.5",
@ -37686,8 +37643,7 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.1.0.tgz",
"integrity": "sha512-laqDSDlCvEASmJR6cXpqaryK855ejQd07vrfYERzhv68YDOoSkKy/URExRP4vAfAOVqHhix80tLbNUcfvZk2VQ==",
"requires": {
}
"requires": {}
},
"react-native-permissions": {
"version": "3.8.4",
@ -37728,8 +37684,7 @@
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.7.1.tgz",
"integrity": "sha512-X2pJG2ttmAbiGlItWedvDkZg1T1ikmEDiz+7HsiIwAIm2UbFqlhqn+B1JF53mSxPzdNaDcCQVHRNPvj8oFu6Yg==",
"requires": {
}
"requires": {}
},
"react-native-screens": {
"version": "3.24.0",
@ -37762,8 +37717,7 @@
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz",
"integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==",
"requires": {
}
"requires": {}
},
"react-native-svg": {
"version": "13.11.0",
@ -39437,8 +39391,7 @@
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz",
"integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==",
"dev": true,
"requires": {
}
"requires": {}
},
"ts-jest": {
"version": "29.1.1",
@ -39759,15 +39712,13 @@
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz",
"integrity": "sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==",
"requires": {
}
"requires": {}
},
"use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"requires": {
}
"requires": {}
},
"utf8": {
"version": "3.0.0",
@ -40100,8 +40051,7 @@
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz",
"integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==",
"requires": {
}
"requires": {}
},
"xdate": {
"version": "0.8.2",

View file

@ -43,7 +43,7 @@
"@tsconfig/react-native": "3.0.2",
"@voximplant/react-native-foreground-service": "3.0.2",
"base-64": "1.0.0",
"commonmark": "npm:@mattermost/commonmark@0.30.1-1",
"commonmark": "npm:@mattermost/commonmark@0.30.1-2",
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#81b5d27509652bae50b4b510ede777dd3bd923cf",
"deep-equal": "2.2.2",
"deepmerge": "4.3.1",

View file

@ -396,6 +396,7 @@ class TestHelper {
first_name: 'true',
channel: 'true',
mention_keys: '',
highlight_keys: '',
},
last_picture_update: 0,
position: '',

View file

@ -28,6 +28,8 @@ interface ClientLicense {
OpenId: string;
RemoteClusterService: string;
SAML: string;
SelfHostedProducts: string;
SharedChannels: string;
SkuShortName: string;
Users: string;
}

View file

@ -13,6 +13,7 @@ type UserNotifyProps = {
first_name: 'true' | 'false';
mark_unread?: 'all' | 'mention';
mention_keys: string;
highlight_keys: string;
push: 'default' | 'all' | 'mention' | 'none';
push_status: 'ooo' | 'offline' | 'away' | 'dnd' | 'online';
user_id?: string;

View file

@ -11,7 +11,7 @@ import type ThreadParticipantsModel from './thread_participant';
import type {Model} from '@nozbe/watermelondb';
import type {Associations} from '@nozbe/watermelondb/Model';
import type Query from '@nozbe/watermelondb/Query';
import type {UserMentionKey} from '@typings/global/markdown';
import type {HighlightWithoutNotificationKey, UserMentionKey} from '@typings/global/markdown';
/**
* The User model represents the 'USER' table and its relationship to other
@ -113,6 +113,9 @@ declare class UserModel extends Model {
/* user mentions keys always excluding @channel, @all, @here */
userMentionKeys: UserMentionKey[];
/* user highlight keys are custom words that gets highlighted without notification */
highlightKeys: HighlightWithoutNotificationKey[];
/** termsOfServiceId : The id of the last accepted terms of service */
termsOfServiceId: string;

View file

@ -13,6 +13,10 @@ export type UserMentionKey = {
caseSensitive?: boolean;
};
export type HighlightWithoutNotificationKey = {
key: string;
};
export type MarkdownBlockStyles = {
adjacentParagraph: ViewStyle;
horizontalRule: ViewStyle;