diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 9e3f00124f84c..40207e0e3894d 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -186,6 +186,10 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim tagName: 'bullet-item', contentModel: HTMLContentModel.block, }), + 'sparkles-icon': HTMLElementModel.fromCustomModel({ + tagName: 'sparkles-icon', + contentModel: HTMLContentModel.mixed, + }), }), [ styles.taskTitleMenuItem, diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/SparklesIconRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/SparklesIconRenderer.tsx new file mode 100644 index 0000000000000..b8c22956809df --- /dev/null +++ b/src/components/HTMLEngineProvider/HTMLRenderers/SparklesIconRenderer.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import {View} from 'react-native'; +import Icon from '@components/Icon'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; + +/** + * Renders a Sparkles SVG icon as an inline custom HTML element + */ +function SparklesIconRenderer() { + const icons = useMemoizedLazyExpensifyIcons(['Sparkles']); + const theme = useTheme(); + const styles = useThemeStyles(); + + return ( + + + + ); +} + +export default SparklesIconRenderer; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts index f77dc115c8545..3d91670c38e75 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts +++ b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts @@ -16,6 +16,7 @@ import NextStepEmailRenderer from './NextStepEmailRenderer'; import PreRenderer from './PreRenderer'; import RBRRenderer from './RBRRenderer'; import ShortMentionRenderer from './ShortMentionRenderer'; +import SparklesIconRenderer from './SparklesIconRenderer'; import TaskTitleRenderer from './TaskTitleRenderer'; import TransactionHistoryLinkRenderer from './TransactionHistoryLinkRenderer'; import UserDetailsRenderer from './UserDetailsRenderer'; @@ -50,6 +51,7 @@ const HTMLEngineProviderComponentList: CustomTagRendererRecord = { 'concierge-link': ConciergeLinkRenderer, 'transaction-history-link': TransactionHistoryLinkRenderer, 'account-manager-link': AccountManagerLinkRenderer, + 'sparkles-icon': SparklesIconRenderer, /* eslint-enable @typescript-eslint/naming-convention */ }; diff --git a/src/components/RenderHTML.tsx b/src/components/RenderHTML.tsx index a52ccf2c8d969..ababd16463b8e 100644 --- a/src/components/RenderHTML.tsx +++ b/src/components/RenderHTML.tsx @@ -5,6 +5,7 @@ import useHasTextAncestor from '@hooks/useHasTextAncestor'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Parser from '@libs/Parser'; import BulletItemRenderer from './HTMLEngineProvider/HTMLRenderers/BulletItemRenderer'; +import SparklesIconRenderer from './HTMLEngineProvider/HTMLRenderers/SparklesIconRenderer'; type LinkPressHandler = NonNullable['onPress']; @@ -53,6 +54,7 @@ function RenderHTML({html: htmlParam, onLinkPress, isSelectable}: RenderHTMLProp const renderers = { /* eslint-disable @typescript-eslint/naming-convention */ 'bullet-item': BulletItemRenderer, + 'sparkles-icon': SparklesIconRenderer, }; const htmlSource = ( diff --git a/src/languages/de.ts b/src/languages/de.ts index 8a6be1581b8f4..60edcacd58d4d 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -1586,7 +1586,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: 'Der Gesamtbetrag ist zu hoch. Verringere die Stunden oder reduziere den Satz.', }, correctRateError: 'Beheben Sie den Kursfehler und versuchen Sie es erneut.', - AskToExplain: `. Erklären ✨`, + AskToExplain: `. Erklären`, duplicateNonDefaultWorkspacePerDiemError: 'Sie können Per-Diem-Ausgaben nicht über mehrere Workspaces hinweg duplizieren, da sich die Sätze zwischen den Workspaces unterscheiden können.', rulesModifiedFields: { diff --git a/src/languages/en.ts b/src/languages/en.ts index 3f10e590d6ed3..70e48407f64e9 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1611,7 +1611,7 @@ const translations = { amountTooLargeError: 'The total amount is too large. Lower the hours or reduce the rate.', }, correctRateError: 'Fix the rate error and try again.', - AskToExplain: `. Explain ✨`, + AskToExplain: `. Explain`, rulesModifiedFields: { reimbursable: (value: boolean) => (value ? 'marked the expense as "reimbursable"' : 'marked the expense as "non-reimbursable"'), billable: (value: boolean) => (value ? 'marked the expense as "billable"' : 'marked the expense as "non-billable"'), diff --git a/src/languages/es.ts b/src/languages/es.ts index b415f6a0dd7fa..414775e1f9854 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1452,7 +1452,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: 'El importe total es demasiado alto. Reduce las horas o disminuye la tasa.', }, correctRateError: 'Corrige el error de la tasa y vuelve a intentarlo.', - AskToExplain: `. Explicar ✨`, + AskToExplain: `. Explicar`, rulesModifiedFields: { reimbursable: (value: boolean) => (value ? 'marcó el gasto como "reembolsable"' : 'marcó el gasto como "no reembolsable"'), billable: (value: boolean) => (value ? 'marcó el gasto como "facturable"' : 'marcó el gasto como "no facturable"'), diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 0b82ad790797b..746e79a43145f 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -1591,7 +1591,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: 'Le montant total est trop élevé. Réduisez le nombre d’heures ou diminuez le taux.', }, correctRateError: 'Corrigez l’erreur de taux et réessayez.', - AskToExplain: `. Expliquer ✨`, + AskToExplain: `. Expliquer`, duplicateNonDefaultWorkspacePerDiemError: 'Vous ne pouvez pas dupliquer les indemnités journalières entre plusieurs espaces de travail, car les taux peuvent différer d’un espace de travail à l’autre.', rulesModifiedFields: { diff --git a/src/languages/it.ts b/src/languages/it.ts index d273792b023f0..89ed00830822c 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -1583,7 +1583,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: 'L’importo totale è troppo alto. Riduci le ore o abbassa la tariffa.', }, correctRateError: "Correggi l'errore di tariffa e riprova.", - AskToExplain: `. Spiega ✨`, + AskToExplain: `. Spiega`, duplicateNonDefaultWorkspacePerDiemError: 'Non puoi duplicare le spese di diaria tra diversi spazi di lavoro perché le tariffe potrebbero essere diverse tra gli spazi di lavoro.', rulesModifiedFields: { reimbursable: (value: boolean) => (value ? 'ha contrassegnato la spesa come "rimborsabile"' : 'ha contrassegnato la spesa come "non rimborsabile"'), diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 0a811ba9bb8c5..9cad80325ba19 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -1573,7 +1573,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: '合計金額が大きすぎます。時間を減らすか、レートを下げてください。', }, correctRateError: 'レートのエラーを修正して、もう一度お試しください。', - AskToExplain: `・説明 ✨`, + AskToExplain: `・説明`, duplicateNonDefaultWorkspacePerDiemError: 'ワークスペースごとに日当レートが異なる場合があるため、日当経費をワークスペース間で複製することはできません。', rulesModifiedFields: { reimbursable: (value: boolean) => (value ? '経費を「精算対象」に指定しました' : '経費を「精算対象外」にマークしました'), diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 6ffe1f3c1a213..975839fbd2149 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -1581,7 +1581,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: 'Het totale bedrag is te hoog. Verlaag het aantal uren of verlaag het tarief.', }, correctRateError: 'Los de tarieffout op en probeer het opnieuw.', - AskToExplain: `. Uitleggen ✨`, + AskToExplain: `. Uitleggen`, duplicateNonDefaultWorkspacePerDiemError: 'Je kunt dagvergoedingen niet dupliceren tussen werkruimtes, omdat de tarieven per werkruimte kunnen verschillen.', rulesModifiedFields: { reimbursable: (value: boolean) => (value ? 'markeerde de uitgave als „terugbetaalbaar”' : 'heeft de uitgave als ‘niet-vergoedbaar’ gemarkeerd'), diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 5fac6256ad35a..4356113bae61d 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -1580,7 +1580,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: 'Łączna kwota jest zbyt wysoka. Zmniejsz liczbę godzin lub obniż stawkę.', }, correctRateError: 'Napraw błąd stawki i spróbuj ponownie.', - AskToExplain: `. Wyjaśnij ✨`, + AskToExplain: `. Wyjaśnij`, duplicateNonDefaultWorkspacePerDiemError: 'Nie możesz duplikować wydatków z tytułu diet między przestrzeniami roboczymi, ponieważ stawki mogą się różnić między poszczególnymi przestrzeniami.', rulesModifiedFields: { diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index a441fe5c534ce..250ae762ee5f9 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -1578,7 +1578,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: 'O valor total é muito alto. Diminua as horas ou reduza a tarifa.', }, correctRateError: 'Corrija o erro de taxa e tente novamente.', - AskToExplain: `. Explicar ✨`, + AskToExplain: `. Explicar`, duplicateNonDefaultWorkspacePerDiemError: 'Você não pode duplicar despesas de diárias entre espaços de trabalho porque as tarifas podem variar entre eles.', rulesModifiedFields: { reimbursable: (value: boolean) => (value ? 'marcou a despesa como "reembolsável"' : 'marcou a despesa como “não reembolsável”'), diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 3f3090d975a42..bba0974a58e33 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -1548,7 +1548,7 @@ const translations: TranslationDeepObject = { amountTooLargeError: '总金额过大。请减少工时或降低费率。', }, correctRateError: '修复费率错误后请重试。', - AskToExplain: `。说明 ✨`, + AskToExplain: `。说明`, duplicateNonDefaultWorkspacePerDiemError: '您无法在不同工作区之间复制每日津贴报销,因为各工作区的补贴标准可能不同。', rulesModifiedFields: { reimbursable: (value: boolean) => (value ? '将该报销单标记为“可报销”' : '将该报销单标记为“不可报销”'),