Files
mattermost-mobile/app/components/markdown/latex_code_block/latex_code_block.js
KobeBergmans 21adcf6085 Latex rendering with react-native-math-view (#5651)
* Added a Latex code window that displays the given code using katex

* Latex is now displayed without having to go to the next screen

* MathView test

* MathView WIP:
Displays math good, but in verry small font
View does not load if there is a syntax error and we are hard stuck in the app

* Math view now is rendered properly, few working points:
Text is not fixed size, it renders in a weird size
Multi Line subscript is not working

* Latex is now rendered properly

* Fixed Error message and moved helper function to utils

* Added some padding and vertical scroll on latex screen

* Updated config option to work on real phone too

* Removed unused code and fixed tests

* Added LatexInline and tried to modify parser, not yet finished though. InlineLatexOption is not yet coded for

* Bugfixes and better line break checking

* Few margin changes for ios, also updated pod files

* padding is now dependend on system

* Add platform import

* Reverted package-lock change

* Added tests

* Bugfixes, UI tweaks and added unit test for latex util

* Added enableLatex to markdown options instead getting the state and config

* Changed the rendering a little bit, it is better but still far from perfect

* Width and height is now correctely displayed. Still not clean enough though

* Inline latex is now rendered properly for most equations

* Inline latex is now working properly

* tests for escaping and unescaping functions

* Added inline latex setting support

* Added inline latex tests

* Delete unused comment

* Added extra regex rule: there must be no word character in front of the inline latex code

* Upgraded math-view package and deleted duplicate entry from gemfile.lock

* Inline error functions are now class functions

* Updated error color to theme error color

* Latex screen now extends the code screen

* Markdown changes:
Now uses dimensions instead of windowdimensions
Removed inline latex function
extracted latex rendering into separate function

* updated latex util test

* Hardcoded latex language in latex code block

* Added proptypes and cleaned up latex code block

* Bugfix latex split lines

* ÃDeleted language prop from latex code block

* Changed commonmark packages to personal repo's

* Latex rendering now goes by commonmark

* Deleted unused function and tests

* Commonmark packages back to normal versions with patch package

* Updated commonmark patch file to right file and changed package lock to point at right repo

* Updated patch files to latest version

* Updated commonmark patch

* Fixed linter errors

* Integrety of commonmark fix

* First fix of ios problems

* Updated snapshot

Co-authored-by: Kobe Bergmans <kobe.bergmans@student.kuleuven.be>
Co-authored-by: Administrator <admin@Macintosh-3.localdomain>
Co-authored-by: kobe bergmans <kobebergmans@kobes-MacBook-Pro.local>
2022-04-19 09:16:38 -04:00

210 lines
6.4 KiB
JavaScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {PropTypes} from 'prop-types';
import React from 'react';
import {Keyboard, View, Text, StyleSheet, Platform} from 'react-native';
import MathView from 'react-native-math-view';
import {goToScreen} from '@actions/navigation';
import FormattedText from '@components/formatted_text';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import {splitLatexCodeInLines} from '@utils/latex';
import {getDisplayNameForLanguage} from '@utils/markdown';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import MarkdownCodeBlock from '../markdown_code_block/markdown_code_block';
const MAX_LINES = 2;
export default class LatexCodeBlock extends MarkdownCodeBlock {
static propTypes = {
theme: PropTypes.object.isRequired,
content: PropTypes.string.isRequired,
textStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
};
handlePress = preventDoubleTap(() => {
const {content} = this.props;
const {intl} = this.context;
const screen = 'Latex';
const passProps = {
content,
};
const languageDisplayName = getDisplayNameForLanguage('latex');
let title;
if (languageDisplayName) {
title = intl.formatMessage(
{
id: 'mobile.routes.code',
defaultMessage: '{language} Code',
},
{
language: languageDisplayName,
},
);
} else {
title = intl.formatMessage({
id: 'mobile.routes.code.noLanguage',
defaultMessage: 'LaTeX Code',
});
}
Keyboard.dismiss();
requestAnimationFrame(() => {
goToScreen(screen, title, passProps);
});
});
splitContent = (content) => {
const lines = splitLatexCodeInLines(content);
const numberOfLines = lines.length;
if (numberOfLines > MAX_LINES) {
return {
content: lines.slice(0, MAX_LINES),
numberOfLines,
};
}
return {
content: lines,
numberOfLines,
};
};
onErrorMessage = (errorMsg) => {
const style = getStyleSheet(this.props.theme);
return <Text style={style.errorText}>{'Error: ' + errorMsg.message}</Text>;
};
onRenderErrorMessage = (errorMsg) => {
const style = getStyleSheet(this.props.theme);
return <Text style={style.errorText}>{'Render error: ' + errorMsg.error.message}</Text>;
};
render() {
const style = getStyleSheet(this.props.theme);
let language = null;
const languageDisplayName = getDisplayNameForLanguage('latex');
if (languageDisplayName) {
language = (
<View style={style.language}>
<Text style={style.languageText}>
{languageDisplayName}
</Text>
</View>
);
}
const {content, numberOfLines} = this.splitContent(this.props.content);
let plusMoreLines = null;
if (numberOfLines > MAX_LINES) {
plusMoreLines = (
<FormattedText
style={style.plusMoreLinesText}
id='mobile.markdown.code.plusMoreLines'
defaultMessage='+{count, number} more {count, plural, one {line} other {lines}}'
values={{
count: numberOfLines - MAX_LINES,
}}
/>
);
}
/**
* Note on the error behavior of math view:
* - onError returns an Error object
* - renderError returns an options object with an error attribute that contains the real Error.
*/
return (
<TouchableWithFeedback
onPress={this.handlePress}
onLongPress={this.handleLongPress}
type={'opacity'}
>
<View style={style.container}>
<View style={style.rightColumn}>
{content.map((latexCode) => (
<View
style={style.code}
key={latexCode}
>
<MathView
math={latexCode}
onError={this.onErrorMessage}
renderError={this.onRenderErrorMessage}
resizeMode={'cover'}
/>
</View>
))}
{plusMoreLines}
</View>
{language}
</View>
</TouchableWithFeedback>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
const codeVerticalPadding = Platform.select({
ios: 4,
android: 0,
});
return {
container: {
borderColor: changeOpacity(theme.centerChannelColor, 0.15),
borderRadius: 3,
borderWidth: StyleSheet.hairlineWidth,
flexDirection: 'row',
flex: 1,
},
rightColumn: {
flexDirection: 'column',
flex: 1,
paddingLeft: 6,
paddingVertical: 4,
},
code: {
flexDirection: 'row',
justifyContent: 'flex-start',
marginLeft: 5,
paddingVertical: codeVerticalPadding,
},
plusMoreLinesText: {
color: changeOpacity(theme.centerChannelColor, 0.4),
fontSize: 11,
marginTop: 2,
},
language: {
alignItems: 'center',
backgroundColor: theme.sidebarHeaderBg,
justifyContent: 'center',
opacity: 0.8,
padding: 6,
position: 'absolute',
right: 0,
top: 0,
},
languageText: {
color: theme.sidebarHeaderTextColor,
fontSize: 12,
},
errorText: {
fontSize: 14,
marginHorizontal: 5,
color: theme.errorTextColor,
},
};
});