chore(release): bump to v0.2.1 / versionCode 9 — theme-crash, double-splash, dm-reopen fixes
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
6ac6a26b9c
commit
4124463097
@ -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
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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() {
|
||||
>
|
||||
<View style={{ flex: 1, backgroundColor: chatBg }}>
|
||||
<DmChatBackground />
|
||||
{isLoading && messages.length === 0 ? (
|
||||
{(isLoading || isFetching) && messages.length === 0 ? (
|
||||
<View style={styles.loadingBox}>
|
||||
<ActivityIndicator color={colors.brandOrange} />
|
||||
</View>
|
||||
|
||||
38
apps/rebreak-native/plugins/with-material-theme-android.js
Normal file
38
apps/rebreak-native/plugins/with-material-theme-android.js
Normal file
@ -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;
|
||||
82
apps/rebreak-native/plugins/with-release-signing-android.js
Normal file
82
apps/rebreak-native/plugins/with-release-signing-android.js
Normal file
@ -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=<password>
|
||||
* keyPassword=<password>
|
||||
* 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;
|
||||
Loading…
x
Reference in New Issue
Block a user