Skip to content

Skip Skia web charts when WebGL shader precision is unavailable#92874

Merged
mountiny merged 20 commits into
Expensify:mainfrom
wildan-m:wildan/92418-skia-web-webgl-precheck
Jul 2, 2026
Merged

Skip Skia web charts when WebGL shader precision is unavailable#92874
mountiny merged 20 commits into
Expensify:mainfrom
wildan-m:wildan/92418-skia-web-webgl-precheck

Conversation

@wildan-m

@wildan-m wildan-m commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

On web, some devices crash the whole page with TypeError: Cannot read properties of null (reading 'rangeMin') (Sentry APP-8SV, 54 users). It's reported on bank-account onboarding, but the cause is charts, not that form. When a chart mounts, Skia's web backend (CanvasKit) sets up WebGL by calling getShaderPrecisionFormat() and reading .rangeMin from the result without checking for null. On devices that can't supply a precision format (software or blocked GPUs), that call returns null, so CanvasKit throws — and because it happens during async setup outside React, no error boundary catches it and the page goes down.

This adds a web-only check that runs the same getShaderPrecisionFormat() test before loading Skia. If it fails, the chart shows an "Unable to display chart" empty state instead of mounting Skia and crashing. The check lives in one shared wrapper used by all web charts (pie, line, bar, and the Victory HTML renderer) and runs on each chart mount. Capable devices are unaffected, and native charts are untouched (this path is web-only).

Fixed Issues

$ #92418
PROPOSAL: #92418 (comment)

Tests

The Sentry crash only reproduces on devices where WebGL cannot supply a shader-precision format, so the unsupported path is simulated by forcing that condition.

  1. Go to Search and open the Spend over time insight (a Skia line chart). Verify the chart renders normally.
  2. Open Top categories and Top merchants. Verify those Skia charts render normally too (regression check on the shared wrapper).
  3. Go back to Spend over time, then simulate an incapable GPU — open the browser console and run:
    WebGLRenderingContext.prototype.getShaderPrecisionFormat = () => null;
    WebGL2RenderingContext.prototype.getShaderPrecisionFormat = () => null;
    Do not reload the page — a full reload clears this override. The probe runs on each chart mount, so trigger a fresh mount with client-side navigation instead.
  4. Switch between the nearby insight tabs — e.g. Top categories and Top merchants. Each switch re-mounts the chart (the probe runs per mount) with the override still active, so no reload is needed.
  5. Verify the chart area shows the "Unable to display chart" empty state and the page does not crash — no TypeError: Cannot read properties of null (reading 'rangeMin') appears in the console. (Before this change, the same step throws that error.)

Platform scope: Steps 3–5 simulate the incapable GPU through the browser console, so they apply to MacOS: Chrome/Safari (desktop). On the other platforms the override can't be injected on-device, so just confirm there's no regression — the Spend over time, Top categories, and Top merchants charts still render normally:

  • mWeb (Android Chrome / iOS Safari): the fix is active here too (mWeb uses the same web chart wrapper), but the GPU override can only be injected via remote debugging (chrome://inspect / Safari Web Inspector) — not required for routine testing.

  • iOS / Android Native: not affected — native charts render their *ChartContent directly and never touch SkiaWebChart/CanvasKit.

  • Verify that no errors appear in the JS console

Local web verification evidence (MacOS Chrome, dev server)
  • Happy path: With a working GPU, the Search "Spend over time" / "Top categories" Skia charts render correctly — confirming the shared wrapper does not regress chart rendering.
  • Fix path: After forcing getShaderPrecisionFormat() to return null (the exact Sentry crash condition) and opening a chart, the chart area shows the "Unable to display chart" empty state, document.querySelectorAll('canvas').length === 0 (Skia/CanvasKit never mounts), and no rangeMin TypeError or other console error is thrown — the page does not crash.

Offline tests

This change does not affect offline behavior. The WebGL capability probe is local and network-independent, so chart rendering on capable devices and the fallback on incapable ones behave identically online and offline.

QA Steps

Regression only — no GPU simulation (the unsupported-device path is covered in the Tests section on desktop web). On each platform:

  1. Go to Search and open the Spend over time insight. Verify the chart renders normally.
  2. Open Top categories and Top merchants. Verify those charts render normally too.
  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Kapture.2026-06-30.at.23.27.40.mp4
Android: mWeb Chrome
Kapture.2026-07-01.at.05.53.25.mp4
iOS: Native
Kapture.2026-06-30.at.23.17.33.mp4
iOS: mWeb Safari
Kapture.2026-06-30.at.23.20.45.mp4
MacOS: Chrome / Safari
Kapture.2026-06-30.at.22.57.56.mp4

wildan-m added 4 commits June 3, 2026 02:58
CanvasKit reads .rangeMin off getShaderPrecisionFormat() without a null
check, so on devices that cannot create a usable WebGL context it throws an
uncaught TypeError during async GL init. Probe WebGL capability up front in a
shared chart wrapper and render the existing fallback view instead of mounting
Skia when the environment can't support it.
- Keep the `object` generic constraint (mirrors WithSkiaWeb) with a scoped
  no-restricted-types disable: Record<string, unknown> rejects the
  interface-based VictoryChartRendererProps that lacks an index signature.
- Drop the now-unnecessary WebGL context type assertion (TS infers it).
- Trim the chart wrapper doc comments to keep the load-bearing "why".
Cover the WebGL capability probe (supported, no-context, null precision
format, throw, memoization) and the wrapper's branch that renders the
fallback instead of mounting Skia when WebGL is unsupported.
@codecov

codecov Bot commented Jun 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/components/Charts/SkiaWebChart/index.tsx 100.00% <100.00%> (ø)
...mponents/Charts/SkiaWebChart/isSkiaWebSupported.ts 100.00% <100.00%> (ø)
src/components/Icon/chunks/illustrations.chunk.ts 0.00% <ø> (ø)
src/components/Charts/BarChart/index.tsx 0.00% <0.00%> (ø)
src/components/Charts/LineChart/index.tsx 0.00% <0.00%> (ø)
src/components/Charts/PieChart/index.tsx 0.00% <0.00%> (ø)
...vider/HTMLRenderers/VictoryChartRenderer/index.tsx 0.00% <0.00%> (ø)
... and 16 files with indirect coverage changes

The session-wide memo cached the first probe result, so a WebGL context
lost after the first chart rendered (or any later capability change) would
still read the stale "supported" value, mount Skia and hit the same crash.
Probe once per chart mount via a lazy initializer (cheap, and re-rendering
no longer recreates contexts) and release the throwaway probe context so it
can't evict a real chart's context.
@wildan-m

wildan-m commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

@mountiny @ShridharGoel @Expensify/design

On WebGL-incapable devices, what should the chart area show instead of the infinite loading spinner?

  1. Empty placeholder
    Show the chart container with no animated spinner — keeps the card's size/shape but removes the perpetual-loading look AND the long-loading/skeleton telemetry.
  2. Keep the spinner
    No change from the current PR. Still crash-free, but keeps the 'forever loading' appearance and emits the 'abnormally long loading' + unresolved-skeleton telemetry on those devices.
  3. 'Chart unavailable' message

current behavior

Kapture.2026-06-08.at.07.32.23.mp4

@mountiny

mountiny commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

@wildan-m What devices are we talking about here, I assume we should have some fallback for these @mateuuszzzzz

@mountiny

mountiny commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

@roryabraham

Copy link
Copy Markdown
Contributor

Can we fallback on SVG-powered charts?

On devices that can't give Skia a usable WebGL surface, render a localized
message telling the user to enable WebGL in their browser settings, instead
of the loading spinner — which never resolved (looked like infinite loading)
and fired long-loading/skeleton telemetry. New en/es strings; copy pending
JaimeGPT + #expensify-open-source approval.
@melvin-bot

melvin-bot Bot commented Jun 9, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running:

npx ts-node ./scripts/generateTranslations.ts --help

Typically, you'd want to translate only what you changed by running npx ts-node ./scripts/generateTranslations.ts --compare-ref main

@wildan-m

wildan-m commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@roryabraham Open to that. Only catch: the charts run on victory-native v41, which renders only via Skia/WebGL (no SVG mode), so an SVG fallback would be a separate implementation — bar/line/pie plus the Victory HTML renderer (arbitrary chart specs from messages). react-native-svg is already in the app and needs no WebGL. How would you suggest approaching it — a specific SVG charting lib, or a minimal custom react-native-svg renderer? And any thoughts on the HTML-renderer case?

@mountiny

Copy link
Copy Markdown
Contributor

I think adding the fallback will be extra work and should be handled in a follow up given right now the app crashes when we run into this issue

So I suggest we add some @Expensify/design graphic to explain that this browser does not support the charts and we look at the scale of this issue and whether its worth adding the fallback

How should the "unavailable state" look like @Expensify/design ?

@shawnborton

Copy link
Copy Markdown
Contributor

On WebGL-incapable devices

What kind of devices are these typically? How often is this happening?

We have plenty of empty state patterns we can use as a placeholder, but I don't know what the correct messaging should be. Do we tell the user to try a different browser? Something else?

@mountiny

Copy link
Copy Markdown
Contributor

From sentry its 70% windows and its chromium only so Edge and Chrome browsers. it happend to 74 unique users in the past 90 days, so its not a large amount, but it does not look like there would be a clear common denominator. It also happened on the latest Chrome versions on Mac and Windows, so it also depends on whether the device they are using supports it, I guess.

@shawnborton

Copy link
Copy Markdown
Contributor

Do you have any suggestions for what copy we would write in the placeholder state?

@mountiny

Copy link
Copy Markdown
Contributor

Your client does not support the required WebGL technology for this feature. Make sure to enable it or use a different browser.

Maybe something like this? I think its worth it to mention the exact technology so they could potentially google this and enable or choose different browser

@roryabraham

Copy link
Copy Markdown
Contributor

I think adding the fallback will be extra work and should be handled in a follow up given right now the app crashes when we run into this issue

sounds good

@shawnborton

Copy link
Copy Markdown
Contributor

Perhaps something simple like this? I'm showing this on the Home page but I would use this same exact empty state pattern elsewhere, like on Spend > Insights:
image

cc @Expensify/design for a quick preview

@dubielzyk-expensify

Copy link
Copy Markdown
Contributor

That feels right and simple to me 👍

@mountiny

Copy link
Copy Markdown
Contributor

same

@dannymcclain

Copy link
Copy Markdown
Contributor

Agree - no need to reinvent the wheel here. I think that works well.

@wildan-m

Copy link
Copy Markdown
Contributor Author

@shawnborton The "monitor with a red X" illustration from the mockup isn't in the codebase yet — which should we use? Closest are the Monitor icon (right shape, no X) and the LaptopWithSecondScreenX illustration (has the X, but it's a laptop + second screen).

Replace the single-line message with the approved empty state: placeholder
Monitor icon + "Unable to display chart" + "Your browser doesn't support
WebGL. Please enable it or switch browsers." (en + es; es via JaimeGPT).
The other languages come from generateTranslations, which skips fork PRs.

Also rewrite the chart tests cast-free so they pass the type-aware lint
(the previous mock casts failed CI ESLint).
@shawnborton

Copy link
Copy Markdown
Contributor

Here is the illustration you need: simple-illustration__monitorsync-no.svg.zip

wildan-m added 2 commits June 29, 2026 06:08
Add the design-provided simple-illustration__monitorsync-no.svg, register it
as MonitorSyncNo, and render it in the chart empty state in place of the
placeholder Monitor icon.
@melvin-bot melvin-bot Bot requested review from mountiny and removed request for a team June 30, 2026 22:55
@melvin-bot

melvin-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

@suneox @mountiny One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@ShridharGoel

Copy link
Copy Markdown
Contributor

@mountiny Can we run the translations workflow here?

Comment thread src/components/Charts/SkiaWebChart/index.tsx Outdated
Comment thread src/components/Charts/SkiaWebChart/isSkiaWebSupported.ts Outdated
@ShridharGoel

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

MacOS: Chrome / Safari
Screen.Recording.2026-07-01.at.10.05.20.AM.mov

@OSBotify

OSBotify commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦜 Polyglot Parrot! 🦜

Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues:

View the translation diff
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 8a6d1bd47ac..b1281eee150 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -22,7 +22,7 @@ const translations: TranslationDeepObject<typeof en> = {
         count: 'Contar',
         cancel: 'Cancelar',
         unableToDisplayChart: 'No se puede mostrar el gráfico',
-        webGLNotSupported: 'Tu navegador no es compatible con WebGL. Por favor, actívalo o cambia de navegador.',
+        webGLNotSupported: 'Tu navegador no es compatible con WebGL. Por favor, habilítalo o cambia de navegador.',
         dismiss: 'Descartar',
         proceed: 'Proceder',
         unshare: 'Dejar de compartir',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 262fa875c43..55abfebfaf2 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -515,7 +515,7 @@ const translations: TranslationDeepObject<typeof en> = {
         restrictions: '制限',
         off: 'オフ',
         unableToDisplayChart: 'グラフを表示できません',
-        webGLNotSupported: 'お使いのブラウザは WebGL に対応していません。WebGL を有効にするか、別のブラウザに切り替えてください。',
+        webGLNotSupported: 'お使いのブラウザは WebGL に対応していません。有効にするか、別のブラウザに切り替えてください。',
     },
     socials: {
         podcast: 'ポッドキャストでフォロー',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 7c743cea377..e479d752f2c 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -515,7 +515,7 @@ const translations: TranslationDeepObject<typeof en> = {
         restrictions: 'Ograniczenia',
         off: 'Wyłączone',
         unableToDisplayChart: 'Nie można wyświetlić wykresu',
-        webGLNotSupported: 'Twoja przeglądarka nie obsługuje WebGL. Włącz je lub zmień przeglądarkę.',
+        webGLNotSupported: 'Twoja przeglądarka nie obsługuje WebGL. Włącz ją albo zmień przeglądarkę.',
     },
     socials: {
         podcast: 'Śledź nas na Podcast',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 2acd25e9909..aa90e3beedd 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -514,7 +514,7 @@ const translations: TranslationDeepObject<typeof en> = {
         restrictions: 'Restrições',
         off: 'Desligado',
         unableToDisplayChart: 'Não foi possível exibir o gráfico',
-        webGLNotSupported: 'Seu navegador não oferece suporte a WebGL. Ative o recurso ou use outro navegador.',
+        webGLNotSupported: 'Seu navegador não é compatível com WebGL. Ative-o ou mude de navegador.',
     },
     socials: {
         podcast: 'Siga-nos no Podcast',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 7099c7d3b8d..ba450e6632d 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -511,7 +511,7 @@ const translations: TranslationDeepObject<typeof en> = {
         restrictions: '限制',
         off: '关',
         unableToDisplayChart: '无法显示图表',
-        webGLNotSupported: '您的浏览器不支持 WebGL。请启用它或更换浏览器。',
+        webGLNotSupported: '您的浏览器不支持 WebGL。请启用该功能或更换浏览器。',
     },
     socials: {
         podcast: '在播客上关注我们',

Note

You can apply these changes to your branch by copying the patch to your clipboard, then running pbpaste | git apply 😉

View workflow run

wildan-m added 3 commits July 2, 2026 03:56
Applies the OpenAI-generated translation improvements from the generateTranslations (Polyglot Parrot) run for es, ja, pl, pt-BR, and zh-hans.
@wildan-m

wildan-m commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @ShridharGoel. Addressed both comment nits in 7f016d6.

@mountiny

mountiny commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@wildan-m conflict

…eb-webgl-precheck

# Conflicts:
#	src/languages/de.ts
#	src/languages/fr.ts
#	src/languages/it.ts
#	src/languages/ja.ts
#	src/languages/nl.ts
#	src/languages/pl.ts
#	src/languages/pt-BR.ts
#	src/languages/zh-hans.ts
@ShridharGoel

ShridharGoel commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

This is still pending, can you check? #92874 (comment)

I don't think the comment is needed, let me know if you think otherwise

@wildan-m

wildan-m commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@mountiny Merged the latest main and resolved the conflicts (add2082) — mergeable again.

mountiny
mountiny previously approved these changes Jul 2, 2026

@mountiny mountiny left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wildan-m can you make that one small change please?

Comment thread src/components/Charts/SkiaWebChart/index.tsx Outdated
Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com>
@mountiny mountiny merged commit 5235ee4 into Expensify:main Jul 2, 2026
37 checks passed
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🚧 mountiny has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

OSBotify commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/mountiny in version: 9.4.27-1 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 No help site changes required — no draft docs PR created.

Why: This PR is a defensive crash fix, not a product/behavior change. On GPUs that can't supply a WebGL shader-precision format, Skia's web backend was throwing TypeError: Cannot read properties of null (reading 'rangeMin') and crashing the page. The fix adds a web-only capability probe and, when unsupported, renders an "Unable to display chart" empty state instead of mounting Skia.

Nothing here changes a documented workflow, feature, setting, tab, or button:

  • No new user-facing feature or flow — capable devices are entirely unaffected, and native charts are untouched.
  • The "Unable to display chart" empty state is an internal error fallback for an edge-case (incapable/blocked GPU), not a feature users configure or interact with.
  • No terminology, navigation, or settings labels changed.

The Search insights charts (Spend over time, Top categories, Top merchants) are not currently documented in App/docs/articles, and even if they were, an internal crash-fallback for unsupported GPUs is not the kind of behavior the help site covers. The only docs/articles grep matches for "chart" are unrelated ("chart of accounts" in accounting integrations).

If you believe a specific help article should call out chart-rendering requirements or the unsupported-device fallback, let me know which article and I'll draft the update.

@OSBotify

OSBotify commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/grgia in version: 9.4.27-2 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants