// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React, {useState, useCallback, useEffect, useRef} from 'react'; import {useIntl, FormattedMessage} from 'react-intl'; import classNames from 'classnames'; import {UniqueServer} from 'types/config'; import {MODAL_TRANSITION_TIMEOUT, URLValidationStatus} from 'common/utils/constants'; import womanLaptop from 'renderer/assets/svg/womanLaptop.svg'; import Header from 'renderer/components/Header'; import Input, {STATUS, SIZE} from 'renderer/components/Input'; import LoadingBackground from 'renderer/components/LoadingScreen/LoadingBackground'; import SaveButton from 'renderer/components/SaveButton/SaveButton'; import 'renderer/css/components/Button.scss'; import 'renderer/css/components/ConfigureServer.scss'; import 'renderer/css/components/LoadingScreen.css'; type ConfigureServerProps = { server?: UniqueServer; mobileView?: boolean; darkMode?: boolean; messageTitle?: string; messageSubtitle?: string; cardTitle?: string; alternateLinkMessage?: string; alternateLinkText?: string; alternateLinkURL?: string; onConnect: (data: UniqueServer) => void; }; function ConfigureServer({ server, mobileView, darkMode, messageTitle, messageSubtitle, cardTitle, alternateLinkMessage, alternateLinkText, alternateLinkURL, onConnect, }: ConfigureServerProps) { const {formatMessage} = useIntl(); const { name: prevName, url: prevURL, id, } = server || {}; const mounted = useRef(false); const [transition, setTransition] = useState<'inFromRight' | 'outToLeft'>(); const [name, setName] = useState(prevName || ''); const [url, setUrl] = useState(prevURL || ''); const [nameError, setNameError] = useState(''); const [urlError, setURLError] = useState<{type: STATUS; value: string}>(); const [showContent, setShowContent] = useState(false); const [waiting, setWaiting] = useState(false); const [validating, setValidating] = useState(false); const validationTimestamp = useRef(); const validationTimeout = useRef(); const editing = useRef(false); const canSave = name && url && !nameError && !validating && urlError && urlError.type !== STATUS.ERROR; useEffect(() => { setTransition('inFromRight'); setShowContent(true); mounted.current = true; return () => { mounted.current = false; }; }, []); const fetchValidationResult = (urlToValidate: string) => { setValidating(true); setURLError({ type: STATUS.INFO, value: formatMessage({id: 'renderer.components.configureServer.url.validating', defaultMessage: 'Validating...'}), }); const requestTime = Date.now(); validationTimestamp.current = requestTime; validateURL(urlToValidate).then(({validatedURL, serverName, message}) => { if (editing.current) { setValidating(false); setURLError(undefined); return; } if (!validationTimestamp.current || requestTime < validationTimestamp.current) { return; } if (validatedURL) { setUrl(validatedURL); } if (serverName) { setName((prev) => { return prev.length ? prev : serverName; }); } if (message) { setTransition(undefined); setURLError(message); } setValidating(false); }); }; const validateName = () => { const newName = name.trim(); if (!newName) { return formatMessage({ id: 'renderer.components.newServerModal.error.nameRequired', defaultMessage: 'Name is required.', }); } return ''; }; const validateURL = async (url: string) => { let message; const validationResult = await window.desktop.validateServerURL(url); if (validationResult?.status === URLValidationStatus.Missing) { message = { type: STATUS.ERROR, value: formatMessage({ id: 'renderer.components.newServerModal.error.urlRequired', defaultMessage: 'URL is required.', }), }; } if (validationResult?.status === URLValidationStatus.Invalid) { message = { type: STATUS.ERROR, value: formatMessage({ id: 'renderer.components.newServerModal.error.urlIncorrectFormatting', defaultMessage: 'URL is not formatted correctly.', }), }; } if (validationResult?.status === URLValidationStatus.Insecure) { message = { type: STATUS.WARNING, value: formatMessage({id: 'renderer.components.configureServer.url.insecure', defaultMessage: 'Your server URL is potentially insecure. For best results, use a URL with the HTTPS protocol.'}), }; } if (validationResult?.status === URLValidationStatus.NotMattermost) { message = { type: STATUS.WARNING, value: formatMessage({id: 'renderer.components.configureServer.url.notMattermost', defaultMessage: 'The server URL provided does not appear to point to a valid Mattermost server. Please verify the URL and check your connection.'}), }; } if (validationResult?.status === URLValidationStatus.URLNotMatched) { message = { type: STATUS.WARNING, value: formatMessage({id: 'renderer.components.configureServer.url.urlNotMatched', defaultMessage: 'The server URL provided does not match the configured Site URL on your Mattermost server. Server version: {serverVersion}'}, {serverVersion: validationResult.serverVersion}), }; } if (validationResult?.status === URLValidationStatus.URLUpdated) { message = { type: STATUS.INFO, value: formatMessage({id: 'renderer.components.configureServer.url.urlUpdated', defaultMessage: 'The server URL provided has been updated to match the configured Site URL on your Mattermost server. Server version: {serverVersion}'}, {serverVersion: validationResult.serverVersion}), }; } if (validationResult?.status === URLValidationStatus.OK) { message = { type: STATUS.SUCCESS, value: formatMessage({id: 'renderer.components.configureServer.url.ok', defaultMessage: 'Server URL is valid. Server version: {serverVersion}'}, {serverVersion: validationResult.serverVersion}), }; } return { validatedURL: validationResult.validatedURL, serverName: validationResult.serverName, message, }; }; const handleNameOnChange = ({target: {value}}: React.ChangeEvent) => { setName(value); if (nameError) { setNameError(''); } }; const handleURLOnChange = ({target: {value}}: React.ChangeEvent) => { setUrl(value); if (urlError) { setURLError(undefined); } editing.current = true; clearTimeout(validationTimeout.current as unknown as number); validationTimeout.current = setTimeout(() => { if (!mounted.current) { return; } editing.current = false; fetchValidationResult(value); }, 1000); }; const handleOnSaveButtonClick = (e: React.MouseEvent) => { submit(e); }; const handleOnCardEnterKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { submit(e); } }; const submit = async (e: React.MouseEvent | React.KeyboardEvent) => { e.preventDefault(); if (!canSave || waiting) { return; } setWaiting(true); const nameError = validateName(); if (nameError) { setTransition(undefined); setNameError(nameError); setWaiting(false); return; } setTransition('outToLeft'); setTimeout(() => { onConnect({ url, name, id, }); }, MODAL_TRANSITION_TIMEOUT); }; const getAlternateLink = useCallback(() => { if (!alternateLinkURL || !alternateLinkMessage || !alternateLinkText) { return undefined; } return (
{alternateLinkMessage} {alternateLinkText}
); }, [transition, darkMode, alternateLinkURL, alternateLinkMessage, alternateLinkText]); return (
{showContent && (
{!mobileView && getAlternateLink()}

{messageTitle || formatMessage({id: 'renderer.components.configureServer.title', defaultMessage: 'Let’s connect to a server'})}

{messageSubtitle || ( (<>
{x}), }} />) }

{cardTitle || formatMessage({id: 'renderer.components.configureServer.cardtitle', defaultMessage: 'Enter your server details'})}

)}
); } export default ConfigureServer;