diff options
| -rw-r--r-- | desktop/packages/mullvad-vpn/src/renderer/lib/html-formatter.tsx | 51 |
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>; } |
