From 41244630970dabf5eeab8d184bb9cb788706bbaa Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Sat, 16 May 2026 09:32:28 +0200 Subject: [PATCH] =?UTF-8?q?chore(release):=20bump=20to=20v0.2.1=20/=20vers?= =?UTF-8?q?ionCode=209=20=E2=80=94=20theme-crash,=20double-splash,=20dm-re?= =?UTF-8?q?open=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Android Theme parent → Theme.MaterialComponents.DayNight.NoActionBar.Bridge (fix BadgeDrawable crash in react-native-bottom-tabs after AccessibilityService toggle) - Plugin with-material-theme-android keeps theme idempotent across prebuilds - Plugin with-release-signing-android wires release signingConfig from key.properties - Splash: align native splash image with JS BrandSplash (icon.png) to eliminate double-splash flicker on app start - DM: reset partner/messages/replyTo state on userId change, disable cache for history query, switch spinner condition to isLoading||isFetching so reopens always load fresh and never show empty-state with stale partner Co-Authored-By: Claude Opus 4.7 --- apps/rebreak-native/CHANGELOG.md | 8 ++ apps/rebreak-native/app.config.ts | 12 ++- apps/rebreak-native/app/dm.tsx | 16 +++- .../plugins/with-material-theme-android.js | 38 +++++++++ .../plugins/with-release-signing-android.js | 82 +++++++++++++++++++ 5 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 apps/rebreak-native/plugins/with-material-theme-android.js create mode 100644 apps/rebreak-native/plugins/with-release-signing-android.js diff --git a/apps/rebreak-native/CHANGELOG.md b/apps/rebreak-native/CHANGELOG.md index df2295b..c497d61 100644 --- a/apps/rebreak-native/CHANGELOG.md +++ b/apps/rebreak-native/CHANGELOG.md @@ -10,6 +10,14 @@ Versioning: `version` follows SemVer, `versionCode` is monotonically increasing. --- +## [0.2.1] — versionCode 9 — 2026-05-16 + +### Fixed +- **Android-Crash nach AccessibilityService-Aktivierung**: BadgeDrawable in der Tab-Bar craste mit `IllegalArgumentException`, weil das Theme `AppCompat` statt `MaterialComponents` nutzte. Theme-Parent auf `Theme.MaterialComponents.DayNight.NoActionBar.Bridge` geändert via Config-Plugin — idempotent nach jedem prebuild. +- **Doppel-Splash auf Android**: Native Android-Splash und JS-Splash sahen unterschiedlich aus (zwei aufeinanderfolgende Splash-Screens). Splash-Image auf `icon.png` umgestellt, matched jetzt exakt den JS-Splash und Landing-Screen. + +--- + ## [0.2.0] — versionCode 8 — 2026-05-16 ### Added diff --git a/apps/rebreak-native/app.config.ts b/apps/rebreak-native/app.config.ts index 178f926..fd85973 100644 --- a/apps/rebreak-native/app.config.ts +++ b/apps/rebreak-native/app.config.ts @@ -4,7 +4,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, name: "ReBreak", slug: "rebreak", - version: "0.2.0", + version: "0.2.1", orientation: "portrait", icon: "./assets/icon.png", scheme: "rebreak", @@ -12,7 +12,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ newArchEnabled: true, splash: { - image: "./assets/splash.png", + image: "./assets/icon.png", resizeMode: "contain", backgroundColor: "#0f172a", }, @@ -20,7 +20,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ ios: { supportsTablet: true, bundleIdentifier: "org.rebreak.app", - buildNumber: "8", + buildNumber: "9", config: { usesNonExemptEncryption: false, }, @@ -39,7 +39,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ android: { package: "org.rebreak.app", - versionCode: 8, + versionCode: 9, adaptiveIcon: { // Foreground muss in der ~66%-Safe-Zone bleiben (Launcher-Mask clippt den // Außenring) → adaptive-foreground.png ist das Logo auf transparentem @@ -88,6 +88,10 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ "./plugins/with-rebreak-protection-android", // Rive-Asset (lyra-avatar.riv) als Android raw-resource bundlen "./plugins/with-rive-asset-android", + // MaterialComponents-Theme-Fix für BadgeDrawable in react-native-bottom-tabs + "./plugins/with-material-theme-android", + // Release-Signing-Block in build.gradle (liest android/key.properties — nicht committen) + "./plugins/with-release-signing-android", ], experiments: { diff --git a/apps/rebreak-native/app/dm.tsx b/apps/rebreak-native/app/dm.tsx index c9f4b1e..001eb1c 100644 --- a/apps/rebreak-native/app/dm.tsx +++ b/apps/rebreak-native/app/dm.tsx @@ -74,6 +74,14 @@ export default function DmScreen() { ); const [sending, setSending] = useState(false); + // Reset aller conversation-spezifischen States wenn userId wechselt (Stack-Reuse) + useEffect(() => { + setMessages([]); + setPartner(null); + partnerRef.current = null; + setReplyTo(null); + }, [userId]); + // Lade meine User-ID useEffect(() => { supabase.auth.getSession().then(({ data }) => { @@ -81,8 +89,8 @@ export default function DmScreen() { }); }, []); - // Lade DM-History - const { isLoading } = useQuery({ + // Lade DM-History — staleTime:0 erzwingt immer frischen Fetch (kein Cache-Hit-Bug) + const { isLoading, isFetching } = useQuery({ queryKey: ['dm-history', userId], queryFn: async () => { console.log('[dm] fetching history for partner', userId, 'me', myUserId); @@ -124,6 +132,8 @@ export default function DmScreen() { } }, enabled: !!userId && !!myUserId, + staleTime: 0, + gcTime: 0, }); // Realtime: neue DMs vom Partner @@ -270,7 +280,7 @@ export default function DmScreen() { > - {isLoading && messages.length === 0 ? ( + {(isLoading || isFetching) && messages.length === 0 ? ( diff --git a/apps/rebreak-native/plugins/with-material-theme-android.js b/apps/rebreak-native/plugins/with-material-theme-android.js new file mode 100644 index 0000000..7388deb --- /dev/null +++ b/apps/rebreak-native/plugins/with-material-theme-android.js @@ -0,0 +1,38 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/** + * Expo Config-Plugin — setzt den AppTheme-Parent auf + * Theme.MaterialComponents.DayNight.NoActionBar.Bridge damit react-native-bottom-tabs' + * BadgeDrawable nicht mit IllegalArgumentException crasht. + * + * Warum Bridge-Variante: bridged MaterialComponents ist ein Superset von AppCompat — + * alle AppCompat-Abhängigkeiten im RN-Stack bleiben kompatibel, aber Material-APIs + * (BadgeDrawable, NavigationBarMenuView etc.) funktionieren. + * + * Warum Plugin statt Hand-Edit: `prebuild --clean` regeneriert styles.xml aus dem + * Expo-AppCompat-Default. Dieses Plugin patcht den Parent-Wert nach jedem prebuild + * idempotent per withAndroidStyles (offizielles Config-Plugins-API). + */ + +const { withAndroidStyles } = require('@expo/config-plugins'); + +const MATERIAL_THEME = 'Theme.MaterialComponents.DayNight.NoActionBar.Bridge'; + +function withMaterialThemeAndroid(config) { + return withAndroidStyles(config, (cfg) => { + const styles = cfg.modResults.resources.style; + + if (!Array.isArray(styles)) return cfg; + + const appTheme = styles.find( + (s) => s.$ && s.$['name'] === 'AppTheme', + ); + + if (appTheme) { + appTheme.$['parent'] = MATERIAL_THEME; + } + + return cfg; + }); +} + +module.exports = withMaterialThemeAndroid; diff --git a/apps/rebreak-native/plugins/with-release-signing-android.js b/apps/rebreak-native/plugins/with-release-signing-android.js new file mode 100644 index 0000000..7a1d110 --- /dev/null +++ b/apps/rebreak-native/plugins/with-release-signing-android.js @@ -0,0 +1,82 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/** + * Expo Config-Plugin — fügt einen Release-Signing-Block in build.gradle ein, + * der key.properties aus android/key.properties liest. + * + * Warum Plugin statt Hand-Edit: `prebuild --clean` regeneriert build.gradle und + * überschreibt manuelle Änderungen. Dieses Plugin patcht den Signing-Block nach + * jedem prebuild idempotent via withAppBuildGradle. + * + * key.properties muss NACH prebuild in android/ abgelegt werden (nicht committen). + * Keystore muss in android/app/rebreak-release.keystore liegen (nicht committen). + * + * Format key.properties (in android/ — wird von rootProject.file() gefunden): + * storePassword= + * keyPassword= + * keyAlias=rebreak + * storeFile=rebreak-release.keystore + * + * Hinweis: storeFile ist relativ zu android/app/ (Gradle-Modul-Root), also + * Keystore in android/app/rebreak-release.keystore ablegen. + */ + +const { withAppBuildGradle } = require('@expo/config-plugins'); + +function withReleaseSigningAndroid(config) { + return withAppBuildGradle(config, (cfg) => { + let gradle = cfg.modResults.contents; + + // Idempotenz: Plugin bereits angewandt + if (gradle.includes('REBREAK_RELEASE_SIGNING')) { + return cfg; + } + + // 1. Properties-Loader vor android { Block einfügen + const propertiesLoader = ` +// REBREAK_RELEASE_SIGNING — injected by with-release-signing-android plugin +def keystorePropertiesFile = rootProject.file("key.properties") +def keystoreProperties = new Properties() +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} +`; + gradle = gradle.replace(/\nandroid \{/, `${propertiesLoader}\nandroid {`); + + // 2. Release-signingConfig in signingConfigs-Block einfügen (nach debug-Block) + // Expo generiert: signingConfigs { debug { ... } } + // Wir fügen release { ... } danach ein, direkt vor der schließenden } + const releaseSigningConfig = ` + release { + if (keystorePropertiesFile.exists()) { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile file(keystoreProperties['storeFile']) + storePassword keystoreProperties['storePassword'] + } + }`; + + // Findet den signingConfigs-Block und fügt release vor der letzten } ein + gradle = gradle.replace( + /(signingConfigs \{[\s\S]*?)( \}\n)( buildTypes)/, + `$1${releaseSigningConfig}\n }\n $3` + ); + + // 3. Im release buildType: signingConfig.debug → signingConfig.release + // Der release-Block liegt nach dem debug-Block. Wir ersetzen die zweite + // Occurrence von signingConfig signingConfigs.debug (im release-Block). + const parts = gradle.split('signingConfig signingConfigs.debug'); + if (parts.length >= 3) { + // parts[0] = vor debug-Block, parts[1] = zwischen debug und release, parts[2] = nach release + gradle = parts[0] + + 'signingConfig signingConfigs.debug' + + parts[1] + + 'signingConfig signingConfigs.release' + + parts.slice(2).join('signingConfig signingConfigs.debug'); + } + + cfg.modResults.contents = gradle; + return cfg; + }); +} + +module.exports = withReleaseSigningAndroid;