summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/html-formatter.tsx51
1 files changed, 34 insertions, 17 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/html-formatter.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/html-formatter.tsx
index f0a0457eb0..43c0721f29 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/html-formatter.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/html-formatter.tsx
@@ -35,29 +35,46 @@ const componentMap: Partial<
} as const;
export function formatHtml(inputString: string): React.ReactElement {
- const formattedString: JSX.Element[] = [];
+ const inputStringArray: Array<string | JSX.Element> = [inputString];
- Object.entries(testMap).forEach(([key, { test, replace }]) => {
- const parts = inputString.split(test).filter((part) => part !== '');
- if (parts.length <= 1) {
- return;
- }
+ const transformedStrings = Object.entries(testMap).reduce((strings, [key, { test, replace }]) => {
+ const newStrings = strings.flatMap((value) => {
+ if (typeof value === 'string') {
+ // If the value is a string we should see if our current transformer should transform it.
+ return value
+ .split(test)
+ .filter((v) => v.length > 0)
+ .map((substring) => {
+ // Create a new RegExp object to avoid `lastIndex` side effects, see:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex#avoiding_side_effects
+ const tester = new RegExp(test);
- parts.map((value, index) => {
- if (test.test(value)) {
- const Component = componentMap[key as keyof typeof componentMap]!;
- const valueWithoutTags = value.replaceAll(replace, '');
+ // If the value is matched for the current transformer then it should be turned into a component
+ if (tester.test(substring)) {
+ const Component = componentMap[key as keyof typeof componentMap]!;
+ const valueWithoutTags = substring.replaceAll(replace, '');
- formattedString.push(<Component key={index}>{valueWithoutTags}</Component>);
+ return <Component key={substring}>{valueWithoutTags}</Component>;
+ } else {
+ // If the value is not a match for the current transformer we should return the string as is,
+ // so it can be potentially manipulated by a later transformer
+ return substring;
+ }
+ });
} else {
- formattedString.push(<React.Fragment key={index}>{value}</React.Fragment>);
+ // If the value is not a string it has already been transformed into a component by a transformer in a previous iteration.
+ return value;
}
});
- });
- if (formattedString.length === 0) {
- formattedString.push(<React.Fragment key={inputString}>{inputString}</React.Fragment>);
- }
+ return newStrings;
+ }, inputStringArray);
+
+ // After all the transformers have been applied,
+ // loop over all non-transformed strings and wrap them in fragments
+ const htmlFormattedInputString = transformedStrings.map((value) =>
+ typeof value === 'string' ? <React.Fragment key={value}>{value}</React.Fragment> : value,
+ );
- return <>{formattedString}</>;
+ return <React.Fragment>{htmlFormattedInputString}</React.Fragment>;
}