fix: the splash keeps the old art, which the new icon had silently replaced

The splash drew the app icon, so yesterday's icon change changed the splash with it —
not asked for and not noticed until it was on a phone.

They were the same picture until the icon became a reduction that has to survive 40
points. The splash has room for the fuller mesh scene, so it keeps it: assets/splash.png
is the exact icon that was there before this change, restored from the previous commit
rather than regenerated, so it is the image that was actually on screen.

AppSplash gains the  prop the monorepo's copy already had — the extraction predates
it, which is why this repo silently kept the old behaviour. Drawn untiled and
letterboxed rather than masked and cropped, because a composition is not an icon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 23:58:39 +01:00
co-authored by Claude Opus 5
parent cb12b1b023
commit debd280f76
3 changed files with 22 additions and 3 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+6 -1
View File
@@ -10,6 +10,8 @@ import { ThemeProvider, useTheme } from './ui/useTheme';
import { VpnScreen } from './vpn/VpnScreen'; import { VpnScreen } from './vpn/VpnScreen';
const appIcon = require('../assets/icon.png'); const appIcon = require('../assets/icon.png');
// The splash keeps the fuller mesh scene the icon used to be, now that the icon is a reduction of it.
const splashArt = require('../assets/splash.png');
const wordmark = require('../assets/wordmark.png'); const wordmark = require('../assets/wordmark.png');
/** /**
@@ -52,6 +54,7 @@ export const App = () => {
<Root onShowCredits={() => setShowCredits(true)} /> <Root onShowCredits={() => setShowCredits(true)} />
{showSplash ? ( {showSplash ? (
<AppSplash <AppSplash
art={splashArt}
icon={appIcon} icon={appIcon}
wordmark={wordmark} wordmark={wordmark}
name="OffScale" name="OffScale"
@@ -59,7 +62,9 @@ export const App = () => {
onDone={() => setShowSplash(false)} onDone={() => setShowSplash(false)}
/> />
) : null} ) : null}
{showCredits ? <AppSplash icon={appIcon} wordmark={wordmark} name="OffScale" hold onDone={() => setShowCredits(false)} /> : null} {showCredits ? (
<AppSplash icon={appIcon} art={splashArt} wordmark={wordmark} name="OffScale" hold onDone={() => setShowCredits(false)} />
) : null}
</ThemeProvider> </ThemeProvider>
</SafeAreaProvider> </SafeAreaProvider>
); );
+16 -2
View File
@@ -13,6 +13,16 @@ import type { ImageSourcePropType } from 'react-native';
export type AppSplashProps = { export type AppSplashProps = {
/** The app's branding icon (its `assets/icon.png` art). */ /** The app's branding icon (its `assets/icon.png` art). */
icon: ImageSourcePropType; icon: ImageSourcePropType;
/**
* Artwork to show INSTEAD of the icon, when the two are not the same picture.
*
* They stopped being the same on 2026-08-15: the icon became a reduction that survives 40 points,
* while the splash keeps the fuller scene there is room for. Without this the splash silently
* inherited every icon change, which is how replacing the icon replaced the splash too.
*
* Drawn untiled and letterboxed rather than masked and cropped — it is a composition, not an icon.
*/
art?: ImageSourcePropType;
/** App name shown under the icon (e.g. "OffTailscale"), and the accessibility label for `wordmark`. */ /** App name shown under the icon (e.g. "OffTailscale"), and the accessibility label for `wordmark`. */
name: string; name: string;
/** /**
@@ -35,7 +45,7 @@ export type AppSplashProps = {
*/ */
export const WORDMARK_ASPECT = 1759 / 345; export const WORDMARK_ASPECT = 1759 / 345;
export const AppSplash = ({ icon, name, wordmark, tagline, onDone, zIndex = 100, hold = false }: AppSplashProps) => { export const AppSplash = ({ icon, art, name, wordmark, tagline, onDone, zIndex = 100, hold = false }: AppSplashProps) => {
const { width, height } = useWindowDimensions(); const { width, height } = useWindowDimensions();
// One native-driven 0→1 timing over the whole intro; opacity + icon scale are INTERPOLATED from it. // One native-driven 0→1 timing over the whole intro; opacity + icon scale are INTERPOLATED from it.
const progress = useRef(new Animated.Value(0)).current; const progress = useRef(new Animated.Value(0)).current;
@@ -78,7 +88,11 @@ export const AppSplash = ({ icon, name, wordmark, tagline, onDone, zIndex = 100,
]} ]}
> >
<Animated.View style={{ transform: [{ translateY: bob }, { scale }] }}> <Animated.View style={{ transform: [{ translateY: bob }, { scale }] }}>
<Image source={icon} style={{ width: size, height: size, borderRadius: size * 0.22 }} resizeMode="cover" /> {art ? (
<Image source={art} style={{ width: size, height: size }} resizeMode="contain" />
) : (
<Image source={icon} style={{ width: size, height: size, borderRadius: size * 0.22 }} resizeMode="cover" />
)}
</Animated.View> </Animated.View>
{wordmark ? ( {wordmark ? (
<Image <Image