Ipad适配问题
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useMemo, useState, useRef, useCallback } from 'react';
|
||||
import { Alert, FlatList, Image, Pressable, StyleSheet, Text, View, Platform, Dimensions } from 'react-native';
|
||||
import { Alert, FlatList, Image, Pressable, StyleSheet, Text, View, Platform, useWindowDimensions } from 'react-native';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { Switch } from 'react-native';
|
||||
@@ -42,8 +42,7 @@ import * as Notifications from 'expo-notifications';
|
||||
import { changeLanguage } from '@/src/i18n';
|
||||
import { fetchLegalLinks } from '@/src/services/legalApi';
|
||||
import { ensurePushTokenRegisteredIfPermitted, setPushPreferences } from '@/src/services/pushApi';
|
||||
|
||||
const { width } = Dimensions.get('window');
|
||||
import { isIPadLike } from '@/src/utils/device';
|
||||
|
||||
type Props = {
|
||||
visible: boolean;
|
||||
@@ -79,6 +78,12 @@ const NATURE_IMAGES = [
|
||||
|
||||
export default function ProfileModal({ visible, name: propName, onClose }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const { width, height } = useWindowDimensions();
|
||||
const isTablet = isIPadLike(width, height);
|
||||
const contentWidth = isTablet ? width - 40 : width - 40;
|
||||
const thumbWidth = isTablet ? Math.min(420, contentWidth - 120) : Math.min(width * 0.6, 300);
|
||||
const widgetImageWidth = isTablet ? Math.min(520, contentWidth - 24) : width * 0.9;
|
||||
const howToSlideWidth = isTablet ? Math.min(640, contentWidth) : width - 32;
|
||||
|
||||
const [page, setPage] = useState<Page>('root');
|
||||
const [navDirection, setNavDirection] = useState<NavDirection>('forward');
|
||||
@@ -192,6 +197,7 @@ export default function ProfileModal({ visible, name: propName, onClose }: Props
|
||||
{page === 'root' ? (
|
||||
<RootPage
|
||||
name={currentName}
|
||||
contentWidth={contentWidth}
|
||||
onOpenFavorites={() => go('favorites', 'forward')}
|
||||
onOpenWidget={() => go('widget', 'forward')}
|
||||
onOpenDailyReminder={() => go('dailyReminder', 'forward')}
|
||||
@@ -200,15 +206,15 @@ export default function ProfileModal({ visible, name: propName, onClose }: Props
|
||||
onOpenTerms={() => openLink(legalLinks.terms)}
|
||||
/>
|
||||
) : page === 'favorites' ? (
|
||||
<FavoritesPage visible={visible} page={page} />
|
||||
<FavoritesPage visible={visible} page={page} thumbWidth={thumbWidth} contentWidth={contentWidth} />
|
||||
) : page === 'dailyReminder' ? (
|
||||
<DailyReminderPage visible={visible} onDone={() => go('root', 'back')} />
|
||||
<DailyReminderPage visible={visible} onDone={() => go('root', 'back')} contentWidth={contentWidth} />
|
||||
) : page === 'language' ? (
|
||||
<LanguagePage />
|
||||
<LanguagePage contentWidth={contentWidth} />
|
||||
) : page === 'widgetHowTo' ? (
|
||||
<WidgetHowToPage />
|
||||
<WidgetHowToPage howToSlideWidth={howToSlideWidth} widgetImageWidth={widgetImageWidth} />
|
||||
) : (
|
||||
<WidgetPage onOpenHowTo={() => go('widgetHowTo', 'forward')} />
|
||||
<WidgetPage onOpenHowTo={() => go('widgetHowTo', 'forward')} widgetImageWidth={widgetImageWidth} />
|
||||
)}
|
||||
</Animated.View>
|
||||
</View>
|
||||
@@ -222,6 +228,7 @@ function toastTodo(t: (key: string) => string) {
|
||||
|
||||
function RootPage({
|
||||
name,
|
||||
contentWidth,
|
||||
onOpenFavorites,
|
||||
onOpenWidget,
|
||||
onOpenDailyReminder,
|
||||
@@ -230,6 +237,7 @@ function RootPage({
|
||||
onOpenTerms,
|
||||
}: {
|
||||
name?: string;
|
||||
contentWidth: number;
|
||||
onOpenFavorites: () => void;
|
||||
onOpenWidget: () => void;
|
||||
onOpenDailyReminder: () => void;
|
||||
@@ -239,7 +247,7 @@ function RootPage({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<View style={[styles.sectionWrap, { width: contentWidth }]}>
|
||||
<View style={styles.header}>
|
||||
<AvatarIcon width={234} height={183} />
|
||||
<Text style={styles.name}>{name || 'Hali'}</Text>
|
||||
@@ -276,11 +284,23 @@ function RootPage({
|
||||
onPress={onOpenLanguage}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
|
||||
<Text style={styles.versionText}>V1.0.0</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function FavoritesPage({ visible, page }: { visible: boolean; page: Page }) {
|
||||
function FavoritesPage({
|
||||
visible,
|
||||
page,
|
||||
thumbWidth,
|
||||
contentWidth,
|
||||
}: {
|
||||
visible: boolean;
|
||||
page: Page;
|
||||
thumbWidth: number;
|
||||
contentWidth: number;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [favorites, setFavorites] = useState<(FavoriteItem & { text: string })[]>([]);
|
||||
|
||||
@@ -317,7 +337,7 @@ function FavoritesPage({ visible, page }: { visible: boolean; page: Page }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.favContainer}>
|
||||
<View style={[styles.favContainer, { width: contentWidth, alignSelf: 'center' }]}>
|
||||
{favorites.length === 0 ? (
|
||||
<Text style={styles.favEmpty}>{t('favorites.empty')}</Text>
|
||||
) : (
|
||||
@@ -339,6 +359,7 @@ function FavoritesPage({ visible, page }: { visible: boolean; page: Page }) {
|
||||
<View style={styles.favRight}>
|
||||
<View style={[
|
||||
styles.favThumb,
|
||||
{ width: thumbWidth },
|
||||
item.themeMode === 'scenery' ? {} : { backgroundColor: item.background }
|
||||
]}>
|
||||
{item.themeMode === 'scenery' ? (
|
||||
@@ -346,7 +367,7 @@ function FavoritesPage({ visible, page }: { visible: boolean; page: Page }) {
|
||||
<Image
|
||||
source={NATURE_IMAGES[parseInt(item.background)]}
|
||||
style={{
|
||||
width: width * 0.6,
|
||||
width: thumbWidth,
|
||||
height: 800, // 假设原图较高,设置一个较大的高度
|
||||
position: 'absolute',
|
||||
bottom: 0, // 关键:将图片底部对齐容器底部
|
||||
@@ -378,7 +399,15 @@ function FavoritesPage({ visible, page }: { visible: boolean; page: Page }) {
|
||||
);
|
||||
}
|
||||
|
||||
function DailyReminderPage({ visible, onDone }: { visible: boolean; onDone: () => void }) {
|
||||
function DailyReminderPage({
|
||||
visible,
|
||||
onDone,
|
||||
contentWidth,
|
||||
}: {
|
||||
visible: boolean;
|
||||
onDone: () => void;
|
||||
contentWidth: number;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [timesPerDay, setTimesPerDay] = useState(3);
|
||||
@@ -525,7 +554,7 @@ function DailyReminderPage({ visible, onDone }: { visible: boolean; onDone: () =
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<View style={styles.remindRow}>
|
||||
<View style={[styles.remindRow, { width: contentWidth }]}>
|
||||
<View style={styles.rowLeft}>
|
||||
<View style={styles.rowIcon}>
|
||||
<RemindIcon width={18} height={18} />
|
||||
@@ -556,7 +585,7 @@ function DailyReminderPage({ visible, onDone }: { visible: boolean; onDone: () =
|
||||
);
|
||||
}
|
||||
|
||||
function WidgetPage({ onOpenHowTo }: { onOpenHowTo: () => void }) {
|
||||
function WidgetPage({ onOpenHowTo, widgetImageWidth }: { onOpenHowTo: () => void; widgetImageWidth: number }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const currentLang = i18n.language;
|
||||
// 需求:个人主页弹窗「小工具」页暂时隐藏锁屏小工具说明/入口
|
||||
@@ -580,13 +609,13 @@ function WidgetPage({ onOpenHowTo }: { onOpenHowTo: () => void }) {
|
||||
<View style={styles.widgetScroll}>
|
||||
{showLockScreenWidget ? (
|
||||
<Pressable style={styles.widgetItem} onPress={onOpenHowTo}>
|
||||
<Image source={widget1} style={styles.widgetImg1} resizeMode="contain" />
|
||||
<Image source={widget1} style={[styles.widgetImg1, { width: widgetImageWidth, height: widgetImageWidth * (156 / 311) }]} resizeMode="contain" />
|
||||
<Text style={styles.widgetLabel}>{t('widget.lockScreen')}</Text>
|
||||
</Pressable>
|
||||
) : null}
|
||||
|
||||
<Pressable style={styles.widgetItem} onPress={onOpenHowTo}>
|
||||
<Image source={widget2} style={styles.widgetImg2} resizeMode="contain" />
|
||||
<Image source={widget2} style={[styles.widgetImg2, { width: widgetImageWidth, height: widgetImageWidth * (175 / 311) }]} resizeMode="contain" />
|
||||
<Text style={styles.widgetLabel}>{t('widget.homeScreen')}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
@@ -594,7 +623,7 @@ function WidgetPage({ onOpenHowTo }: { onOpenHowTo: () => void }) {
|
||||
);
|
||||
}
|
||||
|
||||
function WidgetHowToPage() {
|
||||
function WidgetHowToPage({ howToSlideWidth, widgetImageWidth }: { howToSlideWidth: number; widgetImageWidth: number }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const currentLang = i18n.language;
|
||||
const flatListRef = useRef<FlatList>(null);
|
||||
@@ -631,7 +660,7 @@ function WidgetHowToPage() {
|
||||
|
||||
const onScroll = (event: any) => {
|
||||
const x = event.nativeEvent.contentOffset.x;
|
||||
const index = Math.round(x / (width - 32));
|
||||
const index = Math.round(x / howToSlideWidth);
|
||||
if (index !== activeIndex) {
|
||||
setActiveIndex(index);
|
||||
}
|
||||
@@ -655,14 +684,14 @@ function WidgetHowToPage() {
|
||||
onScrollBeginDrag={onScrollBeginDrag}
|
||||
scrollEventThrottle={16}
|
||||
renderItem={({ item }) => (
|
||||
<View style={styles.howToSlide}>
|
||||
<Image source={item.src} style={styles.howToImg} resizeMode="contain" />
|
||||
<View style={[styles.howToSlide, { width: howToSlideWidth }]}>
|
||||
<Image source={item.src} style={[styles.howToImg, { width: widgetImageWidth, height: widgetImageWidth * (234 / 326) }]} resizeMode="contain" />
|
||||
<Text style={styles.howToDesc}>{item.desc}</Text>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
|
||||
<View style={styles.pagination}>
|
||||
<View style={[styles.pagination, { top: widgetImageWidth * (234 / 326) + 35 }]}>
|
||||
{images.map((_, i) => (
|
||||
<View
|
||||
key={i}
|
||||
@@ -677,7 +706,7 @@ function WidgetHowToPage() {
|
||||
);
|
||||
}
|
||||
|
||||
function LanguagePage() {
|
||||
function LanguagePage({ contentWidth }: { contentWidth: number }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const currentLang = i18n.language;
|
||||
|
||||
@@ -687,8 +716,8 @@ function LanguagePage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<View style={styles.langPage}>
|
||||
<View style={styles.langList}>
|
||||
<View style={[styles.langPage, { width: contentWidth, alignSelf: 'center' }]}>
|
||||
<View style={[styles.langList, { width: contentWidth }]}>
|
||||
{languages.map((lang, index) => (
|
||||
<Pressable
|
||||
key={lang.id}
|
||||
@@ -750,6 +779,9 @@ const styles = StyleSheet.create({
|
||||
pageWrap: {
|
||||
// 给页面切换动画一个稳定的容器,避免布局抖动
|
||||
},
|
||||
sectionWrap: {
|
||||
alignSelf: 'center',
|
||||
},
|
||||
backRow: {
|
||||
alignSelf: 'flex-start',
|
||||
paddingVertical: 4,
|
||||
@@ -806,6 +838,13 @@ const styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
marginBottom: 8,
|
||||
},
|
||||
versionText: {
|
||||
marginTop: 12,
|
||||
textAlign: 'center',
|
||||
color: 'rgba(94,42,40,0.45)',
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
},
|
||||
item: {
|
||||
height: 52,
|
||||
paddingHorizontal: 18,
|
||||
@@ -848,7 +887,7 @@ const styles = StyleSheet.create({
|
||||
paddingVertical: 100,
|
||||
},
|
||||
favList: {
|
||||
paddingHorizontal: 20,
|
||||
paddingHorizontal: 8,
|
||||
paddingBottom: 80,
|
||||
},
|
||||
favCard: {
|
||||
@@ -872,7 +911,6 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: '#FFF4EA',
|
||||
borderRadius: 16,
|
||||
padding: 20,
|
||||
width: width * 0.6,
|
||||
height: 161,
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
@@ -945,7 +983,6 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 18,
|
||||
width: width - 40, // 屏幕宽度减去左右各 20pt
|
||||
alignSelf: 'center',
|
||||
},
|
||||
rowLeft: { flexDirection: 'row', alignItems: 'center', gap: 10 },
|
||||
@@ -1004,12 +1041,12 @@ const styles = StyleSheet.create({
|
||||
width: '100%',
|
||||
},
|
||||
widgetImg1: {
|
||||
width: width * 0.9,
|
||||
height: (width * 0.9) * (156 / 311),
|
||||
width: 320,
|
||||
height: 160,
|
||||
},
|
||||
widgetImg2: {
|
||||
width: width * 0.9,
|
||||
height: (width * 0.9) * (175 / 311),
|
||||
width: 320,
|
||||
height: 176,
|
||||
},
|
||||
widgetLabel: {
|
||||
marginTop: 12,
|
||||
@@ -1023,12 +1060,12 @@ const styles = StyleSheet.create({
|
||||
paddingTop: 20,
|
||||
},
|
||||
howToSlide: {
|
||||
width: width - 32, // 减去 SheetModal 的 paddingHorizontal: 16 * 2
|
||||
width: 320,
|
||||
alignItems: 'center',
|
||||
},
|
||||
howToImg: {
|
||||
width: width * 0.9,
|
||||
height: (width * 0.9) * (234 / 326),
|
||||
width: 320,
|
||||
height: 230,
|
||||
marginBottom: 40,
|
||||
},
|
||||
howToDesc: {
|
||||
@@ -1042,7 +1079,7 @@ const styles = StyleSheet.create({
|
||||
pagination: {
|
||||
flexDirection: 'row',
|
||||
position: 'absolute',
|
||||
top: (width * 0.9) * (234 / 326) + 35, // 根据新的图片高度动态计算
|
||||
top: 265,
|
||||
gap: 8,
|
||||
},
|
||||
dot: {
|
||||
@@ -1064,7 +1101,6 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
width: width - 40, // 屏幕宽度减去左右各 20pt
|
||||
alignSelf: 'center',
|
||||
},
|
||||
langItem: {
|
||||
|
||||
Reference in New Issue
Block a user