diff options
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/renderer/components/WireguardKeys.tsx | 168 | ||||
| -rw-r--r-- | gui/src/renderer/components/WireguardKeysStyles.tsx | 48 | ||||
| -rw-r--r-- | gui/src/renderer/containers/WireguardKeysPage.tsx | 27 |
3 files changed, 243 insertions, 0 deletions
diff --git a/gui/src/renderer/components/WireguardKeys.tsx b/gui/src/renderer/components/WireguardKeys.tsx new file mode 100644 index 0000000000..dadaca4b51 --- /dev/null +++ b/gui/src/renderer/components/WireguardKeys.tsx @@ -0,0 +1,168 @@ +import * as React from 'react'; +import { Component, Text, View } from 'reactxp'; +import { messages } from '../../shared/gettext'; +import { WgKeyState } from '../redux/settings/reducers'; +import * as AppButton from './AppButton'; +import ImageView from './ImageView'; +import { Container, Layout } from './Layout'; +import { BackBarItem, NavigationBar, NavigationContainer } from './NavigationBar'; +import SettingsHeader, { HeaderTitle } from './SettingsHeader'; +import styles from './WireguardKeysStyles'; + +export interface IProps { + keyState: WgKeyState; + isOffline: boolean; + + onGenerateKey: () => void; + onVerifyKey: (publicKey: string) => void; + onVisitWebsiteKey: () => void; + onClose: () => void; +} + +export default class WireguardKeys extends Component<IProps> { + public render() { + return ( + <Layout> + <Container> + <View> + <NavigationContainer> + <NavigationBar> + <BackBarItem action={this.props.onClose}> + {// TRANSLATORS: Back button in navigation bar + messages.pgettext('wireguard-keys-nav', 'Advanced')} + </BackBarItem> + </NavigationBar> + </NavigationContainer> + + <View style={styles.wgkeys__container}> + <SettingsHeader> + <HeaderTitle> + {messages.pgettext('wireguard-keys-nav', 'WireGuard keys')} + </HeaderTitle> + </SettingsHeader> + + <View style={styles.wgkeys__row}> + <Text style={styles.wgkeys__row_label}> + {messages.pgettext('wireguard-keys', 'Public key')} + </Text> + <View style={styles.wgkeys__row_value}>{this.getKeyRow()}</View> + <View style={styles.wgkeys__validity_row}>{this.keyValidityLabel()}</View> + </View> + + <View style={styles.wgkeys__row}>{this.getActionButton()}</View> + <View style={styles.wgkeys__row}> + <AppButton.GreenButton + disabled={this.props.isOffline} + onPress={this.props.onVisitWebsiteKey}> + <AppButton.Label> + {messages.pgettext('wireguard-key-view', 'Manage keys in website')} + </AppButton.Label> + <AppButton.Icon source="icon-extLink" height={16} width={16} /> + </AppButton.GreenButton> + </View> + </View> + </View> + </Container> + </Layout> + ); + } + + /// Action button can either generate or verify a key + private getActionButton() { + switch (this.props.keyState.type) { + case 'key-set': + const publicKey = this.props.keyState.publicKey; + // if the key is known to be invalid, allow the user to generate a new one + if (this.props.keyState.valid === false) { + break; + } + + const verificationCallback = () => this.props.onVerifyKey(publicKey); + + return ( + <AppButton.GreenButton disabled={this.props.isOffline} onPress={verificationCallback}> + <AppButton.Label> + {messages.pgettext('wireguard-key-view', 'Verify key')} + </AppButton.Label> + </AppButton.GreenButton> + ); + + case 'being-verified': + return this.busyButton(messages.pgettext('wireguard-key-view', 'Verifying key')); + case 'being-generated': + return this.busyButton(messages.pgettext('wireguard-key-view', 'Generating key')); + } + return ( + <AppButton.GreenButton disabled={this.props.isOffline} onPress={this.props.onGenerateKey}> + <AppButton.Label>{messages.pgettext('wireguard-key-view', 'Generate key')}</AppButton.Label> + </AppButton.GreenButton> + ); + } + + private busyButton(message: string) { + return ( + <AppButton.GreenButton disabled={true}> + <AppButton.Label>{message}</AppButton.Label> + <AppButton.Icon source="icon-spinner" height={16} width={16} /> + </AppButton.GreenButton> + ); + } + + private getKeyRow() { + switch (this.props.keyState.type) { + case 'being-verified': + case 'key-set': + // mimicking the truncating of the key from website + return ( + <View title={this.props.keyState.publicKey}> + <Text style={styles.wgkeys__row_value}> + {this.props.keyState.publicKey.substring(0, 20) + '...'} + </Text> + </View> + ); + case 'being-generated': + return <ImageView source="icon-spinner" height={25} width={25} />; + case 'too-many-keys': + return ( + <Text style={styles.wgkeys__invalid_key}> + {messages.pgettext('wireguard-key-view', 'Account has too many keys already')} + </Text> + ); + case 'generation-failure': + return ( + <Text style={styles.wgkeys__invalid_key}> + {messages.pgettext('wireguard-key-view', 'Failed to generate key')} + </Text> + ); + default: + return ( + <Text style={styles.wgkeys__row_value}> + {messages.pgettext('wireguard-key-view', 'No key set')} + </Text> + ); + } + } + + private keyValidityLabel() { + switch (this.props.keyState.type) { + case 'being-verified': + return <ImageView source="icon-spinner" height={25} width={25} />; + case 'key-set': + if (this.props.keyState.valid === true) { + return ( + <Text style={styles.wgkeys__valid_key}> + {messages.pgettext('account-view', 'Key is valid')} + </Text> + ); + } else if (this.props.keyState.valid === false) { + return ( + <Text style={styles.wgkeys__invalid_key}> + {messages.pgettext('wireguard-key-view', 'Key is invalid')} + </Text> + ); + } + default: + return ''; + } + } +} diff --git a/gui/src/renderer/components/WireguardKeysStyles.tsx b/gui/src/renderer/components/WireguardKeysStyles.tsx new file mode 100644 index 0000000000..8f94bdcca6 --- /dev/null +++ b/gui/src/renderer/components/WireguardKeysStyles.tsx @@ -0,0 +1,48 @@ +import { Styles } from 'reactxp'; +import { colors } from '../../config.json'; + +export default { + wgkeys__container: Styles.createViewStyle({ + flexDirection: 'column', + flex: 1, + }), + wgkeys__row: Styles.createViewStyle({ + paddingTop: 0, + paddingBottom: 0, + paddingLeft: 24, + paddingRight: 24, + marginBottom: 24, + }), + wgkeys__row_label: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 13, + fontWeight: '600', + lineHeight: 20, + letterSpacing: -0.2, + color: colors.white60, + marginBottom: 9, + }), + wgkeys__validity_row: Styles.createViewStyle({ + paddingTop: 5, + }), + wgkeys__row_value: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 16, + lineHeight: 19, + fontWeight: '800', + color: colors.white, + }), + wgkeys__invalid_key: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 16, + fontWeight: '800', + color: colors.red, + }), + wgkeys__valid_key: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 16, + fontWeight: '600', + lineHeight: 20, + color: colors.green, + }), +}; diff --git a/gui/src/renderer/containers/WireguardKeysPage.tsx b/gui/src/renderer/containers/WireguardKeysPage.tsx new file mode 100644 index 0000000000..2fb185cb1d --- /dev/null +++ b/gui/src/renderer/containers/WireguardKeysPage.tsx @@ -0,0 +1,27 @@ +import { goBack, push } from 'connected-react-router'; +import { shell } from 'electron'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { links } from '../../config.json'; +import WireguardKeys from '../components/WireguardKeys'; +import { IReduxState, ReduxDispatch } from '../redux/store'; +import { ISharedRouteProps } from '../routes'; + +const mapStateToProps = (state: IReduxState, _props: ISharedRouteProps) => ({ + keyState: state.settings.wireguardKeyState, + isOffline: state.connection.isBlocked, +}); +const mapDispatchToProps = (dispatch: ReduxDispatch, props: ISharedRouteProps) => { + const history = bindActionCreators({ push, goBack }, dispatch); + return { + onGenerateKey: () => props.app.generateWireguardKey(), + onVerifyKey: (publicKey: string) => props.app.verifyWireguardKey(publicKey), + onVisitWebsiteKey: () => shell.openExternal(links.manageKeys), + onClose: () => history.goBack(), + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(WireguardKeys); |
