diff --git a/README.md b/README.md
index 1cc6654..9ed5445 100644
--- a/README.md
+++ b/README.md
@@ -26,8 +26,9 @@ Interspace is a wallet wrapper that allows users to:
- Grant ERC-20 token allowances for seamless transactions
### 2. Apps
-- iPhone-style home screen with bookmarked Web3 apps
-- Drag-and-drop app organization with folders
+- iOS inspired home screen with dynamic app icons and folders
+- Horizontal paging with page dots just like iOS
+- Drag-and-drop organization and folder creation
- Built-in browser with wallet injection
- Custom transaction confirmation UI
diff --git a/src/components/apps/AppIcon.tsx b/src/components/apps/AppIcon.tsx
deleted file mode 100644
index 0d09b4c..0000000
--- a/src/components/apps/AppIcon.tsx
+++ /dev/null
@@ -1,208 +0,0 @@
-import React, { useEffect } from 'react';
-import {
- StyleSheet,
- Text,
- View,
- TouchableOpacity,
- Image,
- Pressable,
-} from 'react-native';
-import Animated, {
- useAnimatedStyle,
- useSharedValue,
- withRepeat,
- withSequence,
- withTiming,
- interpolate,
- runOnJS,
-} from 'react-native-reanimated';
-import { Apple } from '@/constants/AppleDesign';
-
-interface AppIconProps {
- id: string;
- name: string;
- url: string;
- iconUrl?: string;
- isEditMode: boolean;
- onPress: () => void;
- onLongPress: () => void;
- onDelete: () => void;
-}
-
-const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
-
-export function AppIcon({
- id,
- name,
- url,
- iconUrl,
- isEditMode,
- onPress,
- onLongPress,
- onDelete,
-}: AppIconProps) {
- const rotation = useSharedValue(0);
- const scale = useSharedValue(1);
-
- // Jiggly animation for edit mode
- useEffect(() => {
- if (isEditMode) {
- rotation.value = withRepeat(
- withSequence(
- withTiming(-2, { duration: 100 }),
- withTiming(2, { duration: 100 }),
- withTiming(-2, { duration: 100 }),
- withTiming(2, { duration: 100 })
- ),
- -1,
- true
- );
- } else {
- rotation.value = withTiming(0, { duration: 100 });
- }
- }, [isEditMode]);
-
- const animatedStyle = useAnimatedStyle(() => {
- return {
- transform: [
- { rotate: `${rotation.value}deg` },
- { scale: scale.value },
- ],
- };
- });
-
- const handlePressIn = () => {
- scale.value = withTiming(0.95, { duration: 100 });
- };
-
- const handlePressOut = () => {
- scale.value = withTiming(1, { duration: 100 });
- };
-
- const getAppInitial = () => {
- return name.charAt(0).toUpperCase();
- };
-
- const getAppColor = () => {
- // Generate consistent color from app name
- const colors = [
- '#FF453A', '#FF9F0A', '#FFD60A', '#30D158',
- '#40C8E0', '#007AFF', '#5856D6', '#AF52DE',
- '#FF2D92',
- ];
- const index = name.charCodeAt(0) % colors.length;
- return colors[index];
- };
-
- return (
-
-
- {/* App Icon */}
-
- {iconUrl ? (
-
- ) : (
-
- {getAppInitial()}
-
- )}
-
-
- {/* Delete button */}
- {isEditMode && (
-
-
- ×
-
-
- )}
-
-
- {/* App Name */}
-
- {name}
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- width: 74,
- height: 90,
- alignItems: 'center',
- },
- iconWrapper: {
- position: 'relative',
- },
- iconContainer: {
- width: 60,
- height: 60,
- borderRadius: Apple.Radius.medium,
- overflow: 'hidden',
- backgroundColor: Apple.Colors.tertiarySystemBackground,
- },
- iconImage: {
- width: '100%',
- height: '100%',
- },
- iconPlaceholder: {
- width: '100%',
- height: '100%',
- justifyContent: 'center',
- alignItems: 'center',
- },
- iconInitial: {
- fontSize: 24,
- fontWeight: '600',
- color: 'white',
- },
- appName: {
- marginTop: 6,
- fontSize: Apple.Typography.caption1.fontSize,
- fontWeight: Apple.Typography.caption1.fontWeight,
- lineHeight: Apple.Typography.caption1.lineHeight,
- color: Apple.Colors.label,
- textAlign: 'center',
- width: '100%',
- paddingHorizontal: 2,
- },
- deleteButton: {
- position: 'absolute',
- top: -8,
- left: -8,
- width: 24,
- height: 24,
- borderRadius: 12,
- backgroundColor: Apple.Colors.systemRed,
- justifyContent: 'center',
- alignItems: 'center',
- ...Apple.Shadows.level2,
- },
- deleteButtonInner: {
- width: 20,
- height: 20,
- borderRadius: 10,
- backgroundColor: Apple.Colors.systemRed,
- justifyContent: 'center',
- alignItems: 'center',
- },
- deleteButtonText: {
- color: 'white',
- fontSize: 18,
- fontWeight: '300',
- lineHeight: 18,
- marginTop: -2,
- },
-});
diff --git a/src/components/apps/DraggableAppIcon.tsx b/src/components/apps/DraggableAppIcon.tsx
index b5736f4..f4c73c0 100644
--- a/src/components/apps/DraggableAppIcon.tsx
+++ b/src/components/apps/DraggableAppIcon.tsx
@@ -160,6 +160,7 @@ export function DraggableAppIcon({
ref={panRef}
onGestureEvent={panHandler}
simultaneousHandlers={longPressRef}
+ waitFor={longPressRef}
enabled={isEditMode}
>
diff --git a/src/components/apps/FolderIcon.tsx b/src/components/apps/FolderIcon.tsx
deleted file mode 100644
index 2a48662..0000000
--- a/src/components/apps/FolderIcon.tsx
+++ /dev/null
@@ -1,235 +0,0 @@
-import React, { useEffect } from 'react';
-import {
- StyleSheet,
- Text,
- View,
- TouchableOpacity,
- Pressable,
-} from 'react-native';
-import Animated, {
- useAnimatedStyle,
- useSharedValue,
- withRepeat,
- withSequence,
- withTiming,
- withSpring,
-} from 'react-native-reanimated';
-import { Apple } from '@/constants/AppleDesign';
-
-interface App {
- id: string;
- name: string;
- url: string;
- iconUrl?: string;
-}
-
-interface FolderIconProps {
- id: string;
- name: string;
- apps: App[];
- color?: string;
- isEditMode: boolean;
- onPress: () => void;
- onLongPress: () => void;
- onDelete: () => void;
-}
-
-const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
-
-export function FolderIcon({
- id,
- name,
- apps,
- color = Apple.Colors.systemGray4,
- isEditMode,
- onPress,
- onLongPress,
- onDelete,
-}: FolderIconProps) {
- const rotation = useSharedValue(0);
- const scale = useSharedValue(1);
-
- // Jiggly animation for edit mode
- useEffect(() => {
- if (isEditMode) {
- rotation.value = withRepeat(
- withSequence(
- withTiming(-2, { duration: 100 }),
- withTiming(2, { duration: 100 }),
- withTiming(-2, { duration: 100 }),
- withTiming(2, { duration: 100 })
- ),
- -1,
- true
- );
- } else {
- rotation.value = withTiming(0, { duration: 100 });
- }
- }, [isEditMode]);
-
- const animatedStyle = useAnimatedStyle(() => {
- return {
- transform: [
- { rotate: `${rotation.value}deg` },
- { scale: scale.value },
- ],
- };
- });
-
- const handlePressIn = () => {
- scale.value = withTiming(0.95, { duration: 100 });
- };
-
- const handlePressOut = () => {
- scale.value = withTiming(1, { duration: 100 });
- };
-
- const getAppColor = (appName: string) => {
- const colors = [
- '#FF453A', '#FF9F0A', '#FFD60A', '#30D158',
- '#40C8E0', '#007AFF', '#5856D6', '#AF52DE',
- '#FF2D92',
- ];
- const index = appName.charCodeAt(0) % colors.length;
- return colors[index];
- };
-
- // Render mini app icons in 3x3 grid
- const renderMiniApps = () => {
- const displayApps = apps.slice(0, 9); // Show max 9 apps
- const emptySlots = 9 - displayApps.length;
-
- return (
-
- {displayApps.map((app, index) => (
-
-
-
- {app.name.charAt(0).toUpperCase()}
-
-
-
- ))}
- {/* Fill empty slots */}
- {Array.from({ length: emptySlots }).map((_, index) => (
-
- ))}
-
- );
- };
-
- return (
-
-
- {/* Folder Background */}
-
- {renderMiniApps()}
-
-
- {/* Delete button */}
- {isEditMode && (
-
-
- ×
-
-
- )}
-
-
- {/* Folder Name */}
-
- {name}
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- width: 74,
- height: 90,
- alignItems: 'center',
- },
- iconWrapper: {
- position: 'relative',
- },
- folderContainer: {
- width: 60,
- height: 60,
- borderRadius: Apple.Radius.medium,
- padding: 8,
- overflow: 'hidden',
- },
- miniAppsGrid: {
- flex: 1,
- flexDirection: 'row',
- flexWrap: 'wrap',
- justifyContent: 'space-between',
- alignContent: 'space-between',
- },
- miniAppIcon: {
- width: '30%',
- height: '30%',
- padding: 1,
- },
- miniAppInner: {
- width: '100%',
- height: '100%',
- borderRadius: 3,
- justifyContent: 'center',
- alignItems: 'center',
- },
- miniAppInitial: {
- fontSize: 6,
- fontWeight: '600',
- color: 'white',
- },
- folderName: {
- marginTop: 6,
- fontSize: Apple.Typography.caption1.fontSize,
- fontWeight: Apple.Typography.caption1.fontWeight,
- lineHeight: Apple.Typography.caption1.lineHeight,
- color: Apple.Colors.label,
- textAlign: 'center',
- width: '100%',
- paddingHorizontal: 2,
- },
- deleteButton: {
- position: 'absolute',
- top: -8,
- left: -8,
- width: 24,
- height: 24,
- borderRadius: 12,
- backgroundColor: Apple.Colors.systemRed,
- justifyContent: 'center',
- alignItems: 'center',
- ...Apple.Shadows.level2,
- },
- deleteButtonInner: {
- width: 20,
- height: 20,
- borderRadius: 10,
- backgroundColor: Apple.Colors.systemRed,
- justifyContent: 'center',
- alignItems: 'center',
- },
- deleteButtonText: {
- color: 'white',
- fontSize: 18,
- fontWeight: '300',
- lineHeight: 18,
- marginTop: -2,
- },
-});
diff --git a/src/components/apps/IOSDragDropAppsScreen.tsx b/src/components/apps/IOSDragDropAppsScreen.tsx
index 3c08e21..e45dacc 100644
--- a/src/components/apps/IOSDragDropAppsScreen.tsx
+++ b/src/components/apps/IOSDragDropAppsScreen.tsx
@@ -10,14 +10,12 @@ import {
Text,
TouchableWithoutFeedback,
LayoutAnimation,
- LayoutChangeEvent,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { LinearGradient } from 'expo-linear-gradient';
import { Apple } from '@/constants/AppleDesign';
import { DraggableAppIcon } from '@/src/components/apps/DraggableAppIcon';
-import { IOSAppIcon } from '@/src/components/apps/IOSAppIcon';
-import { IOSFolderIcon } from '@/src/components/apps/IOSFolderIcon';
+// Icon components are used within DraggableAppIcon
import { IOSSearchWidget } from '@/src/components/apps/IOSSearchWidget';
import { IOSFolderModal } from '@/src/components/apps/IOSFolderModal';
import { SafariBrowser } from '@/src/components/apps/SafariBrowser';
@@ -41,6 +39,8 @@ import {
const { width, height } = Dimensions.get('window');
const ICONS_PER_ROW = 4;
+const ROWS_PER_PAGE = 5; // mimic iOS grid height
+const ICONS_PER_PAGE = ICONS_PER_ROW * ROWS_PER_PAGE;
const ICON_SIZE = 74;
const HORIZONTAL_PADDING = 20;
const ICON_SPACING = (width - HORIZONTAL_PADDING * 2 - ICON_SIZE * ICONS_PER_ROW) / (ICONS_PER_ROW - 1);
@@ -67,6 +67,7 @@ interface Folder {
}
interface GridPosition {
+ page: number;
row: number;
col: number;
x: number;
@@ -94,9 +95,12 @@ export default function IOSDragDropAppsScreen() {
});
const [dragPosition, setDragPosition] = useState({ x: 0, y: 0 });
const [dropTargetIndex, setDropTargetIndex] = useState(-1);
-
+ const [currentPage, setCurrentPage] = useState(0);
+ const [totalPages, setTotalPages] = useState(1);
+
// Grid positions for drag & drop
const gridPositions = useRef([]);
+ const gridItems = useRef<(App | Folder)[]>([]);
const scrollViewRef = useRef(null);
// Animation values
@@ -146,24 +150,87 @@ export default function IOSDragDropAppsScreen() {
setFolders(mockFolders);
}, []);
- // Calculate grid positions
+ const recalcPositions = (
+ appsList: App[],
+ folderList: Folder[],
+ ): { apps: App[]; folders: Folder[] } => {
+ let idx = 0;
+ const sortedFolders = folderList
+ .sort((a, b) => a.position - b.position)
+ .map((f) => ({ ...f, position: idx++ }));
+ const updatedApps = appsList.map((a) => {
+ if (a.folderId) return a;
+ const pos = idx++;
+ return { ...a, position: pos };
+ });
+ return { apps: updatedApps, folders: sortedFolders };
+ };
+
+ const addAppToFolder = (app: App, folder: Folder) => {
+ const updatedFolders = folders.map((f) =>
+ f.id === folder.id
+ ? { ...f, apps: [...f.apps, { ...app, folderId: folder.id, position: f.apps.length }] }
+ : f,
+ );
+ const updatedApps = apps.map((a) =>
+ a.id === app.id ? { ...a, folderId: folder.id } : a,
+ );
+ const { apps: resApps, folders: resFolders } = recalcPositions(updatedApps, updatedFolders);
+ setApps(resApps);
+ setFolders(resFolders);
+ };
+
+ const createFolderFromApps = (dragApp: App, targetApp: App) => {
+ const newFolderId = `folder-${Date.now()}`;
+ const newFolder: Folder = {
+ id: newFolderId,
+ name: 'Folder',
+ color: Apple.Colors.systemBlue,
+ position: targetApp.position,
+ apps: [
+ { ...targetApp, folderId: newFolderId, position: 0 },
+ { ...dragApp, folderId: newFolderId, position: 1 },
+ ],
+ };
+
+ const remainingApps = apps.filter((a) => a.id !== targetApp.id && a.id !== dragApp.id);
+ const updatedApps = [
+ ...remainingApps,
+ { ...targetApp, folderId: newFolderId },
+ { ...dragApp, folderId: newFolderId },
+ ];
+ const updatedFolders = [...folders, newFolder];
+ const { apps: resApps, folders: resFolders } = recalcPositions(updatedApps, updatedFolders);
+ setApps(resApps);
+ setFolders(resFolders);
+ };
+
+ // Calculate grid positions and pages
useEffect(() => {
const standaloneApps = apps.filter(app => !app.folderId);
const allItems = [...standaloneApps, ...folders].sort((a, b) => a.position - b.position);
-
+
const positions: GridPosition[] = [];
+
+ const pages = Math.max(1, Math.ceil(allItems.length / ICONS_PER_PAGE));
+ setTotalPages(pages);
+
allItems.forEach((item, index) => {
- const row = Math.floor(index / ICONS_PER_ROW);
- const col = index % ICONS_PER_ROW;
+ const page = Math.floor(index / ICONS_PER_PAGE);
+ const indexInPage = index % ICONS_PER_PAGE;
+ const row = Math.floor(indexInPage / ICONS_PER_ROW);
+ const col = indexInPage % ICONS_PER_ROW;
positions.push({
+ page,
row,
col,
- x: HORIZONTAL_PADDING + col * (ICON_SIZE + ICON_SPACING),
+ x: page * width + HORIZONTAL_PADDING + col * (ICON_SIZE + ICON_SPACING),
y: 100 + row * (ICON_SIZE + VERTICAL_SPACING),
});
});
-
+
gridPositions.current = positions;
+ gridItems.current = allItems;
}, [apps, folders]);
// Animate widget opacity in edit mode
@@ -200,22 +267,12 @@ export default function IOSDragDropAppsScreen() {
hapticTrigger('impactLight');
setBrowserUrl(app.url);
- // Animate browser opening from widget position
- const widgetY = height - 200; // Approximate widget position
- browserScale.value = 0;
+ browserScale.value = 0.9;
browserOpacity.value = 0;
-
- // Set initial position
- browserScale.value = withSequence(
- withTiming(0.3, { duration: 0 }),
- withSpring(1, {
- damping: 20,
- stiffness: 350,
- })
- );
-
- browserOpacity.value = withTiming(1, { duration: 300 });
- backgroundBlur.value = withTiming(10, { duration: 300 });
+
+ browserScale.value = withTiming(1, { duration: 250 });
+ browserOpacity.value = withTiming(1, { duration: 250 });
+ backgroundBlur.value = withTiming(10, { duration: 250 });
setBrowserVisible(true);
}, [isEditMode, dragState.isActive]);
@@ -277,12 +334,10 @@ export default function IOSDragDropAppsScreen() {
const handleSearchPress = useCallback(() => {
hapticTrigger('impactLight');
- // Animate browser opening from widget
- browserScale.value = withSpring(1, {
- damping: 18,
- stiffness: 300,
- overshootClamping: false,
- });
+ browserScale.value = 0.9;
+ browserOpacity.value = 0;
+
+ browserScale.value = withTiming(1, { duration: 250 });
browserOpacity.value = withTiming(1, { duration: 250 });
backgroundBlur.value = withTiming(10, { duration: 250 });
@@ -331,7 +386,18 @@ export default function IOSDragDropAppsScreen() {
const handleDragEnd = useCallback((x: number, y: number) => {
if (dropTargetIndex >= 0 && dragState.item) {
- handleItemDrop(dragState.item, dropTargetIndex);
+ const target = gridItems.current[dropTargetIndex];
+ if (target && !('apps' in dragState.item)) {
+ if (target && 'apps' in target) {
+ addAppToFolder(dragState.item as App, target as Folder);
+ } else if ((target as App).id !== (dragState.item as App).id) {
+ createFolderFromApps(dragState.item as App, target as App);
+ } else {
+ handleItemDrop(dragState.item, dropTargetIndex);
+ }
+ } else {
+ handleItemDrop(dragState.item, dropTargetIndex);
+ }
}
setDragState({
@@ -382,65 +448,45 @@ export default function IOSDragDropAppsScreen() {
});
- const renderGrid = () => {
+ const renderGrid = (pageIndex: number) => {
// Get all items (apps not in folders + folders)
const standaloneApps = apps.filter(app => !app.folderId);
const allItems = [...standaloneApps, ...folders].sort((a, b) => a.position - b.position);
-
+
+ const pageItems = allItems.slice(
+ pageIndex * ICONS_PER_PAGE,
+ (pageIndex + 1) * ICONS_PER_PAGE,
+ );
+
const rows = [];
- for (let i = 0; i < allItems.length; i += ICONS_PER_ROW) {
- const rowItems = allItems.slice(i, i + ICONS_PER_ROW);
+ for (let i = 0; i < ROWS_PER_PAGE; i++) {
+ const start = i * ICONS_PER_ROW;
+ const rowItems = pageItems.slice(start, start + ICONS_PER_ROW);
rows.push(
- {rowItems.map((item, index) => {
+ {rowItems.map((item, indexInRow) => {
const isFolder = 'apps' in item;
+ const itemIndex = pageIndex * ICONS_PER_PAGE + i * ICONS_PER_ROW + indexInRow;
+ const position = gridPositions.current[itemIndex];
const isDragging = dragState.isActive && dragState.item?.id === item.id;
-
+
return (
- {
- // Store position for drag calculations
- const { x, y } = event.nativeEvent.layout;
- // Update grid positions if needed
- }}
- >
- {!isDragging && (
- isFolder ? (
- {
- const rowIndex = Math.floor(i / ICONS_PER_ROW);
- const colIndex = index;
- const x = HORIZONTAL_PADDING + colIndex * (ICON_SIZE + ICON_SPACING) + ICON_SIZE / 2;
- const y = 100 + rowIndex * (ICON_SIZE + VERTICAL_SPACING) + ICON_SIZE / 2;
- handleFolderPress(item, { x, y });
- }}
- onLongPress={handleLongPress}
- onDelete={() => handleDeleteFolder(item.id)}
- />
- ) : (
- handleAppPress(item)}
- onLongPress={handleLongPress}
- onDelete={() => handleDeleteApp(item.id)}
- />
- )
- )}
-
+ (isFolder ? handleFolderPress(item as Folder, { x: position.x + ICON_SIZE / 2, y: position.y + ICON_SIZE / 2 }) : handleAppPress(item as App))}
+ onLongPress={handleLongPress}
+ onDelete={() => (isFolder ? handleDeleteFolder(item.id) : handleDeleteApp(item.id))}
+ onDragStart={handleDragStart}
+ onDragMove={handleDragMove}
+ onDragEnd={handleDragEnd}
+ isDragging={isDragging}
+ isDropTarget={dropTargetIndex === itemIndex}
+ />
);
})}
{/* Fill empty slots in row */}
@@ -468,9 +514,9 @@ export default function IOSDragDropAppsScreen() {
{/* iOS 17 style gradient background */}
@@ -502,21 +548,30 @@ export default function IOSDragDropAppsScreen() {
{
+ const page = Math.round(e.nativeEvent.contentOffset.x / width);
+ setCurrentPage(page);
+ }}
scrollEnabled={!isEditMode || !dragState.isActive}
>
- {/* App Grid */}
-
- {renderGrid()}
-
-
- {/* Spacer to push widget to bottom */}
-
+ {Array.from({ length: totalPages }).map((_, pageIndex) => (
+
+ {renderGrid(pageIndex)}
+
+ ))}
+
+
+ {Array.from({ length: totalPages }).map((_, idx) => (
+
+ ))}
+
{/* Search Widget at bottom */}
@@ -620,10 +675,6 @@ const styles = StyleSheet.create({
scrollView: {
flex: 1,
},
- scrollContent: {
- flexGrow: 1,
- paddingTop: 10,
- },
gridContainer: {
paddingHorizontal: HORIZONTAL_PADDING,
},
@@ -642,9 +693,20 @@ const styles = StyleSheet.create({
emptySlot: {
width: ICON_SIZE,
},
- spacer: {
- flex: 1,
- minHeight: 140,
+ pageIndicator: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ marginTop: 8,
+ },
+ pageDot: {
+ width: 6,
+ height: 6,
+ borderRadius: 3,
+ backgroundColor: 'rgba(255,255,255,0.3)',
+ marginHorizontal: 3,
+ },
+ activePageDot: {
+ backgroundColor: 'rgba(255,255,255,0.8)',
},
widgetContainer: {
position: 'absolute',
diff --git a/src/components/apps/IOSFolderModal.tsx b/src/components/apps/IOSFolderModal.tsx
index 143f845..7e97d69 100644
--- a/src/components/apps/IOSFolderModal.tsx
+++ b/src/components/apps/IOSFolderModal.tsx
@@ -24,8 +24,8 @@ import { IOSAppIcon } from './IOSAppIcon';
import { hapticTrigger } from '@/src/utils/hapticFeedback';
const { width, height } = Dimensions.get('window');
-const FOLDER_WIDTH = width - 40;
-const FOLDER_HEIGHT = 340;
+const FOLDER_WIDTH = width - 30;
+const FOLDER_HEIGHT = 380;
const ICONS_PER_ROW = 3;
const ICON_SIZE = 74;
const ICON_SPACING = 25;
diff --git a/src/components/apps/IOSSearchWidget.tsx b/src/components/apps/IOSSearchWidget.tsx
index 22f58a3..4f489a8 100644
--- a/src/components/apps/IOSSearchWidget.tsx
+++ b/src/components/apps/IOSSearchWidget.tsx
@@ -44,6 +44,7 @@ const DEFAULT_RECENT_APPS = [
export function IOSSearchWidget({ onPress, recentApps = DEFAULT_RECENT_APPS }: IOSSearchWidgetProps) {
const scale = useSharedValue(1);
const opacity = useSharedValue(1);
+ const blur = useSharedValue(60);
const animatedStyle = useAnimatedStyle(() => {
return {
@@ -58,6 +59,7 @@ export function IOSSearchWidget({ onPress, recentApps = DEFAULT_RECENT_APPS }: I
stiffness: 400,
});
opacity.value = withTiming(0.9, { duration: 100 });
+ blur.value = withTiming(80, { duration: 200 });
};
const handlePressOut = () => {
@@ -66,6 +68,7 @@ export function IOSSearchWidget({ onPress, recentApps = DEFAULT_RECENT_APPS }: I
stiffness: 350,
});
opacity.value = withTiming(1, { duration: 100 });
+ blur.value = withTiming(60, { duration: 200 });
};
return (
@@ -80,7 +83,7 @@ export function IOSSearchWidget({ onPress, recentApps = DEFAULT_RECENT_APPS }: I
{/* Glass morphism background */}
-
+
{/* Gradient overlay for glass effect */}
{
if (visible) {
hapticTrigger('impactLight');
- translateY.value = withSpring(0, {
- damping: 25,
- stiffness: 400,
- mass: 0.8,
- });
- scale.value = withSpring(1, {
- damping: 20,
- stiffness: 350,
- });
+ translateY.value = withTiming(0, { duration: 250 });
+ scale.value = withTiming(1, { duration: 250 });
opacity.value = withTiming(1, { duration: 250 });
- borderRadius.value = withTiming(0, { duration: 300 });
+ borderRadius.value = withTiming(0, { duration: 250 });
} else {
- translateY.value = withSpring(height, {
- damping: 25,
- stiffness: 400,
- });
- scale.value = withTiming(0.9, { duration: 200 });
+ translateY.value = withTiming(height, { duration: 200 });
+ scale.value = withTiming(0.95, { duration: 200 });
opacity.value = withTiming(0, { duration: 200 });
borderRadius.value = withTiming(40, { duration: 200 });
}
diff --git a/src/components/apps/SearchWidget.tsx b/src/components/apps/SearchWidget.tsx
deleted file mode 100644
index c8f0c45..0000000
--- a/src/components/apps/SearchWidget.tsx
+++ /dev/null
@@ -1,131 +0,0 @@
-import React from 'react';
-import {
- StyleSheet,
- Text,
- View,
- TouchableOpacity,
- Dimensions,
-} from 'react-native';
-import { BlurView } from 'expo-blur';
-import { Apple } from '@/constants/AppleDesign';
-import { Ionicons } from '@expo/vector-icons';
-
-interface SearchWidgetProps {
- onPress: () => void;
-}
-
-const { width } = Dimensions.get('window');
-const WIDGET_WIDTH = width - 32; // 16px padding on each side
-
-export function SearchWidget({ onPress }: SearchWidgetProps) {
- return (
-
-
-
-
-
- Search or enter website
-
-
-
-
- A
-
- Aave
-
-
-
- U
-
- Uniswap
-
-
-
- J
-
- Jumper
-
-
-
- H
-
- Hyperliquid
-
-
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- width: WIDGET_WIDTH,
- height: 110,
- marginHorizontal: 16,
- marginBottom: 16,
- borderRadius: Apple.Radius.xlarge,
- overflow: 'hidden',
- ...Apple.Shadows.level3,
- },
- blurContainer: {
- flex: 1,
- backgroundColor: 'rgba(28, 28, 30, 0.8)', // Fallback for blur
- },
- content: {
- flex: 1,
- padding: 16,
- },
- searchBar: {
- height: 36,
- backgroundColor: Apple.Colors.tertiarySystemBackground,
- borderRadius: Apple.Radius.standard,
- flexDirection: 'row',
- alignItems: 'center',
- paddingHorizontal: 12,
- marginBottom: 12,
- },
- searchIcon: {
- marginRight: 8,
- },
- placeholder: {
- color: Apple.Colors.placeholderText,
- fontSize: Apple.Typography.subheadline.fontSize,
- fontWeight: Apple.Typography.subheadline.fontWeight,
- },
- suggestionRow: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- },
- suggestion: {
- alignItems: 'center',
- flex: 1,
- },
- suggestionIcon: {
- width: 28,
- height: 28,
- borderRadius: 6,
- justifyContent: 'center',
- alignItems: 'center',
- marginBottom: 4,
- },
- suggestionInitial: {
- color: 'white',
- fontSize: 12,
- fontWeight: '600',
- },
- suggestionText: {
- color: Apple.Colors.secondaryLabel,
- fontSize: 10,
- fontWeight: Apple.Typography.caption2.fontWeight,
- },
-});