diff --git a/src/NebulaAuth/App.xaml.cs b/src/NebulaAuth/App.xaml.cs index 0d90552..a140999 100644 --- a/src/NebulaAuth/App.xaml.cs +++ b/src/NebulaAuth/App.xaml.cs @@ -3,8 +3,6 @@ using System.Windows; using NebulaAuth.Core; using NebulaAuth.Model; using NebulaAuth.Model.Exceptions; -using NebulaAuth.Model.MAAC; -using NebulaAuth.Model.Mafiles; namespace NebulaAuth; @@ -17,12 +15,8 @@ public partial class App var splashScreen = new SplashScreen("Theme\\SplashScreen.png"); splashScreen.Show(false, true); base.OnStartup(e); - LocManager.Init(); - LocManager.SetApplicationLocalization(Settings.Instance.Language); - Shell.Initialize(); - var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1; - await Storage.Initialize(threads); - MAACStorage.Initialize(); + + await Shell.Initialize(); var mainWindow = new MainWindow(); Current.MainWindow = mainWindow; mainWindow.Show(); diff --git a/src/NebulaAuth/Converters/Converters.xaml b/src/NebulaAuth/Converters/Converters.xaml index 9a7a20c..d99c88e 100644 --- a/src/NebulaAuth/Converters/Converters.xaml +++ b/src/NebulaAuth/Converters/Converters.xaml @@ -23,6 +23,7 @@ + True False diff --git a/src/NebulaAuth/Converters/InverseBooleanToVisibilityConverter.cs b/src/NebulaAuth/Converters/InverseBooleanToVisibilityConverter.cs new file mode 100644 index 0000000..e2baf75 --- /dev/null +++ b/src/NebulaAuth/Converters/InverseBooleanToVisibilityConverter.cs @@ -0,0 +1,19 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace NebulaAuth.Converters; + +public class InverseBooleanToVisibilityConverter : IValueConverter +{ + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + return value is bool b && !b ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + return value is Visibility v && v != Visibility.Visible; + } +} \ No newline at end of file diff --git a/src/NebulaAuth/Core/DialogsController.cs b/src/NebulaAuth/Core/DialogsController.cs index 7be3a6c..9ee4da4 100644 --- a/src/NebulaAuth/Core/DialogsController.cs +++ b/src/NebulaAuth/Core/DialogsController.cs @@ -113,6 +113,16 @@ public static class DialogsController await DialogHost.Show(dialog); } + public static async Task ShowExportDialog() + { + var vm = new MafileExporterVM(); + var dialog = new MafileExporterView + { + DataContext = vm + }; + await DialogHost.Show(dialog); + } + #region CommonDialogs public static async Task ShowConfirmCancelDialog(string? msg = null) diff --git a/src/NebulaAuth/Core/TrayManager.cs b/src/NebulaAuth/Core/TrayManager.cs index 44e9b20..988991d 100644 --- a/src/NebulaAuth/Core/TrayManager.cs +++ b/src/NebulaAuth/Core/TrayManager.cs @@ -69,7 +69,7 @@ public static class TrayManager private static void HideMainWindow() { if (!Init) return; - if (IsEnabled == false) return; + if (!IsEnabled) return; _notifyIcon.Visible = true; MainWindow.Hide(); } diff --git a/src/NebulaAuth/MainWindow.xaml b/src/NebulaAuth/MainWindow.xaml index a151e3a..40f25d5 100644 --- a/src/NebulaAuth/MainWindow.xaml +++ b/src/NebulaAuth/MainWindow.xaml @@ -73,7 +73,7 @@ Kind="FileReplaceOutline" /> - + @@ -92,7 +92,9 @@ + Command="{Binding OpenSetPasswordsDialogCommand}" /> + @@ -356,7 +358,7 @@ + VerticalAlignment="Center" /> diff --git a/src/NebulaAuth/MainWindow.xaml.cs b/src/NebulaAuth/MainWindow.xaml.cs index 13fda2a..760919d 100644 --- a/src/NebulaAuth/MainWindow.xaml.cs +++ b/src/NebulaAuth/MainWindow.xaml.cs @@ -30,7 +30,7 @@ public partial class MainWindow private async void OnApplicationStarted(object? sender, EventArgs e) { ((MainVM) DataContext).CurrentDialogHost = DialogHostInstance; - if (Settings.Instance.IsPasswordSet == false) return; + if (!Settings.Instance.IsPasswordSet) return; Topmost = false; await Dispatcher.InvokeAsync(ShowSetPasswordDialog, DispatcherPriority.ContextIdle); } @@ -46,7 +46,7 @@ public partial class MainWindow var result = await DialogHost.Show(dialog); var pass = vm.Password; - if (result is true && string.IsNullOrWhiteSpace(pass) == false) + if (result is true && !string.IsNullOrWhiteSpace(pass)) { PHandler.SetPassword(pass); } @@ -79,12 +79,12 @@ public partial class MainWindow private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e) { - if (int.TryParse(e.Text, out _) == false) e.Handled = true; + if (!int.TryParse(e.Text, out _)) e.Handled = true; } private void Rectangle_DragEnter(object sender, DragEventArgs e) { - if (e.Data.GetDataPresent(DataFormats.FileDrop) == false) + if (!e.Data.GetDataPresent(DataFormats.FileDrop)) { e.Handled = true; return; @@ -96,7 +96,7 @@ public partial class MainWindow private async void Rectangle_Drop(object sender, DragEventArgs e) { - if (e.Data.GetDataPresent(DataFormats.FileDrop) == false) return; + if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return; var filePaths = (string[]) e.Data.GetData(DataFormats.FileDrop)!; if (filePaths.Length == 0) return; if (DataContext is MainVM mainVm) diff --git a/src/NebulaAuth/Model/MAAC/PortableMaClient.cs b/src/NebulaAuth/Model/MAAC/PortableMaClient.cs index 2ec1137..0de4ec7 100644 --- a/src/NebulaAuth/Model/MAAC/PortableMaClient.cs +++ b/src/NebulaAuth/Model/MAAC/PortableMaClient.cs @@ -187,7 +187,7 @@ public partial class PortableMaClient : ObservableObject, IDisposable private bool IgnoreTimerErrors() { - if (Settings.Instance.IgnorePatchTuesdayErrors == false) return false; + if (!Settings.Instance.IgnorePatchTuesdayErrors) return false; var pstZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"); var pstNow = TimeZoneInfo.ConvertTime(DateTime.UtcNow, pstZone); diff --git a/src/NebulaAuth/Model/MaClient.cs b/src/NebulaAuth/Model/MaClient.cs index b37d974..3be9ca6 100644 --- a/src/NebulaAuth/Model/MaClient.cs +++ b/src/NebulaAuth/Model/MaClient.cs @@ -153,7 +153,7 @@ public static class MaClient if (mafile.SessionData.RefreshToken.IsExpired) throw new SessionPermanentlyExpiredException(); - if (ignoreAccessToken == false) + if (!ignoreAccessToken) { var access = mafile.SessionData.GetMobileToken(); if (access == null || access.Value.IsExpired) diff --git a/src/NebulaAuth/Model/MafileExport/ExportResult.cs b/src/NebulaAuth/Model/MafileExport/ExportResult.cs new file mode 100644 index 0000000..9d47215 --- /dev/null +++ b/src/NebulaAuth/Model/MafileExport/ExportResult.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using NebulaAuth.Model.Entities; + +namespace NebulaAuth.Model.MafileExport; + +public class ExportResult +{ + public string? Error { get; } + public Dictionary? Exported { get; } + public List? NotFound { get; } + public List? Conflict { get; } + + [MemberNotNullWhen(true, nameof(Exported))] + [MemberNotNullWhen(true, nameof(NotFound))] + [MemberNotNullWhen(true, nameof(Conflict))] + [MemberNotNullWhen(false, nameof(Error))] + public bool Success => Error == null; + + private ExportResult(string? error, Dictionary? exported, List? notFound, + List? conflict) + { + Error = error; + Exported = exported; + NotFound = notFound; + Conflict = conflict; + } + + public ExportResult(Dictionary exported, List notFound, List conflict) + : this(null, exported, notFound, conflict) + { + } + + + public ExportResult(string? error) + : this(error, null, null, null) + { + } +} \ No newline at end of file diff --git a/src/NebulaAuth/Model/MafileExport/MafileExportTemplate.cs b/src/NebulaAuth/Model/MafileExport/MafileExportTemplate.cs new file mode 100644 index 0000000..3a5311f --- /dev/null +++ b/src/NebulaAuth/Model/MafileExport/MafileExportTemplate.cs @@ -0,0 +1,16 @@ +namespace NebulaAuth.Model.MafileExport; + +public class MafileExportTemplate +{ + public string Name { get; set; } + public bool UseLoginAsMafileName { get; set; } + public bool IncludeSharedSecret { get; set; } + public bool IncludeIdentitySecret { get; set; } + public bool IncludeRCode { get; set; } + public bool IncludeSessionData { get; set; } + public bool IncludeOtherInfo { get; set; } + public bool IncludeNebulaProxy { get; set; } + public bool IncludeNebulaPassword { get; set; } + public bool IncludeNebulaGroup { get; set; } + public string? Path { get; set; } +} \ No newline at end of file diff --git a/src/NebulaAuth/Model/MafileExport/MafileExporter.cs b/src/NebulaAuth/Model/MafileExport/MafileExporter.cs new file mode 100644 index 0000000..141cd38 --- /dev/null +++ b/src/NebulaAuth/Model/MafileExport/MafileExporter.cs @@ -0,0 +1,159 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using AchiesUtilities.Extensions; +using NebulaAuth.Core; +using NebulaAuth.Model.Entities; +using NebulaAuth.Model.Mafiles; +using SteamLibForked.Models.SteamIds; + +namespace NebulaAuth.Model.MafileExport; + +public static class MafileExporter +{ + public static async Task ExportMafiles(IEnumerable keys, MafileExportTemplate template) + { + if (template.Path == null || !PathIsValid(template.Path)) + { + return new ExportResult(LocManager.GetCodeBehindOrDefault("InvalidPath", "MafileExporter.InvalidPath")); + } + + if (!Directory.Exists(template.Path)) + { + Directory.CreateDirectory(template.Path); + } + + if (IsNebulaUtilizedDirectory(template.Path)) + { + return new ExportResult(LocManager.GetCodeBehindOrDefault("NebulaUtilizedDirectory", + "MafileExporter.NebulaUtilizedDirectory")); + } + + if (!IsAllowedToWrite(template.Path)) + { + return new ExportResult(LocManager.GetCodeBehindOrDefault("AccessDenied", "MafileExporter.AccessDenied")); + } + + + var exported = new Dictionary(); + var notFound = new List(); + var conflict = new List(); + + foreach (var key in keys) + { + SteamId? steamId = null; + if (SteamId64.TryParse(key, out var id64)) + { + steamId = id64; + } + + var maf = Storage.MaFiles.FirstOrDefault(m => m.AccountName.EqualsIgnoreCase(key) || m.SteamId == steamId); + if (maf != null) + { + var fileName = await ExportMafile(template.Path, template, maf); + if (fileName == null) + { + conflict.Add(key); + continue; + } + + exported[maf] = fileName; + } + else + { + notFound.Add(key); + } + } + + return new ExportResult(exported, notFound, conflict); + } + + private static async Task ExportMafile(string path, MafileExportTemplate template, Mafile mafile) + { + var serializeMaf = new Mafile + { + SharedSecret = template.IncludeSharedSecret ? mafile.SharedSecret : null!, + IdentitySecret = template.IncludeIdentitySecret ? mafile.IdentitySecret : null!, + DeviceId = template.IncludeIdentitySecret ? mafile.DeviceId : null!, + RevocationCode = template.IncludeRCode ? mafile.RevocationCode : null!, + AccountName = mafile.AccountName, + SessionData = template.IncludeSessionData ? mafile.SessionData : null!, + SteamId = mafile.SteamId, + ServerTime = template.IncludeOtherInfo ? mafile.ServerTime : 0, + SerialNumber = template.IncludeOtherInfo ? mafile.SerialNumber : 0, + Uri = template.IncludeOtherInfo ? mafile.Uri : null!, + TokenGid = template.IncludeOtherInfo ? mafile.TokenGid : null!, + Secret1 = template.IncludeOtherInfo ? mafile.Secret1 : null!, + Proxy = template.IncludeNebulaProxy ? mafile.Proxy : null!, + Group = template.IncludeNebulaGroup ? mafile.Group : null!, + Password = template.IncludeNebulaPassword ? mafile.Password : null!, + LinkedClient = null, + Filename = null + }; + + var serialized = NebulaSerializer.SerializeMafile(serializeMaf); + var strategy = template.UseLoginAsMafileName ? MafileNamingStrategy.Login : MafileNamingStrategy.SteamId; + var fileName = strategy.GetMafileName(serializeMaf); + var fullPath = Path.Combine(path, fileName); + if (File.Exists(fullPath)) + { + return null; + } + + await File.WriteAllTextAsync(fullPath, serialized); + return fullPath; + } + + private static bool PathIsValid(string path) + { + if (string.IsNullOrWhiteSpace(path)) return false; + + try + { + var full = Path.GetFullPath(path); + + var invalidPathChars = Path.GetInvalidPathChars(); + if (full.IndexOfAny(invalidPathChars) >= 0) return false; + + var invalidFileChars = Path.GetInvalidFileNameChars(); + + foreach (var part in full.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)) + { + if (string.IsNullOrEmpty(part)) continue; + if (part is [_, ':']) continue; + + if (part.IndexOfAny(invalidFileChars) >= 0) return false; + } + + return true; + } + catch + { + return false; + } + } + + private static bool IsAllowedToWrite(string path) + { + try + { + var testFilePath = Path.Combine(path, "test.tmp"); + using (var fs = File.Create(testFilePath)) + { + } + + File.Delete(testFilePath); + return true; + } + catch + { + return false; + } + } + + private static bool IsNebulaUtilizedDirectory(string path) + { + return Storage.MafilesDirectories.Any(x => Path.GetFullPath(path).EqualsIgnoreCase(x)); + } +} \ No newline at end of file diff --git a/src/NebulaAuth/Model/MafileExport/MafileExporterStorage.cs b/src/NebulaAuth/Model/MafileExport/MafileExporterStorage.cs new file mode 100644 index 0000000..6445994 --- /dev/null +++ b/src/NebulaAuth/Model/MafileExport/MafileExporterStorage.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using Newtonsoft.Json; + +namespace NebulaAuth.Model.MafileExport; + +public static class MafileExporterStorage +{ + private const string FILE_NAME = "export_templates.json"; + public static ObservableCollection Templates { get; } = new(); + + + public static void Initialize() + { + LoadOrCreateTemplate(); + } + + public static MafileExportTemplate? GetTemplate(string name) + { + return Templates.FirstOrDefault(x => x.Name == name); + } + + public static void AddTemplate(MafileExportTemplate template) + { + Templates.Add(template); + Save(); + } + + public static void DeleteTemplate(MafileExportTemplate template) + { + if (Templates.Remove(template)) + { + Save(); + } + } + + private static void LoadOrCreateTemplate() + { + if (!File.Exists(FILE_NAME)) + { + Templates.Clear(); + foreach (var mafileExportTemplate in CreateDefaultTemplate()) + { + Templates.Add(mafileExportTemplate); + } + + Save(); + return; + } + + var json = File.ReadAllText(FILE_NAME); + try + { + var loadedTemplates = JsonConvert.DeserializeObject>(json); + if (loadedTemplates != null) + { + Templates.Clear(); + foreach (var tmpl in loadedTemplates) + { + Templates.Add(tmpl); + } + } + } + catch (Exception ex) + { + Shell.Logger.Error(ex, $"Failed to deserialize {FILE_NAME}"); + Templates.Clear(); + Save(); + } + } + + public static void Save() + { + var json = JsonConvert.SerializeObject(Templates, Formatting.Indented); + File.WriteAllText(FILE_NAME, json); + } + + private static IEnumerable CreateDefaultTemplate() + { + return + [ + new MafileExportTemplate + { + Name = "Default", + UseLoginAsMafileName = false, + IncludeSharedSecret = true, + IncludeIdentitySecret = true, + IncludeRCode = true, + IncludeSessionData = true, + IncludeOtherInfo = true, + IncludeNebulaProxy = true, + IncludeNebulaPassword = true, + IncludeNebulaGroup = true, + Path = null + }, + new MafileExportTemplate + { + Name = "Auth only", + UseLoginAsMafileName = true, + IncludeSharedSecret = true, + IncludeIdentitySecret = false, + IncludeRCode = false, + IncludeSessionData = false, + IncludeOtherInfo = false, + IncludeNebulaProxy = false, + IncludeNebulaPassword = false, + IncludeNebulaGroup = false, + Path = null + } + ]; + } +} \ No newline at end of file diff --git a/src/NebulaAuth/Model/Mafiles/MafilesStorageHelper.cs b/src/NebulaAuth/Model/Mafiles/MafilesStorageHelper.cs index bda9e96..c1dcae9 100644 --- a/src/NebulaAuth/Model/Mafiles/MafilesStorageHelper.cs +++ b/src/NebulaAuth/Model/Mafiles/MafilesStorageHelper.cs @@ -1,12 +1,13 @@ -using NebulaAuth.Model.Entities; -using System.IO; +using System.IO; +using NebulaAuth.Model.Entities; namespace NebulaAuth.Model.Mafiles; internal static class MafilesStorageHelper { /// - /// Returns or creates a new one and updates the property. is always not after this method call. + /// Returns or creates a new one and updates the property. + /// is always not after this method call. /// /// /// diff --git a/src/NebulaAuth/Model/Mafiles/Storage.cs b/src/NebulaAuth/Model/Mafiles/Storage.cs index ce64835..f11f93f 100644 --- a/src/NebulaAuth/Model/Mafiles/Storage.cs +++ b/src/NebulaAuth/Model/Mafiles/Storage.cs @@ -22,12 +22,19 @@ public static class Storage public const string DIR_MAFILES = "maFiles"; public const string DIR_REMOVED_MAFILES = "maFiles_removed"; public const string DIR_BACKUP_MAFILES = "maFiles_backup"; + + public static readonly string[] MafilesDirectories; public static string MafilesDirectory { get; } = Path.GetFullPath(DIR_MAFILES); public static string RemovedMafilesDirectory { get; } = Path.GetFullPath(DIR_REMOVED_MAFILES); public static string BackupMafilesDirectory { get; } = Path.GetFullPath(DIR_BACKUP_MAFILES); public static ObservableCollection MaFiles { get; private set; } = []; + static Storage() + { + MafilesDirectories = [MafilesDirectory, RemovedMafilesDirectory, BackupMafilesDirectory]; + } + public static async Task Initialize(int threadCount, CancellationToken token = default) { Directory.CreateDirectory(MafilesDirectory); @@ -60,6 +67,7 @@ public static class Storage }, token); MaFiles = new ObservableCollection(localList.OrderBy(m => m.AccountName)); + } /// diff --git a/src/NebulaAuth/Model/NebulaSerializer.cs b/src/NebulaAuth/Model/NebulaSerializer.cs index 0783cf9..7908887 100644 --- a/src/NebulaAuth/Model/NebulaSerializer.cs +++ b/src/NebulaAuth/Model/NebulaSerializer.cs @@ -38,7 +38,7 @@ public static class NebulaSerializer var data = Serializer.Deserialize(cont); var mobileData = data.Data; var info = data.Info; - if (info.IsExtended == false) + if (!info.IsExtended) throw new FormatException("Mafile is not extended data"); @@ -58,7 +58,7 @@ public static class NebulaSerializer private static T? GetPropertyValue(string name, Dictionary dictionary) { - if (dictionary.TryGetValue(name, out var prop) == false) return default; + if (!dictionary.TryGetValue(name, out var prop)) return default; var value = prop.Value; try { diff --git a/src/NebulaAuth/Model/ProxyStorage.cs b/src/NebulaAuth/Model/ProxyStorage.cs index 53cda1c..7f94419 100644 --- a/src/NebulaAuth/Model/ProxyStorage.cs +++ b/src/NebulaAuth/Model/ProxyStorage.cs @@ -28,7 +28,7 @@ public static class ProxyStorage static ProxyStorage() { - if (File.Exists("proxies.json") == false) + if (!File.Exists("proxies.json")) return; try { diff --git a/src/NebulaAuth/Model/SessionHandler.cs b/src/NebulaAuth/Model/SessionHandler.cs index a04ed52..7e3a0e3 100644 --- a/src/NebulaAuth/Model/SessionHandler.cs +++ b/src/NebulaAuth/Model/SessionHandler.cs @@ -45,7 +45,7 @@ public static partial class SessionHandler return await func(); } catch (SessionInvalidException ex) - when (refreshTokenExpired == false || password != null) + when (!refreshTokenExpired || password != null) { if (ex is SessionPermanentlyExpiredException) { diff --git a/src/NebulaAuth/Model/Settings.cs b/src/NebulaAuth/Model/Settings.cs index 0e8746d..e4d3963 100644 --- a/src/NebulaAuth/Model/Settings.cs +++ b/src/NebulaAuth/Model/Settings.cs @@ -25,7 +25,7 @@ public partial class Settings : ObservableObject static Settings() { - if (File.Exists("settings.json") == false) + if (!File.Exists("settings.json")) { Instance = new Settings(); Instance.PropertyChanged += SettingsOnPropertyChanged; diff --git a/src/NebulaAuth/Model/Shell.cs b/src/NebulaAuth/Model/Shell.cs index f9d598a..7a74a5d 100644 --- a/src/NebulaAuth/Model/Shell.cs +++ b/src/NebulaAuth/Model/Shell.cs @@ -1,6 +1,12 @@ using System; +using System.IO; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using NebulaAuth.Core; using NebulaAuth.Model.Exceptions; +using NebulaAuth.Model.MAAC; +using NebulaAuth.Model.MafileExport; +using NebulaAuth.Model.Mafiles; using NLog; using NLog.Extensions.Logging; using SteamLib.Core; @@ -14,8 +20,11 @@ public static class Shell public static Logger Logger { get; private set; } = null!; public static ILogger ExtensionsLogger { get; private set; } = null!; - public static void Initialize() + public static async Task Initialize() { + File.Delete("log.log"); + LocManager.Init(); + LocManager.SetApplicationLocalization(Settings.Instance.Language); Logger = LogManager.GetLogger("Logger"); var lp = new NLogLoggerProvider(); var logger = lp.CreateLogger("SteamLib"); @@ -27,13 +36,18 @@ public static class Shell try { - TimeAligner.AlignTime(); + await TimeAligner.AlignTimeAsync(); } catch (Exception ex) { throw new CantAlignTimeException("", ex); } + var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1; + await Storage.Initialize(threads); + MAACStorage.Initialize(); + MafileExporterStorage.Initialize(); + ExtensionsLogger.LogDebug("Application started"); } diff --git a/src/NebulaAuth/NLog.config b/src/NebulaAuth/NLog.config index c9bf400..259810c 100644 --- a/src/NebulaAuth/NLog.config +++ b/src/NebulaAuth/NLog.config @@ -5,8 +5,7 @@ throwExceptions="true"> - + diff --git a/src/NebulaAuth/NebulaAuth.csproj b/src/NebulaAuth/NebulaAuth.csproj index 5b58580..6237b34 100644 --- a/src/NebulaAuth/NebulaAuth.csproj +++ b/src/NebulaAuth/NebulaAuth.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/NebulaAuth/Utility/LanguageUtility.cs b/src/NebulaAuth/Utility/LanguageUtility.cs index 253b10c..884ecb7 100644 --- a/src/NebulaAuth/Utility/LanguageUtility.cs +++ b/src/NebulaAuth/Utility/LanguageUtility.cs @@ -31,8 +31,8 @@ public static class LanguageUtility "RU", "BY", "KZ", "KG", "TJ", "TM", "UZ", "AM", "AZ", "GE", "MD" ]; - return cisRegions.Any(r => userRegion.EndsWith(r, StringComparison.OrdinalIgnoreCase)) - ? LocalizationLanguage.Russian + return cisRegions.Any(r => userRegion.EndsWith(r, StringComparison.OrdinalIgnoreCase)) + ? LocalizationLanguage.Russian : LocalizationLanguage.English; } } \ No newline at end of file diff --git a/src/NebulaAuth/View/MafileExporterView.xaml b/src/NebulaAuth/View/MafileExporterView.xaml new file mode 100644 index 0000000..aafeb63 --- /dev/null +++ b/src/NebulaAuth/View/MafileExporterView.xaml @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +