mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
fix: respect safe area insets and improve drag visual feedback
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Alert, Platform, Pressable, Text, View } from "react-native";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||
import { CameraView, useCameraPermissions } from "expo-camera";
|
||||
import type { BarcodeScanningResult } from "expo-camera";
|
||||
@@ -13,7 +14,6 @@ const styles = StyleSheet.create((theme) => ({
|
||||
},
|
||||
header: {
|
||||
paddingHorizontal: theme.spacing[6],
|
||||
paddingTop: theme.spacing[6],
|
||||
paddingBottom: theme.spacing[4],
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
@@ -32,7 +32,6 @@ const styles = StyleSheet.create((theme) => ({
|
||||
body: {
|
||||
flex: 1,
|
||||
paddingHorizontal: theme.spacing[6],
|
||||
paddingBottom: theme.spacing[6],
|
||||
},
|
||||
cameraWrap: {
|
||||
flex: 1,
|
||||
@@ -132,6 +131,7 @@ function extractOfferUrlFromScan(result: BarcodeScanningResult): string | null {
|
||||
|
||||
export default function PairScanScreen() {
|
||||
const { theme } = useUnistyles();
|
||||
const insets = useSafeAreaInsets();
|
||||
const router = useRouter();
|
||||
const { upsertDaemonFromOfferUrl } = useDaemonRegistry();
|
||||
|
||||
@@ -172,17 +172,17 @@ export default function PairScanScreen() {
|
||||
if (Platform.OS === "web") {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.header}>
|
||||
<View style={[styles.header, { paddingTop: insets.top + theme.spacing[2] }]}>
|
||||
<Text style={styles.headerTitle}>Scan QR</Text>
|
||||
<Pressable onPress={() => router.back()}>
|
||||
<Text style={styles.headerButtonText}>Close</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
<View style={styles.body}>
|
||||
<View style={[styles.body, { paddingBottom: insets.bottom + theme.spacing[6] }]}>
|
||||
<View style={styles.permissionCard}>
|
||||
<Text style={styles.permissionTitle}>Not available on web</Text>
|
||||
<Text style={styles.permissionBody}>
|
||||
QR scanning isn’t supported in the web build. Use “Paste link” instead.
|
||||
QR scanning isn't supported in the web build. Use "Paste link" instead.
|
||||
</Text>
|
||||
<Pressable style={styles.permissionButton} onPress={() => router.replace("/settings")}>
|
||||
<Text style={styles.permissionButtonText}>Back to Settings</Text>
|
||||
@@ -197,14 +197,14 @@ export default function PairScanScreen() {
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.header}>
|
||||
<View style={[styles.header, { paddingTop: insets.top + theme.spacing[2] }]}>
|
||||
<Text style={styles.headerTitle}>Scan QR</Text>
|
||||
<Pressable onPress={() => router.back()}>
|
||||
<Text style={styles.headerButtonText}>Close</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<View style={styles.body}>
|
||||
<View style={[styles.body, { paddingBottom: insets.bottom + theme.spacing[6] }]}>
|
||||
{!granted ? (
|
||||
<View style={styles.permissionCard}>
|
||||
<Text style={styles.permissionTitle}>Camera permission</Text>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Platform,
|
||||
} from "react-native";
|
||||
import { router } from "expo-router";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles";
|
||||
import { Sun, Moon, Monitor } from "lucide-react-native";
|
||||
import { Fonts } from "@/constants/theme";
|
||||
@@ -378,6 +379,8 @@ const styles = StyleSheet.create((theme) => ({
|
||||
|
||||
|
||||
export default function SettingsScreen() {
|
||||
const { theme } = useUnistyles();
|
||||
const insets = useSafeAreaInsets();
|
||||
const { settings, isLoading: settingsLoading, updateSettings, resetSettings } = useAppSettings();
|
||||
const {
|
||||
daemons,
|
||||
@@ -561,7 +564,7 @@ export default function SettingsScreen() {
|
||||
<View style={styles.container}>
|
||||
<MenuHeader title="Settings" />
|
||||
|
||||
<ScrollView style={styles.scrollView}>
|
||||
<ScrollView style={styles.scrollView} contentContainerStyle={{ paddingBottom: insets.bottom }}>
|
||||
<View style={styles.content}>
|
||||
{/* Host Management */}
|
||||
<View style={styles.section}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { ActivityIndicator, Pressable, Text, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||
import { Check, X, XCircle } from "lucide-react-native";
|
||||
import { useDownloadStore, formatSpeed, formatEta } from "@/stores/download-store";
|
||||
@@ -8,6 +9,7 @@ const AUTO_DISMISS_DELAY = 3000;
|
||||
|
||||
export function DownloadToast() {
|
||||
const { theme } = useUnistyles();
|
||||
const insets = useSafeAreaInsets();
|
||||
const downloads = useDownloadStore((state) => state.downloads);
|
||||
const activeDownloadId = useDownloadStore((state) => state.activeDownloadId);
|
||||
const dismissDownload = useDownloadStore((state) => state.dismissDownload);
|
||||
@@ -39,7 +41,7 @@ export function DownloadToast() {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container} pointerEvents="box-none">
|
||||
<View style={[styles.container, { bottom: theme.spacing[4] + insets.bottom }]} pointerEvents="box-none">
|
||||
<View style={styles.toast}>
|
||||
{activeDownload.status === "downloading" ? (
|
||||
<ActivityIndicator size="small" color={theme.colors.foreground} />
|
||||
@@ -89,7 +91,6 @@ export function DownloadToast() {
|
||||
const styles = StyleSheet.create((theme) => ({
|
||||
container: {
|
||||
position: "absolute",
|
||||
bottom: theme.spacing[4],
|
||||
left: theme.spacing[4],
|
||||
right: theme.spacing[4],
|
||||
zIndex: 1000,
|
||||
|
||||
@@ -61,10 +61,14 @@ function SortableItem<T>({
|
||||
// Trigger drag - handled by dnd-kit's listeners
|
||||
};
|
||||
|
||||
const baseTransform = CSS.Transform.toString(transform);
|
||||
const scaleTransform = isDragging ? "scale(1.02)" : "";
|
||||
const combinedTransform = [baseTransform, scaleTransform].filter(Boolean).join(" ");
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transform: combinedTransform || undefined,
|
||||
transition,
|
||||
opacity: isDragging ? 0.5 : 1,
|
||||
opacity: isDragging ? 0.9 : 1,
|
||||
zIndex: isDragging ? 1000 : 1,
|
||||
};
|
||||
|
||||
|
||||
@@ -649,6 +649,7 @@ const styles = StyleSheet.create((theme) => ({
|
||||
},
|
||||
sectionDragging: {
|
||||
opacity: 0.9,
|
||||
transform: [{ scale: 1.02 }],
|
||||
},
|
||||
chevron: {
|
||||
opacity: 0.5,
|
||||
|
||||
@@ -617,14 +617,25 @@ function AgentScreenContent({
|
||||
<View style={styles.headerRightContent}>
|
||||
<Pressable onPress={toggleFileExplorer} style={styles.menuButton}>
|
||||
{isMobile ? (
|
||||
<Folder
|
||||
size={20}
|
||||
color={
|
||||
isExplorerOpen
|
||||
? theme.colors.foreground
|
||||
: theme.colors.foregroundMuted
|
||||
}
|
||||
/>
|
||||
checkout?.isGit ? (
|
||||
<GitBranch
|
||||
size={20}
|
||||
color={
|
||||
isExplorerOpen
|
||||
? theme.colors.foreground
|
||||
: theme.colors.foregroundMuted
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Folder
|
||||
size={20}
|
||||
color={
|
||||
isExplorerOpen
|
||||
? theme.colors.foreground
|
||||
: theme.colors.foregroundMuted
|
||||
}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<PanelRight
|
||||
size={16}
|
||||
|
||||
Reference in New Issue
Block a user