Update Popup Screen Localization Patch

This commit is contained in:
cyberalby2 2026-03-06 23:24:27 +01:00
parent 8677939111
commit 70be0cf8f2
3 changed files with 389 additions and 350 deletions

View file

@ -1,383 +1,398 @@
import React from 'react'; import React from 'react';
import { import {
View, View,
Text, Text,
StyleSheet, StyleSheet,
TouchableOpacity, TouchableOpacity,
Modal, Modal,
Dimensions, Dimensions,
Platform, Platform,
} from 'react-native'; } from 'react-native';
import { MaterialIcons } from '@expo/vector-icons'; import { MaterialIcons } from '@expo/vector-icons';
import { useTheme } from '../contexts/ThemeContext'; import { useTheme } from '../contexts/ThemeContext';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
import AndroidUpdatePopup from './AndroidUpdatePopup'; import AndroidUpdatePopup from './AndroidUpdatePopup';
import { t } from 'i18next';
const { width, height } = Dimensions.get('window'); const { width, height } = Dimensions.get('window');
interface UpdatePopupProps { interface UpdatePopupProps {
visible: boolean; visible: boolean;
updateInfo: { updateInfo: {
isAvailable: boolean; isAvailable: boolean;
manifest?: { manifest?: {
id?: string; id?: string;
version?: string; version?: string;
description?: string; description?: string;
}; };
}; };
onUpdateNow: () => void; onUpdateNow: () => void;
onUpdateLater: () => void; onUpdateLater: () => void;
onDismiss: () => void; onDismiss: () => void;
isInstalling?: boolean; isInstalling?: boolean;
} }
const UpdatePopup: React.FC<UpdatePopupProps> = ({ const UpdatePopup: React.FC<UpdatePopupProps> = ({
visible, visible,
updateInfo, updateInfo,
onUpdateNow, onUpdateNow,
onUpdateLater, onUpdateLater,
onDismiss, onDismiss,
isInstalling = false, isInstalling = false,
}) => { }) => {
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const getReleaseNotes = () => { const getReleaseNotes = () => {
const manifest: any = updateInfo?.manifest || {}; const manifest: any = updateInfo?.manifest || {};
return ( return (
manifest.description || manifest.description ||
manifest.releaseNotes || manifest.releaseNotes ||
manifest.extra?.releaseNotes || manifest.extra?.releaseNotes ||
manifest.metadata?.releaseNotes || manifest.metadata?.releaseNotes ||
'' ''
); );
}; };
const handleUpdateNow = () => { const handleUpdateNow = () => {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
} }
onUpdateNow(); onUpdateNow();
}; };
const handleUpdateLater = () => { const handleUpdateLater = () => {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
} }
onUpdateLater(); onUpdateLater();
}; };
const handleDismiss = () => { const handleDismiss = () => {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
} }
onDismiss(); onDismiss();
}; };
if (!visible || !updateInfo.isAvailable) { if (!visible || !updateInfo.isAvailable) {
return null; return null;
} }
// iOS implementation with full features // iOS implementation with full features
return ( return (
<Modal <Modal
visible={visible} visible={visible}
transparent transparent
animationType="fade" animationType="fade"
statusBarTranslucent={true} statusBarTranslucent={true}
presentationStyle="overFullScreen" presentationStyle="overFullScreen"
supportedOrientations={['portrait', 'landscape', 'landscape-left', 'landscape-right']} supportedOrientations={[
> 'portrait',
<View style={styles.overlay}> 'landscape',
<View style={[ 'landscape-left',
styles.popup, 'landscape-right',
{ ]}
backgroundColor: currentTheme.colors.darkBackground || '#1a1a1a', >
borderColor: currentTheme.colors.elevation2 || '#333333', <View style={styles.overlay}>
marginTop: insets.top + 20, <View
marginBottom: insets.bottom + 20, style={[
} styles.popup,
]}> {
{/* Header */} backgroundColor: currentTheme.colors.darkBackground || '#1a1a1a',
<View style={styles.header}> borderColor: currentTheme.colors.elevation2 || '#333333',
<View style={[ marginTop: insets.top + 20,
styles.iconContainer, marginBottom: insets.bottom + 20,
{ backgroundColor: `${currentTheme.colors.primary}20` } },
]}> ]}
<MaterialIcons >
name="system-update" {/* Header */}
size={32} <View style={styles.header}>
color={currentTheme.colors.primary} <View
/> style={[
</View> styles.iconContainer,
<Text style={[ { backgroundColor: `${currentTheme.colors.primary}20` },
styles.title, ]}
{ color: currentTheme.colors.highEmphasis } >
]}> <MaterialIcons
Update Available name="system-update"
</Text> size={32}
<Text style={[ color={currentTheme.colors.primary}
styles.subtitle, />
{ color: currentTheme.colors.mediumEmphasis } </View>
]}> <Text style={[styles.title, { color: currentTheme.colors.highEmphasis }]}>
A new version of Nuvio is ready to install {t('update_popup.update_available')}
</Text> </Text>
</View> <Text
style={[styles.subtitle, { color: currentTheme.colors.mediumEmphasis }]}
>
{t('update_popup.new_version')}
</Text>
</View>
{/* Update Info */} {/* Update Info */}
<View style={styles.updateInfo}> <View style={styles.updateInfo}>
<View style={styles.infoRow}> <View style={styles.infoRow}>
<MaterialIcons <MaterialIcons
name="info-outline" name="info-outline"
size={16} size={16}
color={currentTheme.colors.primary} color={currentTheme.colors.primary}
/> />
<Text style={[ <Text
styles.infoLabel, style={[
{ color: currentTheme.colors.mediumEmphasis } styles.infoLabel,
]}> { color: currentTheme.colors.mediumEmphasis },
Version: ]}
</Text> >
<Text {t('update_popup.version')}
style={[ </Text>
styles.infoValue, <Text
{ color: currentTheme.colors.highEmphasis } style={[styles.infoValue, { color: currentTheme.colors.highEmphasis }]}
]} numberOfLines={3}
numberOfLines={3} ellipsizeMode="tail"
ellipsizeMode="tail" selectable
selectable >
> {updateInfo.manifest?.id || 'Latest'}
{updateInfo.manifest?.id || 'Latest'} </Text>
</Text> </View>
</View>
{!!getReleaseNotes() && ( {!!getReleaseNotes() && (
<View style={styles.descriptionContainer}> <View style={styles.descriptionContainer}>
<Text style={[ <Text
styles.description, style={[
{ color: currentTheme.colors.mediumEmphasis } styles.description,
]}> { color: currentTheme.colors.mediumEmphasis },
{getReleaseNotes()} ]}
</Text> >
</View> {getReleaseNotes()}
)} </Text>
</View> </View>
)}
</View>
{/* Actions */} {/* Actions */}
<View style={styles.actions}> <View style={styles.actions}>
<TouchableOpacity <TouchableOpacity
style={[ style={[
styles.button, styles.button,
styles.primaryButton, styles.primaryButton,
{ backgroundColor: currentTheme.colors.primary }, { backgroundColor: currentTheme.colors.primary },
isInstalling && styles.disabledButton isInstalling && styles.disabledButton,
]} ]}
onPress={handleUpdateNow} onPress={handleUpdateNow}
disabled={isInstalling} disabled={isInstalling}
activeOpacity={0.8} activeOpacity={0.8}
> >
{isInstalling ? ( {isInstalling ? (
<> <>
<MaterialIcons name="install-mobile" size={18} color="white" /> <MaterialIcons name="install-mobile" size={18} color="white" />
<Text style={styles.buttonText}>Installing...</Text> <Text style={styles.buttonText}>{t('update_popup.installing')}</Text>
</> </>
) : ( ) : (
<> <>
<MaterialIcons name="download" size={18} color="white" /> <MaterialIcons name="download" size={18} color="white" />
<Text style={styles.buttonText}>Update Now</Text> <Text style={styles.buttonText}>{t('update_popup.update_now')}</Text>
</> </>
)} )}
</TouchableOpacity> </TouchableOpacity>
<View style={styles.secondaryActions}> <View style={styles.secondaryActions}>
<TouchableOpacity <TouchableOpacity
style={[ style={[
styles.button, styles.button,
styles.secondaryButton, styles.secondaryButton,
{ {
backgroundColor: currentTheme.colors.darkBackground || '#2a2a2a', backgroundColor: currentTheme.colors.darkBackground || '#2a2a2a',
borderColor: currentTheme.colors.elevation3 || '#444444', borderColor: currentTheme.colors.elevation3 || '#444444',
} },
]} ]}
onPress={handleUpdateLater} onPress={handleUpdateLater}
disabled={isInstalling} disabled={isInstalling}
activeOpacity={0.7} activeOpacity={0.7}
> >
<Text style={[ <Text
styles.secondaryButtonText, style={[
{ color: currentTheme.colors.mediumEmphasis } styles.secondaryButtonText,
]}> { color: currentTheme.colors.mediumEmphasis },
Later ]}
</Text> >
</TouchableOpacity> {t('update_popup.later')}
</Text>
</TouchableOpacity>
<TouchableOpacity <TouchableOpacity
style={[ style={[
styles.button, styles.button,
styles.secondaryButton, styles.secondaryButton,
{ {
backgroundColor: currentTheme.colors.darkBackground || '#2a2a2a', backgroundColor: currentTheme.colors.darkBackground || '#2a2a2a',
borderColor: currentTheme.colors.elevation3 || '#444444', borderColor: currentTheme.colors.elevation3 || '#444444',
} },
]} ]}
onPress={handleDismiss} onPress={handleDismiss}
disabled={isInstalling} disabled={isInstalling}
activeOpacity={0.7} activeOpacity={0.7}
> >
<Text style={[ <Text
styles.secondaryButtonText, style={[
{ color: currentTheme.colors.mediumEmphasis } styles.secondaryButtonText,
]}> { color: currentTheme.colors.mediumEmphasis },
Dismiss ]}
</Text> >
</TouchableOpacity> {t('update_popup.dismiss')}
</View> </Text>
</View> </TouchableOpacity>
</View> </View>
</View> </View>
</Modal> </View>
); </View>
</Modal>
);
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
overlay: { overlay: {
flex: 1, flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.8)', backgroundColor: 'rgba(0, 0, 0, 0.8)',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
paddingHorizontal: 20, paddingHorizontal: 20,
}, },
popup: { popup: {
width: Math.min(width - 40, 400), width: Math.min(width - 40, 400),
borderRadius: 20, borderRadius: 20,
borderWidth: 1, borderWidth: 1,
backgroundColor: '#1a1a1a', // Solid background - not transparent backgroundColor: '#1a1a1a', // Solid background - not transparent
...(Platform.OS === 'ios' ? { ...(Platform.OS === 'ios'
shadowColor: '#000', ? {
shadowOffset: { width: 0, height: 10 }, shadowColor: '#000',
shadowOpacity: 0.5, shadowOffset: { width: 0, height: 10 },
shadowRadius: 20, shadowOpacity: 0.5,
} : { shadowRadius: 20,
elevation: 15, }
}), : {
overflow: 'hidden', elevation: 15,
}, }),
header: { overflow: 'hidden',
alignItems: 'center', },
paddingHorizontal: 24, header: {
paddingTop: 32, alignItems: 'center',
paddingBottom: 20, paddingHorizontal: 24,
}, paddingTop: 32,
iconContainer: { paddingBottom: 20,
width: 64, },
height: 64, iconContainer: {
borderRadius: 32, width: 64,
alignItems: 'center', height: 64,
justifyContent: 'center', borderRadius: 32,
marginBottom: 16, alignItems: 'center',
}, justifyContent: 'center',
title: { marginBottom: 16,
fontSize: 24, },
fontWeight: '700', title: {
letterSpacing: 0.3, fontSize: 24,
marginBottom: 8, fontWeight: '700',
textAlign: 'center', letterSpacing: 0.3,
}, marginBottom: 8,
subtitle: { textAlign: 'center',
fontSize: 16, },
textAlign: 'center', subtitle: {
lineHeight: 22, fontSize: 16,
}, textAlign: 'center',
updateInfo: { lineHeight: 22,
paddingHorizontal: 24, },
paddingBottom: 20, updateInfo: {
}, paddingHorizontal: 24,
infoRow: { paddingBottom: 20,
flexDirection: 'row', },
alignItems: 'flex-start', infoRow: {
marginBottom: 12, flexDirection: 'row',
}, alignItems: 'flex-start',
infoLabel: { marginBottom: 12,
fontSize: 14, },
fontWeight: '500', infoLabel: {
marginLeft: 8, fontSize: 14,
marginRight: 8, fontWeight: '500',
marginTop: 2, marginLeft: 8,
minWidth: 60, marginRight: 8,
}, marginTop: 2,
infoValue: { minWidth: 60,
fontSize: 14, },
fontWeight: '600', infoValue: {
flex: 1, fontSize: 14,
lineHeight: 20, fontWeight: '600',
}, flex: 1,
descriptionContainer: { lineHeight: 20,
marginTop: 8, },
padding: 12, descriptionContainer: {
borderRadius: 8, marginTop: 8,
backgroundColor: 'rgba(255, 255, 255, 0.15)', padding: 12,
}, borderRadius: 8,
description: { backgroundColor: 'rgba(255, 255, 255, 0.15)',
fontSize: 14, },
lineHeight: 20, description: {
}, fontSize: 14,
actions: { lineHeight: 20,
paddingHorizontal: 24, },
paddingBottom: 20, actions: {
}, paddingHorizontal: 24,
button: { paddingBottom: 20,
flexDirection: 'row', },
alignItems: 'center', button: {
justifyContent: 'center', flexDirection: 'row',
paddingVertical: 14, alignItems: 'center',
paddingHorizontal: 20, justifyContent: 'center',
borderRadius: 12, paddingVertical: 14,
gap: 8, paddingHorizontal: 20,
marginBottom: 12, borderRadius: 12,
}, gap: 8,
primaryButton: { marginBottom: 12,
...(Platform.OS === 'ios' ? { },
shadowColor: '#000', primaryButton: {
shadowOffset: { width: 0, height: 4 }, ...(Platform.OS === 'ios'
shadowOpacity: 0.2, ? {
shadowRadius: 8, shadowColor: '#000',
} : { shadowOffset: { width: 0, height: 4 },
elevation: 4, shadowOpacity: 0.2,
}), shadowRadius: 8,
}, }
secondaryButton: { : {
borderWidth: 1, elevation: 4,
flex: 1, }),
marginHorizontal: 4, },
}, secondaryButton: {
disabledButton: { borderWidth: 1,
opacity: 0.6, flex: 1,
}, marginHorizontal: 4,
buttonText: { },
color: 'white', disabledButton: {
fontSize: 16, opacity: 0.6,
fontWeight: '600', },
letterSpacing: 0.3, buttonText: {
}, color: 'white',
secondaryActions: { fontSize: 16,
flexDirection: 'row', fontWeight: '600',
gap: 8, letterSpacing: 0.3,
}, },
secondaryButtonText: { secondaryActions: {
fontSize: 15, flexDirection: 'row',
fontWeight: '500', gap: 8,
}, },
footer: { secondaryButtonText: {
paddingHorizontal: 24, fontSize: 15,
paddingBottom: 24, fontWeight: '500',
alignItems: 'center', },
}, footer: {
footerText: { paddingHorizontal: 24,
fontSize: 12, paddingBottom: 24,
textAlign: 'center', alignItems: 'center',
opacity: 0.7, },
}, footerText: {
fontSize: 12,
textAlign: 'center',
opacity: 0.7,
},
}); });
export default UpdatePopup; export default UpdatePopup;

View file

@ -1549,5 +1549,20 @@
"moderate": "moderate", "moderate": "moderate",
"mild": "mild", "mild": "mild",
"none": "none" "none": "none"
},
"skip_intro_button": {
"skip": "Skip",
"skip_intro": "Skip Intro",
"skip_ending": "Skip Ending",
"skip_recap": "Skip Recap"
},
"update_popup": {
"update_available": "Update Available",
"new_version": "A new version of Nuvio is ready to install",
"version": "Version:",
"update_now": "Update Now",
"installing": "Installing...",
"later": "Later",
"dismiss": "Dismiss"
} }
} }

View file

@ -1555,5 +1555,14 @@
"skip_intro": "Salta Inizio", "skip_intro": "Salta Inizio",
"skip_ending": "Salta Fine", "skip_ending": "Salta Fine",
"skip_recap": "Salta Riassunto" "skip_recap": "Salta Riassunto"
},
"update_popup": {
"update_available": "Aggiornamento Disponibile",
"new_version": "Una nuova versione di Nuvio è pronta per esser installata",
"version": "Versione:",
"update_now": "Aggiorna Ora",
"installing": "Installazione...",
"later": "Dopo",
"dismiss": "Ignora"
} }
} }