import React, { useEffect, useMemo, useRef, useCallback } from 'react' import { Link } from 'react-router-dom' import { Menu, MenuButton, MenuItem, useMenuState } from 'reakit' import classNames from 'classnames' import { useOrgParams } from '~/utils/organization' import { useRecoilState } from 'recoil' import { consoleUIState } from '../Console' import useDashboard, { useHasPermission } from '../DashboardContext' import Transition from '../Transition' import { dropdownMenuClassNames } from '~/components/DropdownMenu' import useCopyToClipboard from '~/utils/useCopyToClipboard' import { trpc } from '~/utils/trpc' import IconClipboard from '~/icons/compiled/Clipboard' import { notify } from '~/components/NotificationCenter' import { UI_STATE } from '~/components/TransactionUI/useTransaction' import { DEVELOPMENT_ORG_ENV_NAME, DEVELOPMENT_ORG_ENV_SLUG, } from '~/utils/environments' import IconChevronDown from '~/icons/compiled/ChevronDown' import SettingsIcon from '~/icons/compiled/Settings' import useEnvSwitcher from '~/utils/useOrgEnvSwitcher' import EnvironmentColor from '~/components/EnvironmentColor' import { useDebounce } from 'use-debounce' import IVSpinner from '~/components/IVSpinner' function ApiKeyButton({ apiKey }: { apiKey: string }) { const { isCopied, onCopyClick } = useCopyToClipboard() useEffect(() => { if (isCopied) { notify.success('Copied token to clipboard') } }, [isCopied]) return ( ) } function ConnectionIndicator({ state }: { state: UI_STATE | null }) { return ( ) } function ConnectionStatus({ state }: { state: UI_STATE }) { if (state === 'CONNECTING' || state === 'HOST_DROPPED') { return } if ( state === 'COMPLETED' || state === 'USURPED' || // usurped is still online state === 'IN_PROGRESS' ) { return Online } if ( state === 'HOST_NOT_FOUND' || state === 'SERVER_DROPPED' || state === 'IDLE' ) { return Offline } return <> } export default function ModeSwitch() { const { orgEnvSlug, envSlug, isDevMode, orgSlug } = useOrgParams<{ '*': string }>() const { organization } = useDashboard() const [hostState] = useRecoilState(consoleUIState) const canAccessEnvironments = useHasPermission('ACCESS_ORG_ENVIRONMENTS') const canUpdateEnvironments = useHasPermission('WRITE_ORG_SETTINGS') const { currentEnvName, envOptions, switchToEnvironment, isNonEnvPage } = useEnvSwitcher({ organization, orgEnvSlug, envSlug, }) // we use this data when the user is in live mode. // when in dev mode, we use the websocket connection's status from consoleUIState. const status = trpc.useQuery(['dashboard.dev-host-status']) const derivedStatus = useMemo(() => { if (isDevMode) { if ( hostState === 'COMPLETED' || hostState === 'USURPED' || hostState === 'IN_PROGRESS' ) { return 'IN_PROGRESS' } if (hostState === 'HOST_NOT_FOUND' || hostState === 'SERVER_DROPPED') { return 'SERVER_DROPPED' } if (hostState === 'HOST_DROPPED') { return 'HOST_DROPPED' } return 'IDLE' } if (status?.data?.hasOnlineDevHost) { return 'IN_PROGRESS' } return 'IDLE' }, [isDevMode, hostState, status.data]) const menu = useMenuState({ animated: 250, gutter: 4, placement: 'bottom-end', }) const currentItemRef = useRef(null) useEffect(() => { if (menu.visible) { currentItemRef.current?.focus() } }, [menu.visible]) const onMenuButtonClick = useCallback( (event: React.MouseEvent) => { // hold down meta key to automatically toggle between prod & dev if (event.metaKey || event.ctrlKey) { event.preventDefault() switchToEnvironment( currentEnvName === DEVELOPMENT_ORG_ENV_NAME ? null : DEVELOPMENT_ORG_ENV_SLUG ) } }, [currentEnvName, switchToEnvironment] ) // wait until menu is hidden before showing dev host status + key const [debounced_isDevMode] = useDebounce(isDevMode, 250) if (envOptions.length <= 1) return null if (!canAccessEnvironments) return null if (isNonEnvPage) return null return (
{isDevMode && } {currentEnvName}
e.stopPropagation()} className="bg-white shadow-dropdown rounded-lg border border-[#D9DEEF] focus:outline-none text-[13px] overflow-hidden w-[270px] pb-1" >

Environments

{canUpdateEnvironments && ( )}
{envOptions.map(env => ( { e.preventDefault() menu.hide() switchToEnvironment(env.path) }} ref={env.isCurrent ? currentItemRef : undefined} className={classNames( 'px-2.5 py-1.5 text-sm w-full flex items-center justify-between focus:outline-none rounded-md', { 'bg-primary-50 bg-opacity-50 text-primary-500 font-medium': env.isCurrent, 'text-gray-700 focus:bg-gray-50 hover:bg-gray-50': !env.isCurrent, } )} > {env.name} {env.name === DEVELOPMENT_ORG_ENV_NAME && ( )} ))}
{debounced_isDevMode && (

Personal development key:

The Development environment is unique to your user account.{' '} Learn more ›

)}
) }