diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 2938f9a79..c324e476e 100755 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -227,6 +227,7 @@ "remotePortError": "an error occurred when trying to set the remote server port", "remotePortWarning": "restart the server to apply the new port", "saveQueueFailed": "failed to save queue", + "serverLockSingleServer": "only one server is allowed when server is locked", "serverNotSelectedError": "no server selected", "serverRequired": "server required", "sessionExpiredError": "your session has expired", diff --git a/src/renderer/features/login/routes/login-route.tsx b/src/renderer/features/login/routes/login-route.tsx index f82c920a2..20a9cda2f 100644 --- a/src/renderer/features/login/routes/login-route.tsx +++ b/src/renderer/features/login/routes/login-route.tsx @@ -17,7 +17,12 @@ import { IgnoreCorsSslSwitches } from '/@/renderer/features/servers/components/i import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page'; import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary'; import { AppRoute } from '/@/renderer/router/routes'; -import { useAuthStoreActions, useCurrentServer } from '/@/renderer/store'; +import { + getServerById, + useAuthStoreActions, + useCurrentServer, + useServerList, +} from '/@/renderer/store'; import { Button } from '/@/shared/components/button/button'; import { Center } from '/@/shared/components/center/center'; import { Code } from '/@/shared/components/code/code'; @@ -46,11 +51,14 @@ const SERVER_NAMES: Record = { [ServerType.SUBSONIC]: 'OpenSubsonic', }; +const normalizeUrl = (url: string) => url.replace(/\/$/, ''); + const LoginRoute = () => { const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); - const { addServer, setCurrentServer } = useAuthStoreActions(); + const { addServer, setCurrentServer, updateServer } = useAuthStoreActions(); const currentServer = useCurrentServer(); + const serverList = useServerList(); // Check if server lock is configured const serverLock = isServerLock(); @@ -141,24 +149,43 @@ const LoginRoute = () => { }); } + const normalizedUrl = normalizeUrl(serverUrl); + const existingServer = + serverLock && + Object.values(serverList).find((s) => normalizeUrl(s.url) === normalizedUrl); + const serverItem: ServerListItemWithCredential = { credential: data.credential, id: nanoid(), isAdmin: data.isAdmin, name: serverName, type: serverType as ServerType, - url: serverUrl.replace(/\/$/, ''), + url: normalizedUrl, userId: data.userId, username: data.username, }; - if (data.ndCredential !== undefined) { - serverItem.ndCredential = data.ndCredential; + if (existingServer) { + const updates: Partial = { + credential: data.credential, + isAdmin: data.isAdmin, + userId: data.userId, + username: data.username, + }; + if (data.ndCredential !== undefined) { + updates.ndCredential = data.ndCredential; + } + updateServer(existingServer.id, updates); + const updated = getServerById(existingServer.id); + if (updated) setCurrentServer(updated); + } else { + if (data.ndCredential !== undefined) { + serverItem.ndCredential = data.ndCredential; + } + addServer(serverItem); + setCurrentServer(serverItem); } - addServer(serverItem); - setCurrentServer(serverItem); - toast.success({ message: t('form.addServer.success', { postProcess: 'sentenceCase' }), }); diff --git a/src/renderer/features/servers/components/add-server-form.tsx b/src/renderer/features/servers/components/add-server-form.tsx index 1e23caac6..67454c8cf 100644 --- a/src/renderer/features/servers/components/add-server-form.tsx +++ b/src/renderer/features/servers/components/add-server-form.tsx @@ -13,7 +13,7 @@ import JellyfinIcon from '/@/renderer/features/servers/assets/jellyfin.png'; import NavidromeIcon from '/@/renderer/features/servers/assets/navidrome.png'; import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png'; import { IgnoreCorsSslSwitches } from '/@/renderer/features/servers/components/ignore-cors-ssl-switches'; -import { useAuthStoreActions } from '/@/renderer/store'; +import { useAuthStoreActions, useServerList } from '/@/renderer/store'; import { Checkbox } from '/@/shared/components/checkbox/checkbox'; import { Divider } from '/@/shared/components/divider/divider'; import { Group } from '/@/shared/components/group/group'; @@ -98,6 +98,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => { const focusTrapRef = useFocusTrap(true); const [isLoading, setIsLoading] = useState(false); const { addServer, setCurrentServer } = useAuthStoreActions(); + const serverList = useServerList(); const { servers: discovered } = useAutodiscovery(); const serverLock = isServerLock(); @@ -128,6 +129,13 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => { }; const handleSubmit = form.onSubmit(async (values) => { + if (serverLock && Object.keys(serverList).length >= 1) { + toast.error({ + message: t('error.serverLockSingleServer', { postProcess: 'sentenceCase' }), + }); + return; + } + const authFunction = api.controller.authenticate; if (!authFunction) { diff --git a/src/renderer/features/servers/components/server-list.tsx b/src/renderer/features/servers/components/server-list.tsx index 713b4b98e..e5a1860e1 100644 --- a/src/renderer/features/servers/components/server-list.tsx +++ b/src/renderer/features/servers/components/server-list.tsx @@ -2,6 +2,7 @@ import { openContextModal } from '@mantine/modals'; import isElectron from 'is-electron'; import { useTranslation } from 'react-i18next'; +import { isServerLock } from '/@/renderer/features/action-required/utils/window-properties'; import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png'; import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png'; import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png'; @@ -23,6 +24,7 @@ export const ServerList = () => { const { t } = useTranslation(); const currentServer = useCurrentServer(); const serverListQuery = useServerList(); + const serverLock = isServerLock(); const handleAddServerModal = () => { openContextModal({ @@ -70,15 +72,17 @@ export const ServerList = () => { ); })} - - - + {!serverLock && ( + + + + )} {isElectron() && ( <>