diff --git a/COPYING.md b/COPYING.md index 116a7b5b1..16efae2cc 100644 --- a/COPYING.md +++ b/COPYING.md @@ -52,6 +52,8 @@ ProtonVPN Windows app includes the following 3rd party software: | [The MIT License](https://github.com/davideicardi/DynamicExpresso/blob/master/LICENSE). * [FontAwesome.WPF](https://github.com/charri/Font-Awesome-WPF/) by charri | [The MIT License](https://github.com/charri/Font-Awesome-WPF/blob/master/LICENSE). +* [Gu.Wpf.Adorners](https://github.com/GuOrg/Gu.Wpf.Adorners) by Johan Larsson + | [The MIT License](https://github.com/GuOrg/Gu.Wpf.Adorners/blob/master/LICENSE). * [MvvmLightLibsStd10](http://www.mvvmlight.net/) by Laurent Bugnion (GalaSoft) | [The MIT License](https://github.com/lbugnion/mvvmlight/blob/master/LICENSE). * [Newtonsoft.Json](https://www.newtonsoft.com/json) by James Newton-King @@ -83,4 +85,6 @@ ProtonVPN Windows app includes the following 3rd party software: * [System.Buffers](https://dot.net) by Microsoft | [The MIT License](https://github.com/dotnet/corefx/blob/master/LICENSE.TXT). * [System.Collections.Immutable](https://dot.net) by Microsoft - | [The MIT License](https://github.com/dotnet/corefx/blob/master/LICENSE.TXT). \ No newline at end of file + | [The MIT License](https://github.com/dotnet/corefx/blob/master/LICENSE.TXT). +* [WpfScreenHelper](https://github.com/micdenny/WpfScreenHelper) by Michael Denny + | [The MIT License](https://github.com/micdenny/WpfScreenHelper/blob/master/LICENSE). \ No newline at end of file diff --git a/Setup/ProtonVPN.aip b/Setup/ProtonVPN.aip index 0b34868d2..365d8b964 100644 --- a/Setup/ProtonVPN.aip +++ b/Setup/ProtonVPN.aip @@ -103,6 +103,7 @@ + @@ -165,6 +166,7 @@ + @@ -285,6 +287,8 @@ + + @@ -658,6 +662,8 @@ + + diff --git a/src/GlobalAssemblyInfo.cs b/src/GlobalAssemblyInfo.cs index 011760650..51445b364 100644 --- a/src/GlobalAssemblyInfo.cs +++ b/src/GlobalAssemblyInfo.cs @@ -9,11 +9,11 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ProtonVPN")] -[assembly: AssemblyCopyright("Copyright © 2021 Proton Technologies AG")] +[assembly: AssemblyCopyright("Copyright © 2021 Proton Technologies AG")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("1.24.2.0")] -[assembly: AssemblyFileVersion("1.24.2.0")] +[assembly: AssemblyVersion("1.25.0.0")] +[assembly: AssemblyFileVersion("1.25.0.0")] [assembly: ComVisible(false)] [assembly: AssemblyInformationalVersion("$AssemblyVersion")] \ No newline at end of file diff --git a/src/ProtonVPN.App/About/UpdateViewModel.cs b/src/ProtonVPN.App/About/UpdateViewModel.cs index dbb9a7c83..52f522bde 100644 --- a/src/ProtonVPN.App/About/UpdateViewModel.cs +++ b/src/ProtonVPN.App/About/UpdateViewModel.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -23,14 +23,13 @@ using System.Windows; using System.Windows.Input; using GalaSoft.MvvmLight.Command; -using ProtonVPN.BugReporting.Diagnostic; using ProtonVPN.Common.KillSwitch; using ProtonVPN.Common.OS.Processes; using ProtonVPN.Common.Vpn; using ProtonVPN.Core.Modals; using ProtonVPN.Core.MVVM; +using ProtonVPN.Core.OS; using ProtonVPN.Core.Service.Settings; -using ProtonVPN.Core.Service.Vpn; using ProtonVPN.Core.Settings; using ProtonVPN.Core.Update; using ProtonVPN.Core.Vpn; @@ -45,7 +44,6 @@ public class UpdateViewModel : ViewModel, IUpdateStateAware, IVpnStateAware private readonly IOsProcesses _osProcesses; private readonly IModals _modals; private readonly IAppSettings _appSettings; - private readonly IVpnServiceManager _vpnServiceManager; private readonly ISystemState _systemState; private readonly ISettingsServiceClientManager _settingsServiceClientManager; @@ -57,7 +55,6 @@ public UpdateViewModel( IOsProcesses osProcesses, IModals modals, IAppSettings appSettings, - IVpnServiceManager vpnServiceManager, ISystemState systemState, ISettingsServiceClientManager settingsServiceClientManager) { @@ -65,7 +62,6 @@ public UpdateViewModel( _osProcesses = osProcesses; _modals = modals; _appSettings = appSettings; - _vpnServiceManager = vpnServiceManager; _systemState = systemState; _settingsServiceClientManager = settingsServiceClientManager; diff --git a/src/ProtonVPN.App/App.config b/src/ProtonVPN.App/App.config index ec7cfd678..a18a4cc36 100644 --- a/src/ProtonVPN.App/App.config +++ b/src/ProtonVPN.App/App.config @@ -361,6 +361,9 @@ True + + + diff --git a/src/ProtonVPN.App/App.xaml.cs b/src/ProtonVPN.App/App.xaml.cs index aa5c30673..05cd14e8c 100644 --- a/src/ProtonVPN.App/App.xaml.cs +++ b/src/ProtonVPN.App/App.xaml.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -25,7 +25,6 @@ using System.Threading.Tasks; using System.Windows; using Microsoft.Toolkit.Uwp.Notifications; -using ProtonVPN.Common.Cli; using ProtonVPN.Common.Configuration; using ProtonVPN.Common.CrashReporting; using ProtonVPN.Common.Extensions; @@ -83,8 +82,6 @@ private static async Task Run(string[] args) _bootstrapper = new Bootstrapper(args); _bootstrapper.Initialize(); - HandleIntentionalCrash(app, args); - app.Run(); } } @@ -150,15 +147,6 @@ private static Common.Configuration.Config GetConfig() return config; } - private static void HandleIntentionalCrash(Application app, string[] args) - { - var option = new CommandLineOption("crash", args); - if (!option.Exists()) - return; - - app.Deactivated += (sender, ea) => throw new StackOverflowException("Intentional crash test"); - } - private static void SetDllDirectories() { Kernel32.SetDefaultDllDirectories(Kernel32.SetDefaultDllDirectoriesFlags.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); diff --git a/src/ProtonVPN.App/BugReporting/Actions/CategoryAction.cs b/src/ProtonVPN.App/BugReporting/Actions/CategoryAction.cs new file mode 100644 index 000000000..a10b86b23 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Actions/CategoryAction.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.Actions +{ + public abstract class CategoryAction + { + public string Category { get; } + + protected CategoryAction(string category) + { + Category = category; + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Actions/FillTheFormAction.cs b/src/ProtonVPN.App/BugReporting/Actions/FillTheFormAction.cs new file mode 100644 index 000000000..0564e5241 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Actions/FillTheFormAction.cs @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.Actions +{ + public class FillTheFormAction : CategoryAction + { + public FillTheFormAction(string category) : base(category) + { + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Actions/FinishReportAction.cs b/src/ProtonVPN.App/BugReporting/Actions/FinishReportAction.cs new file mode 100644 index 000000000..82afe7fc7 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Actions/FinishReportAction.cs @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + + +namespace ProtonVPN.BugReporting.Actions +{ + public class FinishReportAction + { + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Actions/FormStateChange.cs b/src/ProtonVPN.App/BugReporting/Actions/FormStateChange.cs new file mode 100644 index 000000000..098cc4111 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Actions/FormStateChange.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.Actions +{ + public class FormStateChange + { + public FormStateChange(FormState state) + { + State = state; + } + + public FormState State { get; } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Actions/GoBackAfterFailureAction.cs b/src/ProtonVPN.App/BugReporting/Actions/GoBackAfterFailureAction.cs new file mode 100644 index 000000000..7ef2329ce --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Actions/GoBackAfterFailureAction.cs @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.Actions +{ + public class GoBackAfterFailureAction + { + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Actions/RetryAction.cs b/src/ProtonVPN.App/BugReporting/Actions/RetryAction.cs new file mode 100644 index 000000000..a3b2b47e6 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Actions/RetryAction.cs @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.Actions +{ + public class RetryAction + { + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Actions/SelectCategoryAction.cs b/src/ProtonVPN.App/BugReporting/Actions/SelectCategoryAction.cs new file mode 100644 index 000000000..185997586 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Actions/SelectCategoryAction.cs @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.Actions +{ + public class SelectCategoryAction : CategoryAction + { + public SelectCategoryAction(string category) : base(category) + { + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Actions/SendReportAction.cs b/src/ProtonVPN.App/BugReporting/Actions/SendReportAction.cs new file mode 100644 index 000000000..c7b782648 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Actions/SendReportAction.cs @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +using System.Collections.Generic; +using ProtonVPN.BugReporting.FormElements; + +namespace ProtonVPN.BugReporting.Actions +{ + public class SendReportAction + { + public SendReportAction(string category, IList formElements, bool sendErrorLogs) + { + Category = category; + FormElements = formElements; + SendErrorLogs = sendErrorLogs; + } + + public string Category; + public IList FormElements; + public bool SendErrorLogs; + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/BugReport.cs b/src/ProtonVPN.App/BugReporting/BugReport.cs index c71c40d30..f3f489e8d 100644 --- a/src/ProtonVPN.App/BugReporting/BugReport.cs +++ b/src/ProtonVPN.App/BugReporting/BugReport.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * diff --git a/src/ProtonVPN.App/BugReporting/BugReportingModule.cs b/src/ProtonVPN.App/BugReporting/BugReportingModule.cs index e89c863ca..b80188963 100644 --- a/src/ProtonVPN.App/BugReporting/BugReportingModule.cs +++ b/src/ProtonVPN.App/BugReporting/BugReportingModule.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -20,10 +20,12 @@ using Autofac; using ProtonVPN.BugReporting.Attachments.Source; using ProtonVPN.BugReporting.Diagnostic; +using ProtonVPN.BugReporting.FormElements; using ProtonVPN.Common.Helpers; using ProtonVPN.Common.Logging; using ProtonVPN.Common.OS.Net.NetworkInterface; using ProtonVPN.Common.OS.Processes; +using ProtonVPN.Core.OS; namespace ProtonVPN.BugReporting { @@ -37,8 +39,8 @@ protected override void Load(ContainerBuilder builder) builder.Register(c => { - var appConfig = c.Resolve(); - var logger = c.Resolve(); + Common.Configuration.Config appConfig = c.Resolve(); + ILogger logger = c.Resolve(); return new Attachments.Attachments( new FilesToAttachments( @@ -86,6 +88,8 @@ protected override void Load(ContainerBuilder builder) .SingleInstance(); builder.RegisterType().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); } } -} +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Diagnostic/NetworkAdapterLog.cs b/src/ProtonVPN.App/BugReporting/Diagnostic/NetworkAdapterLog.cs index 2b37330fc..06e7ff5c6 100644 --- a/src/ProtonVPN.App/BugReporting/Diagnostic/NetworkAdapterLog.cs +++ b/src/ProtonVPN.App/BugReporting/Diagnostic/NetworkAdapterLog.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -18,6 +18,7 @@ */ using System.IO; +using ProtonVPN.Common.Extensions; using ProtonVPN.Common.OS.Net.NetworkInterface; namespace ProtonVPN.BugReporting.Diagnostic @@ -40,9 +41,9 @@ private string Content { get { - var str = string.Empty; - var interfaces = _networkInterfaces.GetInterfaces(); - foreach (var networkInterface in interfaces) + string str = string.Empty; + INetworkInterface[] interfaces = _networkInterfaces.GetInterfaces(); + foreach (INetworkInterface networkInterface in interfaces) { str += GetInterfaceDetails(networkInterface); } @@ -53,11 +54,9 @@ private string Content private string GetInterfaceDetails(INetworkInterface networkInterface) { - var active = networkInterface.IsActive ? "true" : "false"; - return $"Name: {networkInterface.Name}\n" + $"Description: {networkInterface.Description}\n" + - $"Active: {active}\n\n"; + $"Active: {networkInterface.IsActive.ToYesNoString()}\n\n"; } } -} +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/EmailValidator.cs b/src/ProtonVPN.App/BugReporting/EmailValidator.cs index 261bc9d47..d53439da2 100644 --- a/src/ProtonVPN.App/BugReporting/EmailValidator.cs +++ b/src/ProtonVPN.App/BugReporting/EmailValidator.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -28,7 +28,9 @@ internal class EmailValidator public static bool IsValid(string email) { if (string.IsNullOrWhiteSpace(email)) + { return false; + } try { @@ -37,8 +39,8 @@ public static bool IsValid(string email) string DomainMapper(Match match) { - var idn = new IdnMapping(); - var domainName = idn.GetAscii(match.Groups[2].Value); + IdnMapping idn = new(); + string domainName = idn.GetAscii(match.Groups[2].Value); return match.Groups[1].Value + domainName; } @@ -64,4 +66,4 @@ string DomainMapper(Match match) } } } -} +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Form.xaml b/src/ProtonVPN.App/BugReporting/Form.xaml deleted file mode 100644 index 2bd22256c..000000000 --- a/src/ProtonVPN.App/BugReporting/Form.xaml +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ProtonVPN.App/BugReporting/FormElements/EmailInput.cs b/src/ProtonVPN.App/BugReporting/FormElements/EmailInput.cs new file mode 100644 index 000000000..8fe6143b9 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/FormElements/EmailInput.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.FormElements +{ + public class EmailInput : SingleLineTextInput + { + public override bool IsValid() + { + return base.IsValid() && EmailValidator.IsValid(Value); + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FormElements/FormElement.cs b/src/ProtonVPN.App/BugReporting/FormElements/FormElement.cs new file mode 100644 index 000000000..148507c9b --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/FormElements/FormElement.cs @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +using ProtonVPN.Common.Extensions; +using ProtonVPN.Core.MVVM; + +namespace ProtonVPN.BugReporting.FormElements +{ + public abstract class FormElement : ViewModel + { + private string _value; + public string Value + { + get => _value; + set => Set(ref _value, value); + } + + public string Label { get; set; } + public string SubmitLabel { get; set; } + public string Placeholder { get; set; } + public bool IsMandatory { get; set; } + + public virtual bool IsValid() + { + return !IsMandatory || !Value.IsNullOrEmpty() && !Value.Trim().IsNullOrEmpty(); + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FormElements/FormElementBuilder.cs b/src/ProtonVPN.App/BugReporting/FormElements/FormElementBuilder.cs new file mode 100644 index 000000000..1d9a39d86 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/FormElements/FormElementBuilder.cs @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +using System.Collections.Generic; +using System.Linq; +using ProtonVPN.Common.Logging; +using ProtonVPN.Core.Api.Contracts.ReportAnIssue; +using ProtonVPN.Core.ReportAnIssue; +using ProtonVPN.Translations; + +namespace ProtonVPN.BugReporting.FormElements +{ + public class FormElementBuilder : IFormElementBuilder + { + private readonly ILogger _logger; + private readonly IReportAnIssueFormDataProvider _reportAnIssueFormDataProvider; + + public FormElementBuilder(ILogger logger, IReportAnIssueFormDataProvider reportAnIssueFormDataProvider) + { + _logger = logger; + _reportAnIssueFormDataProvider = reportAnIssueFormDataProvider; + } + + public List GetFormElements(string categorySubmitName) + { + IList inputs = _reportAnIssueFormDataProvider.GetInputs(categorySubmitName); + List formElements = new() { GetEmailField() }; + formElements.AddRange(inputs.Select(MapField).Where(element => element != null)); + return formElements; + } + + private FormElement GetEmailField() + { + return new EmailInput + { + SubmitLabel = "email", + Placeholder = Translation.Get("BugReport_lbl_EmailPlaceholder"), + Label = Translation.Get("BugReport_lbl_Email"), + IsMandatory = true + }; + } + + private FormElement MapField(IssueInput input) + { + switch (input.Type) + { + case InputType.SingleLineInput: + return CreateSingleLineTextField(input); + case InputType.MultiLineInput: + return CreateMultiLineTextField(input); + default: + _logger.Info($"[FormElementBuilder] Unknown input type {input.Type}. This field won't be added to the form."); + return null; + } + } + + private FormElement CreateSingleLineTextField(IssueInput input) + { + return new SingleLineTextInput + { + Label = input.Label, + Placeholder = input.Placeholder, + SubmitLabel = input.SubmitLabel, + IsMandatory = input.IsMandatory + }; + } + + private FormElement CreateMultiLineTextField(IssueInput input) + { + return new MultiLineTextInput + { + Label = input.Label, + Placeholder = input.Placeholder, + SubmitLabel = input.SubmitLabel, + IsMandatory = input.IsMandatory + }; + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FormElements/FormElementExtensions.cs b/src/ProtonVPN.App/BugReporting/FormElements/FormElementExtensions.cs new file mode 100644 index 000000000..75ea61713 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/FormElements/FormElementExtensions.cs @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +using System.Collections.Generic; +using System.Linq; + +namespace ProtonVPN.BugReporting.FormElements +{ + public static class FormElementExtensions + { + public static FormElement GetEmailField(this IList elements) + { + return elements.FirstOrDefault(IsEmailField); + } + + public static bool IsEmailField(this FormElement element) + { + return element is EmailInput; + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FormElements/IFormElementBuilder.cs b/src/ProtonVPN.App/BugReporting/FormElements/IFormElementBuilder.cs new file mode 100644 index 000000000..f0a83dab7 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/FormElements/IFormElementBuilder.cs @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +using System.Collections.Generic; + +namespace ProtonVPN.BugReporting.FormElements +{ + public interface IFormElementBuilder + { + List GetFormElements(string categorySubmitName); + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FormElements/MultiLineTextInput.cs b/src/ProtonVPN.App/BugReporting/FormElements/MultiLineTextInput.cs new file mode 100644 index 000000000..e0f45f863 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/FormElements/MultiLineTextInput.cs @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.FormElements +{ + public class MultiLineTextInput : FormElement + { + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FormElements/SingleLineTextInput.cs b/src/ProtonVPN.App/BugReporting/FormElements/SingleLineTextInput.cs new file mode 100644 index 000000000..9d964a365 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/FormElements/SingleLineTextInput.cs @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.FormElements +{ + public class SingleLineTextInput : FormElement + { + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/SentViewModel.cs b/src/ProtonVPN.App/BugReporting/FormState.cs similarity index 83% rename from src/ProtonVPN.App/BugReporting/SentViewModel.cs rename to src/ProtonVPN.App/BugReporting/FormState.cs index 9bf5bdabc..953fd51e0 100644 --- a/src/ProtonVPN.App/BugReporting/SentViewModel.cs +++ b/src/ProtonVPN.App/BugReporting/FormState.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -17,11 +17,13 @@ * along with ProtonVPN. If not, see . */ -using ProtonVPN.Core.MVVM; - namespace ProtonVPN.BugReporting { - public class SentViewModel : ViewModel + public enum FormState { + Editing, + Sending, + FailedToSend, + Sent } -} +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FormViewModel.cs b/src/ProtonVPN.App/BugReporting/FormViewModel.cs deleted file mode 100644 index 27182f49e..000000000 --- a/src/ProtonVPN.App/BugReporting/FormViewModel.cs +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2020 Proton Technologies AG - * - * This file is part of ProtonVPN. - * - * ProtonVPN is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ProtonVPN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ProtonVPN. If not, see . - */ - -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using ProtonVPN.Core.Auth; -using ProtonVPN.Core.Models; -using ProtonVPN.Core.Settings; -using ProtonVPN.Core.User; -using ProtonVPN.Translations; -using ProtonVPN.Validation; - -namespace ProtonVPN.BugReporting -{ - public class FormViewModel : ValidationViewModel, IUserDataAware, ILogoutAware - { - private string _whatWentWrong; - private string _stepsToReproduce; - private string _email; - private bool _includeLogs; - - private readonly IUserStorage _userStorage; - private readonly IReportFieldProvider _reportFieldProvider; - - public FormViewModel(IUserStorage userStorage, IReportFieldProvider reportFieldProvider) - { - _userStorage = userStorage; - _reportFieldProvider = reportFieldProvider; - } - - public string Email - { - get => _email; - set - { - Set(ref _email, value); - Validate(); - } - } - - public string WhatWentWrong - { - get => _whatWentWrong; - set - { - Set(ref _whatWentWrong, value); - Validate(); - } - } - - public string StepsToReproduce - { - get => _stepsToReproduce; - set - { - Set(ref _stepsToReproduce, value); - Validate(); - } - } - - public bool IncludeLogs - { - get => _includeLogs; - set => Set(ref _includeLogs, value); - } - - public void Load() - { - ClearForm(); - LoadEmail(); - } - - public KeyValuePair[] GetFields() - { - return _reportFieldProvider.GetFields(Description, Email); - } - - public void OnUserDataChanged() - { - LoadEmail(); - } - - public void OnUserLoggedOut() - { - Email = string.Empty; - } - - private string Description => $"What went wrong?\n\n{WhatWentWrong}\n\n" + - $"What are the exact steps you performed?\n\n{StepsToReproduce}"; - - private void ClearForm() - { - WhatWentWrong = string.Empty; - StepsToReproduce = string.Empty; - IncludeLogs = true; - } - - private void LoadEmail() - { - User user = _userStorage.User(); - - if (EmailValidator.IsValid(user.Username)) - { - Email = user.Username; - } - } - - public new bool HasErrors => base.HasErrors || string.IsNullOrEmpty(Email); - - private void Validate([CallerMemberName] string field = null) - { - switch (field) - { - case nameof(Email): - if (!EmailValidator.IsValid(Email)) - { - SetError(nameof(Email), Translation.Get("BugReport_msg_EmailNotValid")); - } - else - { - ClearError(field); - } - - break; - case nameof(WhatWentWrong): - if (string.IsNullOrEmpty(WhatWentWrong)) - { - SetError(nameof(WhatWentWrong), "empty"); - } - else - { - ClearError(field); - } - - break; - case nameof(StepsToReproduce): - if (string.IsNullOrEmpty(StepsToReproduce)) - { - SetError(nameof(StepsToReproduce), "empty"); - } - else - { - ClearError(field); - } - - break; - } - } - } -} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/IBugReport.cs b/src/ProtonVPN.App/BugReporting/IBugReport.cs index f2d1c457c..43f0cf07d 100644 --- a/src/ProtonVPN.App/BugReporting/IBugReport.cs +++ b/src/ProtonVPN.App/BugReporting/IBugReport.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * diff --git a/src/ProtonVPN.App/BugReporting/IReportFieldProvider.cs b/src/ProtonVPN.App/BugReporting/IReportFieldProvider.cs index b521657f7..df824d7e1 100644 --- a/src/ProtonVPN.App/BugReporting/IReportFieldProvider.cs +++ b/src/ProtonVPN.App/BugReporting/IReportFieldProvider.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -18,11 +18,12 @@ */ using System.Collections.Generic; +using ProtonVPN.BugReporting.FormElements; namespace ProtonVPN.BugReporting { public interface IReportFieldProvider { - KeyValuePair[] GetFields(string description, string email); + KeyValuePair[] GetFields(string category, IList formElements); } } \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/ReportBugModalView.xaml b/src/ProtonVPN.App/BugReporting/ReportBugModalView.xaml index 78c187ec0..cd43099e9 100644 --- a/src/ProtonVPN.App/BugReporting/ReportBugModalView.xaml +++ b/src/ProtonVPN.App/BugReporting/ReportBugModalView.xaml @@ -1,5 +1,5 @@  - - - - - - - - + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:bugReporting="clr-namespace:ProtonVPN.BugReporting" + xmlns:wpf="clr-namespace:ProtonVPN.Core.Wpf" + xmlns:translations="clr-namespace:ProtonVPN.Translations;assembly=ProtonVPN.Translations" + xmlns:resource="clr-namespace:ProtonVPN.Resource;assembly=ProtonVPN.Resource" + xmlns:steps="clr-namespace:ProtonVPN.BugReporting.Steps" + xmlns:screens="clr-namespace:ProtonVPN.BugReporting.Screens" + Style="{StaticResource BaseWindowStyle}" + Title="{translations:Loc BugReport_ttl}" + mc:Ignorable="d" + Width="653" + Height="Auto" + MaxHeight="700" + SizeToContent="Height" + d:DataContext="{d:DesignInstance bugReporting:ReportBugModalViewModel}"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FailureView.xaml.cs b/src/ProtonVPN.App/BugReporting/Screens/FailureView.xaml.cs similarity index 90% rename from src/ProtonVPN.App/BugReporting/FailureView.xaml.cs rename to src/ProtonVPN.App/BugReporting/Screens/FailureView.xaml.cs index 724316931..71f46274e 100644 --- a/src/ProtonVPN.App/BugReporting/FailureView.xaml.cs +++ b/src/ProtonVPN.App/BugReporting/Screens/FailureView.xaml.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -17,7 +17,7 @@ * along with ProtonVPN. If not, see . */ -namespace ProtonVPN.BugReporting +namespace ProtonVPN.BugReporting.Screens { public partial class FailureView { @@ -26,4 +26,4 @@ public FailureView() InitializeComponent(); } } -} +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/FailureViewModel.cs b/src/ProtonVPN.App/BugReporting/Screens/FailureViewModel.cs similarity index 60% rename from src/ProtonVPN.App/BugReporting/FailureViewModel.cs rename to src/ProtonVPN.App/BugReporting/Screens/FailureViewModel.cs index 3192db755..69401418d 100644 --- a/src/ProtonVPN.App/BugReporting/FailureViewModel.cs +++ b/src/ProtonVPN.App/BugReporting/Screens/FailureViewModel.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -18,25 +18,29 @@ */ using System.Windows.Input; +using Caliburn.Micro; using GalaSoft.MvvmLight.Command; +using ProtonVPN.BugReporting.Actions; using ProtonVPN.Core.Modals; -using ProtonVPN.Core.MVVM; using ProtonVPN.Modals; -namespace ProtonVPN.BugReporting +namespace ProtonVPN.BugReporting.Screens { - public class FailureViewModel : ViewModel + public class FailureViewModel : Screen { + private string _error; private readonly IModals _modals; + private readonly IEventAggregator _eventAggregator; - public FailureViewModel(IModals modals) + public FailureViewModel(IModals modals, IEventAggregator eventAggregator) { _modals = modals; + _eventAggregator = eventAggregator; TroubleshootCommand = new RelayCommand(TroubleshootAction); + RetryCommand = new RelayCommand(RetryAction); + BackCommand = new RelayCommand(BackAction); } - private string _error; - public string Error { get => _error; @@ -44,10 +48,22 @@ public string Error } public ICommand TroubleshootCommand { get; set; } + public ICommand RetryCommand { get; set; } + public ICommand BackCommand { get; set; } private void TroubleshootAction() { _modals.Show(); } + + private void RetryAction() + { + _eventAggregator.PublishOnUIThread(new RetryAction()); + } + + private void BackAction() + { + _eventAggregator.PublishOnUIThread(new GoBackAfterFailureAction()); + } } -} +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/SendingView.xaml b/src/ProtonVPN.App/BugReporting/Screens/SendingView.xaml similarity index 76% rename from src/ProtonVPN.App/BugReporting/SendingView.xaml rename to src/ProtonVPN.App/BugReporting/Screens/SendingView.xaml index dc4a07602..768d343d7 100644 --- a/src/ProtonVPN.App/BugReporting/SendingView.xaml +++ b/src/ProtonVPN.App/BugReporting/Screens/SendingView.xaml @@ -1,5 +1,5 @@  -. xmlns:controls="clr-namespace:ProtonVPN.Resource.Controls;assembly=ProtonVPN.Resource" mc:Ignorable="d"> - - + + + Margin="0,20,0,0" + FontSize="16" + HorizontalAlignment="Center" + Text="{translations:Loc BugReport_msg_Sending}" /> - + \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/SendingView.xaml.cs b/src/ProtonVPN.App/BugReporting/Screens/SendingView.xaml.cs similarity index 90% rename from src/ProtonVPN.App/BugReporting/SendingView.xaml.cs rename to src/ProtonVPN.App/BugReporting/Screens/SendingView.xaml.cs index 54e0135fe..91d9054ad 100644 --- a/src/ProtonVPN.App/BugReporting/SendingView.xaml.cs +++ b/src/ProtonVPN.App/BugReporting/Screens/SendingView.xaml.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -17,7 +17,7 @@ * along with ProtonVPN. If not, see . */ -namespace ProtonVPN.BugReporting +namespace ProtonVPN.BugReporting.Screens { public partial class SendingView { @@ -26,4 +26,4 @@ public SendingView() InitializeComponent(); } } -} +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/SendingViewModel.cs b/src/ProtonVPN.App/BugReporting/Screens/SendingViewModel.cs similarity index 82% rename from src/ProtonVPN.App/BugReporting/SendingViewModel.cs rename to src/ProtonVPN.App/BugReporting/Screens/SendingViewModel.cs index 32e6e0a1a..a8d52c561 100644 --- a/src/ProtonVPN.App/BugReporting/SendingViewModel.cs +++ b/src/ProtonVPN.App/BugReporting/Screens/SendingViewModel.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Proton Technologies AG + * Copyright (c) 2021 Proton Technologies AG * * This file is part of ProtonVPN. * @@ -17,11 +17,11 @@ * along with ProtonVPN. If not, see . */ -using ProtonVPN.Core.MVVM; +using Caliburn.Micro; -namespace ProtonVPN.BugReporting +namespace ProtonVPN.BugReporting.Screens { - public class SendingViewModel : ViewModel + public class SendingViewModel : Screen { } -} +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/SentView.xaml b/src/ProtonVPN.App/BugReporting/Screens/SentView.xaml similarity index 50% rename from src/ProtonVPN.App/BugReporting/SentView.xaml rename to src/ProtonVPN.App/BugReporting/Screens/SentView.xaml index 7503f2a57..8da6db96e 100644 --- a/src/ProtonVPN.App/BugReporting/SentView.xaml +++ b/src/ProtonVPN.App/BugReporting/Screens/SentView.xaml @@ -1,5 +1,5 @@  - + mc:Ignorable="d" + d:DataContext="{d:DesignInstance screens:SentViewModel}"> - - - - - - + \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Steps/CategorySelectionView.xaml.cs b/src/ProtonVPN.App/BugReporting/Steps/CategorySelectionView.xaml.cs new file mode 100644 index 000000000..537c8213f --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Steps/CategorySelectionView.xaml.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.Steps +{ + public partial class CategorySelectionView + { + public CategorySelectionView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Steps/CategorySelectionViewModel.cs b/src/ProtonVPN.App/BugReporting/Steps/CategorySelectionViewModel.cs new file mode 100644 index 000000000..3be4ce8d1 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Steps/CategorySelectionViewModel.cs @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +using System.Collections.Generic; +using System.Windows.Input; +using Caliburn.Micro; +using GalaSoft.MvvmLight.CommandWpf; +using ProtonVPN.BugReporting.Actions; +using ProtonVPN.Core.Api.Contracts.ReportAnIssue; +using ProtonVPN.Core.ReportAnIssue; + +namespace ProtonVPN.BugReporting.Steps +{ + public class CategorySelectionViewModel : Screen + { + private readonly IEventAggregator _eventAggregator; + private readonly IReportAnIssueFormDataProvider _reportAnIssueFormDataProvider; + + public CategorySelectionViewModel(IEventAggregator eventAggregator, IReportAnIssueFormDataProvider reportAnIssueFormDataProvider) + { + _eventAggregator = eventAggregator; + _reportAnIssueFormDataProvider = reportAnIssueFormDataProvider; + SelectCategoryCommand = new RelayCommand(SelectCategoryAction); + } + + public List Categories => _reportAnIssueFormDataProvider.GetCategories(); + + public ICommand SelectCategoryCommand { get; } + + public void SelectCategoryAction(string category) + { + _eventAggregator.PublishOnUIThread(new SelectCategoryAction(category)); + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Steps/FormView.xaml b/src/ProtonVPN.App/BugReporting/Steps/FormView.xaml new file mode 100644 index 000000000..aca1cc842 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Steps/FormView.xaml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Steps/StepsContainerView.xaml.cs b/src/ProtonVPN.App/BugReporting/Steps/StepsContainerView.xaml.cs new file mode 100644 index 000000000..8f930e093 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Steps/StepsContainerView.xaml.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +namespace ProtonVPN.BugReporting.Steps +{ + public partial class StepsContainerView + { + public StepsContainerView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/BugReporting/Steps/StepsContainerViewModel.cs b/src/ProtonVPN.App/BugReporting/Steps/StepsContainerViewModel.cs new file mode 100644 index 000000000..c0a226a40 --- /dev/null +++ b/src/ProtonVPN.App/BugReporting/Steps/StepsContainerViewModel.cs @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021 Proton Technologies AG + * + * This file is part of ProtonVPN. + * + * ProtonVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonVPN. If not, see . + */ + +using System.Windows.Input; +using Caliburn.Micro; +using GalaSoft.MvvmLight.Command; +using ProtonVPN.About; +using ProtonVPN.BugReporting.Actions; +using ProtonVPN.Common.Extensions; +using ProtonVPN.Core.Auth; +using ProtonVPN.Core.ReportAnIssue; + +namespace ProtonVPN.BugReporting.Steps +{ + public class StepsContainerViewModel : Screen, + ILogoutAware, + IHandle, + IHandle, + IHandle + { + private readonly IEventAggregator _eventAggregator; + private readonly IReportAnIssueFormDataProvider _reportAnIssueFormDataProvider; + private readonly CategorySelectionViewModel _categorySelectionViewModel; + private readonly SolutionsViewModel _solutionsViewModel; + private readonly FormViewModel _formViewModel; + + private Screen _screenViewModel; + private int _step = 1; + private string _category; + + public ICommand GoBackCommand { get; } + + public bool IsToShowBackButton => Step > 1; + + public Screen ScreenViewModel + { + get => _screenViewModel; + set => Set(ref _screenViewModel, value); + } + + public UpdateViewModel UpdateViewModel { get; } + + public int Step + { + get => _step; + set + { + Set(ref _step, value); + ScreenViewModel = GetScreen(); + NotifyOfPropertyChange(nameof(IsToShowBackButton)); + } + } + + public StepsContainerViewModel(IEventAggregator eventAggregator, + IReportAnIssueFormDataProvider reportAnIssueFormDataProvider, + UpdateViewModel updateViewModel, + CategorySelectionViewModel categorySelectionViewModel, + SolutionsViewModel solutionsViewModel, + FormViewModel formViewModel) + { + eventAggregator.Subscribe(this); + + _eventAggregator = eventAggregator; + _reportAnIssueFormDataProvider = reportAnIssueFormDataProvider; + _categorySelectionViewModel = categorySelectionViewModel; + _solutionsViewModel = solutionsViewModel; + _formViewModel = formViewModel; + + UpdateViewModel = updateViewModel; + ScreenViewModel = categorySelectionViewModel; + GoBackCommand = new RelayCommand(GoBackAction); + } + + private Screen GetScreen() + { + switch (Step) + { + case 1: + return _categorySelectionViewModel; + case 2: + return _solutionsViewModel; + case 3: + return _formViewModel; + default: + return _categorySelectionViewModel; + } + } + + public void OnUserLoggedOut() + { + ShowFirstStep(); + } + + public void Handle(FormStateChange message) + { + if (message.State == FormState.Sent) + { + ShowFirstStep(); + } + } + + public void Handle(SelectCategoryAction message) + { + _category = message.Category; + + if (HasSuggestions(message.Category)) + { + Step = 2; + } + else + { + _eventAggregator.PublishOnUIThread(new FillTheFormAction(message.Category)); + } + } + + private bool HasSuggestions(string category) + { + return !_reportAnIssueFormDataProvider.GetSuggestions(category).IsNullOrEmpty(); + } + + public void Handle(FillTheFormAction message) + { + Step = 3; + } + + private void GoBackAction() + { + switch (Step) + { + case <= 1: + return; + case 3 when !HasSuggestions(_category): + Step = 1; + break; + default: + Step--; + break; + } + } + + private void ShowFirstStep() + { + Step = 1; + } + } +} \ No newline at end of file diff --git a/src/ProtonVPN.App/Config/Url/ActiveUrls.cs b/src/ProtonVPN.App/Config/Url/ActiveUrls.cs index 1f210c816..ba85ad80d 100644 --- a/src/ProtonVPN.App/Config/Url/ActiveUrls.cs +++ b/src/ProtonVPN.App/Config/Url/ActiveUrls.cs @@ -65,6 +65,7 @@ public ActiveUrls(Common.Configuration.Config config, IOsProcesses processes) public IActiveUrl TorUrl => Url(_config.TorUrl); public IActiveUrl AboutSmartProtocolUrl => Url(_config.AboutSmartProtocolUrl); public IActiveUrl IncorrectSystemTimeArticleUrl => Url(_config.IncorrectSystemTimeArticleUrl); + public IActiveUrl AssignVpnConnectionsUrl => Url(_config.AssignVpnConnectionsUrl); private ActiveUrl Url(string url) { diff --git a/src/ProtonVPN.App/Config/Url/IActiveUrls.cs b/src/ProtonVPN.App/Config/Url/IActiveUrls.cs index 791d263fd..ceb5ca727 100644 --- a/src/ProtonVPN.App/Config/Url/IActiveUrls.cs +++ b/src/ProtonVPN.App/Config/Url/IActiveUrls.cs @@ -53,5 +53,6 @@ public interface IActiveUrls IActiveUrl TorUrl { get; } IActiveUrl AboutSmartProtocolUrl { get; } IActiveUrl IncorrectSystemTimeArticleUrl { get; } + IActiveUrl AssignVpnConnectionsUrl { get; } } } \ No newline at end of file diff --git a/src/ProtonVPN.App/Core/AppSettings.cs b/src/ProtonVPN.App/Core/AppSettings.cs index 451b72412..6426adb6a 100644 --- a/src/ProtonVPN.App/Core/AppSettings.cs +++ b/src/ProtonVPN.App/Core/AppSettings.cs @@ -27,7 +27,7 @@ using ProtonVPN.Common.Extensions; using ProtonVPN.Common.KillSwitch; using ProtonVPN.Common.Networking; -using ProtonVPN.Core.Announcements; +using ProtonVPN.Core.Api.Contracts.ReportAnIssue; using ProtonVPN.Core.Auth; using ProtonVPN.Core.Models; using ProtonVPN.Core.Native.Structures; @@ -37,6 +37,7 @@ using ProtonVPN.Core.Settings.Contracts; using ProtonVPN.Core.Storage; using ProtonVPN.Settings; +using Announcement = ProtonVPN.Core.Announcements.Announcement; namespace ProtonVPN.Core { @@ -45,7 +46,7 @@ internal class AppSettings : IAppSettings, INotifyPropertyChanged, ILoggedInAwar private readonly ISettingsStorage _storage; private readonly UserSettings _userSettings; private readonly Common.Configuration.Config _config; - private readonly HashSet _accessedPerUserProperties = new HashSet(); + private readonly HashSet _accessedPerUserProperties = new(); public AppSettings(ISettingsStorage storage, UserSettings userSettings, Common.Configuration.Config config) { @@ -68,6 +69,12 @@ public IReadOnlyList Announcements set => SetPerUser(value); } + public List ReportAnIssueFormData + { + get => GetPerUser>() ?? new List(); + set => SetPerUser(value); + } + public DateTime ProfileChangesSyncedAt { get => GetPerUser(); diff --git a/src/ProtonVPN.App/Core/AutoConnect.cs b/src/ProtonVPN.App/Core/AutoConnect.cs index dff070fb6..f7edf6243 100644 --- a/src/ProtonVPN.App/Core/AutoConnect.cs +++ b/src/ProtonVPN.App/Core/AutoConnect.cs @@ -54,7 +54,7 @@ public async Task Load(bool autoLogin) return; try { - var profile = await _profileManager.GetProfileById(_appSettings.AutoConnect); + Profile profile = await _profileManager.GetProfileById(_appSettings.AutoConnect); if (profile == null) { diff --git a/src/ProtonVPN.App/Core/Bootstraper.cs b/src/ProtonVPN.App/Core/Bootstraper.cs index 5113567a4..dd6cf34c3 100644 --- a/src/ProtonVPN.App/Core/Bootstraper.cs +++ b/src/ProtonVPN.App/Core/Bootstraper.cs @@ -49,6 +49,7 @@ using ProtonVPN.Core.Network; using ProtonVPN.Core.OS.Net; using ProtonVPN.Core.Profiles; +using ProtonVPN.Core.ReportAnIssue; using ProtonVPN.Core.Servers; using ProtonVPN.Core.Service; using ProtonVPN.Core.Service.Settings; @@ -200,19 +201,20 @@ private void LoadServersFromCache() private async Task IsUserValid() { + LoginViewModel loginViewModel = Resolve(); try { - ApiResponseResult validateResult = await Resolve().GetValidateResult(); - if (validateResult.Failure) + AuthResult result = await Resolve().GetValidateResult(); + if (result.Failure) { - Resolve().SetError(validateResult.Error); + loginViewModel.HandleAuthFailure(result); ShowLoginForm(); return false; } } catch (HttpRequestException ex) { - Resolve().SetError(ex.Message); + loginViewModel.HandleAuthFailure(AuthResult.Fail(ex.Message)); ShowLoginForm(); return false; } @@ -518,6 +520,7 @@ private async Task SwitchToAppWindow(bool autoLogin) Resolve().CheckForInsecureWiFi(); await Resolve().StoreLatestEvent(); Resolve().Start(); + await Resolve().FetchData(); } private void LoadViewModels() diff --git a/src/ProtonVPN.App/Core/Ioc/AppModule.cs b/src/ProtonVPN.App/Core/Ioc/AppModule.cs index c49bd33ec..a886fefd5 100644 --- a/src/ProtonVPN.App/Core/Ioc/AppModule.cs +++ b/src/ProtonVPN.App/Core/Ioc/AppModule.cs @@ -23,8 +23,6 @@ using Caliburn.Micro; using ProtonVPN.About; using ProtonVPN.Account; -using ProtonVPN.BugReporting; -using ProtonVPN.BugReporting.Diagnostic; using ProtonVPN.Common.Configuration; using ProtonVPN.Common.Logging; using ProtonVPN.Common.OS.Processes; @@ -279,7 +277,6 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().AsImplementedInterfaces().AsSelf().SingleInstance(); builder.RegisterType().AsImplementedInterfaces().AsSelf().SingleInstance(); builder.RegisterType().SingleInstance(); - builder.RegisterType().As().SingleInstance(); builder.RegisterType().AsImplementedInterfaces().AsSelf().SingleInstance(); builder.Register(c => new VpnInfoChecker( c.Resolve(), @@ -287,7 +284,6 @@ protected override void Load(ContainerBuilder builder) c.Resolve(), c.Resolve(), c.Resolve())).SingleInstance(); - builder.RegisterType().As().SingleInstance(); builder.RegisterType().AsImplementedInterfaces().AsSelf().SingleInstance(); builder.RegisterType().AsImplementedInterfaces().AsSelf().SingleInstance(); builder.RegisterType().As().SingleInstance(); diff --git a/src/ProtonVPN.App/Core/Ioc/CoreModule.cs b/src/ProtonVPN.App/Core/Ioc/CoreModule.cs index 1bd11af89..16dc31c7b 100644 --- a/src/ProtonVPN.App/Core/Ioc/CoreModule.cs +++ b/src/ProtonVPN.App/Core/Ioc/CoreModule.cs @@ -47,6 +47,7 @@ using ProtonVPN.Core.OS.Net; using ProtonVPN.Core.OS.Net.Dns; using ProtonVPN.Core.OS.Net.DoH; +using ProtonVPN.Core.ReportAnIssue; using ProtonVPN.Core.Servers; using ProtonVPN.Core.Service; using ProtonVPN.Core.Settings; @@ -57,7 +58,6 @@ using ProtonVPN.HumanVerification; using ProtonVPN.Modals.ApiActions; using ProtonVPN.Settings; -using ProtonVPN.Translations; using ProtonVPN.Vpn; using Module = Autofac.Module; @@ -283,6 +283,8 @@ protected override void Load(ContainerBuilder builder) builder.Register(c => new NtpClient(c.Resolve().NtpServerUrl, c.Resolve())) .As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); } } } \ No newline at end of file diff --git a/src/ProtonVPN.App/Core/LanguageProvider.cs b/src/ProtonVPN.App/Core/LanguageProvider.cs index bd6d83820..aeaa51517 100644 --- a/src/ProtonVPN.App/Core/LanguageProvider.cs +++ b/src/ProtonVPN.App/Core/LanguageProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using ProtonVPN.Common.Extensions; @@ -29,7 +29,7 @@ public List GetAll() } catch (Exception e) when (e.IsFileAccessException()) { - _logger.Error(e); + _logger.Error("Couldn't get language file.", e); return new List{ _defaultLocale }; } } @@ -37,9 +37,9 @@ public List GetAll() private List InternalGetAll() { var langs = new List { _defaultLocale }; - var files = Directory.GetFiles(_translationsFolder, ResourceFile, SearchOption.AllDirectories); + string[] files = Directory.GetFiles(_translationsFolder, ResourceFile, SearchOption.AllDirectories); - foreach (var file in files) + foreach (string file in files) { var dirInfo = new DirectoryInfo(file); if (dirInfo.Parent != null) diff --git a/src/ProtonVPN.App/Core/Service/Vpn/INetworkAdapterValidator.cs b/src/ProtonVPN.App/Core/Service/Vpn/INetworkAdapterValidator.cs index 509a4a729..9992fe53b 100644 --- a/src/ProtonVPN.App/Core/Service/Vpn/INetworkAdapterValidator.cs +++ b/src/ProtonVPN.App/Core/Service/Vpn/INetworkAdapterValidator.cs @@ -21,6 +21,6 @@ namespace ProtonVPN.Core.Service.Vpn { public interface INetworkAdapterValidator { - bool IsAdapterAvailable(); + bool IsOpenVpnAdapterAvailable(); } } \ No newline at end of file diff --git a/src/ProtonVPN.App/Core/Service/Vpn/NetworkAdapterValidator.cs b/src/ProtonVPN.App/Core/Service/Vpn/NetworkAdapterValidator.cs index 50dff6d2b..a46d29cdc 100644 --- a/src/ProtonVPN.App/Core/Service/Vpn/NetworkAdapterValidator.cs +++ b/src/ProtonVPN.App/Core/Service/Vpn/NetworkAdapterValidator.cs @@ -17,51 +17,59 @@ * along with ProtonVPN. If not, see . */ -using ProtonVPN.Common.Networking; +using ProtonVPN.Common.Logging; using ProtonVPN.Common.OS.Net; using ProtonVPN.Common.OS.Net.NetworkInterface; -using ProtonVPN.Core.Settings; -using Sentry; -using Sentry.Protocol; namespace ProtonVPN.Core.Service.Vpn { public class NetworkAdapterValidator : INetworkAdapterValidator { private readonly INetworkInterfaceLoader _networkInterfaceLoader; - private readonly IAppSettings _appSettings; + private readonly ILogger _logger; - public NetworkAdapterValidator(INetworkInterfaceLoader networkInterfaceLoader, IAppSettings appSettings) + public NetworkAdapterValidator(INetworkInterfaceLoader networkInterfaceLoader, ILogger logger) { _networkInterfaceLoader = networkInterfaceLoader; - _appSettings = appSettings; + _logger = logger; } - public bool IsAdapterAvailable() + public bool IsOpenVpnAdapterAvailable() { - INetworkInterface openVpnTunInterface = _networkInterfaceLoader.GetOpenVpnTunInterface(); INetworkInterface openVpnTapInterface = _networkInterfaceLoader.GetOpenVpnTapInterface(); - if (openVpnTunInterface == null && openVpnTapInterface == null) - { - return false; - } + INetworkInterface openVpnTunInterface = _networkInterfaceLoader.GetOpenVpnTunInterface(); + bool isOpenVpnAdapterAvailable = openVpnTapInterface != null || openVpnTunInterface != null; + + LogIsOpenVpnAdapterAvailable(isOpenVpnAdapterAvailable, + CreateInterfaceLogMessage("TAP", openVpnTapInterface), + CreateInterfaceLogMessage("TUN", openVpnTunInterface)); - if (openVpnTunInterface == null && _appSettings.NetworkAdapterType == OpenVpnAdapter.Tun) + return isOpenVpnAdapterAvailable; + } + + private string CreateInterfaceLogMessage(string interfaceType, INetworkInterface networkInterface) + { + if (networkInterface == null) { - _appSettings.NetworkAdapterType = OpenVpnAdapter.Tap; - SendTunFallbackEvent(); + return $"The {interfaceType} adapter is unavailable."; } - return true; + return $"The {interfaceType} adapter is available (Index: {networkInterface.Index}, " + + $"Name: '{networkInterface.Name}', Description: '{networkInterface.Description}')."; } - private void SendTunFallbackEvent() + private void LogIsOpenVpnAdapterAvailable(bool isOpenVpnAdapterAvailable, params string[] openVpnInterfaces) { - SentrySdk.CaptureEvent(new SentryEvent + string logMessage = $"[NetworkAdapterValidator] Checking which OpenVPN adapters are available. " + + string.Join(" ", openVpnInterfaces); + if (isOpenVpnAdapterAvailable) { - Message = "TUN adapter not found. Adapter changed to TAP.", - Level = SentryLevel.Info, - }); + _logger.Info(logMessage); + } + else + { + _logger.Warn(logMessage); + } } } } \ No newline at end of file diff --git a/src/ProtonVPN.App/Core/Service/Vpn/VpnConnector.cs b/src/ProtonVPN.App/Core/Service/Vpn/VpnConnector.cs index bb10481c1..8f7d01dc6 100644 --- a/src/ProtonVPN.App/Core/Service/Vpn/VpnConnector.cs +++ b/src/ProtonVPN.App/Core/Service/Vpn/VpnConnector.cs @@ -42,7 +42,6 @@ public class VpnConnector : IVpnConnector, ILogoutAware private readonly IAppSettings _appSettings; private readonly GuestHoleState _guestHoleState; private readonly IUserStorage _userStorage; - private readonly INetworkAdapterValidator _networkAdapterValidator; private readonly ServerManager _serverManager; private readonly ILogger _logger; @@ -59,7 +58,6 @@ public VpnConnector( IAppSettings appSettings, GuestHoleState guestHoleState, IUserStorage userStorage, - INetworkAdapterValidator networkAdapterValidator, ServerManager serverManager, ILogger logger) { @@ -68,7 +66,6 @@ public VpnConnector( _appSettings = appSettings; _guestHoleState = guestHoleState; _userStorage = userStorage; - _networkAdapterValidator = networkAdapterValidator; _serverManager = serverManager; _logger = logger; } @@ -103,24 +100,6 @@ private async Task GetQuickConnectProfileAsync() } public async Task ConnectToBestProfileAsync(Profile profile, Profile fallbackProfile = null, int? maxServers = null) - { - await ValidateConnectionAsync(() => ExecuteConnectToBestProfileAsync(profile, fallbackProfile, maxServers)); - } - - private async Task ValidateConnectionAsync(Func connectionFunction) - { - if (_networkAdapterValidator.IsAdapterAvailable()) - { - await connectionFunction(); - } - else - { - RaiseVpnStateChanged(new VpnStateChangedEventArgs(new VpnState(VpnStatus.Disconnected), - VpnError.NoTapAdaptersError, false)); - } - } - - private async Task ExecuteConnectToBestProfileAsync(Profile profile, Profile fallbackProfile = null, int? maxServers = null) { IList profiles = CreateProfilePreferenceList(profile, fallbackProfile); VpnManagerProfileCandidates profileCandidates = GetBestProfileCandidates(profiles); @@ -201,21 +180,11 @@ private async Task ConnectAsync(VpnManagerProfileCandidates profileCandidates, V } public async Task ConnectToPreSortedCandidatesAsync(IReadOnlyCollection sortedCandidates, VpnProtocol vpnProtocol) - { - await ValidateConnectionAsync(() => ExecuteConnectToPreSortedCandidatesAsync(sortedCandidates, vpnProtocol)); - } - - private async Task ExecuteConnectToPreSortedCandidatesAsync(IReadOnlyCollection sortedCandidates, VpnProtocol vpnProtocol) { await _profileConnector.ConnectWithPreSortedCandidates(sortedCandidates, vpnProtocol); } public async Task ConnectToProfileAsync(Profile profile, VpnProtocol? vpnProtocol = null) - { - await ValidateConnectionAsync(() => ExecuteConnectToProfileAsync(profile, vpnProtocol)); - } - - private async Task ExecuteConnectToProfileAsync(Profile profile, VpnProtocol? vpnProtocol = null) { VpnManagerProfileCandidates profileCandidates = GetProfileCandidates(profile); await ConnectToProfileCandidatesAsync(profileCandidates, vpnProtocol); @@ -238,7 +207,7 @@ private void SetPropertiesOnVpnStateChanged(VpnStateChangedEventArgs e) LastServer = _serverManager.GetServerByEntryIpAndLabel(e.State.EntryIp, e.State.Label); } - State = new VpnState(e.State.Status, LastServer, e.State.VpnProtocol); + State = new VpnState(e.State.Status, LastServer, e.State.VpnProtocol, e.State.NetworkAdapterType); NetworkBlocked = e.NetworkBlocked; RaiseVpnStateChanged(new VpnStateChangedEventArgs(State, e.Error, e.NetworkBlocked)); diff --git a/src/ProtonVPN.App/Core/Service/Vpn/VpnServiceManager.cs b/src/ProtonVPN.App/Core/Service/Vpn/VpnServiceManager.cs index e5f439c09..17822bbb8 100644 --- a/src/ProtonVPN.App/Core/Service/Vpn/VpnServiceManager.cs +++ b/src/ProtonVPN.App/Core/Service/Vpn/VpnServiceManager.cs @@ -174,6 +174,7 @@ private static VpnProtocolContract Map(VpnProtocol protocol) VpnProtocol.OpenVpnTcp => VpnProtocolContract.OpenVpnTcp, VpnProtocol.WireGuard => VpnProtocolContract.WireGuard, VpnProtocol.Smart => VpnProtocolContract.Smart, + _ => throw new NotImplementedException("VpnProtocol has an unknown value.") }; } @@ -186,6 +187,7 @@ private static VpnProtocol Map(VpnProtocolContract protocol) VpnProtocolContract.OpenVpnTcp => VpnProtocol.OpenVpnTcp, VpnProtocolContract.WireGuard => VpnProtocol.WireGuard, VpnProtocolContract.Smart => VpnProtocol.Smart, + _ => throw new NotImplementedException("VpnProtocol has an unknown value.") }; } diff --git a/src/ProtonVPN.App/Login/ViewModels/LoginViewModel.cs b/src/ProtonVPN.App/Login/ViewModels/LoginViewModel.cs index 9cb5fb814..45abfa509 100644 --- a/src/ProtonVPN.App/Login/ViewModels/LoginViewModel.cs +++ b/src/ProtonVPN.App/Login/ViewModels/LoginViewModel.cs @@ -18,7 +18,6 @@ */ using System.ComponentModel; -using System.Net; using System.Net.Http; using System.Security; using System.Threading.Tasks; @@ -29,7 +28,6 @@ using ProtonVPN.Common.Vpn; using ProtonVPN.Config.Url; using ProtonVPN.Core.Api; -using ProtonVPN.Core.Api.Contracts; using ProtonVPN.Core.Auth; using ProtonVPN.Core.Modals; using ProtonVPN.Core.MVVM; @@ -255,7 +253,7 @@ private async void LoginAction() LoginErrorViewModel.ClearError(); - ApiResponseResult loginResult = await _userAuth.LoginUserAsync(username, Password); + AuthResult loginResult = await _userAuth.LoginUserAsync(username, Password); await HandleLoginResultAsync(loginResult); } catch (HttpRequestException ex) @@ -271,31 +269,34 @@ private async void LoginAction() } } - private async Task HandleLoginResultAsync(ApiResponseResult loginResult) + private async Task HandleLoginResultAsync(AuthResult result) { - if (loginResult.Success) + if (result.Success) { AfterLogin(); } else { - await HandleLoginFailureAsync(loginResult); + await HandleLoginFailureAsync(result); } } - private async Task HandleLoginFailureAsync(ApiResponseResult loginResult) + public void HandleAuthFailure(AuthResult result) { - if (loginResult.Actions.IsNullOrEmpty()) // If Actions exist, it should be handled by ActionableFailureApiResultEventHandler + if (!result.Error.IsNullOrEmpty()) { - string error = loginResult.Error; - if (loginResult.StatusCode == HttpStatusCode.Unauthorized) - { - error = Translation.Get("Login_Error_msg_Unauthorized"); - } + LoginErrorViewModel.SetError(result.Error); + } - LoginErrorViewModel.SetError(error); + if (result.Value == AuthError.NoVpnAccess) + { + _modals.Show(); } + } + private async Task HandleLoginFailureAsync(AuthResult result) + { + HandleAuthFailure(result); Password = new SecureString(); ShowLoginForm(); await DisableGuestHole(); diff --git a/src/ProtonVPN.App/Modals/AssignVpnConnectionsModalView.xaml b/src/ProtonVPN.App/Modals/AssignVpnConnectionsModalView.xaml new file mode 100644 index 000000000..94d9fd8a3 --- /dev/null +++ b/src/ProtonVPN.App/Modals/AssignVpnConnectionsModalView.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + +