fix: remove per-host "Add connection" button that blocked multi-host setups

Users connecting multiple daemons (e.g. local + remote via Tailscale) hit
a "belongs to X, not Y" error because the edit-host modal's Add Connection
button scoped new connections to the current host's server ID. Remove that
button and its supporting state so all connections go through the top-level
flow which has no server ID constraint.
This commit is contained in:
Mohamed Boudra
2026-04-04 23:45:03 +07:00
parent c65a851205
commit a693ff560c
+2 -62
View File
@@ -184,13 +184,10 @@ interface HostsSectionProps {
routeServerId: string; routeServerId: string;
theme: ReturnType<typeof useUnistyles>["theme"]; theme: ReturnType<typeof useUnistyles>["theme"];
handleEditDaemon: (profile: HostProfile) => void; handleEditDaemon: (profile: HostProfile) => void;
setAddConnectionTargetServerId: (id: string | null) => void;
setPendingEditReopenServerId: (id: string | null) => void;
setIsAddHostMethodVisible: (visible: boolean) => void; setIsAddHostMethodVisible: (visible: boolean) => void;
isAddHostMethodVisible: boolean; isAddHostMethodVisible: boolean;
isDirectHostVisible: boolean; isDirectHostVisible: boolean;
isPasteLinkVisible: boolean; isPasteLinkVisible: boolean;
addConnectionTargetServerId: string | null;
closeAddConnectionFlow: () => void; closeAddConnectionFlow: () => void;
goBackToAddConnectionMethods: () => void; goBackToAddConnectionMethods: () => void;
setIsDirectHostVisible: (visible: boolean) => void; setIsDirectHostVisible: (visible: boolean) => void;
@@ -210,7 +207,6 @@ interface HostsSectionProps {
handleSaveEditDaemon: (label: string) => Promise<void>; handleSaveEditDaemon: (label: string) => Promise<void>;
handleRemoveConnection: (serverId: string, connectionId: string) => Promise<void>; handleRemoveConnection: (serverId: string, connectionId: string) => Promise<void>;
handleRemoveDaemon: (profile: HostProfile) => void; handleRemoveDaemon: (profile: HostProfile) => void;
handleAddConnectionFromModal: () => void;
restartConfirmationMessage: string; restartConfirmationMessage: string;
waitForCondition: ( waitForCondition: (
predicate: () => boolean, predicate: () => boolean,
@@ -250,8 +246,6 @@ function HostsSection(props: HostsSectionProps) {
style={styles.addButton} style={styles.addButton}
textStyle={styles.addButtonText} textStyle={styles.addButtonText}
onPress={() => { onPress={() => {
props.setAddConnectionTargetServerId(null);
props.setPendingEditReopenServerId(null);
props.setIsAddHostMethodVisible(true); props.setIsAddHostMethodVisible(true);
}} }}
> >
@@ -271,22 +265,17 @@ function HostsSection(props: HostsSectionProps) {
props.setIsPasteLinkVisible(true); props.setIsPasteLinkVisible(true);
}} }}
onScanQr={() => { onScanQr={() => {
const targetServerId = props.addConnectionTargetServerId; const sourceServerId = props.routeServerId || undefined;
const source = targetServerId ? "editHost" : "settings";
const sourceServerId = props.routeServerId || targetServerId || undefined;
props.closeAddConnectionFlow(); props.closeAddConnectionFlow();
router.push({ router.push({
pathname: "/pair-scan", pathname: "/pair-scan",
params: targetServerId params: { source: "settings", sourceServerId },
? { source, targetServerId, sourceServerId }
: { source, sourceServerId },
}); });
}} }}
/> />
<AddHostModal <AddHostModal
visible={props.isDirectHostVisible} visible={props.isDirectHostVisible}
targetServerId={props.addConnectionTargetServerId ?? undefined}
onClose={props.closeAddConnectionFlow} onClose={props.closeAddConnectionFlow}
onCancel={props.goBackToAddConnectionMethods} onCancel={props.goBackToAddConnectionMethods}
onSaved={({ serverId, hostname, isNewHost }) => { onSaved={({ serverId, hostname, isNewHost }) => {
@@ -298,7 +287,6 @@ function HostsSection(props: HostsSectionProps) {
<PairLinkModal <PairLinkModal
visible={props.isPasteLinkVisible} visible={props.isPasteLinkVisible}
targetServerId={props.addConnectionTargetServerId ?? undefined}
onClose={props.closeAddConnectionFlow} onClose={props.closeAddConnectionFlow}
onCancel={props.goBackToAddConnectionMethods} onCancel={props.goBackToAddConnectionMethods}
onSaved={({ serverId, hostname, isNewHost }) => { onSaved={({ serverId, hostname, isNewHost }) => {
@@ -378,7 +366,6 @@ function HostsSection(props: HostsSectionProps) {
onSave={(label) => void props.handleSaveEditDaemon(label)} onSave={(label) => void props.handleSaveEditDaemon(label)}
onRemoveConnection={props.handleRemoveConnection} onRemoveConnection={props.handleRemoveConnection}
onRemoveHost={props.handleRemoveDaemon} onRemoveHost={props.handleRemoveDaemon}
onAddConnection={props.handleAddConnectionFromModal}
restartConfirmationMessage={props.restartConfirmationMessage} restartConfirmationMessage={props.restartConfirmationMessage}
waitForCondition={props.waitForCondition} waitForCondition={props.waitForCondition}
isScreenMountedRef={props.isMountedRef} isScreenMountedRef={props.isMountedRef}
@@ -741,10 +728,6 @@ export default function SettingsScreen() {
const [isAddHostMethodVisible, setIsAddHostMethodVisible] = useState(false); const [isAddHostMethodVisible, setIsAddHostMethodVisible] = useState(false);
const [isDirectHostVisible, setIsDirectHostVisible] = useState(false); const [isDirectHostVisible, setIsDirectHostVisible] = useState(false);
const [isPasteLinkVisible, setIsPasteLinkVisible] = useState(false); const [isPasteLinkVisible, setIsPasteLinkVisible] = useState(false);
const [addConnectionTargetServerId, setAddConnectionTargetServerId] = useState<string | null>(
null,
);
const [pendingEditReopenServerId, setPendingEditReopenServerId] = useState<string | null>(null);
const [pendingNameHost, setPendingNameHost] = useState<{ const [pendingNameHost, setPendingNameHost] = useState<{
serverId: string; serverId: string;
hostname: string | null; hostname: string | null;
@@ -823,7 +806,6 @@ export default function SettingsScreen() {
setIsAddHostMethodVisible(false); setIsAddHostMethodVisible(false);
setIsDirectHostVisible(false); setIsDirectHostVisible(false);
setIsPasteLinkVisible(false); setIsPasteLinkVisible(false);
setAddConnectionTargetServerId(null);
}, []); }, []);
const goBackToAddConnectionMethods = useCallback(() => { const goBackToAddConnectionMethods = useCallback(() => {
@@ -842,24 +824,6 @@ export default function SettingsScreen() {
handleEditDaemon(profile); handleEditDaemon(profile);
}, [daemons, handleEditDaemon, params.editHost]); }, [daemons, handleEditDaemon, params.editHost]);
useEffect(() => {
if (!pendingEditReopenServerId) return;
if (isAddHostMethodVisible || isDirectHostVisible || isPasteLinkVisible) return;
const profile = daemons.find((daemon) => daemon.serverId === pendingEditReopenServerId) ?? null;
setPendingEditReopenServerId(null);
setAddConnectionTargetServerId(null);
if (profile) {
handleEditDaemon(profile);
}
}, [
daemons,
handleEditDaemon,
isAddHostMethodVisible,
isDirectHostVisible,
isPasteLinkVisible,
pendingEditReopenServerId,
]);
const handleSaveEditDaemon = useCallback( const handleSaveEditDaemon = useCallback(
async (nextLabelRaw: string) => { async (nextLabelRaw: string) => {
if (!editingServerId) return; if (!editingServerId) return;
@@ -897,15 +861,6 @@ export default function SettingsScreen() {
setPendingRemoveHost(profile); setPendingRemoveHost(profile);
}, []); }, []);
const handleAddConnectionFromModal = useCallback(() => {
if (!editingServerId) return;
const serverId = editingServerId;
setEditingDaemon(null);
setAddConnectionTargetServerId(serverId);
setPendingEditReopenServerId(serverId);
setIsAddHostMethodVisible(true);
}, [editingServerId]);
const handleThemeChange = useCallback( const handleThemeChange = useCallback(
(newTheme: AppSettings["theme"]) => { (newTheme: AppSettings["theme"]) => {
void updateSettings({ theme: newTheme }); void updateSettings({ theme: newTheme });
@@ -951,13 +906,10 @@ export default function SettingsScreen() {
routeServerId, routeServerId,
theme, theme,
handleEditDaemon, handleEditDaemon,
setAddConnectionTargetServerId,
setPendingEditReopenServerId,
setIsAddHostMethodVisible, setIsAddHostMethodVisible,
isAddHostMethodVisible, isAddHostMethodVisible,
isDirectHostVisible, isDirectHostVisible,
isPasteLinkVisible, isPasteLinkVisible,
addConnectionTargetServerId,
closeAddConnectionFlow, closeAddConnectionFlow,
goBackToAddConnectionMethods, goBackToAddConnectionMethods,
setIsDirectHostVisible, setIsDirectHostVisible,
@@ -977,7 +929,6 @@ export default function SettingsScreen() {
handleSaveEditDaemon, handleSaveEditDaemon,
handleRemoveConnection, handleRemoveConnection,
handleRemoveDaemon, handleRemoveDaemon,
handleAddConnectionFromModal,
restartConfirmationMessage: "This will restart the daemon. The app will reconnect automatically.", restartConfirmationMessage: "This will restart the daemon. The app will reconnect automatically.",
waitForCondition, waitForCondition,
isMountedRef, isMountedRef,
@@ -1042,7 +993,6 @@ interface HostDetailModalProps {
onSave: (label: string) => void; onSave: (label: string) => void;
onRemoveConnection: (serverId: string, connectionId: string) => Promise<void>; onRemoveConnection: (serverId: string, connectionId: string) => Promise<void>;
onRemoveHost: (host: HostProfile) => void; onRemoveHost: (host: HostProfile) => void;
onAddConnection: () => void;
restartConfirmationMessage: string; restartConfirmationMessage: string;
waitForCondition: ( waitForCondition: (
predicate: () => boolean, predicate: () => boolean,
@@ -1060,7 +1010,6 @@ function HostDetailModal({
onSave, onSave,
onRemoveConnection, onRemoveConnection,
onRemoveHost, onRemoveHost,
onAddConnection,
restartConfirmationMessage, restartConfirmationMessage,
waitForCondition, waitForCondition,
isScreenMountedRef, isScreenMountedRef,
@@ -1291,15 +1240,6 @@ function HostDetailModal({
/> />
); );
})} })}
<Button
variant="outline"
size="md"
style={styles.addButton}
textStyle={styles.addButtonText}
onPress={onAddConnection}
>
+ Add connection
</Button>
</View> </View>
</View> </View>
) : null} ) : null}