wip
This commit is contained in:
parent
1c05f6cd98
commit
ef5322b6ca
@ -1,4 +1,4 @@
|
||||
import { Dimensions, Platform, PixelRatio, AppState, AppStateStatus } from 'react-native';
|
||||
import { Dimensions, Platform, AppState, AppStateStatus } from 'react-native';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { isDesktop } from './environment';
|
||||
|
||||
@ -6,7 +6,7 @@ import { isDesktop } from './environment';
|
||||
export enum SizeClass {
|
||||
Compact, // Small size (iPhone width or height in landscape)
|
||||
Regular, // Standard size (iPad, or iPhone height in portrait)
|
||||
Large, // Additional size for larger screens (not in iOS, but useful for our app)
|
||||
Large, // Additional size for larger screens (not in iOS, but useful for our app)
|
||||
}
|
||||
|
||||
// Interface for the result of getSizeClass
|
||||
@ -14,17 +14,17 @@ export interface SizeClassInfo {
|
||||
// Size classes
|
||||
horizontalSizeClass: SizeClass;
|
||||
verticalSizeClass: SizeClass;
|
||||
|
||||
|
||||
// Overall size class (derived from horizontal and vertical)
|
||||
sizeClass: SizeClass;
|
||||
|
||||
|
||||
// Orientation
|
||||
orientation: 'portrait' | 'landscape';
|
||||
|
||||
|
||||
// Helper properties
|
||||
isCompact: boolean;
|
||||
isLarge: boolean;
|
||||
|
||||
|
||||
// Legacy support
|
||||
isLargeScreen: boolean;
|
||||
}
|
||||
@ -38,13 +38,12 @@ export function getSizeClass(): SizeClassInfo {
|
||||
const { width, height } = Dimensions.get('window');
|
||||
const isLandscape = width > height;
|
||||
const orientation = isLandscape ? 'landscape' : 'portrait';
|
||||
|
||||
|
||||
// Get density for more accurate sizing
|
||||
const pixelDensity = PixelRatio.get();
|
||||
|
||||
|
||||
// Determine horizontal size class (following iOS conventions)
|
||||
let horizontalSizeClass: SizeClass;
|
||||
|
||||
|
||||
if (Platform.OS === 'ios' && Platform.isPad) {
|
||||
// iPads always have Regular width
|
||||
horizontalSizeClass = SizeClass.Regular;
|
||||
@ -59,10 +58,10 @@ export function getSizeClass(): SizeClassInfo {
|
||||
// Regular iPhones: Compact width
|
||||
horizontalSizeClass = SizeClass.Compact;
|
||||
}
|
||||
|
||||
|
||||
// Determine vertical size class (following iOS conventions)
|
||||
let verticalSizeClass: SizeClass;
|
||||
|
||||
|
||||
if (Platform.OS === 'ios' && Platform.isPad) {
|
||||
// iPads always have Regular height
|
||||
verticalSizeClass = SizeClass.Regular;
|
||||
@ -76,31 +75,31 @@ export function getSizeClass(): SizeClassInfo {
|
||||
// iPhones in portrait: Regular height
|
||||
verticalSizeClass = SizeClass.Regular;
|
||||
}
|
||||
|
||||
|
||||
// Derive overall size class based on the user's specification:
|
||||
// Regular width + any height = Large screen
|
||||
let sizeClass: SizeClass;
|
||||
|
||||
|
||||
if (horizontalSizeClass === SizeClass.Compact) {
|
||||
sizeClass = SizeClass.Compact;
|
||||
} else {
|
||||
// If width is Regular or Large, overall size class is Large
|
||||
sizeClass = SizeClass.Large;
|
||||
}
|
||||
|
||||
|
||||
// Determine isLargeScreen based on horizontal size class being at least Regular
|
||||
const isLargeScreen = horizontalSizeClass !== SizeClass.Compact;
|
||||
|
||||
|
||||
return {
|
||||
horizontalSizeClass,
|
||||
verticalSizeClass,
|
||||
sizeClass,
|
||||
orientation,
|
||||
|
||||
|
||||
// Helper properties
|
||||
isCompact: sizeClass === SizeClass.Compact,
|
||||
isLarge: sizeClass === SizeClass.Large,
|
||||
|
||||
|
||||
// Legacy support
|
||||
isLargeScreen,
|
||||
};
|
||||
@ -111,7 +110,7 @@ export function getSizeClass(): SizeClassInfo {
|
||||
*/
|
||||
export function useSizeClass(): SizeClassInfo {
|
||||
const [sizeClassInfo, setSizeClassInfo] = useState<SizeClassInfo>(getSizeClass());
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Update size class when dimensions change
|
||||
const updateSizeClass = () => {
|
||||
@ -122,12 +121,12 @@ export function useSizeClass(): SizeClassInfo {
|
||||
`horizontal=${SizeClass[newInfo.horizontalSizeClass]}`,
|
||||
`vertical=${SizeClass[newInfo.verticalSizeClass]}`,
|
||||
`orientation=${newInfo.orientation}`,
|
||||
`isLargeScreen=${newInfo.isLargeScreen}`
|
||||
`isLargeScreen=${newInfo.isLargeScreen}`,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const dimensionSubscription = Dimensions.addEventListener('change', updateSizeClass);
|
||||
|
||||
|
||||
// Also update when app becomes active
|
||||
const handleAppStateChange = (nextAppState: AppStateStatus) => {
|
||||
if (nextAppState === 'active') {
|
||||
@ -136,16 +135,13 @@ export function useSizeClass(): SizeClassInfo {
|
||||
};
|
||||
|
||||
const appStateSubscription = AppState.addEventListener('change', handleAppStateChange);
|
||||
|
||||
|
||||
// Clean up
|
||||
return () => {
|
||||
dimensionSubscription.remove();
|
||||
appStateSubscription.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
return sizeClassInfo;
|
||||
}
|
||||
|
||||
// For backward compatibility
|
||||
export const useIsLargeScreen = useSizeClass;
|
||||
|
||||
@ -1,173 +0,0 @@
|
||||
import React, { createContext, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { AppState, AppStateStatus, Dimensions, Platform, useWindowDimensions } from 'react-native';
|
||||
import { isDesktop } from '../../blue_modules/environment';
|
||||
|
||||
interface ILargeScreenContext {
|
||||
isLargeScreen: boolean;
|
||||
}
|
||||
|
||||
const DRAWER_WIDTH = 320;
|
||||
const MIN_CONTENT_WIDTH = 375;
|
||||
const MIN_CONTENT_HEIGHT_PORTRAIT = 500; // Height requirement in portrait mode
|
||||
const MIN_CONTENT_HEIGHT_LANDSCAPE = 400;
|
||||
const REQUIRED_WIDTH = DRAWER_WIDTH + MIN_CONTENT_WIDTH;
|
||||
const WIDE_SCREEN_RATIO = 2.0; // Width/height ratio that indicates a wide screen (typical for large phones in landscape)
|
||||
|
||||
const useLargeScreenDetection = () => {
|
||||
const dimensions = useWindowDimensions();
|
||||
const previousValidWidthRef = useRef<number>(dimensions.width || 500);
|
||||
const previousValidHeightRef = useRef<number>(dimensions.height || 800);
|
||||
const [dimensionState, setDimensionState] = useState({
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleDimensionChange = ({ window }: { window: { width: number; height: number } }) => {
|
||||
console.debug('[LargeScreen] Dimension changed:', window.width, window.height);
|
||||
setDimensionState({ width: window.width, height: window.height });
|
||||
};
|
||||
|
||||
const dimensionSubscription = Dimensions.addEventListener('change', handleDimensionChange);
|
||||
|
||||
return () => {
|
||||
dimensionSubscription.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (dimensions.width > 0 && dimensions.height > 0) {
|
||||
previousValidWidthRef.current = dimensions.width;
|
||||
previousValidHeightRef.current = dimensions.height;
|
||||
console.debug('[LargeScreen] Valid dimensions update:', dimensions.width, dimensions.height);
|
||||
}
|
||||
}, [dimensions.width, dimensions.height]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleAppStateChange = (nextAppState: AppStateStatus) => {
|
||||
if (nextAppState === 'active') {
|
||||
const currentDimensions = Dimensions.get('window');
|
||||
console.debug('[LargeScreen] App active, dimension check:', currentDimensions.width, currentDimensions.height);
|
||||
|
||||
if (currentDimensions.width > 0 && currentDimensions.height > 0) {
|
||||
previousValidWidthRef.current = currentDimensions.width;
|
||||
previousValidHeightRef.current = currentDimensions.height;
|
||||
setDimensionState({
|
||||
width: currentDimensions.width,
|
||||
height: currentDimensions.height,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.debug('[LargeScreen] App state changed to:', nextAppState);
|
||||
}
|
||||
};
|
||||
|
||||
console.debug('[LargeScreen] Setting up AppState subscription');
|
||||
const subscription = AppState.addEventListener('change', handleAppStateChange);
|
||||
|
||||
// Trigger an initial check with current state
|
||||
handleAppStateChange(AppState.currentState);
|
||||
|
||||
return () => {
|
||||
console.debug('[LargeScreen] Cleaning up AppState subscription');
|
||||
try {
|
||||
subscription.remove();
|
||||
} catch (error) {
|
||||
console.warn('[LargeScreen] Error cleaning up AppState subscription:', error);
|
||||
// Fallback for older React Native versions if needed
|
||||
try {
|
||||
// @ts-ignore - for backward compatibility
|
||||
AppState.removeEventListener?.('change', handleAppStateChange);
|
||||
} catch (fallbackError) {
|
||||
console.error('[LargeScreen] Failed to clean up with fallback:', fallbackError);
|
||||
}
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isLargeScreen = useMemo(() => {
|
||||
// Prioritize the state from dimension changes for more reliable rotation support
|
||||
const effectiveWidth =
|
||||
dimensionState.width > 0 ? dimensionState.width : dimensions.width > 0 ? dimensions.width : previousValidWidthRef.current;
|
||||
|
||||
const effectiveHeight =
|
||||
dimensionState.height > 0 ? dimensionState.height : dimensions.height > 0 ? dimensions.height : previousValidHeightRef.current;
|
||||
|
||||
// For rotation cases, always use the larger dimension as "width" and smaller as "height"
|
||||
const largerDimension = Math.max(effectiveWidth, effectiveHeight);
|
||||
const smallerDimension = Math.min(effectiveWidth, effectiveHeight);
|
||||
|
||||
// In portrait, width is smaller; in landscape, height is smaller
|
||||
const isLandscape = effectiveWidth > effectiveHeight;
|
||||
|
||||
// Calculate screen ratio - wide screens like large phones in landscape have high ratios
|
||||
const screenRatio = largerDimension / smallerDimension;
|
||||
const isWideScreen = screenRatio >= WIDE_SCREEN_RATIO;
|
||||
|
||||
// Use different height requirements based on orientation
|
||||
const minRequiredHeight = isLandscape ? MIN_CONTENT_HEIGHT_LANDSCAPE : MIN_CONTENT_HEIGHT_PORTRAIT;
|
||||
|
||||
if (smallerDimension <= 375) {
|
||||
console.debug(`[LargeScreen] Smaller dimension ${smallerDimension} <= 375, forcing isLargeScreen=false`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special case for large phones in landscape mode (wide screens)
|
||||
// They typically have plenty of width but might be slightly short on height
|
||||
const isLargeLandscapePhone =
|
||||
isLandscape &&
|
||||
isWideScreen &&
|
||||
largerDimension >= 900 && // Common for large phone models in landscape
|
||||
smallerDimension >= 400; // Still need reasonable height
|
||||
|
||||
// For proper drawer display, require adequate width and height
|
||||
const hasAdequateWidth = largerDimension >= REQUIRED_WIDTH;
|
||||
const hasAdequateHeight = smallerDimension >= minRequiredHeight;
|
||||
|
||||
const isTabletOrDesktop = Platform.OS === 'ios' ? Platform.isPad || isDesktop : false;
|
||||
const defaultValue = isTabletOrDesktop;
|
||||
|
||||
// Consider a device "large screen" if:
|
||||
// 1. It has adequate width AND adequate height, OR
|
||||
// 2. It's a large phone in landscape mode with sufficient dimensions, OR
|
||||
// 3. It's a tablet/desktop by platform detection
|
||||
const result = (hasAdequateWidth && hasAdequateHeight) || isLargeLandscapePhone || defaultValue;
|
||||
|
||||
console.debug(
|
||||
`[LargeScreen] Calculation:`,
|
||||
`dimensions=${effectiveWidth}x${effectiveHeight}`,
|
||||
`isLandscape=${isLandscape}`,
|
||||
`screenRatio=${screenRatio.toFixed(2)}`,
|
||||
`isWideScreen=${isWideScreen}`,
|
||||
`largerDim=${largerDimension}`,
|
||||
`smallerDim=${smallerDimension}`,
|
||||
`requiredWidth=${REQUIRED_WIDTH}`,
|
||||
`requiredHeight=${minRequiredHeight}`,
|
||||
`hasWidth=${hasAdequateWidth}`,
|
||||
`hasHeight=${hasAdequateHeight}`,
|
||||
`isLargeLandscapePhone=${isLargeLandscapePhone}`,
|
||||
`result=${result}`,
|
||||
`default=${defaultValue}`,
|
||||
);
|
||||
|
||||
return result;
|
||||
}, [dimensions.width, dimensions.height, dimensionState.width, dimensionState.height]);
|
||||
|
||||
return { isLargeScreen };
|
||||
};
|
||||
|
||||
type LargeScreenProviderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const LargeScreenContext = createContext<ILargeScreenContext>({
|
||||
isLargeScreen: false,
|
||||
});
|
||||
|
||||
export const LargeScreenProvider: React.FC<LargeScreenProviderProps> = ({ children }) => {
|
||||
const { isLargeScreen } = useLargeScreenDetection();
|
||||
|
||||
const contextValue = useMemo(() => ({ isLargeScreen }), [isLargeScreen]);
|
||||
|
||||
return <LargeScreenContext.Provider value={contextValue}>{children}</LargeScreenContext.Provider>;
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { createContext, ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import { AppState, AppStateStatus, Dimensions, Platform, useWindowDimensions } from 'react-native';
|
||||
import { Dimensions, Platform, useWindowDimensions } from 'react-native';
|
||||
import { isDesktop, isTablet } from '../../blue_modules/environment';
|
||||
import useAppState from '../../hooks/useAppState';
|
||||
|
||||
export enum SizeClass {
|
||||
Compact,
|
||||
@ -70,30 +71,12 @@ const useSizeClassDetection = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { currentAppState } = useAppState();
|
||||
useEffect(() => {
|
||||
const handleAppStateChange = (nextAppState: AppStateStatus) => {
|
||||
if (nextAppState === 'active') {
|
||||
determineSize();
|
||||
}
|
||||
};
|
||||
|
||||
const subscription = AppState.addEventListener('change', handleAppStateChange);
|
||||
handleAppStateChange(AppState.currentState);
|
||||
|
||||
return () => {
|
||||
console.debug('[SizeClass] Cleaning up AppState subscription');
|
||||
try {
|
||||
subscription.remove();
|
||||
} catch (error) {
|
||||
console.warn('[SizeClass] Error cleaning up AppState subscription:', error);
|
||||
try {
|
||||
AppState.removeEventListener?.('change', handleAppStateChange);
|
||||
} catch (fallbackError) {
|
||||
console.error('[SizeClass] Failed to clean up with fallback:', fallbackError);
|
||||
}
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
if (currentAppState === 'active') {
|
||||
determineSize();
|
||||
}
|
||||
}, [currentAppState]);
|
||||
|
||||
const sizeClass = useMemo(() => {
|
||||
if (
|
||||
|
||||
@ -6,6 +6,7 @@ import { useTheme } from './themes';
|
||||
|
||||
interface SafeAreaProps extends ViewProps {
|
||||
floatingButtonHeight?: number;
|
||||
orientation?: 'portrait' | 'landscape';
|
||||
}
|
||||
|
||||
const SafeArea = (props: SafeAreaProps) => {
|
||||
@ -13,19 +14,32 @@ const SafeArea = (props: SafeAreaProps) => {
|
||||
const { colors } = useTheme();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const padding = useMemo(
|
||||
() =>
|
||||
props.orientation === 'portrait'
|
||||
? {
|
||||
paddingTop: insets.top,
|
||||
paddingBottom: insets.bottom,
|
||||
}
|
||||
: {
|
||||
paddingTop: insets.left,
|
||||
paddingBottom: insets.right + (floatingButtonHeight ?? 0),
|
||||
paddingLeft: insets.top,
|
||||
paddingRight: insets.bottom,
|
||||
},
|
||||
[insets, props.orientation, floatingButtonHeight],
|
||||
);
|
||||
|
||||
const componentStyle = useMemo(() => {
|
||||
return StyleSheet.compose(
|
||||
{
|
||||
flex: 1,
|
||||
backgroundColor: colors.background,
|
||||
paddingTop: insets.top,
|
||||
paddingBottom: insets.bottom + (floatingButtonHeight ?? 0),
|
||||
paddingLeft: insets.left,
|
||||
paddingRight: insets.right,
|
||||
...padding,
|
||||
},
|
||||
style,
|
||||
);
|
||||
}, [colors.background, style, insets, floatingButtonHeight]);
|
||||
}, [colors.background, padding, style]);
|
||||
|
||||
return <View style={componentStyle} {...otherProps} />;
|
||||
};
|
||||
|
||||
@ -418,7 +418,14 @@ type FlatListRefType = FlatList<any> & {
|
||||
getNativeScrollRef(): View;
|
||||
};
|
||||
|
||||
const ListHeaderSeparator = () => <View style={{ width: 16, height: 20 }} />;
|
||||
const styles = StyleSheet.create({
|
||||
listHeaderSeparator: {
|
||||
width: 16,
|
||||
height: 20,
|
||||
},
|
||||
});
|
||||
|
||||
const ListHeaderSeparator = () => <View style={styles.listHeaderSeparator} />;
|
||||
|
||||
const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props, ref) => {
|
||||
const {
|
||||
@ -704,10 +711,6 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
|
||||
contentLargeScreen: {
|
||||
paddingHorizontal: sizeClass === SizeClass.Large ? 16 : 12,
|
||||
},
|
||||
separatorStyle: {
|
||||
width: 16,
|
||||
height: 20,
|
||||
},
|
||||
});
|
||||
|
||||
return isFlatList ? (
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
// Reexport from the new location for backward compatibility
|
||||
export { SizeClass, useIsLargeScreen, useSizeClass } from './useSizeClass';
|
||||
@ -1,9 +1,9 @@
|
||||
// This file reexports from the main sizeClass module for backward compatibility
|
||||
import { useSizeClass as useSizeClassOriginal, SizeClass, useIsLargeScreen } from '../blue_modules/sizeClass';
|
||||
import { useSizeClass as useSizeClassOriginal, SizeClass } from '../blue_modules/sizeClass';
|
||||
import type { SizeClassInfo } from '../blue_modules/sizeClass';
|
||||
|
||||
export { SizeClass, useIsLargeScreen };
|
||||
export { SizeClass };
|
||||
export type { SizeClassInfo };
|
||||
|
||||
// Main hook
|
||||
export const useSizeClass = useSizeClassOriginal;
|
||||
|
||||
export const useIsLargeScreen = useSizeClassOriginal;
|
||||
|
||||
@ -12,6 +12,7 @@ export type ScanQRCodeParamList = {
|
||||
onBarScanned?: (data: string) => void;
|
||||
showFileImportButton?: boolean;
|
||||
backdoorVisible?: boolean;
|
||||
orientation?: 'portrait';
|
||||
animatedQRCodeData?: Record<string, any>;
|
||||
};
|
||||
|
||||
|
||||
@ -35,7 +35,6 @@ const DrawerRoot = () => {
|
||||
const { sizeClass, isLargeScreen } = useSizeClass();
|
||||
|
||||
const getDrawerWidth = useMemo(() => {
|
||||
// Use size class for more flexible drawer width
|
||||
switch (sizeClass) {
|
||||
case SizeClass.Large:
|
||||
return 320;
|
||||
|
||||
@ -15,14 +15,11 @@ const WalletsAddMultisig = lazy(() => import('../screen/wallets/WalletsAddMultis
|
||||
const WalletsAddMultisigStep2 = lazy(() => import('../screen/wallets/addMultisigStep2'));
|
||||
const WalletsAddMultisigHelp = lazy(() => import('../screen/wallets/addMultisigHelp'));
|
||||
|
||||
export const AddComponent: React.FC = () => {
|
||||
console.log('Rendering AddComponent wrapper');
|
||||
return (
|
||||
<Suspense fallback={<LazyLoadingIndicator />}>
|
||||
<WalletsAdd />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
export const AddComponent: React.FC = () => (
|
||||
<Suspense fallback={<LazyLoadingIndicator />}>
|
||||
<WalletsAdd />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
export const ImportWalletDiscoveryComponent = () => (
|
||||
<Suspense fallback={<LazyLoadingIndicator />}>
|
||||
|
||||
@ -195,6 +195,7 @@ const MainRoot = () => {
|
||||
options={{
|
||||
headerShown: false,
|
||||
statusBarHidden: true,
|
||||
orientation: 'portrait',
|
||||
presentation: 'fullScreenModal',
|
||||
}}
|
||||
/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user