mirror of
https://github.com/Termix-SSH/Mobile.git
synced 2026-07-28 05:51:43 +02:00
6c40d2f0cc
* fix: register physical Tab key on iPadOS/iOS hardware keyboards (#14) The native module only registered Shift+Tab but not bare Tab, so iOS intercepted it for UI focus navigation. Register an unmodified Tab UIKeyCommand with wantsPriorityOverSystemBehavior and handle both Tab and Shift+Tab in the same JS branch. Fixes Termix-SSH/Support#557 * fix: pass jumpHosts, forceKeyboardInteractive, and overrideCredentialUsername to terminal connection (#15) These fields were defined on SSHHost but omitted from the terminal hostConfig passthrough. Without jumpHosts the backend cannot route through jump servers; without forceKeyboardInteractive hosts that require keyboard-interactive auth fail silently; without overrideCredentialUsername shared credentials use the wrong username. Fixes Termix-SSH/Support#422 Fixes Termix-SSH/Support#638 * fix: improve mobile terminal scroll recovery (#17) * Add mobile remote desktop sessions (#18) * feat: add mobile remote desktop sessions * feat: add remote desktop input controls * feat: polish remote desktop controls * feat: add mobile terminal font selection (#19) * feat: add mobile shift tab hotkey (#20) * feat: initial UI redesign * feat: revamp server login system * chore: update app icon/background color * fix: remove unused fields in the settings tab * feat: improve the user pin system and update all settings modals to use new ui * fix: active server not updating unless app restarts * feat: add version in settings * feat: improve host page UI (updated host form and added credential management) * fix: preserve composed iOS terminal input * ci: restore mobile checks * feat: rewrite of connection system for all types * fix: preserve mobile jump hosts (#30) * fix: guard android user ca trust (#29) * fix: capture ios hardware tab key (#27) * fix: reset terminal input after special keys (#28) * fix: open oidc in system browser (#26) * fix: preserve websocket auth context (#25) * fix: keep none-auth terminal prompts alive (#23) * fix: allow local network SSL in Android app (#21) * feat: allow oidc passkeys * feat: bundeled xterm into app and added docker loading screen * feat: add 2fa, api key, and active sessions to settings and addressed multiple bug fixes and inconsistencies with termix web * chore: update repo images and readme * chore: update readme to split table for screenshots * chore: rename ios to apple in build workflow * fix: android build failure * feat: add snippet management and fix several bugs * fix: web login and oidc incorrect logic * chore: run linter --------- Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
149 lines
4.5 KiB
TypeScript
149 lines
4.5 KiB
TypeScript
import { Stack } from "expo-router";
|
|
import { AppProvider, useAppContext } from "./AppContext";
|
|
import { TerminalSessionsProvider } from "./contexts/TerminalSessionsContext";
|
|
import { TerminalCustomizationProvider } from "./contexts/TerminalCustomizationContext";
|
|
import { KeyboardProvider } from "./contexts/KeyboardContext";
|
|
import { KeyboardCustomizationProvider } from "./contexts/KeyboardCustomizationContext";
|
|
import {
|
|
ThemeProvider,
|
|
useTheme,
|
|
useThemeColor,
|
|
} from "./contexts/ThemeContext";
|
|
import { AppLockProvider, useAppLock } from "./contexts/AppLockContext";
|
|
import { LockScreen } from "@/app/components/LockScreen";
|
|
import AuthFlow from "@/app/authentication/AuthFlow";
|
|
import { View, Text, ActivityIndicator, TouchableOpacity } from "react-native";
|
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
import { StatusBar } from "expo-status-bar";
|
|
import { Toaster } from "sonner-native";
|
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
import { useFonts } from "expo-font";
|
|
import { FONT_MAP, MONO_FONT, MONO_FONT_BOLD } from "./constants/fonts";
|
|
import "../global.css";
|
|
import UpdateRequired from "@/app/authentication/UpdateRequired";
|
|
|
|
function RootLayoutContent() {
|
|
const {
|
|
authFlowVisible,
|
|
openAuthFlow,
|
|
showUpdateScreen,
|
|
isLoading,
|
|
setIsLoading,
|
|
} = useAppContext();
|
|
const accent = useThemeColor()("accent-brand");
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<View className="flex-1 items-center justify-center bg-background">
|
|
<ActivityIndicator size="large" color={accent} />
|
|
<Text
|
|
className="mt-4 text-base text-foreground"
|
|
style={{ fontFamily: MONO_FONT }}
|
|
>
|
|
Initializing…
|
|
</Text>
|
|
<TouchableOpacity
|
|
onPress={() => {
|
|
setIsLoading(false);
|
|
openAuthFlow("server");
|
|
}}
|
|
className="mt-6 border border-border bg-card px-6 py-3"
|
|
>
|
|
<Text
|
|
className="text-foreground"
|
|
style={{ fontFamily: MONO_FONT_BOLD }}
|
|
>
|
|
Cancel
|
|
</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (showUpdateScreen) return <UpdateRequired />;
|
|
|
|
// The tab shell always renders once loaded. When the user isn't connected,
|
|
// the tabs themselves show a "no server connected" empty state. The auth flow
|
|
// is layered on top as a dismissible full-screen overlay.
|
|
return (
|
|
<View className="flex-1 bg-background">
|
|
<Stack screenOptions={{ headerShown: false }}>
|
|
<Stack.Screen name="index" options={{ headerShown: false }} />
|
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
|
</Stack>
|
|
<AppLockGate />
|
|
{authFlowVisible ? (
|
|
<View className="absolute inset-0 bg-background">
|
|
<AuthFlow />
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function AppLockGate() {
|
|
const { enabled, locked } = useAppLock();
|
|
if (!enabled || !locked) return null;
|
|
return <LockScreen />;
|
|
}
|
|
|
|
function ThemedToaster() {
|
|
const { isDark } = useTheme();
|
|
const card = useThemeColor()("card");
|
|
const border = useThemeColor()("border");
|
|
return (
|
|
<Toaster
|
|
theme={isDark ? "dark" : "light"}
|
|
position="top-center"
|
|
toastOptions={{
|
|
style: {
|
|
backgroundColor: card,
|
|
borderWidth: 1,
|
|
borderColor: border,
|
|
borderRadius: 0,
|
|
},
|
|
}}
|
|
richColors={false}
|
|
closeButton
|
|
duration={4000}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function ThemedStatusBar() {
|
|
const { isDark } = useTheme();
|
|
return <StatusBar style={isDark ? "light" : "dark"} />;
|
|
}
|
|
|
|
export default function RootLayout() {
|
|
const [fontsLoaded] = useFonts(FONT_MAP);
|
|
|
|
return (
|
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
<SafeAreaProvider>
|
|
<ThemeProvider>
|
|
<ThemedStatusBar />
|
|
{!fontsLoaded ? (
|
|
<View className="flex-1 bg-background" />
|
|
) : (
|
|
<AppLockProvider>
|
|
<AppProvider>
|
|
<TerminalSessionsProvider>
|
|
<TerminalCustomizationProvider>
|
|
<KeyboardProvider>
|
|
<KeyboardCustomizationProvider>
|
|
<RootLayoutContent />
|
|
<ThemedToaster />
|
|
</KeyboardCustomizationProvider>
|
|
</KeyboardProvider>
|
|
</TerminalCustomizationProvider>
|
|
</TerminalSessionsProvider>
|
|
</AppProvider>
|
|
</AppLockProvider>
|
|
)}
|
|
</ThemeProvider>
|
|
</SafeAreaProvider>
|
|
</GestureHandlerRootView>
|
|
);
|
|
}
|