feat: Implement automatic update checking and user notifications for new versions

This commit is contained in:
zimplexing
2025-08-01 13:53:29 +08:00
parent 10bfbbbf8e
commit 18aba87ddb
18 changed files with 1524 additions and 4 deletions

View File

@@ -0,0 +1,133 @@
import React from "react";
import { View, StyleSheet, Platform, ActivityIndicator } from "react-native";
import { ThemedText } from "../ThemedText";
import { StyledButton } from "../StyledButton";
import { useUpdateStore } from "@/stores/updateStore";
import { UPDATE_CONFIG } from "@/constants/UpdateConfig";
export function UpdateSection() {
const {
currentVersion,
remoteVersion,
updateAvailable,
downloading,
downloadProgress,
checkForUpdate,
setShowUpdateModal,
} = useUpdateStore();
const [checking, setChecking] = React.useState(false);
const handleCheckUpdate = async () => {
setChecking(true);
try {
await checkForUpdate(false);
} finally {
setChecking(false);
}
};
return (
<View style={styles.sectionContainer}>
<ThemedText style={styles.sectionTitle}></ThemedText>
<View style={styles.row}>
<ThemedText style={styles.label}></ThemedText>
<ThemedText style={styles.value}>v{currentVersion}</ThemedText>
</View>
{updateAvailable && (
<View style={styles.row}>
<ThemedText style={styles.label}></ThemedText>
<ThemedText style={[styles.value, styles.newVersion]}>
v{remoteVersion}
</ThemedText>
</View>
)}
{downloading && (
<View style={styles.row}>
<ThemedText style={styles.label}></ThemedText>
<ThemedText style={styles.value}>{downloadProgress}%</ThemedText>
</View>
)}
<View style={styles.buttonContainer}>
<StyledButton
title={checking ? "检查中..." : "检查更新"}
onPress={handleCheckUpdate}
disabled={checking || downloading}
style={styles.button}
>
{checking && <ActivityIndicator color="#fff" size="small" />}
</StyledButton>
{updateAvailable && !downloading && (
<StyledButton
title="立即更新"
onPress={() => setShowUpdateModal(true)}
style={[styles.button, styles.updateButton]}
/>
)}
</View>
{UPDATE_CONFIG.AUTO_CHECK && (
<ThemedText style={styles.hint}>
{UPDATE_CONFIG.CHECK_INTERVAL / (60 * 60 * 1000)}
</ThemedText>
)}
</View>
);
}
const styles = StyleSheet.create({
sectionContainer: {
marginBottom: 24,
padding: 16,
backgroundColor: Platform.select({
ios: "rgba(255, 255, 255, 0.05)",
android: "rgba(255, 255, 255, 0.05)",
default: "transparent",
}),
borderRadius: 8,
},
sectionTitle: {
fontSize: Platform.isTV ? 24 : 20,
fontWeight: "bold",
marginBottom: 16,
},
row: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 12,
},
label: {
fontSize: Platform.isTV ? 18 : 16,
color: "#999",
},
value: {
fontSize: Platform.isTV ? 18 : 16,
},
newVersion: {
color: "#00bb5e",
fontWeight: "bold",
},
buttonContainer: {
flexDirection: "row",
gap: 12,
marginTop: 16,
},
button: {
flex: 1,
},
updateButton: {
backgroundColor: "#00bb5e",
},
hint: {
fontSize: Platform.isTV ? 14 : 12,
color: "#666",
marginTop: 12,
textAlign: "center",
},
});