Files
RN_Template/RN_TEMPLATE/app/screens/AboutScreen.tsx
2026-02-05 13:16:05 +08:00

218 lines
6.4 KiB
TypeScript

import { FC, useCallback, useState } from "react"
import { Image, ImageStyle, TextStyle, View, ViewStyle } from "react-native"
import * as Application from "expo-application"
import * as WebBrowser from "expo-web-browser"
import { Icon } from "@/components/Icon"
import { ListItem } from "@/components/ListItem"
import { Modal } from "@/components/Modal"
import { Screen } from "@/components/Screen"
import { Text } from "@/components/Text"
import { translate } from "@/i18n/translate"
import { AppStackScreenProps } from "@/navigators/navigationTypes"
import { useAppTheme } from "@/theme/context"
import { $styles } from "@/theme/styles"
import type { ThemedStyle } from "@/theme/types"
import { s } from "@/utils/responsive"
import { useHeader } from "@/utils/useHeader"
const appLogo = require("@assets/images/logo.png")
const usingHermes = typeof HermesInternal === "object" && HermesInternal !== null
export const AboutScreen: FC<AppStackScreenProps<"About">> = function AboutScreen({ navigation }) {
const { themed, theme } = useAppTheme()
const [showVersionDetails, setShowVersionDetails] = useState(false)
// @ts-expect-error
const usingFabric = global.nativeFabricUIManager != null
const appVersion = Application.nativeApplicationVersion || "1.0.0"
const openPrivacyPolicy = useCallback(async () => {
// TODO: Replace with actual privacy policy URL
await WebBrowser.openBrowserAsync("https://example.com/privacy")
}, [])
const openTermsOfService = useCallback(async () => {
// TODO: Replace with actual terms of service URL
await WebBrowser.openBrowserAsync("https://example.com/terms")
}, [])
useHeader(
{
titleTx: "aboutScreen:title",
leftIcon: "back",
onLeftPress: () => navigation.goBack(),
},
[],
)
return (
<Screen preset="fixed" contentContainerStyle={$styles.flex1}>
{/* Logo Section */}
<View style={themed($logoSection)}>
<Image style={themed($logo)} source={appLogo} resizeMode="contain" />
<Text size="lg" weight="medium" style={themed($appName)}>
{Application.applicationName}
</Text>
<Text size="sm" style={themed($versionText)}>
v{appVersion}
</Text>
</View>
{/* List Items Section */}
<View style={themed($listContainer)}>
<ListItem
tx="aboutScreen:privacyPolicy"
leftIcon="shield"
leftIconColor={theme.colors.tint}
rightIcon="caretRight"
onPress={openPrivacyPolicy}
/>
<ListItem
tx="aboutScreen:termsOfService"
leftIcon="fileText"
leftIconColor={theme.colors.tint}
rightIcon="caretRight"
onPress={openTermsOfService}
/>
<ListItem
tx="aboutScreen:version"
leftIcon="info"
leftIconColor={theme.colors.tint}
RightComponent={
<View style={$rightContainer}>
<Text size="xs" style={themed($versionBadge)}>
v{appVersion}
</Text>
<Icon icon="caretRight" size={24} color={theme.colors.textDim} />
</View>
}
onPress={() => setShowVersionDetails(true)}
/>
</View>
{/* Version Details Modal */}
<Modal
visible={showVersionDetails}
onClose={() => setShowVersionDetails(false)}
titleTx="aboutScreen:appInfo"
confirmButtonProps={{
tx: "common:ok",
onPress: () => setShowVersionDetails(false),
}}
>
<View style={themed($infoRow)}>
<Text size="sm" style={themed($infoLabel)}>
{translate("aboutScreen:appName")}
</Text>
<Text size="sm">{Application.applicationName}</Text>
</View>
<View style={themed($infoRow)}>
<Text size="sm" style={themed($infoLabel)}>
{translate("aboutScreen:version")}
</Text>
<Text size="sm">{Application.nativeApplicationVersion}</Text>
</View>
<View style={themed($infoRow)}>
<Text size="sm" style={themed($infoLabel)}>
{translate("aboutScreen:buildVersion")}
</Text>
<Text size="sm">{Application.nativeBuildVersion}</Text>
</View>
<View style={themed($infoRow)}>
<Text size="sm" style={themed($infoLabel)}>
{translate("aboutScreen:appId")}
</Text>
<Text size="sm" style={themed($infoValue)}>
{Application.applicationId}
</Text>
</View>
<View style={themed($infoRow)}>
<Text size="sm" style={themed($infoLabel)}>
Hermes
</Text>
<Text size="sm">{usingHermes ? "Enabled" : "Disabled"}</Text>
</View>
<View style={themed($infoRowLast)}>
<Text size="sm" style={themed($infoLabel)}>
Fabric
</Text>
<Text size="sm">{usingFabric ? "Enabled" : "Disabled"}</Text>
</View>
</Modal>
</Screen>
)
}
const $logoSection: ThemedStyle<ViewStyle> = ({ spacing }) => ({
alignItems: "center",
paddingHorizontal: spacing.lg,
paddingTop: spacing.xxxl,
paddingBottom: spacing.xl,
})
const $listContainer: ThemedStyle<ViewStyle> = ({ spacing }) => ({
flex: 1,
paddingHorizontal: spacing.lg,
})
const $logo: ThemedStyle<ImageStyle> = ({ spacing }) => ({
height: s(100),
width: s(100),
marginBottom: spacing.md,
})
const $appName: ThemedStyle<TextStyle> = ({ spacing }) => ({
marginBottom: spacing.xs,
})
const $versionText: ThemedStyle<TextStyle> = ({ colors }) => ({
color: colors.textDim,
})
const $rightContainer: ViewStyle = {
flexDirection: "row",
alignItems: "center",
}
const $versionBadge: ThemedStyle<TextStyle> = ({ colors, spacing }) => ({
color: colors.textDim,
marginRight: spacing.xs,
})
const $infoRow: ThemedStyle<ViewStyle> = ({ spacing }) => ({
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: spacing.sm,
borderBottomWidth: 1,
borderBottomColor: "rgba(0,0,0,0.05)",
})
const $infoRowLast: ThemedStyle<ViewStyle> = ({ spacing }) => ({
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: spacing.sm,
})
const $infoLabel: ThemedStyle<TextStyle> = ({ colors }) => ({
color: colors.textDim,
flexShrink: 0,
marginRight: s(16),
})
const $infoValue: ThemedStyle<TextStyle> = () => ({
flex: 1,
textAlign: "right",
})