summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--gui/assets/images/icon-reload.svg2
-rw-r--r--gui/src/renderer/components/AppButton.tsx2
-rw-r--r--gui/src/renderer/components/Modal.tsx153
-rw-r--r--gui/src/renderer/components/Support.tsx110
-rw-r--r--gui/src/renderer/components/SupportStyles.tsx22
-rw-r--r--gui/src/renderer/components/WireguardKeys.tsx12
-rw-r--r--gui/src/renderer/containers/SupportPage.tsx2
8 files changed, 187 insertions, 117 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cff66c23d4..f6f429bf02 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ Line wrap the file at 100 chars. Th
- Add reconnect button to the desktop app.
- Add monochrome option for the tray icon on Windows and Linux.
- Show OS notification when account is close to expiry on desktop platforms.
+- Warn users running old app versions when creating problem report.
#### Android
- Add option to enable or disable local network sharing.
diff --git a/gui/assets/images/icon-reload.svg b/gui/assets/images/icon-reload.svg
index 33c7e5ec2c..873727a0a5 100644
--- a/gui/assets/images/icon-reload.svg
+++ b/gui/assets/images/icon-reload.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-rotate-cw"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg> \ No newline at end of file
+<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="redo-alt" class="svg-inline--fa fa-redo-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#000000" d="M256.455 8c66.269.119 126.437 26.233 170.859 68.685l35.715-35.715C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.75c-30.864-28.899-70.801-44.907-113.23-45.273-92.398-.798-170.283 73.977-169.484 169.442C88.764 348.009 162.184 424 256 424c41.127 0 79.997-14.678 110.629-41.556 4.743-4.161 11.906-3.908 16.368.553l39.662 39.662c4.872 4.872 4.631 12.815-.482 17.433C378.202 479.813 319.926 504 256 504 119.034 504 8.001 392.967 8 256.002 7.999 119.193 119.646 7.755 256.455 8z"></path></svg>
diff --git a/gui/src/renderer/components/AppButton.tsx b/gui/src/renderer/components/AppButton.tsx
index 212e5cb1a3..9f6fe860b3 100644
--- a/gui/src/renderer/components/AppButton.tsx
+++ b/gui/src/renderer/components/AppButton.tsx
@@ -84,7 +84,7 @@ interface IState {
textAdjustment: number;
}
-class BaseButton extends Component<IProps, IState> {
+export class BaseButton extends Component<IProps, IState> {
public state: IState = {
hovered: false,
textAdjustment: 0,
diff --git a/gui/src/renderer/components/Modal.tsx b/gui/src/renderer/components/Modal.tsx
index 5742e68f2b..b0a972955d 100644
--- a/gui/src/renderer/components/Modal.tsx
+++ b/gui/src/renderer/components/Modal.tsx
@@ -1,52 +1,123 @@
import * as React from 'react';
+import { Component, Styles, Text, View } from 'reactxp';
+import { colors } from '../../config.json';
+import ImageView from './ImageView';
-export class ModalContent extends React.Component {
- public render() {
- return (
- <div
- style={{
- position: 'absolute',
- display: 'flex',
- flexDirection: 'column',
- flex: 1,
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- }}>
- {this.props.children}
- </div>
- );
- }
+const styles = {
+ modalAlertBackground: Styles.createViewStyle({
+ flex: 1,
+ justifyContent: 'center',
+ paddingLeft: 14,
+ paddingRight: 14,
+ }),
+ modalAlert: Styles.createViewStyle({
+ backgroundColor: colors.darkBlue,
+ borderRadius: 11,
+ padding: 16,
+ }),
+ modalAlertIcon: Styles.createViewStyle({
+ alignItems: 'center',
+ marginBottom: 12,
+ marginTop: 4,
+ }),
+ modalAlertMessage: Styles.createTextStyle({
+ fontFamily: 'Open Sans',
+ fontSize: 16,
+ fontWeight: '500',
+ lineHeight: 20,
+ color: colors.white80,
+ }),
+ modalAlertButtonContainer: Styles.createViewStyle({
+ marginTop: 16,
+ }),
+};
+
+export const ModalContent: React.FC = ({ children }) => {
+ return (
+ <div
+ style={{
+ position: 'absolute',
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ }}>
+ {children}
+ </div>
+ );
+};
+
+const ModalBackground: React.FC = ({ children }) => {
+ return (
+ <div
+ style={{
+ backgroundColor: 'rgba(0,0,0,0.5)',
+ position: 'absolute',
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ }}>
+ {children}
+ </div>
+ );
+};
+
+export const ModalContainer: React.FC = ({ children }) => {
+ return <div style={{ position: 'relative', flex: 1 }}>{children}</div>;
+};
+
+export enum ModalAlertType {
+ Info = 1,
+ Warning,
+}
+
+interface IModalAlertProps {
+ type?: ModalAlertType;
+ message: string;
+ buttons: React.ReactNode[];
}
-export class ModalAlert extends React.Component {
+export class ModalAlert extends Component<IModalAlertProps> {
public render() {
return (
- <div
- style={{
- backgroundColor: 'rgba(0,0,0,0.5)',
- position: 'absolute',
- display: 'flex',
- flexDirection: 'column',
- flex: 1,
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- }}>
- {this.props.children}
- </div>
+ <ModalBackground>
+ <View style={styles.modalAlertBackground}>
+ <View style={styles.modalAlert}>
+ {this.props.type && (
+ <View style={styles.modalAlertIcon}>{this.renderTypeIcon(this.props.type)}</View>
+ )}
+ <Text style={styles.modalAlertMessage}>{this.props.message}</Text>
+ {this.props.buttons.map((button, index) => (
+ <View key={index} style={styles.modalAlertButtonContainer}>
+ {button}
+ </View>
+ ))}
+ </View>
+ </View>
+ </ModalBackground>
);
}
-}
-interface IModalContainerProps {
- children?: React.ReactNode;
-}
-
-export class ModalContainer extends React.Component<IModalContainerProps> {
- public render() {
- return <div style={{ position: 'relative', flex: 1 }}>{this.props.children}</div>;
+ private renderTypeIcon(type: ModalAlertType) {
+ let source = '';
+ let color = '';
+ switch (type) {
+ case ModalAlertType.Info:
+ source = 'icon-alert';
+ color = colors.white;
+ break;
+ case ModalAlertType.Warning:
+ source = 'icon-alert';
+ color = colors.red;
+ break;
+ }
+ return <ImageView height={44} width={44} source={source} tintColor={color} />;
}
}
diff --git a/gui/src/renderer/components/Support.tsx b/gui/src/renderer/components/Support.tsx
index 5313e7449c..c7cae9889d 100644
--- a/gui/src/renderer/components/Support.tsx
+++ b/gui/src/renderer/components/Support.tsx
@@ -1,10 +1,11 @@
import * as React from 'react';
import { Component, Text, TextInput, View } from 'reactxp';
+import { links } from '../../config.json';
import { messages } from '../../shared/gettext';
import * as AppButton from './AppButton';
import ImageView from './ImageView';
import { Container, Layout } from './Layout';
-import { ModalAlert, ModalContainer, ModalContent } from './Modal';
+import { ModalAlert, ModalAlertType, ModalContainer, ModalContent } from './Modal';
import { BackBarItem, NavigationBar, NavigationItems } from './NavigationBar';
import SettingsHeader, { HeaderSubTitle, HeaderTitle } from './SettingsHeader';
import styles from './SupportStyles';
@@ -26,6 +27,7 @@ interface ISupportState {
savedReport?: string;
sendState: SendState;
disableActions: boolean;
+ showOutdatedVersionWarning: boolean;
}
interface ISupportProps {
@@ -39,6 +41,8 @@ interface ISupportProps {
clearReportForm: () => void;
collectProblemReport: (accountsToRedact: string[]) => Promise<string>;
sendProblemReport: (email: string, message: string, savedReport: string) => Promise<void>;
+ outdatedVersion: boolean;
+ onExternalLink: (url: string) => void;
}
export default class Support extends Component<ISupportProps, ISupportState> {
@@ -48,6 +52,7 @@ export default class Support extends Component<ISupportProps, ISupportState> {
savedReport: undefined,
sendState: SendState.Initial,
disableActions: false,
+ showOutdatedVersionWarning: false,
};
private collectLogPromise?: Promise<string>;
@@ -58,6 +63,7 @@ export default class Support extends Component<ISupportProps, ISupportState> {
// seed initial data from props
this.state.email = props.defaultEmail;
this.state.message = props.defaultMessage;
+ this.state.showOutdatedVersionWarning = props.outdatedVersion;
}
public validate() {
@@ -110,12 +116,12 @@ export default class Support extends Component<ISupportProps, ISupportState> {
}
};
- public onCancelConfirmation = () => {
+ public onCancelNoEmailDialog = () => {
this.setState({ sendState: SendState.Initial });
};
public render() {
- const { sendState } = this.state;
+ const { sendState, showOutdatedVersionWarning } = this.state;
const header = (
<SettingsHeader>
<HeaderTitle>{messages.pgettext('support-view', 'Report a problem')}</HeaderTitle>
@@ -152,11 +158,8 @@ export default class Support extends Component<ISupportProps, ISupportState> {
</View>
</View>
</ModalContent>
- {sendState === SendState.Confirm ? (
- <ModalAlert>{this.renderConfirm()}</ModalAlert>
- ) : (
- undefined
- )}
+ {sendState === SendState.Confirm && this.renderNoEmailDialog()}
+ {showOutdatedVersionWarning && this.renderOutdateVersionWarningDialog()}
</ModalContainer>
</Container>
</Layout>
@@ -228,8 +231,59 @@ export default class Support extends Component<ISupportProps, ISupportState> {
}
}
- private renderConfirm() {
- return <ConfirmNoEmailDialog onConfirm={this.onSend} onDismiss={this.onCancelConfirmation} />;
+ private renderNoEmailDialog() {
+ const message = messages.pgettext(
+ 'support-view',
+ 'You are about to send the problem report without a way for us to get back to you. If you want an answer to your report you will have to enter an email address.',
+ );
+ return (
+ <ModalAlert
+ type={ModalAlertType.Warning}
+ message={message}
+ buttons={[
+ <AppButton.RedButton key="proceed" onPress={this.onSend}>
+ {messages.pgettext('support-view', 'Send anyway')}
+ </AppButton.RedButton>,
+ <AppButton.BlueButton key="cancel" onPress={this.onCancelNoEmailDialog}>
+ {messages.pgettext('support-view', 'Back')}
+ </AppButton.BlueButton>,
+ ]}
+ />
+ );
+ }
+
+ private acknowledgeOutdateVersion = () => {
+ this.setState({ showOutdatedVersionWarning: false });
+ };
+
+ private openDownloadLink = () => this.props.onExternalLink(links.download);
+
+ private renderOutdateVersionWarningDialog() {
+ const message = messages.pgettext(
+ 'support-view',
+ 'You are using an old version of the app. Please upgrade and see if the problem still exists before sending a report.',
+ );
+ return (
+ <ModalAlert
+ type={ModalAlertType.Warning}
+ message={message}
+ buttons={[
+ <AppButton.GreenButton
+ key="upgrade"
+ disabled={this.props.isOffline}
+ onPress={this.openDownloadLink}>
+ <AppButton.Label>{messages.pgettext('support-view', 'Upgrade app')}</AppButton.Label>
+ <AppButton.Icon height={16} width={16} source="icon-extLink" />
+ </AppButton.GreenButton>,
+ <AppButton.RedButton key="proceed" onPress={this.acknowledgeOutdateVersion}>
+ {messages.pgettext('support-view', 'Continue anyway')}
+ </AppButton.RedButton>,
+ <AppButton.BlueButton key="cancel" onPress={this.props.onClose}>
+ {messages.pgettext('support-view', 'Cancel')}
+ </AppButton.BlueButton>,
+ ]}
+ />
+ );
}
private renderForm() {
@@ -388,39 +442,3 @@ export default class Support extends Component<ISupportProps, ISupportState> {
});
}
}
-
-interface IConfirmNoEmailDialogProps {
- onConfirm: () => void;
- onDismiss: () => void;
-}
-
-class ConfirmNoEmailDialog extends Component<IConfirmNoEmailDialogProps> {
- public render() {
- return (
- <View style={styles.confirm_no_email_background}>
- <View style={styles.confirm_no_email_dialog}>
- <Text style={styles.confirm_no_email_warning}>
- {messages.pgettext(
- 'support-view',
- 'You are about to send the problem report without a way for us to get back to you. If you want an answer to your report you will have to enter an email address.',
- )}
- </Text>
- <AppButton.GreenButton onPress={this.confirm}>
- {messages.pgettext('support-view', 'Send anyway')}
- </AppButton.GreenButton>
- <AppButton.RedButton onPress={this.dismiss} style={styles.confirm_no_email_back_button}>
- {messages.pgettext('support-view', 'Back')}
- </AppButton.RedButton>
- </View>
- </View>
- );
- }
-
- private confirm = () => {
- this.props.onConfirm();
- };
-
- private dismiss = () => {
- this.props.onDismiss();
- };
-}
diff --git a/gui/src/renderer/components/SupportStyles.tsx b/gui/src/renderer/components/SupportStyles.tsx
index 05fd89bfd0..d883c806ba 100644
--- a/gui/src/renderer/components/SupportStyles.tsx
+++ b/gui/src/renderer/components/SupportStyles.tsx
@@ -114,26 +114,4 @@ export default {
color: colors.white,
marginBottom: 4,
}),
- confirm_no_email_background: Styles.createViewStyle({
- flex: 1,
- justifyContent: 'center',
- paddingLeft: 14,
- paddingRight: 14,
- }),
- confirm_no_email_dialog: Styles.createViewStyle({
- backgroundColor: colors.darkBlue,
- borderRadius: 11,
- padding: 16,
- }),
- confirm_no_email_warning: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 16,
- fontWeight: '500',
- lineHeight: 20,
- color: colors.white80,
- marginBottom: 12,
- }),
- confirm_no_email_back_button: Styles.createViewStyle({
- marginTop: 16,
- }),
};
diff --git a/gui/src/renderer/components/WireguardKeys.tsx b/gui/src/renderer/components/WireguardKeys.tsx
index 4451b8e341..dacdfcdc72 100644
--- a/gui/src/renderer/components/WireguardKeys.tsx
+++ b/gui/src/renderer/components/WireguardKeys.tsx
@@ -78,24 +78,24 @@ export default class WireguardKeys extends Component<IProps> {
<View style={styles.wgkeys__row}>{this.getGenerateButton()}</View>
<View style={styles.wgkeys__row}>
- <AppButton.GreenButton
+ <AppButton.BlueButton
disabled={this.isVerifyButtonDisabled()}
onPress={this.getOnVerifyKeyCb()}>
<AppButton.Label>
{messages.pgettext('wireguard-key-view', 'Verify key')}
</AppButton.Label>
- </AppButton.GreenButton>
+ </AppButton.BlueButton>
</View>
<View style={styles.wgkeys__row}>
<AppButton.BlockingButton
disabled={this.props.isOffline}
onPress={this.props.onVisitWebsiteKey}>
- <AppButton.GreenButton>
+ <AppButton.BlueButton>
<AppButton.Label>
{messages.pgettext('wireguard-key-view', 'Manage keys')}
</AppButton.Label>
<AppButton.Icon source="icon-extLink" height={16} width={16} />
- </AppButton.GreenButton>
+ </AppButton.BlueButton>
</AppButton.BlockingButton>
</View>
</View>
@@ -155,10 +155,10 @@ export default class WireguardKeys extends Component<IProps> {
private busyButton(message: string) {
return (
- <AppButton.GreenButton disabled={true}>
+ <AppButton.BlueButton disabled={true}>
<AppButton.Label>{message}</AppButton.Label>
<AppButton.Icon source="icon-spinner" height={16} width={16} />
- </AppButton.GreenButton>
+ </AppButton.BlueButton>
);
}
diff --git a/gui/src/renderer/containers/SupportPage.tsx b/gui/src/renderer/containers/SupportPage.tsx
index 755e67f4de..d666afbd55 100644
--- a/gui/src/renderer/containers/SupportPage.tsx
+++ b/gui/src/renderer/containers/SupportPage.tsx
@@ -12,6 +12,7 @@ const mapStateToProps = (state: IReduxState) => ({
defaultMessage: state.support.message,
accountHistory: state.account.accountHistory,
isOffline: state.connection.isBlocked,
+ outdatedVersion: state.version.currentIsOutdated,
});
const mapDispatchToProps = (dispatch: ReduxDispatch) => {
@@ -29,6 +30,7 @@ const mapDispatchToProps = (dispatch: ReduxDispatch) => {
clearReportForm,
collectProblemReport,
sendProblemReport,
+ onExternalLink: (url: string) => shell.openExternal(url),
};
};