// @flow import moment from 'moment'; import React, { Component } from 'react'; import { Layout, Container, Header } from './Layout'; import { BackendError } from '../lib/backend'; import ExternalLinkSVG from '../assets/images/icon-extLink.svg'; import ChevronRightSVG from '../assets/images/icon-chevron.svg'; import type { HeaderBarStyle } from './HeaderBar'; import type { ConnectionReduxState } from '../redux/connection/reducers'; import type { SettingsReduxState } from '../redux/settings/reducers'; import type { RelayLocation } from '../lib/ipc-facade'; export type ConnectProps = { accountExpiry: string, connection: ConnectionReduxState, settings: SettingsReduxState, onSettings: () => void, onSelectLocation: () => void, onConnect: () => void, onCopyIP: () => void, onDisconnect: () => void, onExternalLink: (type: string) => void, }; export default class Connect extends Component { props: ConnectProps; state = { isFirstPass: true, showCopyIPMessage: false }; _copyTimer: ?number; componentDidMount() { this.setState({ isFirstPass: false }); } componentWillUnmount() { if(this._copyTimer) { clearTimeout(this._copyTimer); this._copyTimer = null; } this.setState({ isFirstPass: true, showCopyIPMessage: false }); } render(): React.Element<*> { const error = this.displayError(); const child = error ? this.renderError(error) : this.renderMap(); return (
{ child } ); } renderError(error: BackendError): React.Element<*> { return (
{ error.title }
{ error.message }
{ error.type === 'NO_CREDIT' ?
: null }
); } _findRelayName(relay: RelayLocation): ?string { const countries = this.props.settings.relayLocations; const countryPredicate = (countryCode) => (country) => country.code === countryCode; if(relay.country) { const country = countries.find(countryPredicate(relay.country)); if(country) { return country.name; } } else if(relay.city) { const [countryCode, cityCode] = relay.city; const country = countries.find(countryPredicate(countryCode)); if(country) { const city = country.cities.find((city) => city.code === cityCode); if(city) { return city.name; } } } return null; } _getLocationName(): string { const { relaySettings } = this.props.settings; if(relaySettings.normal) { const location = relaySettings.normal.location; if(location === 'any') { return 'Automatic'; } else { return this._findRelayName(location) || 'Unknown'; } } else if(relaySettings.custom_tunnel_endpoint) { return 'Custom'; } else { throw new Error('Unsupported relay settings.'); } } renderMap(): React.Element<*> { let [ isConnecting, isConnected, isDisconnected ] = [false, false, false]; switch(this.props.connection.status) { case 'connecting': isConnecting = true; break; case 'connected': isConnected = true; break; case 'disconnected': isDisconnected = true; break; } // We decided to not include the map in the first beta release to customers // but it MUST be included in the following releases. Therefore we choose // to just comment it out const map = undefined; /* const altitude = (isConnecting ? 300 : 100) * 1000; const { location } = this.props.connection; const map = */ return (
{ map }
{ this._renderIsBlockingInternetMessage() }
{ /* show spinner when connecting */ }
{ this.networkSecurityMessage() }
{ /* ********************************** Begin: Location block ********************************** */ } { /* location when connecting or disconnected */ } { isConnecting || isDisconnected ?
{ this.props.connection.country }
: null } { /* location when connected */ } { isConnected ?
{ this.props.connection.city } { this.props.connection.city &&
} { this.props.connection.country }
:null } { /* ********************************** End: Location block ********************************** */ }
{ (isConnected || isDisconnected) ? ( { this.state.showCopyIPMessage ? 'IP copied to clipboard!' : this.props.connection.ip }) : null }
{ /* ********************************** Begin: Footer block ********************************** */ } { /* footer when disconnected */ } { isDisconnected ?
: null } { /* footer when connecting */ } { isConnecting ?
: null } { /* footer when connected */ } { isConnected ?
: null } { /* ********************************** End: Footer block ********************************** */ }
); } _renderIsBlockingInternetMessage() { let animationClass = 'hide'; if (this.props.connection.status === 'connecting') { animationClass = 'show'; } return
 
blocking internet
; } // Handlers onExternalLink(type: string) { this.props.onExternalLink(type); } onIPAddressClick() { this._copyTimer && clearTimeout(this._copyTimer); this._copyTimer = setTimeout(() => this.setState({ showCopyIPMessage: false }), 3000); this.setState({ showCopyIPMessage: true }); this.props.onCopyIP(); } // Private headerStyle(): HeaderBarStyle { switch(this.props.connection.status) { case 'disconnected': return 'error'; case 'connecting': case 'connected': return 'success'; } throw new Error('Invalid ConnectionState'); } networkSecurityClass(): string { let classes = ['connect__status-security']; if(this.props.connection.status === 'connected') { classes.push('connect__status-security--secure'); } else if(this.props.connection.status === 'disconnected') { classes.push('connect__status-security--unsecured'); } return classes.join(' '); } networkSecurityMessage(): string { switch(this.props.connection.status) { case 'connected': return 'Secure connection'; case 'connecting': return 'Creating secure connection'; default: return 'Unsecured connection'; } } spinnerClass(): string { var classes = ['connect__status-icon']; if(this.props.connection.status !== 'connecting') { classes.push('connect__status-icon--hidden'); } return classes.join(' '); } ipAddressClass(): string { var classes = ['connect__status-ipaddress']; if(this.props.connection.status === 'connecting') { classes.push('connect__status-ipaddress--invisible'); } return classes.join(' '); } displayError(): ?BackendError { // Offline? if(!this.props.connection.isOnline) { return new BackendError('NO_INTERNET'); } // No credit? const expiry = this.props.accountExpiry; if(expiry && moment(expiry).isSameOrBefore(moment())) { return new BackendError('NO_CREDIT'); } return null; } }