179 lines · 7.1 KB · TypeScript
RawDownload
import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";import { useNavigation } from "@react-navigation/native";import { SymbolView } from "../../components/AppSymbol";import type { EnvironmentId } from "@t3tools/contracts";import { useCallback, useEffect, useState } from "react";import { Platform, ScrollView, View } from "react-native";import { useSafeAreaInsets } from "react-native-safe-area-context"; import { AppText as Text } from "../../components/AppText";import { AndroidScreenHeader } from "../../components/AndroidScreenHeader";import { hasCloudPublicConfig } from "../cloud/publicConfig";import { CloudEnvironmentRows } from "../connection/CloudEnvironmentRows";import { ConnectionEnvironmentRow } from "../connection/ConnectionEnvironmentRow";import { splitEnvironmentSections } from "../connection/environmentSections";import { cn } from "../../lib/cn";import { useThemeColor } from "../../lib/useThemeColor";import { useRemoteConnections } from "../../state/use-remote-environment-registry";import {  applyShowcaseLocalEnvironmentDisplayUrls,  resolveShowcaseEnvironmentUpdateDisplayUrl,  SHOWCASE_AVAILABLE_CLOUD_ENVIRONMENTS,  SHOWCASE_CONNECTED_CLOUD_ENVIRONMENTS,} from "../showcase/showcaseEnvironmentRows";import { markNativeShowcaseReady } from "../showcase/nativeShowcaseScene"; const SHOWCASE_ENABLED = process.env.EXPO_PUBLIC_SHOWCASE === "1"; export function SettingsEnvironmentsRouteScreen() {  const {    connectedEnvironments,    onReconnectEnvironment,    onRemoveEnvironmentPress,    onUpdateEnvironment,  } = useRemoteConnections();  const navigation = useNavigation();  const insets = useSafeAreaInsets();  const environmentSections = splitEnvironmentSections({    connectedEnvironments,    cloudEnvironments: null,  });  const localEnvironments = SHOWCASE_ENABLED    ? applyShowcaseLocalEnvironmentDisplayUrls(environmentSections.localEnvironments)    : environmentSections.localEnvironments;  const connectedCloudEnvironments = SHOWCASE_ENABLED    ? SHOWCASE_CONNECTED_CLOUD_ENVIRONMENTS    : environmentSections.connectedCloudEnvironments;  const hasLocalEnvironments = localEnvironments.length > 0;  const [expandedId, setExpandedId] = useState<EnvironmentId | null>(null);  const accentColor = useThemeColor("--color-icon-muted");  const headerIconColor = useThemeColor("--color-icon");   useEffect(() => {    if (!SHOWCASE_ENABLED) return;    const timer = setTimeout(() => markNativeShowcaseReady("environments"), 500);    return () => clearTimeout(timer);  }, []);   const handleToggle = useCallback((environmentId: EnvironmentId) => {    setExpandedId((prev) => (prev === environmentId ? null : environmentId));  }, []);  const handleUpdateEnvironment = useCallback(    (      environmentId: EnvironmentId,      updates: { readonly label: string; readonly displayUrl: string },    ) => {      if (!SHOWCASE_ENABLED) return onUpdateEnvironment(environmentId, updates);      const actualEnvironment = environmentSections.localEnvironments.find(        (environment) => environment.environmentId === environmentId,      );      const presentedEnvironment = localEnvironments.find(        (environment) => environment.environmentId === environmentId,      );      return onUpdateEnvironment(environmentId, {        ...updates,        displayUrl:          actualEnvironment && presentedEnvironment            ? resolveShowcaseEnvironmentUpdateDisplayUrl({                actualDisplayUrl: actualEnvironment.displayUrl,                presentedDisplayUrl: presentedEnvironment.displayUrl,                submittedDisplayUrl: updates.displayUrl,              })            : updates.displayUrl,      });    },    [environmentSections.localEnvironments, localEnvironments, onUpdateEnvironment],  );   return (    <View collapsable={false} className="flex-1 bg-sheet">      {Platform.OS === "android" ? (        <>          {/* Android renders its own in-screen header instead of the native bar. */}          <NativeStackScreenOptions options={{ headerShown: false }} />          <AndroidScreenHeader            title="Environments"            onBack={() => navigation.goBack()}            actions={[              {                accessibilityLabel: "Add environment",                icon: "plus",                onPress: () =>                  navigation.navigate("SettingsSheet", { screen: "SettingsEnvironmentNew" }),              },            ]}          />        </>      ) : (        <NativeHeaderToolbar placement="right">          <NativeHeaderToolbar.Button            icon="plus"            onPress={() =>              navigation.navigate("SettingsSheet", { screen: "SettingsEnvironmentNew" })            }            separateBackground            tintColor={headerIconColor}          />        </NativeHeaderToolbar>      )}      <ScrollView        contentInsetAdjustmentBehavior="automatic"        showsVerticalScrollIndicator={false}        className="flex-1"        contentContainerClassName="px-5 pt-4"        contentContainerStyle={{          paddingBottom: Math.max(insets.bottom, 18) + 18,        }}      >        {hasLocalEnvironments ? (          <View collapsable={false} className="overflow-hidden rounded-[24px] bg-card">            {localEnvironments.map((environment, index) => (              <View                key={environment.environmentId}                collapsable={false}                className={cn(index !== 0 && "border-t border-border")}              >                <ConnectionEnvironmentRow                  environment={environment}                  expanded={expandedId === environment.environmentId}                  onToggle={() => handleToggle(environment.environmentId)}                  onReconnect={onReconnectEnvironment}                  onRemove={onRemoveEnvironmentPress}                  onUpdate={handleUpdateEnvironment}                />              </View>            ))}          </View>        ) : (          <View collapsable={false} className="items-center gap-3 rounded-[24px] bg-card px-6 py-8">            <View className="h-12 w-12 items-center justify-center rounded-[16px] bg-subtle">              <SymbolView                name="point.3.connected.trianglepath.dotted"                size={20}                tintColor={accentColor}                type="monochrome"              />            </View>            <Text className="text-center text-sm leading-normal text-foreground-muted">              No environments connected yet.{"\n"}Tap{" "}              <Text className="font-t3-bold text-foreground">+</Text> to add one.            </Text>          </View>        )}         {hasCloudPublicConfig() || SHOWCASE_ENABLED ? (          <CloudEnvironmentRows            connectedCloudEnvironments={connectedCloudEnvironments}            onReconnectEnvironment={onReconnectEnvironment}            {...(SHOWCASE_ENABLED              ? {                  showcaseAvailableEnvironments: SHOWCASE_AVAILABLE_CLOUD_ENVIRONMENTS,                  showcaseSignedIn: true,                }              : {})}          />        ) : null}      </ScrollView>    </View>  );}