mirror of
https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies.git
synced 2026-07-25 06:14:31 +00:00
1.5.6 Bug fixes and code clean-ups
- Update: Added "Copy Password" to Mafile context menu (if available) - Update: "Save Password" now pre-fills the current password in the "Login Again" dialog when encryption is enabled - Fix: Prevented overlapping confirmation cycles in AutoConfirmer - Fix: Added a Snackbar notification when a confirmation cycle is skipped due to a too frequent timer interval - Fix: Fixed a rare bug where the previous proxy was used instead of the current one during session refresh - Fix: Corrected "Session Permanently Expired" message showing on unrelated errors (e.g. network issues) - UI-Fix: Fixed visual glitch where proxy input appeared non-empty when switching to a mafile-specific proxy - Cleanup: Refactored and cleaned up codebase, improved styling and formatting via ReSharper
This commit is contained in:
@@ -5,19 +5,16 @@
|
||||
|
||||
|
||||
namespace NebulaAuth.LegacyConverter;
|
||||
|
||||
public class Manifest
|
||||
{
|
||||
[JsonProperty("encrypted")]
|
||||
public bool Encrypted { get; set; }
|
||||
[JsonProperty("encrypted")] public bool Encrypted { get; set; }
|
||||
|
||||
[JsonProperty("first_run")]
|
||||
public bool FirstRun { get; set; }
|
||||
[JsonProperty("first_run")] public bool FirstRun { get; set; }
|
||||
|
||||
[JsonProperty("entries")]
|
||||
public Entry[] Entries { get; set; }
|
||||
[JsonProperty("entries")] public Entry[] Entries { get; set; }
|
||||
|
||||
[JsonProperty("periodic_checking")]
|
||||
public bool PeriodicChecking { get; set; }
|
||||
[JsonProperty("periodic_checking")] public bool PeriodicChecking { get; set; }
|
||||
|
||||
[JsonProperty("periodic_checking_interval")]
|
||||
public long PeriodicCheckingInterval { get; set; }
|
||||
@@ -28,21 +25,16 @@ public class Manifest
|
||||
[JsonProperty("auto_confirm_market_transactions")]
|
||||
public bool AutoConfirmMarketTransactions { get; set; }
|
||||
|
||||
[JsonProperty("auto_confirm_trades")]
|
||||
public bool AutoConfirmTrades { get; set; }
|
||||
[JsonProperty("auto_confirm_trades")] public bool AutoConfirmTrades { get; set; }
|
||||
}
|
||||
|
||||
public class Entry
|
||||
{
|
||||
[JsonProperty("encryption_iv")]
|
||||
public string EncryptionIv { get; set; }
|
||||
[JsonProperty("encryption_iv")] public string EncryptionIv { get; set; }
|
||||
|
||||
[JsonProperty("encryption_salt")]
|
||||
public string EncryptionSalt { get; set; }
|
||||
[JsonProperty("encryption_salt")] public string EncryptionSalt { get; set; }
|
||||
|
||||
[JsonProperty("filename")]
|
||||
public string Filename { get; set; }
|
||||
[JsonProperty("filename")] public string Filename { get; set; }
|
||||
|
||||
[JsonProperty("steamid")]
|
||||
public ulong SteamId { get; set; }
|
||||
[JsonProperty("steamid")] public ulong SteamId { get; set; }
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using AchiesUtilities.Extensions;
|
||||
using System.Reflection;
|
||||
using AchiesUtilities.Extensions;
|
||||
using NebulaAuth.LegacyConverter;
|
||||
using Newtonsoft.Json;
|
||||
using SteamLib.Utility.MafileSerialization;
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var mafileSerializer = new MafileSerializer(new MafileSerializerSettings
|
||||
@@ -12,10 +11,10 @@ try
|
||||
DeserializationOptions =
|
||||
{
|
||||
AllowDeviceIdGeneration = true,
|
||||
AllowSessionIdGeneration = true,
|
||||
AllowSessionIdGeneration = true
|
||||
}
|
||||
});
|
||||
var currentPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
if (currentPath != null)
|
||||
Environment.CurrentDirectory = currentPath;
|
||||
const string toStoreFolder = "ConvertedMafiles";
|
||||
@@ -108,7 +107,6 @@ try
|
||||
|
||||
foreach (var path in args)
|
||||
{
|
||||
|
||||
if (Path.Exists(path) == false)
|
||||
{
|
||||
Console.WriteLine($"NOT VALID PATH: '{path}'");
|
||||
@@ -147,15 +145,12 @@ try
|
||||
{
|
||||
Console.WriteLine("-----------------------------------------");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//Local Functions
|
||||
|
||||
void Write(string maf, string name)
|
||||
{
|
||||
|
||||
var path = Path.Combine(toStoreFolder, name + "_legacy.mafile");
|
||||
File.WriteAllText(path, maf);
|
||||
}
|
||||
@@ -172,8 +167,6 @@ try
|
||||
var iv = entry.EncryptionIv;
|
||||
var salt = entry.EncryptionSalt;
|
||||
return SDAEncryptor.DecryptData(password, salt, iv, cipherText);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
namespace NebulaAuth.LegacyConverter;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
namespace NebulaAuth.LegacyConverter;
|
||||
|
||||
#pragma warning disable all
|
||||
#pragma warning disable SYSLIB0023
|
||||
@@ -13,24 +11,23 @@ using System.Security.Cryptography;
|
||||
//Resharper disable all
|
||||
//Pragma is disabled because it's legacy code from SDA
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This class provides the controls that will encrypt and decrypt the *.maFile files
|
||||
///
|
||||
/// Passwords entered will be passed into 100k rounds of PBKDF2 (RFC2898) with a cryptographically random salt.
|
||||
/// The generated key will then be passed into AES-256 (RijndalManaged) which will encrypt the data
|
||||
/// in cypher block chaining (CBC) mode, and then write both the PBKDF2 salt and encrypted data onto the disk.
|
||||
/// This class provides the controls that will encrypt and decrypt the *.maFile files
|
||||
/// Passwords entered will be passed into 100k rounds of PBKDF2 (RFC2898) with a cryptographically random salt.
|
||||
/// The generated key will then be passed into AES-256 (RijndalManaged) which will encrypt the data
|
||||
/// in cypher block chaining (CBC) mode, and then write both the PBKDF2 salt and encrypted data onto the disk.
|
||||
/// </summary>
|
||||
public static class SDAEncryptor
|
||||
{
|
||||
private const int PBKDF2_ITERATIONS = 50000; //Set to 50k to make program not unbearably slow. May increase in future.
|
||||
private const int
|
||||
PBKDF2_ITERATIONS = 50000; //Set to 50k to make program not unbearably slow. May increase in future.
|
||||
|
||||
private const int SALT_LENGTH = 8;
|
||||
private const int KEY_SIZE_BYTES = 32;
|
||||
private const int IV_LENGTH = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an 8-byte cryptographically random salt in base64 encoding
|
||||
/// Returns an 8-byte cryptographically random salt in base64 encoding
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetRandomSalt()
|
||||
@@ -40,11 +37,12 @@ public static class SDAEncryptor
|
||||
{
|
||||
rng.GetBytes(salt);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(salt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a 16-byte cryptographically random initialization vector (IV) in base64 encoding
|
||||
/// Returns a 16-byte cryptographically random initialization vector (IV) in base64 encoding
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetInitializationVector()
|
||||
@@ -54,14 +52,14 @@ public static class SDAEncryptor
|
||||
{
|
||||
rng.GetBytes(IV);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(IV);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generates an encryption key derived using a password, a random salt, and specified number of rounds of PBKDF2
|
||||
///
|
||||
/// TODO: pass in password via SecureString?
|
||||
/// Generates an encryption key derived using a password, a random salt, and specified number of rounds of PBKDF2
|
||||
/// TODO: pass in password via SecureString?
|
||||
/// </summary>
|
||||
/// <param name="password"></param>
|
||||
/// <param name="salt"></param>
|
||||
@@ -72,19 +70,22 @@ public static class SDAEncryptor
|
||||
{
|
||||
throw new ArgumentException("Password is empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(salt))
|
||||
{
|
||||
throw new ArgumentException("Salt is empty");
|
||||
}
|
||||
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, Convert.FromBase64String(salt), PBKDF2_ITERATIONS))
|
||||
|
||||
using (Rfc2898DeriveBytes pbkdf2 =
|
||||
new Rfc2898DeriveBytes(password, Convert.FromBase64String(salt), PBKDF2_ITERATIONS))
|
||||
{
|
||||
return pbkdf2.GetBytes(KEY_SIZE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to decrypt and return data given an encrypted base64 encoded string. Must use the same
|
||||
/// password, salt, IV, and ciphertext that was used during the original encryption of the data.
|
||||
/// Tries to decrypt and return data given an encrypted base64 encoded string. Must use the same
|
||||
/// password, salt, IV, and ciphertext that was used during the original encryption of the data.
|
||||
/// </summary>
|
||||
/// <param name="password"></param>
|
||||
/// <param name="passwordSalt"></param>
|
||||
@@ -97,14 +98,17 @@ public static class SDAEncryptor
|
||||
{
|
||||
throw new ArgumentException("Password is empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(passwordSalt))
|
||||
{
|
||||
throw new ArgumentException("Salt is empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(IV))
|
||||
{
|
||||
throw new ArgumentException("Initialization Vector is empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(encryptedData))
|
||||
{
|
||||
throw new ArgumentException("Encrypted data is empty");
|
||||
@@ -143,13 +147,14 @@ public static class SDAEncryptor
|
||||
plaintext = null;
|
||||
}
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a string given a password, salt, and initialization vector, then returns result in base64 encoded string.
|
||||
///
|
||||
/// To retrieve this data, you must decrypt with the same password, salt, IV, and cyphertext that was used during encryption
|
||||
/// Encrypts a string given a password, salt, and initialization vector, then returns result in base64 encoded string.
|
||||
/// To retrieve this data, you must decrypt with the same password, salt, IV, and cyphertext that was used during
|
||||
/// encryption
|
||||
/// </summary>
|
||||
/// <param name="password"></param>
|
||||
/// <param name="passwordSalt"></param>
|
||||
@@ -162,18 +167,22 @@ public static class SDAEncryptor
|
||||
{
|
||||
throw new ArgumentException("Password is empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(passwordSalt))
|
||||
{
|
||||
throw new ArgumentException("Salt is empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(IV))
|
||||
{
|
||||
throw new ArgumentException("Initialization Vector is empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(plaintext))
|
||||
{
|
||||
throw new ArgumentException("Plaintext data is empty");
|
||||
}
|
||||
|
||||
byte[] key = GetEncryptionKey(password, passwordSalt);
|
||||
byte[] ciphertext;
|
||||
|
||||
@@ -194,10 +203,12 @@ public static class SDAEncryptor
|
||||
{
|
||||
swEncypt.Write(plaintext);
|
||||
}
|
||||
|
||||
ciphertext = msEncrypt.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(ciphertext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "changelog", "changelog", "{
|
||||
changelog\1.5.3.html = changelog\1.5.3.html
|
||||
changelog\1.5.4.html = changelog\1.5.4.html
|
||||
changelog\1.5.5.html = changelog\1.5.5.html
|
||||
changelog\1.5.6.html = changelog\1.5.6.html
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
|
||||
+24
-21
@@ -11,38 +11,41 @@
|
||||
<SolidColorBrush x:Key="MaterialDesignPaper">#1E2025</SolidColorBrush>
|
||||
<FontFamily x:Key="Roboto">pack://application:,,,/Fonts/Roboto/#Roboto</FontFamily>
|
||||
<FontFamily x:Key="RobotoSymbols">pack://application:,,,/Fonts/Roboto/#Roboto Symbols</FontFamily>
|
||||
<converters:CoefficientConverter x:Key="CoefficientConverter"/>
|
||||
<converters:ReverseBooleanConverter x:Key="ReverseBooleanConverter"/>
|
||||
<converters:ProxyTextConverter x:Key="ProxyTextConverter"/>
|
||||
<converters:ProxyDataTextConverter x:Key="ProxyDataTextConverter"/>
|
||||
<converters:MultiCommandParameterConverter x:Key="MultiCommandParameterConverter"/>
|
||||
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
|
||||
<converters:AnyMafilesToVisibilityConverter x:Key="AnyMafilesToVisibilityConverter"/>
|
||||
<converters:PortableMaClientStatusToColorConverter x:Key="PortableMaClientStatusToColorConverter"/>
|
||||
<converters:CoefficientConverter x:Key="CoefficientConverter" />
|
||||
<converters:ReverseBooleanConverter x:Key="ReverseBooleanConverter" />
|
||||
<converters:ProxyTextConverter x:Key="ProxyTextConverter" />
|
||||
<converters:ProxyDataTextConverter x:Key="ProxyDataTextConverter" />
|
||||
<converters:MultiCommandParameterConverter x:Key="MultiCommandParameterConverter" />
|
||||
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter" />
|
||||
<converters:AnyMafilesToVisibilityConverter x:Key="AnyMafilesToVisibilityConverter" />
|
||||
<converters:PortableMaClientStatusToColorConverter x:Key="PortableMaClientStatusToColorConverter" />
|
||||
<!-- Background converters-->
|
||||
<background:BackgroundImageVisibleConverter x:Key="BackgroundImageVisibleConverter"/>
|
||||
<background:BackgroundSourceConverter x:Key="BackgroundSourceConverter"/>
|
||||
<background:BackgroundImageVisibleConverter x:Key="BackgroundImageVisibleConverter" />
|
||||
<background:BackgroundSourceConverter x:Key="BackgroundSourceConverter" />
|
||||
|
||||
<system:Boolean x:Key="True">True</system:Boolean>
|
||||
<system:Boolean x:Key="False">False</system:Boolean>
|
||||
|
||||
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
||||
|
||||
<materialDesign:BundledTheme BaseTheme="Dark" PrimaryColor="LightGreen" SecondaryColor="Purple" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/materialdesigntheme.snackbar.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/materialdesigntheme.Dark.xaml" />
|
||||
|
||||
|
||||
<materialDesign:BundledTheme BaseTheme="Dark" PrimaryColor="LightGreen" SecondaryColor="Purple" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/materialdesigntheme.snackbar.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/materialdesigntheme.Dark.xaml" />
|
||||
<!-- Theme-->
|
||||
<ResourceDictionary Source="Theme/Brushes.xaml"/>
|
||||
<ResourceDictionary Source="Theme/WindowStyle/WindowStyle.xaml"/>
|
||||
<ResourceDictionary Source="Theme/Brushes.xaml" />
|
||||
<ResourceDictionary Source="Theme/WindowStyle/WindowStyle.xaml" />
|
||||
|
||||
<!--Controls-->
|
||||
<ResourceDictionary Source="View/ConfirmationTemplates.xaml"/>
|
||||
<ResourceDictionary Source="View/ConfirmationTemplates.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
</Application>
|
||||
@@ -1,12 +1,11 @@
|
||||
using NebulaAuth.Core;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.Model.Exceptions;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace NebulaAuth;
|
||||
|
||||
|
||||
public partial class App
|
||||
{
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
@@ -27,9 +26,9 @@ public partial class App
|
||||
msg = LocManager.Get("CantAlignTimeError");
|
||||
}
|
||||
|
||||
MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Stop, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly);
|
||||
MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Stop, MessageBoxResult.OK,
|
||||
MessageBoxOptions.DefaultDesktopOnly);
|
||||
throw;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@ using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
@@ -8,12 +8,14 @@ namespace NebulaAuth.Converters;
|
||||
public class AnyMafilesToVisibilityConverter : IValueConverter
|
||||
{
|
||||
private static bool _everAnyMafiles;
|
||||
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (_everAnyMafiles)
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (value is 0)
|
||||
{
|
||||
return Visibility.Visible;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using NebulaAuth.Model;
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using NebulaAuth.Model;
|
||||
|
||||
namespace NebulaAuth.Converters.Background;
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ public class BackgroundSourceConverter : IValueConverter
|
||||
{
|
||||
if (value is BackgroundMode.Custom)
|
||||
{
|
||||
if(File.Exists("Background.png"))
|
||||
if (File.Exists("Background.png"))
|
||||
return new BitmapImage(new Uri(Path.GetFullPath("Background.png")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new BitmapImage(new Uri("pack://application:,,,/Theme/Background.jpg"));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Globalization;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using System;
|
||||
|
||||
namespace NebulaAuth.Converters;
|
||||
|
||||
@@ -16,6 +16,7 @@ public class ColorToBrushConverter : IValueConverter
|
||||
rv.Freeze();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return Binding.DoNothing;
|
||||
}
|
||||
|
||||
@@ -25,6 +26,7 @@ public class ColorToBrushConverter : IValueConverter
|
||||
{
|
||||
return brush.Color;
|
||||
}
|
||||
|
||||
return default(Color);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Globalization;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using System;
|
||||
|
||||
namespace NebulaAuth.Converters;
|
||||
|
||||
@@ -13,8 +13,8 @@ public class PortableMaClientStatusToColorConverter : IValueConverter
|
||||
{
|
||||
return new SolidColorBrush(Color.FromRgb(187, 224, 139));
|
||||
}
|
||||
return new SolidColorBrush(Color.FromRgb(224, 139, 139));
|
||||
|
||||
return new SolidColorBrush(Color.FromRgb(224, 139, 139));
|
||||
}
|
||||
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
|
||||
@@ -25,4 +25,4 @@ public class ReverseBooleanConverter : IValueConverter
|
||||
|
||||
throw new ArgumentException("Value must be of type bool", nameof(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Data;
|
||||
|
||||
@@ -9,12 +10,13 @@ public class ValueConverterGroup : List<IValueConverter>, IValueConverter
|
||||
{
|
||||
#region IValueConverter Members
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture)
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
|
||||
return this.Aggregate(value,
|
||||
(current, converter) => converter.Convert(current, targetType, parameter, culture));
|
||||
}
|
||||
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture)
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.View;
|
||||
using NebulaAuth.View.Dialogs;
|
||||
@@ -10,12 +11,10 @@ namespace NebulaAuth.Core;
|
||||
|
||||
public static class DialogsController
|
||||
{
|
||||
|
||||
#region CommonDialogs
|
||||
|
||||
public static async Task<bool> ShowConfirmCancelDialog(string? msg = null)
|
||||
{
|
||||
|
||||
var content = msg == null ? new ConfirmCancelDialog() : new ConfirmCancelDialog(msg);
|
||||
|
||||
var result = await DialogHost.Show(content);
|
||||
@@ -31,11 +30,13 @@ public static class DialogsController
|
||||
|
||||
#endregion
|
||||
|
||||
public static async Task<LoginAgainVM?> ShowLoginAgainDialog(string username)
|
||||
public static async Task<LoginAgainVM?> ShowLoginAgainDialog(string username, string? currentPassword = null)
|
||||
{
|
||||
var vm = new LoginAgainVM
|
||||
{
|
||||
UserName = username
|
||||
UserName = username,
|
||||
Password = currentPassword ?? string.Empty,
|
||||
SavePassword = PHandler.IsPasswordSet
|
||||
};
|
||||
var content = new LoginAgainDialog
|
||||
{
|
||||
@@ -50,10 +51,11 @@ public static class DialogsController
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task<LoginAgainOnImportVM?> ShowLoginAgainOnImportDialog(Mafile mafile, IEnumerable<MaProxy> proxies)
|
||||
public static async Task<LoginAgainOnImportVM?> ShowLoginAgainOnImportDialog(Mafile mafile,
|
||||
IEnumerable<MaProxy> proxies)
|
||||
{
|
||||
var vm = new LoginAgainOnImportVM(mafile, proxies);
|
||||
var content = new LoginAgainOnImportDialog()
|
||||
var content = new LoginAgainOnImportDialog
|
||||
{
|
||||
DataContext = vm
|
||||
};
|
||||
@@ -66,7 +68,7 @@ public static class DialogsController
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task ShowProxyManager(MaProxy? currentProxy)
|
||||
public static async Task<bool> ShowProxyManager()
|
||||
{
|
||||
var vm = new ProxyManagerVM();
|
||||
var view = new ProxyManagerView
|
||||
@@ -74,20 +76,21 @@ public static class DialogsController
|
||||
DataContext = vm
|
||||
};
|
||||
await DialogHost.Show(view);
|
||||
return vm.AnyChanges;
|
||||
}
|
||||
|
||||
public static void CloseDialog()
|
||||
{
|
||||
DialogHost.Close(null);
|
||||
}
|
||||
|
||||
public static async Task ShowLinkerDialog()
|
||||
{
|
||||
var vm = new LinkAccountVM();
|
||||
var view = new LinkerView()
|
||||
var view = new LinkerView
|
||||
{
|
||||
DataContext = vm
|
||||
};
|
||||
await DialogHost.Show(view);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using CodingSeb.Localization;
|
||||
using CodingSeb.Localization.Loaders;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using CodingSeb.Localization;
|
||||
using CodingSeb.Localization.Loaders;
|
||||
|
||||
namespace NebulaAuth.Core;
|
||||
|
||||
@@ -11,6 +11,7 @@ public static class LocManager
|
||||
{
|
||||
public const string CODE_BEHIND_PATH_PART = "CodeBehind";
|
||||
public const string COMMON_PATH_PART = "Common";
|
||||
|
||||
public static void SetApplicationLocalization(LocalizationLanguage language)
|
||||
{
|
||||
Loc.Instance.CurrentLanguage = GetLanguageCode(language);
|
||||
@@ -35,7 +36,6 @@ public static class LocManager
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
Loc.LogOutMissingTranslations = true;
|
||||
@@ -47,7 +47,8 @@ public static class LocManager
|
||||
|
||||
public static void ReloadFiles()
|
||||
{
|
||||
string exampleFileFileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "localization.loc.json");
|
||||
var exampleFileFileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
|
||||
"localization.loc.json");
|
||||
LocalizationLoader.Instance.ClearAllTranslations();
|
||||
LocalizationLoader.Instance.AddFile(exampleFileFileName);
|
||||
}
|
||||
@@ -64,14 +65,14 @@ public static class LocManager
|
||||
|
||||
public static string? GetCommon(params string[] path)
|
||||
{
|
||||
return GetConcat(COMMON_PATH_PART, path);
|
||||
return GetConcat(COMMON_PATH_PART, path);
|
||||
}
|
||||
|
||||
public static string GetCommonOrDefault(string def, params string[] path)
|
||||
{
|
||||
return GetCommon(path) ?? def;
|
||||
return GetCommon(path) ?? def;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static string? Get(params string[] path)
|
||||
{
|
||||
@@ -92,9 +93,10 @@ public static class LocManager
|
||||
{
|
||||
return Get(path) ?? def;
|
||||
}
|
||||
|
||||
private static string? GetConcat(string first, string[] path)
|
||||
{
|
||||
string[] newArray = new string[path.Length + 1];
|
||||
var newArray = new string[path.Length + 1];
|
||||
newArray[0] = first;
|
||||
Array.Copy(path, 0, newArray, 1, path.Length);
|
||||
return Get(newArray);
|
||||
|
||||
@@ -5,12 +5,11 @@ namespace NebulaAuth.Core;
|
||||
|
||||
public class SnackbarController
|
||||
{
|
||||
|
||||
public static SnackbarMessageQueue MessageQueue { get; } = new() { DiscardDuplicates = true};
|
||||
private const int MIN_SNACKBAR_TIME = 1200;
|
||||
|
||||
public static SnackbarMessageQueue MessageQueue { get; } = new() {DiscardDuplicates = true};
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="duration">Default duration is 1 second</param>
|
||||
@@ -21,13 +20,13 @@ public class SnackbarController
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="duration">Default duration is 1 second</param>
|
||||
/// <param name="actionText">Default: 'OK'</param>
|
||||
public static void SendSnackbarWithButton(string text, string actionText = "OK", Action? action = null, TimeSpan? duration = null)
|
||||
public static void SendSnackbarWithButton(string text, string actionText = "OK", Action? action = null,
|
||||
TimeSpan? duration = null)
|
||||
{
|
||||
duration ??= GetSnackbarTime(text);
|
||||
Action<object?> argAction;
|
||||
@@ -48,10 +47,9 @@ public class SnackbarController
|
||||
var duration = str.Length / 0.03;
|
||||
if (duration < MIN_SNACKBAR_TIME)
|
||||
{
|
||||
duration = MIN_SNACKBAR_TIME;
|
||||
duration = MIN_SNACKBAR_TIME;
|
||||
}
|
||||
|
||||
return TimeSpan.FromMilliseconds(duration);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
using NebulaAuth.Model;
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shell;
|
||||
using NebulaAuth.Model;
|
||||
using Color = System.Drawing.Color;
|
||||
|
||||
namespace NebulaAuth.Core;
|
||||
@@ -14,6 +16,7 @@ public static class ThemeManager
|
||||
{
|
||||
public static System.Windows.Media.Color DefaultBackgroundColor = System.Windows.Media.Color.FromRgb(30, 32, 37);
|
||||
private static readonly Window MainWindow = Application.Current.MainWindow!;
|
||||
|
||||
static ThemeManager()
|
||||
{
|
||||
Settings.Instance.PropertyChanged += SettingsOnPropertyChanged;
|
||||
@@ -44,13 +47,13 @@ public static class ThemeManager
|
||||
var diameter = 14;
|
||||
var bitmap = new Bitmap(diameter, diameter);
|
||||
var graphics = Graphics.FromImage(bitmap);
|
||||
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
var brush = new SolidBrush(Color.FromArgb(c.A, c.R, c.G, c.B));
|
||||
graphics.FillEllipse(brush, 0, 0, diameter, diameter);
|
||||
var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
|
||||
BitmapSizeOptions.FromEmptyOptions());
|
||||
|
||||
MainWindow.TaskbarItemInfo = new();
|
||||
MainWindow.TaskbarItemInfo = new TaskbarItemInfo();
|
||||
MainWindow.TaskbarItemInfo.Overlay = bitmapSource;
|
||||
}
|
||||
}
|
||||
@@ -59,7 +62,6 @@ public static class ThemeManager
|
||||
{
|
||||
var color = Settings.Instance.BackgroundColor ?? DefaultBackgroundColor;
|
||||
Application.Current.Resources["WindowBackground"] = new SolidColorBrush(color);
|
||||
|
||||
}
|
||||
|
||||
public static void InitializeTheme()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using NebulaAuth.Model;
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using NebulaAuth.Model;
|
||||
using Application = System.Windows.Application;
|
||||
|
||||
namespace NebulaAuth.Core;
|
||||
@@ -29,7 +29,7 @@ public static class TrayManager
|
||||
|
||||
var contextMenu = new ContextMenuStrip();
|
||||
|
||||
contextMenu.Items.Add("Выйти", null!, onClick: OnExitClick);
|
||||
contextMenu.Items.Add("Выйти", null!, OnExitClick);
|
||||
|
||||
_notifyIcon.ContextMenuStrip = contextMenu;
|
||||
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
using AutoUpdaterDotNET;
|
||||
using NebulaAuth.Model;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using AutoUpdaterDotNET;
|
||||
using NebulaAuth.Model;
|
||||
|
||||
namespace NebulaAuth.Core;
|
||||
|
||||
public static class UpdateManager
|
||||
{
|
||||
private const string UPDATE_URL = "https://raw.githubusercontent.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/master/NebulaAuth/update.xml";
|
||||
private const string UPDATE_URL =
|
||||
"https://raw.githubusercontent.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/master/NebulaAuth/update.xml";
|
||||
|
||||
public static void CheckForUpdates()
|
||||
{
|
||||
string jsonPath = Path.Combine(Environment.CurrentDirectory, "update-settings.json");
|
||||
var jsonPath = Path.Combine(Environment.CurrentDirectory, "update-settings.json");
|
||||
AutoUpdater.PersistenceProvider = new JsonFilePersistenceProvider(jsonPath);
|
||||
AutoUpdater.ShowSkipButton = false;
|
||||
if (Settings.Instance.AllowAutoUpdate)
|
||||
AutoUpdater.UpdateMode = Mode.ForcedDownload;
|
||||
AutoUpdater.Start(UPDATE_URL);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<CodingSebLocalization />
|
||||
<CodingSebLocalization />
|
||||
</Weavers>
|
||||
+196
-87
@@ -15,21 +15,19 @@
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Foreground="#FFF5F5F5"
|
||||
FontFamily="{md:MaterialDesignFont}"
|
||||
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance viewModel:MainVM}"
|
||||
|
||||
|
||||
>
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance viewModel:MainVM}">
|
||||
<b:Interaction.Behaviors>
|
||||
<windowStyle:WindowChromeRenderedBehavior />
|
||||
</b:Interaction.Behaviors>
|
||||
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Command="{Binding Path=PasteMafilesFromClipboardCommand}"
|
||||
Key="V"
|
||||
Modifiers="Control"/>
|
||||
<KeyBinding Command="{Binding Path=PasteMafilesFromClipboardCommand}"
|
||||
Key="V"
|
||||
Modifiers="Control" />
|
||||
</Window.InputBindings>
|
||||
<Border Name="DragNDropBorder" Panel.ZIndex="3" Opacity="1" AllowDrop="True" DragDrop.DragEnter="Rectangle_DragEnter" DragLeave="Rectangle_DragLeave" Drop="Rectangle_Drop">
|
||||
<Border Name="DragNDropBorder" Panel.ZIndex="3" Opacity="1" AllowDrop="True"
|
||||
DragDrop.DragEnter="Rectangle_DragEnter" DragLeave="Rectangle_DragLeave" Drop="Rectangle_Drop">
|
||||
<md:DialogHost DialogOpened="DialogHost_DialogOpened" DialogClosed="DialogHost_DialogClosed">
|
||||
<Grid ZIndex="0">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -37,39 +35,61 @@
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Grid.RowSpan="2" Opacity="0.6" Stretch="UniformToFill" RenderOptions.BitmapScalingMode="LowQuality" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding Settings.BackgroundMode, Converter={StaticResource BackgroundImageVisibleConverter}}" Source="{Binding Settings.BackgroundMode, Mode=OneWay, Converter={StaticResource BackgroundSourceConverter}}" />
|
||||
<Rectangle Grid.Row="0" Grid.RowSpan="2" Opacity="0.5" Fill="#FF1F1D1D" Visibility="{Binding Settings.BackgroundMode, Converter={StaticResource BackgroundImageVisibleConverter}}" />
|
||||
<Rectangle Name="DragNDropOverlay" Grid.Row="0" Grid.RowSpan="3" Panel.ZIndex="1" Fill="#242123" Opacity="0.9" Visibility="Hidden" />
|
||||
<StackPanel Name="DragNDropPanel" Grid.Row="0" ZIndex="2" w:FontScaleWindow.Scale="1" w:FontScaleWindow.ResizeFont="True" Grid.RowSpan="3" Visibility="Hidden" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<TextBlock FontWeight="Bold" Foreground="#9a65b8" Text="{Tr MainWindow.Global.DragNDropHint}"/>
|
||||
<md:PackIcon Foreground="#9a65b8" HorizontalAlignment="Center" Width="36" Height="36" Kind="FileReplaceOutline" />
|
||||
<Image Grid.RowSpan="2" Opacity="0.6" Stretch="UniformToFill"
|
||||
RenderOptions.BitmapScalingMode="LowQuality" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{Binding Settings.BackgroundMode, Converter={StaticResource BackgroundImageVisibleConverter}}"
|
||||
Source="{Binding Settings.BackgroundMode, Mode=OneWay, Converter={StaticResource BackgroundSourceConverter}}" />
|
||||
<Rectangle Grid.Row="0" Grid.RowSpan="2" Opacity="0.5" Fill="#FF1F1D1D"
|
||||
Visibility="{Binding Settings.BackgroundMode, Converter={StaticResource BackgroundImageVisibleConverter}}" />
|
||||
<Rectangle Name="DragNDropOverlay" Grid.Row="0" Grid.RowSpan="3" Panel.ZIndex="1" Fill="#242123"
|
||||
Opacity="0.9" Visibility="Hidden" />
|
||||
<StackPanel Name="DragNDropPanel" Grid.Row="0" ZIndex="2" w:FontScaleWindow.Scale="1"
|
||||
w:FontScaleWindow.ResizeFont="True" Grid.RowSpan="3" Visibility="Hidden"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<TextBlock FontWeight="Bold" Foreground="#9a65b8" Text="{Tr MainWindow.Global.DragNDropHint}" />
|
||||
<md:PackIcon Foreground="#9a65b8" HorizontalAlignment="Center" Width="36" Height="36"
|
||||
Kind="FileReplaceOutline" />
|
||||
</StackPanel>
|
||||
<ToolBarTray IsLocked="True" Grid.Row="0" >
|
||||
<ToolBarTray IsLocked="True" Grid.Row="0">
|
||||
<ToolBarTray.Background>
|
||||
<SolidColorBrush Opacity="0.6" Color="#FF1A1C25" />
|
||||
</ToolBarTray.Background>
|
||||
<ToolBar Background="#00FFFFFF" ClipToBounds="False" Style="{StaticResource MaterialDesignToolBar}" w:FontScaleWindow.Scale="0.68" w:FontScaleWindow.ResizeFont="True">
|
||||
<ToolBar Background="#00FFFFFF" ClipToBounds="False" Style="{StaticResource MaterialDesignToolBar}"
|
||||
w:FontScaleWindow.Scale="0.68" w:FontScaleWindow.ResizeFont="True">
|
||||
<Menu w:FontScaleWindow.Scale="0.68" w:FontScaleWindow.ResizeFont="True">
|
||||
<MenuItem Header="{Tr MainWindow.Menu.File.Caption}">
|
||||
<MenuItem Header="{Tr MainWindow.Menu.File.Import}" Command="{Binding AddMafileCommand}" />
|
||||
<MenuItem IsEnabled="{Binding IsMafileSelected}" Header="{Tr MainWindow.Menu.File.Remove}" Command="{Binding RemoveMafileCommand}" />
|
||||
<MenuItem Header="{Tr MainWindow.Menu.File.OpenFolder}" Command="{Binding OpenMafileFolderCommand}" />
|
||||
<MenuItem Header="{Tr MainWindow.Menu.File.Import}"
|
||||
Command="{Binding AddMafileCommand}" />
|
||||
<MenuItem IsEnabled="{Binding IsMafileSelected}"
|
||||
Header="{Tr MainWindow.Menu.File.Remove}"
|
||||
Command="{Binding RemoveMafileCommand}" />
|
||||
<MenuItem Header="{Tr MainWindow.Menu.File.OpenFolder}"
|
||||
Command="{Binding OpenMafileFolderCommand}" />
|
||||
<MenuItem IsEnabled="False" Header="⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" Height="15" />
|
||||
<MenuItem Header="{Tr MainWindow.Menu.File.Settings}" Command="{Binding OpenSettingsDialogCommand}" />
|
||||
<MenuItem Header="{Tr MainWindow.Menu.File.Settings}"
|
||||
Command="{Binding OpenSettingsDialogCommand}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Menu w:FontScaleWindow.Scale="0.68" w:FontScaleWindow.ResizeFont="True">
|
||||
<MenuItem Header="{Tr MainWindow.Menu.Account.Caption}">
|
||||
<MenuItem Header="{Tr MainWindow.Menu.Account.Link}" Command="{Binding LinkAccountCommand}" />
|
||||
<MenuItem Header="{Tr MainWindow.Menu.Account.Unlink}" Command="{Binding RemoveAuthenticatorCommand}"/>
|
||||
<MenuItem Header="{Tr MainWindow.Menu.Account.Link}"
|
||||
Command="{Binding LinkAccountCommand}" />
|
||||
<MenuItem Header="{Tr MainWindow.Menu.Account.Unlink}"
|
||||
Command="{Binding RemoveAuthenticatorCommand}" />
|
||||
<MenuItem IsEnabled="False" Header="⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" Height="15" />
|
||||
<MenuItem IsEnabled="{Binding IsMafileSelected}" Header="{Tr MainWindow.Menu.Account.RefreshSession}" Command="{Binding RefreshSessionCommand}" />
|
||||
<MenuItem IsEnabled="{Binding IsMafileSelected}" Header="{Tr MainWindow.Menu.Account.LoginAgain}" Command="{Binding LoginAgainCommand}" />
|
||||
<MenuItem IsEnabled="{Binding IsMafileSelected}"
|
||||
Header="{Tr MainWindow.Menu.Account.RefreshSession}"
|
||||
Command="{Binding RefreshSessionCommand}" />
|
||||
<MenuItem IsEnabled="{Binding IsMafileSelected}"
|
||||
Header="{Tr MainWindow.Menu.Account.LoginAgain}"
|
||||
Command="{Binding LoginAgainCommand}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Separator />
|
||||
<ComboBox ToolTip="{Tr MainWindow.AppBar.GroupToolTip}" md:HintAssist.Hint="{Tr MainWindow.AppBar.GroupsHint}"
|
||||
MinWidth="100" Margin="8,0,8,0" VerticalAlignment="Center" IsEditable="True"
|
||||
<ComboBox ToolTip="{Tr MainWindow.AppBar.GroupToolTip}"
|
||||
md:HintAssist.Hint="{Tr MainWindow.AppBar.GroupsHint}"
|
||||
MinWidth="100" Margin="8,0,8,0" VerticalAlignment="Center" IsEditable="True"
|
||||
ItemsSource="{Binding Groups}"
|
||||
SelectedValue="{Binding SelectedGroup}">
|
||||
|
||||
@@ -82,37 +102,43 @@
|
||||
</ResourceDictionary>
|
||||
</FrameworkElement.Resources>
|
||||
<UIElement.InputBindings>
|
||||
<KeyBinding Key="Enter" Command="{Binding AddGroupCommand}" CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType=ComboBox}}" />
|
||||
<KeyBinding Key="Enter" Command="{Binding AddGroupCommand}"
|
||||
CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType=ComboBox}}" />
|
||||
</UIElement.InputBindings>
|
||||
</ComboBox>
|
||||
<ComboBox ToolTip="{Tr MainWindow.AppBar.Proxy.ProxyManipulateToolTip}"
|
||||
|
||||
|
||||
MinWidth="80"
|
||||
Margin="8,0,8,0"
|
||||
Margin="8,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
md:HintAssist.Hint="{Tr MainWindow.AppBar.Proxy.ProxyHint}"
|
||||
md:ComboBoxAssist.ShowSelectedItem="False"
|
||||
SelectedItem="{Binding SelectedProxy}"
|
||||
md:ComboBoxAssist.ShowSelectedItem="False"
|
||||
SelectedValue="{Binding SelectedProxy}"
|
||||
ItemsSource="{Binding Proxies}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type entities:MaProxy}">
|
||||
<TextBlock Text="{Binding Converter={StaticResource ProxyTextConverter}, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Text="{Binding Converter={StaticResource ProxyTextConverter}, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
<FrameworkElement.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.Proxy.ProxyOpenManagerHint}" Command="{Binding OpenProxyManagerCommand}" />
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.Proxy.ProxyOpenManagerHint}"
|
||||
Command="{Binding OpenProxyManagerCommand}" />
|
||||
</ContextMenu>
|
||||
</FrameworkElement.ContextMenu>
|
||||
<UIElement.InputBindings>
|
||||
<KeyBinding Key="Delete" Command="{Binding RemoveProxyCommand}" />
|
||||
</UIElement.InputBindings>
|
||||
</ComboBox>
|
||||
<md:PackIcon Kind="StopAlert" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FFFF0000" Margin="3" ToolTipService.InitialShowDelay="300">
|
||||
<md:PackIcon Kind="StopAlert" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Foreground="#FFFF0000" Margin="3" ToolTipService.InitialShowDelay="300">
|
||||
<md:PackIcon.ToolTip>
|
||||
<TextBlock>
|
||||
<Run Text="{Tr MainWindow.AppBar.Proxy.ProxyAlert.MafileProxyInUse, IsDynamic=False}"/>
|
||||
<Run Text=" "/><Run Text="{Binding SelectedMafile.Proxy.Data, FallbackValue='', Mode=OneWay}"/>
|
||||
<Run
|
||||
Text="{Tr MainWindow.AppBar.Proxy.ProxyAlert.MafileProxyInUse, IsDynamic=False}" />
|
||||
<Run Text=" " />
|
||||
<Run Text="{Binding SelectedMafile.Proxy.Data, FallbackValue='', Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</md:PackIcon.ToolTip>
|
||||
<UIElement.Visibility>
|
||||
@@ -126,27 +152,53 @@
|
||||
</Binding>
|
||||
</UIElement.Visibility>
|
||||
</md:PackIcon>
|
||||
<md:PackIcon Kind="StopAlert" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FFFFA500" Margin="3" ToolTip="{Tr MainWindow.AppBar.Proxy.ProxyAlert.DefaultInUse}" ToolTipService.InitialShowDelay="300" Visibility="{Binding IsDefaultProxy, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<ToggleButton ToolTip="{Tr MainWindow.AppBar.MarketTimerHint}" Foreground="{DynamicResource PrimaryHueDarkForegroundBrush}" IsEnabled="{Binding IsMafileSelected}" Margin="2" Style="{StaticResource MaterialDesignFlatToggleButton}" IsChecked="{Binding MarketTimerEnabled}" >
|
||||
<md:PackIcon Kind="ShoppingCart"/>
|
||||
<md:PackIcon Kind="StopAlert" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Foreground="#FFFFA500" Margin="3"
|
||||
ToolTip="{Tr MainWindow.AppBar.Proxy.ProxyAlert.DefaultInUse}"
|
||||
ToolTipService.InitialShowDelay="300"
|
||||
Visibility="{Binding IsDefaultProxy, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<ToggleButton ToolTip="{Tr MainWindow.AppBar.MarketTimerHint}"
|
||||
Foreground="{DynamicResource PrimaryHueDarkForegroundBrush}"
|
||||
IsEnabled="{Binding IsMafileSelected}" Margin="2"
|
||||
Style="{StaticResource MaterialDesignFlatToggleButton}"
|
||||
IsChecked="{Binding MarketTimerEnabled}">
|
||||
<md:PackIcon Kind="ShoppingCart" />
|
||||
<FrameworkElement.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.TimersContextMenu.SwitchMAACGroup}" Command="{Binding Path=SwitchMAACOnGroupCommand}" CommandParameter="{StaticResource True}"/>
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.TimersContextMenu.SwitchMAACAll}" Command="{Binding Path=SwitchMAACOnAllCommand}" CommandParameter="{StaticResource True}"/>
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.TimersContextMenu.SwitchMAACGroup}"
|
||||
Command="{Binding Path=SwitchMAACOnGroupCommand}"
|
||||
CommandParameter="{StaticResource True}" />
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.TimersContextMenu.SwitchMAACAll}"
|
||||
Command="{Binding Path=SwitchMAACOnAllCommand}"
|
||||
CommandParameter="{StaticResource True}" />
|
||||
</ContextMenu>
|
||||
</FrameworkElement.ContextMenu>
|
||||
</ToggleButton>
|
||||
<ToggleButton ToolTip="{Tr MainWindow.AppBar.TradeTimerHint}" Foreground="{DynamicResource PrimaryHueDarkForegroundBrush}" IsEnabled="{Binding IsMafileSelected}" Margin="2" Style="{StaticResource MaterialDesignFlatToggleButton}" IsChecked="{Binding TradeTimerEnabled}" >
|
||||
<ToggleButton ToolTip="{Tr MainWindow.AppBar.TradeTimerHint}"
|
||||
Foreground="{DynamicResource PrimaryHueDarkForegroundBrush}"
|
||||
IsEnabled="{Binding IsMafileSelected}" Margin="2"
|
||||
Style="{StaticResource MaterialDesignFlatToggleButton}"
|
||||
IsChecked="{Binding TradeTimerEnabled}">
|
||||
<md:PackIcon Kind="AccountArrowRight" />
|
||||
<FrameworkElement.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.TimersContextMenu.SwitchMAACGroup}" Command="{Binding Path=SwitchMAACOnGroupCommand}" CommandParameter="{StaticResource False}"/>
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.TimersContextMenu.SwitchMAACAll}" Command="{Binding Path=SwitchMAACOnAllCommand}" CommandParameter="{StaticResource False}"/>
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.TimersContextMenu.SwitchMAACGroup}"
|
||||
Command="{Binding Path=SwitchMAACOnGroupCommand}"
|
||||
CommandParameter="{StaticResource False}" />
|
||||
<MenuItem Header="{Tr MainWindow.AppBar.TimersContextMenu.SwitchMAACAll}"
|
||||
Command="{Binding Path=SwitchMAACOnAllCommand}"
|
||||
CommandParameter="{StaticResource False}" />
|
||||
</ContextMenu>
|
||||
</FrameworkElement.ContextMenu>
|
||||
</ToggleButton>
|
||||
<TextBox md:HintAssist.Hint="{Tr Common.Abbreviations.Time.Seconds}" Margin="10,0,0,0" MinWidth="30" Style="{StaticResource MaterialDesignFloatingHintTextBox}" Text="{Binding TimerCheckSeconds, UpdateSourceTrigger=PropertyChanged, Delay=700}" PreviewTextInput="UIElement_OnPreviewTextInput" />
|
||||
<ToggleButton ToolTip="{Tr MainWindow.AppBar.ShowAutoConfirmAccountsHint}" HorizontalAlignment="Right" IsChecked="{Binding MaacDisplay}" Foreground="WhiteSmoke" VerticalAlignment="Center" Style="{StaticResource MaterialDesignFlatPrimaryToggleButton}" Margin="4">
|
||||
<TextBox md:HintAssist.Hint="{Tr Common.Abbreviations.Time.Seconds}" Margin="10,0,0,0"
|
||||
MinWidth="30" Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding TimerCheckSeconds, UpdateSourceTrigger=PropertyChanged, Delay=700}"
|
||||
PreviewTextInput="UIElement_OnPreviewTextInput" />
|
||||
<ToggleButton ToolTip="{Tr MainWindow.AppBar.ShowAutoConfirmAccountsHint}"
|
||||
HorizontalAlignment="Right" IsChecked="{Binding MaacDisplay}"
|
||||
Foreground="WhiteSmoke" VerticalAlignment="Center"
|
||||
Style="{StaticResource MaterialDesignFlatPrimaryToggleButton}" Margin="4">
|
||||
<md:PackIcon Kind="Accounts" />
|
||||
</ToggleButton>
|
||||
</ToolBar>
|
||||
@@ -162,74 +214,107 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ListBox w:FontScaleWindow.Scale="0.8" w:FontScaleWindow.ResizeFont="True" Grid.Row="0" Margin="10,15,0,0" ItemsSource="{Binding MaFiles}" SelectedValue="{Binding SelectedMafile}">
|
||||
<ListBox w:FontScaleWindow.Scale="0.8" w:FontScaleWindow.ResizeFont="True" Grid.Row="0"
|
||||
Margin="10,15,0,0" ItemsSource="{Binding MaFiles}"
|
||||
SelectedValue="{Binding SelectedMafile}">
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem" BasedOn="{StaticResource MaterialDesignListBoxItem}">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate >
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock HorizontalAlignment="Stretch" Text="{Binding AccountName}">
|
||||
<TextBlock HorizontalAlignment="Stretch" Text="{Binding AccountName}">
|
||||
<TextBlock.Foreground>
|
||||
<Binding Mode="OneWay" Path="LinkedClient.IsError" Converter="{StaticResource PortableMaClientStatusToColorConverter}">
|
||||
<Binding Mode="OneWay" Path="LinkedClient.IsError"
|
||||
Converter="{StaticResource PortableMaClientStatusToColorConverter}">
|
||||
<Binding.FallbackValue>
|
||||
<SolidColorBrush Color="WhiteSmoke"/>
|
||||
<SolidColorBrush Color="WhiteSmoke" />
|
||||
</Binding.FallbackValue>
|
||||
</Binding>
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<StackPanel Visibility="{Binding LinkedClient, Converter={StaticResource NullableToVisibilityConverter}}" Grid.Column="1" Orientation="Horizontal">
|
||||
<md:PackIcon Kind="ShoppingCart" Visibility="{Binding LinkedClient.AutoConfirmMarket, Converter={StaticResource BooleanToVisibilityConverter}, FallbackValue=Collapsed}"/>
|
||||
<md:PackIcon Kind="AccountArrowRight" Visibility="{Binding LinkedClient.AutoConfirmTrades, Converter={StaticResource BooleanToVisibilityConverter}, FallbackValue=Collapsed}"/>
|
||||
<StackPanel
|
||||
Visibility="{Binding LinkedClient, Converter={StaticResource NullableToVisibilityConverter}}"
|
||||
Grid.Column="1" Orientation="Horizontal">
|
||||
<md:PackIcon Kind="ShoppingCart"
|
||||
Visibility="{Binding LinkedClient.AutoConfirmMarket, Converter={StaticResource BooleanToVisibilityConverter}, FallbackValue=Collapsed}" />
|
||||
<md:PackIcon Kind="AccountArrowRight"
|
||||
Visibility="{Binding LinkedClient.AutoConfirmTrades, Converter={StaticResource BooleanToVisibilityConverter}, FallbackValue=Collapsed}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
<ListBox.InputBindings>
|
||||
<KeyBinding Key="C" Modifiers="Control" Command="{Binding DataContext.CopyLoginCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}"/>
|
||||
<KeyBinding Key="X" Modifiers="Control" Command="{Binding DataContext.CopyMafileCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}"/>
|
||||
<KeyBinding Key="C" Modifiers="Control"
|
||||
Command="{Binding DataContext.CopyLoginCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}" />
|
||||
<KeyBinding Key="X" Modifiers="Control"
|
||||
Command="{Binding DataContext.CopyMafileCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}" />
|
||||
</ListBox.InputBindings>
|
||||
<FrameworkElement.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem InputGestureText="Ctrl+C" Header="{Tr MainWindow.ContextMenus.Mafile.CopyLogin}" Command="{Binding CopyLoginCommand}" CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopySteamId}" Command="{Binding CopySteamIdCommand}" CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem InputGestureText="Ctrl+X" Header="{Tr MainWindow.ContextMenus.Mafile.CopyMafile}" Command="{Binding CopyMafileCommand}" CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.AddToGroup}" ItemsSource="{Binding Groups}">
|
||||
<MenuItem InputGestureText="Ctrl+C"
|
||||
Header="{Tr MainWindow.ContextMenus.Mafile.CopyLogin}"
|
||||
Command="{Binding CopyLoginCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopySteamId}"
|
||||
Command="{Binding CopySteamIdCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem InputGestureText="Ctrl+X"
|
||||
Header="{Tr MainWindow.ContextMenus.Mafile.CopyMafile}"
|
||||
Command="{Binding CopyMafileCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.AddToGroup}"
|
||||
ItemsSource="{Binding Groups}">
|
||||
<ItemsControl.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource MaterialDesignMenuItem}" TargetType="{x:Type MenuItem}">
|
||||
<Setter Property="Command" Value="{Binding DataContext.AddToGroupCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<Style BasedOn="{StaticResource MaterialDesignMenuItem}"
|
||||
TargetType="{x:Type MenuItem}">
|
||||
<Setter Property="Command"
|
||||
Value="{Binding DataContext.AddToGroupCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<Setter Property="CommandParameter">
|
||||
<Setter.Value>
|
||||
<MultiBinding Converter="{StaticResource MultiCommandParameterConverter}">
|
||||
<MultiBinding
|
||||
Converter="{StaticResource MultiCommandParameterConverter}">
|
||||
<Binding />
|
||||
<Binding Path="PlacementTarget.SelectedValue" RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
|
||||
<Binding Path="PlacementTarget.SelectedValue"
|
||||
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
|
||||
</MultiBinding>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ItemsControl.ItemContainerStyle>
|
||||
</MenuItem>
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.RemoveFromGroup}" Command="{Binding Path=RemoveGroupCommand}" CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.RemoveFromGroup}"
|
||||
Command="{Binding Path=RemoveGroupCommand}"
|
||||
CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopyPassword}"
|
||||
Command="{Binding Path=CopyPasswordCommand}"
|
||||
CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
|
||||
</ContextMenu>
|
||||
</FrameworkElement.ContextMenu>
|
||||
|
||||
|
||||
</ListBox>
|
||||
<Border Grid.Row="0" Margin="10" Padding="5" Visibility="{Binding MaFiles.Count, Converter={StaticResource AnyMafilesToVisibilityConverter}, Mode=OneWay}">
|
||||
<Border Grid.Row="0" Margin="10" Padding="5"
|
||||
Visibility="{Binding MaFiles.Count, Converter={StaticResource AnyMafilesToVisibilityConverter}, Mode=OneWay}">
|
||||
<Border.Background>
|
||||
<SolidColorBrush Color="DarkGray" Opacity="0.5"/>
|
||||
<SolidColorBrush Color="DarkGray" Opacity="0.5" />
|
||||
</Border.Background>
|
||||
<TextBlock TextWrapping="WrapWithOverflow" FontSize="16" Text="{Tr MainWindow.Global.StartTip}"/>
|
||||
<TextBlock TextWrapping="WrapWithOverflow" FontSize="16"
|
||||
Text="{Tr MainWindow.Global.StartTip}" />
|
||||
</Border>
|
||||
<TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}" md:TextFieldAssist.HasClearButton="True" w:FontScaleWindow.Scale="0.7" w:FontScaleWindow.ResizeFont="True" Grid.Row="1" Margin="10,5,10,10" md:HintAssist.Hint="{Tr MainWindow.LeftPart.SearchBoxHint}" Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
md:TextFieldAssist.HasClearButton="True" w:FontScaleWindow.Scale="0.7"
|
||||
w:FontScaleWindow.ResizeFont="True" Grid.Row="1" Margin="10,5,10,10"
|
||||
md:HintAssist.Hint="{Tr MainWindow.LeftPart.SearchBoxHint}"
|
||||
Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid>
|
||||
<md:Card Grid.Column="1" Margin="10,10,15,10" UniformCornerRadius="15">
|
||||
<Control.Background>
|
||||
@@ -247,7 +332,10 @@
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBox w:FontScaleWindow.Scale="1.1" w:FontScaleWindow.ResizeFont="True" IsReadOnly="True" HorizontalContentAlignment="Center" Background="#00FFFFFF" Style="{StaticResource MaterialDesignFilledTextBox}" Text="{Binding Code, FallbackValue=Code}">
|
||||
<TextBox w:FontScaleWindow.Scale="1.1" w:FontScaleWindow.ResizeFont="True"
|
||||
IsReadOnly="True" HorizontalContentAlignment="Center" Background="#00FFFFFF"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Text="{Binding Code, FallbackValue=Code}">
|
||||
<TextBox.InputBindings>
|
||||
<MouseBinding Gesture="LeftClick" Command="{Binding CopyCodeCommand}" />
|
||||
</TextBox.InputBindings>
|
||||
@@ -257,14 +345,27 @@
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ProgressBar Margin="5" Foreground="#FF9932CC" Height="15" md:TransitionAssist.DisableTransitions="True" Value="{Binding CodeProgress}" />
|
||||
<ProgressBar Margin="5" Foreground="#FF9932CC" Height="15"
|
||||
md:TransitionAssist.DisableTransitions="True"
|
||||
Value="{Binding CodeProgress}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Button IsEnabled="{Binding IsMafileSelected}" w:FontScaleWindow.Scale="0.7" w:FontScaleWindow.ResizeFont="True" Grid.Row="1" Style="{StaticResource MaterialDesignOutlinedButton}" Margin="10" Command="{Binding GetConfirmationsCommand}" Width="{Binding Path=ActualWidth, Converter={StaticResource CoefficientConverter}, ConverterParameter=0.6, RelativeSource={RelativeSource AncestorType=md:Card}}">
|
||||
<TextBlock TextWrapping="Wrap" TextTrimming="WordEllipsis" Text="{Tr MainWindow.RightPart.LoadConfirmations}"/>
|
||||
<Button IsEnabled="{Binding IsMafileSelected}" w:FontScaleWindow.Scale="0.7"
|
||||
w:FontScaleWindow.ResizeFont="True" Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}" Margin="10"
|
||||
Command="{Binding GetConfirmationsCommand}"
|
||||
Width="{Binding Path=ActualWidth, Converter={StaticResource CoefficientConverter}, ConverterParameter=0.6, RelativeSource={RelativeSource AncestorType=md:Card}}">
|
||||
<TextBlock TextWrapping="Wrap" TextTrimming="WordEllipsis"
|
||||
Text="{Tr MainWindow.RightPart.LoadConfirmations}" />
|
||||
</Button>
|
||||
<ItemsControl md:RippleAssist.IsDisabled="True" Focusable="False" Grid.Row="2" w:FontScaleWindow.Scale="0.72" w:FontScaleWindow.ResizeFont="True" Margin="10" Visibility="{Binding ConfirmationsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" ItemsSource="{Binding Confirmations}"/>
|
||||
<Button Grid.Row="3" Margin="1,0,1,3" w:FontScaleWindow.Scale="0.8" w:FontScaleWindow.ResizeFont="True" Command="{Binding ConfirmLoginCommand}" Content="{Tr MainWindow.RightPart.ConfirmLogin}"/>
|
||||
<ItemsControl md:RippleAssist.IsDisabled="True" Focusable="False" Grid.Row="2"
|
||||
w:FontScaleWindow.Scale="0.72" w:FontScaleWindow.ResizeFont="True"
|
||||
Margin="10"
|
||||
Visibility="{Binding ConfirmationsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ItemsSource="{Binding Confirmations}" />
|
||||
<Button Grid.Row="3" Margin="1,0,1,3" w:FontScaleWindow.Scale="0.8"
|
||||
w:FontScaleWindow.ResizeFont="True" Command="{Binding ConfirmLoginCommand}"
|
||||
Content="{Tr MainWindow.RightPart.ConfirmLogin}" />
|
||||
</Grid>
|
||||
</md:Card>
|
||||
</Grid>
|
||||
@@ -273,15 +374,23 @@
|
||||
<SolidColorBrush Opacity="0.5" Color="DimGray" />
|
||||
</Border.Background>
|
||||
<Grid>
|
||||
<ToolBarPanel Orientation="Horizontal" Margin="5" w:FontScaleWindow.Scale="0.73" w:FontScaleWindow.ResizeFont="True">
|
||||
<ToolBarPanel Orientation="Horizontal" Margin="5" w:FontScaleWindow.Scale="0.73"
|
||||
w:FontScaleWindow.ResizeFont="True">
|
||||
<TextBlock FontWeight="Bold" Text="{Tr MainWindow.Footer.Account}" />
|
||||
<TextBlock Text="{Binding SelectedMafile.AccountName}" />
|
||||
<TextBlock Text="|" Margin="10,0,10,0" />
|
||||
<TextBlock FontWeight="Bold" Text="{Tr MainWindow.Footer.Group}" />
|
||||
<TextBlock FontWeight="Normal" Text="{Binding SelectedMafile.Group, TargetNullValue=' ', FallbackValue=' ', Mode=OneWay}" />
|
||||
<TextBlock FontWeight="Normal"
|
||||
Text="{Binding SelectedMafile.Group, TargetNullValue=' ', FallbackValue=' ', Mode=OneWay}" />
|
||||
</ToolBarPanel>
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" w:FontScaleWindow.Scale="0.7" w:FontScaleWindow.ResizeFont="True" Margin="0,0,10,0">
|
||||
<Hyperlink NavigateUri="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies" Foreground="{DynamicResource PrimaryHueMidBrush}" RequestNavigate="Hyperlink_OnRequestNavigate">by achies</Hyperlink>
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" w:FontScaleWindow.Scale="0.7"
|
||||
w:FontScaleWindow.ResizeFont="True" Margin="0,0,10,0">
|
||||
<Hyperlink
|
||||
NavigateUri="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies"
|
||||
Foreground="{DynamicResource PrimaryHueMidBrush}"
|
||||
RequestNavigate="Hyperlink_OnRequestNavigate">
|
||||
by achies
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.View.Dialogs;
|
||||
using NebulaAuth.ViewModel;
|
||||
using NebulaAuth.ViewModel.Other;
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
@@ -12,6 +6,12 @@ using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Threading;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.View.Dialogs;
|
||||
using NebulaAuth.ViewModel;
|
||||
using NebulaAuth.ViewModel.Other;
|
||||
|
||||
namespace NebulaAuth;
|
||||
|
||||
@@ -37,7 +37,7 @@ public partial class MainWindow
|
||||
private async Task ShowSetPasswordDialog()
|
||||
{
|
||||
var vm = new SetEncryptPasswordVM();
|
||||
var dialog = new SetCryptPasswordDialog()
|
||||
var dialog = new SetCryptPasswordDialog
|
||||
{
|
||||
DataContext = vm
|
||||
};
|
||||
@@ -49,12 +49,20 @@ public partial class MainWindow
|
||||
}
|
||||
}
|
||||
|
||||
private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(e.Uri.ToString())
|
||||
{
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#region Dran'n'Drop
|
||||
|
||||
private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
|
||||
{
|
||||
if (int.TryParse(e.Text, out _) == false) e.Handled = true;
|
||||
|
||||
}
|
||||
|
||||
private void Rectangle_DragEnter(object sender, DragEventArgs e)
|
||||
@@ -72,12 +80,13 @@ public partial class MainWindow
|
||||
private async void Rectangle_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop) == false) return;
|
||||
string[] filePaths = (string[])e.Data.GetData(DataFormats.FileDrop)!;
|
||||
var filePaths = (string[]) e.Data.GetData(DataFormats.FileDrop)!;
|
||||
if (filePaths.Length == 0) return;
|
||||
if (DataContext is MainVM mainVm)
|
||||
{
|
||||
await mainVm.AddMafile(filePaths);
|
||||
}
|
||||
|
||||
DragNDropPanel.Visibility = Visibility.Hidden;
|
||||
DragNDropOverlay.Visibility = Visibility.Hidden;
|
||||
}
|
||||
@@ -98,15 +107,6 @@ public partial class MainWindow
|
||||
{
|
||||
DragNDropBorder.AllowDrop = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(e.Uri.ToString())
|
||||
{
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -6,14 +6,12 @@ public class LoginConfirmationResult
|
||||
{
|
||||
[MemberNotNullWhen(false, nameof(Error))]
|
||||
public bool Success { get; set; }
|
||||
|
||||
public string IP { get; set; } = null!;
|
||||
public string Country { get; set; } = null!;
|
||||
public LoginConfirmationError? Error { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public enum LoginConfirmationError
|
||||
{
|
||||
NoRequests,
|
||||
|
||||
@@ -5,7 +5,7 @@ using NebulaAuth.Model.Comparers;
|
||||
namespace NebulaAuth.Model.Entities;
|
||||
|
||||
public class MaProxy
|
||||
{
|
||||
{
|
||||
public int Id { get; }
|
||||
public ProxyData Data { get; }
|
||||
|
||||
|
||||
@@ -20,8 +20,7 @@ public partial class Mafile : MobileDataExtended
|
||||
set => SetProperty(ref _linkedClient, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private PortableMaClient? _linkedClient;
|
||||
[JsonIgnore] private PortableMaClient? _linkedClient;
|
||||
|
||||
|
||||
public void SetSessionData(MobileSessionData? sessionData)
|
||||
@@ -48,7 +47,9 @@ public partial class Mafile : MobileDataExtended
|
||||
SteamId = data.SteamId
|
||||
};
|
||||
}
|
||||
public static Mafile FromMobileDataExtended(MobileDataExtended data, MaProxy? proxy, string? group, string? password)
|
||||
|
||||
public static Mafile FromMobileDataExtended(MobileDataExtended data, MaProxy? proxy, string? group,
|
||||
string? password)
|
||||
{
|
||||
var result = FromMobileDataExtended(data);
|
||||
result.Proxy = proxy;
|
||||
|
||||
@@ -8,9 +8,11 @@ namespace NebulaAuth.Model.Entities;
|
||||
public class MarketMultiConfirmation : Confirmation
|
||||
{
|
||||
public ObservableCollection<MarketConfirmation> Confirmations { get; }
|
||||
public MarketMultiConfirmation(IEnumerable<MarketConfirmation> confirmations) : base(0, 0, 0, 0, ConfirmationType.Unknown, "")
|
||||
|
||||
public MarketMultiConfirmation(IEnumerable<MarketConfirmation> confirmations) : base(0, 0, 0, 0,
|
||||
ConfirmationType.Unknown, "")
|
||||
{
|
||||
Confirmations = new(confirmations);
|
||||
Confirmations = new ObservableCollection<MarketConfirmation>(confirmations);
|
||||
Time = Confirmations.FirstOrDefault()?.Time ?? default;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using SteamLib;
|
||||
|
||||
namespace NebulaAuth.Model.Exceptions;
|
||||
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
using AchiesUtilities.Extensions;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AchiesUtilities.Extensions;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NLog;
|
||||
|
||||
namespace NebulaAuth.Model.MAAC;
|
||||
|
||||
public static class MultiAccountAutoConfirmer
|
||||
{
|
||||
private const string LOC_PATH = "MAAC";
|
||||
private static readonly ReaderWriterLockSlim Lock = new();
|
||||
public static ObservableCollection<Mafile> Clients { get; }
|
||||
private static Timer Timer { get; }
|
||||
private static readonly ReaderWriterLockSlim Lock = new();
|
||||
private const string LOC_PATH = "MAAC";
|
||||
|
||||
static MultiAccountAutoConfirmer()
|
||||
{
|
||||
@@ -26,7 +27,37 @@ public static class MultiAccountAutoConfirmer
|
||||
UpdateTimer();
|
||||
}
|
||||
|
||||
private static readonly SemaphoreSlim ExecutionLock = new(1, 1);
|
||||
|
||||
// ReSharper disable once AsyncVoidMethod //Already safe
|
||||
private static async void TimerConfirm(object? state)
|
||||
{
|
||||
bool isHeld = false;
|
||||
try
|
||||
{
|
||||
isHeld = await ExecutionLock.WaitAsync(0);
|
||||
if (!isHeld)
|
||||
{
|
||||
SnackbarController.SendSnackbar(GetLocalization("TimerPreventedOverlap"));
|
||||
return;
|
||||
}
|
||||
await TimerConfirmInternal();
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Shell.Logger.Error(e, "Error in MAAC timer");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (isHeld)
|
||||
{
|
||||
ExecutionLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task TimerConfirmInternal()
|
||||
{
|
||||
var clients = Lock.ReadLock(() => Clients.ToArray());
|
||||
var enabledClients = clients.Where(x => x.LinkedClient is { IsError: false }).ToArray();
|
||||
@@ -78,23 +109,22 @@ public static class MultiAccountAutoConfirmer
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (added);
|
||||
} while (added);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static bool TryAddToConfirm(Mafile mafile)
|
||||
{
|
||||
return Lock.WriteLock(() =>
|
||||
{
|
||||
if (Clients.Contains(mafile)) return false;
|
||||
Clients.Add(mafile);
|
||||
mafile.LinkedClient = new PortableMaClient(mafile);
|
||||
return true;
|
||||
});
|
||||
{
|
||||
if (Clients.Contains(mafile)) return false;
|
||||
Clients.Add(mafile);
|
||||
mafile.LinkedClient = new PortableMaClient(mafile);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AchiesUtilities.Web.Models;
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model.Entities;
|
||||
@@ -9,30 +18,23 @@ using SteamLib.Core.Interfaces;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.SteamMobile.Confirmations;
|
||||
using SteamLib.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AchiesUtilities.Web.Models;
|
||||
|
||||
namespace NebulaAuth.Model.MAAC;
|
||||
|
||||
public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
{
|
||||
private const string LOC_PATH = "MAAC";
|
||||
public Mafile Mafile { get; }
|
||||
private HttpClient Client { get; }
|
||||
private HttpClientHandler ClientHandler { get; }
|
||||
private DynamicProxy Proxy { get; }
|
||||
|
||||
[ObservableProperty] private bool _autoConfirmTrades;
|
||||
[ObservableProperty] private bool _autoConfirmMarket;
|
||||
[ObservableProperty] private bool _isError;
|
||||
private const string LOC_PATH = "MAAC";
|
||||
|
||||
private readonly CancellationTokenSource _cts = new();
|
||||
[ObservableProperty] private bool _autoConfirmMarket;
|
||||
|
||||
[ObservableProperty] private bool _autoConfirmTrades;
|
||||
[ObservableProperty] private bool _isError;
|
||||
|
||||
public PortableMaClient(Mafile mafile)
|
||||
{
|
||||
Mafile = mafile;
|
||||
@@ -45,7 +47,7 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
Mafile.PropertyChanged += Mafile_PropertyChanged;
|
||||
}
|
||||
|
||||
private void Mafile_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
private void Mafile_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Mafile.SessionData))
|
||||
{
|
||||
@@ -84,12 +86,14 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
Shell.Logger.Warn(ex, "Timer {accountName}: Error GetConf in timer.", Mafile.AccountName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var toConfirm = new List<Confirmation>();
|
||||
if (AutoConfirmMarket)
|
||||
{
|
||||
var market = conf.Where(c => c.ConfType == ConfirmationType.MarketSellTransaction);
|
||||
toConfirm.AddRange(market);
|
||||
}
|
||||
|
||||
if (AutoConfirmTrades)
|
||||
{
|
||||
var trade = conf.Where(c => c.ConfType == ConfirmationType.Trade);
|
||||
@@ -99,7 +103,8 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
if (toConfirm.Count == 0) return 0;
|
||||
try
|
||||
{
|
||||
Shell.Logger.Debug("Timer {accountName}: Sending confirmations. Count: {count}", Mafile.AccountName, toConfirm.Count);
|
||||
Shell.Logger.Debug("Timer {accountName}: Sending confirmations. Count: {count}", Mafile.AccountName,
|
||||
toConfirm.Count);
|
||||
var success = await HandleTimerRequest(() => SendConfirmations(toConfirm));
|
||||
Shell.Logger.Debug("Timer {accountName}: Confirmation sent: {confirmResult}", Mafile.AccountName, success);
|
||||
return toConfirm.Count;
|
||||
@@ -114,12 +119,14 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
|
||||
private async Task<IEnumerable<Confirmation>> GetConfirmations()
|
||||
{
|
||||
return await SteamMobileConfirmationsApi.GetConfirmations(Client, Mafile, Mafile.SessionData!.SteamId, _cts.Token);
|
||||
return await SteamMobileConfirmationsApi.GetConfirmations(Client, Mafile, Mafile.SessionData!.SteamId,
|
||||
_cts.Token);
|
||||
}
|
||||
|
||||
private async Task<bool> SendConfirmations(IEnumerable<Confirmation> confirmations)
|
||||
{
|
||||
return await SteamMobileConfirmationsApi.SendMultipleConfirmations(Client, confirmations, Mafile.SessionData!.SteamId, Mafile, true, _cts.Token);
|
||||
return await SteamMobileConfirmationsApi.SendMultipleConfirmations(Client, confirmations,
|
||||
Mafile.SessionData!.SteamId, Mafile, true, _cts.Token);
|
||||
}
|
||||
|
||||
private async Task<T> HandleTimerRequest<T>(Func<Task<T>> func)
|
||||
@@ -131,26 +138,30 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
innerException = ex; //Ignored
|
||||
innerException = ex; //Ignored
|
||||
}
|
||||
catch (SessionInvalidException ex)
|
||||
{
|
||||
if (IgnoreTimerErrors())
|
||||
{
|
||||
Shell.Logger.Warn("Timer {accountName}: Session error while requesting in timer. Ignored due to IgnorePatchTuesdayErrors setting", Mafile.AccountName);
|
||||
Shell.Logger.Warn(
|
||||
"Timer {accountName}: Session error while requesting in timer. Ignored due to IgnorePatchTuesdayErrors setting",
|
||||
Mafile.AccountName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Shell.Logger.Warn("Timer {accountName}: Session error while requesting in timer. Timer disabled", Mafile.AccountName);
|
||||
Shell.Logger.Warn("Timer {accountName}: Session error while requesting in timer. Timer disabled",
|
||||
Mafile.AccountName);
|
||||
SetError();
|
||||
}
|
||||
|
||||
innerException = ex;
|
||||
}
|
||||
catch (Exception ex) when (ExceptionHandler.Handle(ex, prefix: GetTimerPrefix()))
|
||||
catch (Exception ex) when (ExceptionHandler.Handle(ex, GetTimerPrefix()))
|
||||
{
|
||||
innerException = ex;
|
||||
}
|
||||
|
||||
throw new ApplicationException("Swallowed", innerException);
|
||||
}
|
||||
|
||||
@@ -163,13 +174,16 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
var pstNow = TimeZoneInfo.ConvertTime(DateTime.UtcNow, pstZone);
|
||||
|
||||
var startTime = pstNow.Date.AddHours(15).AddMinutes(55); // 15:55 PST //22:55 GMT //00:55 GMT+2
|
||||
var endTime = pstNow.Date.AddHours(17).AddMinutes(15); // 17:15 PST //00:15 GMT //02:15 GMT+2
|
||||
var endTime = pstNow.Date.AddHours(17).AddMinutes(15); // 17:15 PST //00:15 GMT //02:15 GMT+2
|
||||
|
||||
return pstNow.DayOfWeek == DayOfWeek.Tuesday && pstNow >= startTime && pstNow <= endTime;
|
||||
}
|
||||
|
||||
|
||||
private HttpClientHandlerPair Chp() => new(Client, ClientHandler);
|
||||
private HttpClientHandlerPair Chp()
|
||||
{
|
||||
return new HttpClientHandlerPair(Client, ClientHandler);
|
||||
}
|
||||
|
||||
private static string GetLocalization(string key)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
using AchiesUtilities.Web.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using AchiesUtilities.Web.Models;
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using SteamLib.Api.Mobile;
|
||||
@@ -8,15 +13,9 @@ using SteamLib.Exceptions;
|
||||
using SteamLib.ProtoCore.Services;
|
||||
using SteamLib.SteamMobile.Confirmations;
|
||||
using SteamLib.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
|
||||
public static class MaClient
|
||||
{
|
||||
private static HttpClientHandler ClientHandler { get; }
|
||||
@@ -26,7 +25,6 @@ public static class MaClient
|
||||
private static DynamicProxy Proxy { get; }
|
||||
|
||||
public static ProxyData? DefaultProxy { get; set; }
|
||||
public static HttpClientHandlerPair Chp => new(Client, ClientHandler);
|
||||
|
||||
static MaClient()
|
||||
{
|
||||
@@ -52,6 +50,7 @@ public static class MaClient
|
||||
ClientHandler.CookieContainer.AddMinimalMobileCookies();
|
||||
AdmissionHelper.TransferCommunityCookies(ClientHandler.CookieContainer);
|
||||
}
|
||||
|
||||
Proxy.SetData(account.Proxy?.Data);
|
||||
}
|
||||
}
|
||||
@@ -66,7 +65,8 @@ public static class MaClient
|
||||
public static Task LoginAgain(Mafile mafile, string password, bool savePassword, ICaptchaResolver? resolver)
|
||||
{
|
||||
SetProxy(mafile);
|
||||
return SessionHandler.LoginAgain(Chp, mafile, password, savePassword);
|
||||
return SessionHandler.LoginAgain(new HttpClientHandlerPair(Client, ClientHandler), mafile, password,
|
||||
savePassword);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,27 +74,30 @@ public static class MaClient
|
||||
{
|
||||
ValidateMafile(mafile, true);
|
||||
SetProxy(mafile);
|
||||
return SessionHandler.RefreshMobileToken(Chp, mafile);
|
||||
return SessionHandler.RefreshMobileToken(new HttpClientHandlerPair(Client, ClientHandler), mafile);
|
||||
}
|
||||
|
||||
public static Task<bool> SendConfirmation(Mafile mafile, Confirmation confirmation, bool confirm)
|
||||
{
|
||||
ValidateMafile(mafile);
|
||||
SetProxy(mafile);
|
||||
return SteamMobileConfirmationsApi.SendConfirmation(Client, confirmation, mafile.SessionData!.SteamId, mafile, confirm);
|
||||
return SteamMobileConfirmationsApi.SendConfirmation(Client, confirmation, mafile.SessionData!.SteamId, mafile,
|
||||
confirm);
|
||||
}
|
||||
|
||||
public static Task<bool> SendMultipleConfirmation(Mafile mafile, IEnumerable<Confirmation> confirmations, bool confirm)
|
||||
public static Task<bool> SendMultipleConfirmation(Mafile mafile, IEnumerable<Confirmation> confirmations,
|
||||
bool confirm)
|
||||
{
|
||||
var enumerable = confirmations.ToList();
|
||||
if (enumerable.Count == 0)
|
||||
{
|
||||
return Task.FromResult(result: false);
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
ValidateMafile(mafile);
|
||||
SetProxy(mafile);
|
||||
return SteamMobileConfirmationsApi.SendMultipleConfirmations(Client, enumerable, mafile.SessionData!.SteamId, mafile, confirm);
|
||||
return SteamMobileConfirmationsApi.SendMultipleConfirmations(Client, enumerable, mafile.SessionData!.SteamId,
|
||||
mafile, confirm);
|
||||
}
|
||||
|
||||
public static Task<RemoveAuthenticator_Response> RemoveAuthenticator(Mafile mafile)
|
||||
@@ -105,6 +108,7 @@ public static class MaClient
|
||||
{
|
||||
throw new InvalidOperationException("This mafile does not have R-Code");
|
||||
}
|
||||
|
||||
var token = mafile.SessionData!.GetMobileToken()!;
|
||||
return SteamMobileApi.RemoveAuthenticator(Client, token.Value.Token, mafile.RevocationCode);
|
||||
}
|
||||
@@ -126,7 +130,6 @@ public static class MaClient
|
||||
if (access == null || access.Value.IsExpired)
|
||||
throw new SessionPermanentlyExpiredException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static async Task<LoginConfirmationResult> ConfirmLoginRequest(Mafile mafile)
|
||||
@@ -143,6 +146,7 @@ public static class MaClient
|
||||
Error = LoginConfirmationError.NoRequests
|
||||
};
|
||||
}
|
||||
|
||||
if (sessions.ClientIds.Count > 1)
|
||||
{
|
||||
return new LoginConfirmationResult
|
||||
@@ -169,4 +173,10 @@ public static class MaClient
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
|
||||
public static HttpClientHandlerPair GetHttpClientHandlerPair(Mafile mafile)
|
||||
{
|
||||
SetProxy(mafile);
|
||||
return new HttpClientHandlerPair(Client, ClientHandler);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using NebulaAuth.Model.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.Model.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SteamLib;
|
||||
using SteamLib.Utility.MafileSerialization;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using NebulaAuth.Model.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
@@ -34,13 +34,13 @@ public static class NebulaSerializer
|
||||
var info = data.Info;
|
||||
if (info.IsExtended == false)
|
||||
throw new FormatException("Mafile is not extended data");
|
||||
|
||||
|
||||
|
||||
var props = info.UnusedProperties ?? new Dictionary<string, JProperty>();
|
||||
var proxy = GetPropertyValue<MaProxy>("Proxy", props);
|
||||
var group = GetPropertyValue<string>("Group", props);
|
||||
var password = GetPropertyValue<string>("Password", props);
|
||||
var mafile = Mafile.FromMobileDataExtended((MobileDataExtended)mobileData, proxy, group, password);
|
||||
var mafile = Mafile.FromMobileDataExtended((MobileDataExtended) mobileData, proxy, group, password);
|
||||
|
||||
|
||||
if (!info.SteamIdValid)
|
||||
@@ -82,9 +82,7 @@ public static class NebulaSerializer
|
||||
{
|
||||
return MafileSerializer.SerializeLegacy(data, Formatting.Indented, properties);
|
||||
}
|
||||
else
|
||||
{
|
||||
return MafileSerializer.Serialize(data);
|
||||
}
|
||||
|
||||
return MafileSerializer.Serialize(data);
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,14 @@ namespace NebulaAuth.Model;
|
||||
|
||||
public static class PHandler
|
||||
{
|
||||
public static bool IsPasswordSet => _k.Length > 0;
|
||||
private static byte[] _k = [];
|
||||
public static bool IsPasswordSet => _k.Length > 0;
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="password"></param>
|
||||
/// <returns><see langword="true"/> if password was set and not empty. Otherwise - <see langword="false"/></returns>
|
||||
/// <returns><see langword="true" /> if password was set and not empty. Otherwise - <see langword="false" /></returns>
|
||||
public static bool SetPassword(string? password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
@@ -80,5 +79,16 @@ public static class PHandler
|
||||
return decryptedText;
|
||||
}
|
||||
|
||||
|
||||
public static string? DecryptPassword(string? encryptedPassword)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(encryptedPassword)) return null;
|
||||
try
|
||||
{
|
||||
return Decrypt(encryptedPassword);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AchiesUtilities.Collections;
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using System.Linq;
|
||||
using AchiesUtilities.Web.Proxy.Parsing;
|
||||
using NebulaAuth.Core;
|
||||
using Newtonsoft.Json;
|
||||
@@ -12,14 +12,13 @@ namespace NebulaAuth.Model;
|
||||
|
||||
public static class ProxyStorage
|
||||
{
|
||||
|
||||
public const string FORMAT = ADDRESS_FORMAT + ":{USER}:{PASS}";
|
||||
public const string ADDRESS_FORMAT = "{IP}:{PORT}";
|
||||
|
||||
public static readonly ProxyParser DefaultScheme = new(
|
||||
ProxyDefaultFormats.UniversalColon, false, ProxyProtocol.HTTP,
|
||||
ProxyPatternProtocol.All,
|
||||
ProxyPatternHostFormat.Domain | ProxyPatternHostFormat.IPv4,
|
||||
ProxyPatternHostFormat.Domain | ProxyPatternHostFormat.IPv4,
|
||||
PatternRequirement.Optional,
|
||||
PatternRequirement.Optional);
|
||||
|
||||
@@ -29,7 +28,6 @@ public static class ProxyStorage
|
||||
|
||||
static ProxyStorage()
|
||||
{
|
||||
|
||||
if (File.Exists("proxies.json") == false)
|
||||
return;
|
||||
try
|
||||
@@ -145,7 +143,7 @@ public static class ProxyStorage
|
||||
|
||||
private class ProxiesSchema
|
||||
{
|
||||
public ObservableDictionary<int, ProxyData> ProxiesData = [];
|
||||
public int? DefaultProxy;
|
||||
public ObservableDictionary<int, ProxyData> ProxiesData = [];
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,24 @@
|
||||
using AchiesUtilities.Web.Models;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using SteamLib.Exceptions;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AchiesUtilities.Web.Models;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.View.Dialogs;
|
||||
using SteamLib.Exceptions;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
public static partial class SessionHandler
|
||||
{
|
||||
|
||||
private static readonly SemaphoreSlim Semaphore = new(1, 1);
|
||||
|
||||
public static async Task<T> Handle<T>(Func<Task<T>> func, Mafile mafile,
|
||||
HttpClientHandlerPair? chp = null, string? snackbarPrefix = null)
|
||||
{
|
||||
chp ??= MaClient.Chp;
|
||||
chp ??= MaClient.GetHttpClientHandlerPair(mafile);
|
||||
await Semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
@@ -50,12 +49,14 @@ public static partial class SessionHandler
|
||||
{
|
||||
if (ex is SessionPermanentlyExpiredException)
|
||||
{
|
||||
Shell.Logger.Debug(ex, "RefreshToken on mafile {name} {steamid} is expired", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
Shell.Logger.Debug(ex, "RefreshToken on mafile {name} {steamid} is expired", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
refreshTokenExpired = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Shell.Logger.Debug(ex, "MobileToken on mafile {name} {steamid} is expired", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
Shell.Logger.Debug(ex, "MobileToken on mafile {name} {steamid} is expired", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,21 +68,25 @@ public static partial class SessionHandler
|
||||
var refreshed = await RefreshInternal(chp, mafile);
|
||||
if (refreshed)
|
||||
{
|
||||
SnackbarController.SendSnackbar(snackbarPrefix + LocManager.GetCodeBehindOrDefault("SessionWasRefreshedAutomatically", "SessionHandler", "SessionWasRefreshedAutomatically"));
|
||||
SnackbarController.SendSnackbar(snackbarPrefix +
|
||||
LocManager.GetCodeBehindOrDefault("SessionWasRefreshedAutomatically",
|
||||
"SessionHandler", "SessionWasRefreshedAutomatically"));
|
||||
try
|
||||
{
|
||||
return await func();
|
||||
}
|
||||
catch (SessionInvalidException ex)
|
||||
{
|
||||
Shell.Logger.Info(ex, "MobileToken on {name} {steamid} was refreshed but after it, error occured", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
Shell.Logger.Info(ex, "MobileToken on {name} {steamid} was refreshed but after it, error occured",
|
||||
mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
if (password == null)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shell.Logger.Debug("Session on mafile {name} {steamid} is invalid/expired", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
Shell.Logger.Debug("Session on mafile {name} {steamid} is invalid/expired", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
|
||||
//State: mobileToken invalid/expired, refreshToken invalid/expired
|
||||
if (password != null)
|
||||
@@ -89,7 +94,8 @@ public static partial class SessionHandler
|
||||
var logged = await LoginAgainInternal(chp, mafile, password, true);
|
||||
if (logged)
|
||||
{
|
||||
Shell.Logger.Debug("Mafile {name} {steamid} was succesfully auto-relogined", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
Shell.Logger.Debug("Mafile {name} {steamid} was succesfully auto-relogined", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
return await func();
|
||||
}
|
||||
}
|
||||
@@ -99,7 +105,6 @@ public static partial class SessionHandler
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool MobileTokenExpired(Mafile mafile)
|
||||
{
|
||||
var mobileToken = mafile.SessionData?.GetMobileToken();
|
||||
@@ -108,7 +113,8 @@ public static partial class SessionHandler
|
||||
|
||||
private static bool RefreshTokenExpired(Mafile mafile)
|
||||
{
|
||||
return mafile.SessionData?.RefreshToken.IsExpired != false;
|
||||
var refreshToken = mafile.SessionData?.RefreshToken;
|
||||
return refreshToken == null || refreshToken.Value.IsExpired;
|
||||
}
|
||||
|
||||
private static string? GetPassword(Mafile mafile)
|
||||
@@ -136,9 +142,10 @@ public static partial class SessionHandler
|
||||
await RefreshMobileToken(chp, mafile);
|
||||
return true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (SessionInvalidException ex)
|
||||
{
|
||||
Shell.Logger.Debug(ex, "Failed to refresh session on mafile {name} {steamid}", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
Shell.Logger.Debug(ex, "Failed to refresh session on mafile {name} {steamid}", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -149,11 +156,10 @@ public static partial class SessionHandler
|
||||
var t = Task.Run(OnLoginStarted);
|
||||
try
|
||||
{
|
||||
|
||||
await LoginAgain(chp, mafile, password, savePassword);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception ex) //TODO: this will catch any error, even Proxy/Http/Socket errors.
|
||||
{
|
||||
Shell.Logger.Debug(ex, "Failed to relogin mafile {name} {steamid}", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
@@ -169,10 +175,7 @@ public static partial class SessionHandler
|
||||
private static async Task OnLoginStarted()
|
||||
{
|
||||
if (DialogHost.IsDialogOpen(null)) return;
|
||||
await Application.Current.Dispatcher.BeginInvoke(async () =>
|
||||
{
|
||||
await DialogHost.Show(new WaitLoginDialog());
|
||||
});
|
||||
await Application.Current.Dispatcher.BeginInvoke(async () => { await DialogHost.Show(new WaitLoginDialog()); });
|
||||
}
|
||||
|
||||
private static void OnLoginCompleted()
|
||||
@@ -180,7 +183,7 @@ public static partial class SessionHandler
|
||||
var currentSession = DialogHost.GetDialogSession(null);
|
||||
Application.Current.Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
if (currentSession is { Content: WaitLoginDialog, IsEnded: false })
|
||||
if (currentSession is {Content: WaitLoginDialog, IsEnded: false})
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -193,6 +196,4 @@ public static partial class SessionHandler
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using AchiesUtilities.Web.Models;
|
||||
using System.Threading.Tasks;
|
||||
using AchiesUtilities.Web.Models;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using SteamLib.Account;
|
||||
using SteamLib.Api.Mobile;
|
||||
@@ -6,7 +7,6 @@ using SteamLib.Authentication;
|
||||
using SteamLib.Authentication.LoginV2;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.SteamMobile;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
@@ -14,11 +14,13 @@ public partial class SessionHandler //API
|
||||
{
|
||||
public static async Task RefreshMobileToken(HttpClientHandlerPair chp, Mafile mafile)
|
||||
{
|
||||
if (mafile.SessionData is not { RefreshToken.IsExpired: false })
|
||||
if (mafile.SessionData is not {RefreshToken.IsExpired: false})
|
||||
throw new SessionPermanentlyExpiredException(SessionInvalidException.SESSION_NULL_MSG);
|
||||
|
||||
var mobileToken = await SteamMobileApi.RefreshJwt(chp.Client, mafile.SessionData.RefreshToken.Token, mafile.SessionData.SteamId);
|
||||
Shell.Logger.Info("MobileToken on {name} {steamid} successfully refreshed", mafile.AccountName, mafile.SessionData.SteamId);
|
||||
var mobileToken = await SteamMobileApi.RefreshJwt(chp.Client, mafile.SessionData.RefreshToken.Token,
|
||||
mafile.SessionData.SteamId);
|
||||
Shell.Logger.Info("MobileToken on {name} {steamid} successfully refreshed", mafile.AccountName,
|
||||
mafile.SessionData.SteamId);
|
||||
|
||||
var newToken = SteamTokenHelper.Parse(mobileToken);
|
||||
mafile.SessionData.SetMobileToken(newToken);
|
||||
@@ -38,7 +40,7 @@ public partial class SessionHandler //API
|
||||
Logger = Shell.ExtensionsLogger,
|
||||
SteamGuardProvider = sgGenerator,
|
||||
DeviceDetails = LoginV2ExecutorOptions.GetMobileDefaultDevice(),
|
||||
WebsiteId = "Mobile",
|
||||
WebsiteId = "Mobile"
|
||||
};
|
||||
chp.Handler.CookieContainer.ClearMobileSessionCookies();
|
||||
var result = await LoginV2Executor.DoLogin(options, mafile.AccountName, password);
|
||||
@@ -47,7 +49,7 @@ public partial class SessionHandler //API
|
||||
|
||||
//Triggers PropertyChanged event for PortableMaClient handling session updated from MaClient
|
||||
//RETHINK: it makes double operation when session handled from PortableMaClient (more often scenario) which is unwanted behaviour
|
||||
mafile.SetSessionData((MobileSessionData)result);
|
||||
mafile.SetSessionData((MobileSessionData) result);
|
||||
if (PHandler.IsPasswordSet)
|
||||
mafile.Password = savePassword ? PHandler.Encrypt(password) : null;
|
||||
Storage.UpdateMafile(mafile);
|
||||
|
||||
@@ -1,36 +1,22 @@
|
||||
using NebulaAuth.Core;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using NebulaAuth.Core;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
public partial class Settings : ObservableObject
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[ObservableProperty] private BackgroundMode _backgroundMode = BackgroundMode.Default;
|
||||
[ObservableProperty] private bool _hideToTray;
|
||||
[ObservableProperty] private int _timerSeconds = 60;
|
||||
[ObservableProperty] private Color? _backgroundColor;
|
||||
[ObservableProperty] private Color? _iconColor;
|
||||
[ObservableProperty] private bool _isPasswordSet;
|
||||
[ObservableProperty] private LocalizationLanguage _language = LocalizationLanguage.English;
|
||||
[ObservableProperty] private bool _legacyMode = true;
|
||||
[ObservableProperty] private bool _allowAutoUpdate;
|
||||
[ObservableProperty] private bool _useAccountNameAsMafileName;
|
||||
[ObservableProperty] private bool _ignorePatchTuesdayErrors;
|
||||
#endregion
|
||||
|
||||
public static Settings Instance { get; }
|
||||
|
||||
static Settings()
|
||||
{
|
||||
if (File.Exists("settings.json") == false)
|
||||
{
|
||||
Instance = new();
|
||||
Instance = new Settings();
|
||||
Instance.PropertyChanged += SettingsOnPropertyChanged;
|
||||
return;
|
||||
}
|
||||
@@ -45,8 +31,9 @@ public partial class Settings : ObservableObject
|
||||
{
|
||||
SnackbarController.SendSnackbar("Ошибка при загрузке настроек. Настройки были сброшены");
|
||||
SnackbarController.SendSnackbar(ex.Message);
|
||||
Instance = new();
|
||||
Instance = new Settings();
|
||||
}
|
||||
|
||||
Instance.PropertyChanged += SettingsOnPropertyChanged;
|
||||
}
|
||||
|
||||
@@ -61,10 +48,26 @@ public partial class Settings : ObservableObject
|
||||
File.WriteAllText("settings.json", json);
|
||||
}
|
||||
|
||||
}
|
||||
#region Properties
|
||||
|
||||
[ObservableProperty] private BackgroundMode _backgroundMode = BackgroundMode.Default;
|
||||
[ObservableProperty] private bool _hideToTray;
|
||||
[ObservableProperty] private int _timerSeconds = 60;
|
||||
[ObservableProperty] private Color? _backgroundColor;
|
||||
[ObservableProperty] private Color? _iconColor;
|
||||
[ObservableProperty] private bool _isPasswordSet;
|
||||
[ObservableProperty] private LocalizationLanguage _language = LocalizationLanguage.English;
|
||||
[ObservableProperty] private bool _legacyMode = true;
|
||||
[ObservableProperty] private bool _allowAutoUpdate;
|
||||
[ObservableProperty] private bool _useAccountNameAsMafileName;
|
||||
[ObservableProperty] private bool _ignorePatchTuesdayErrors;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum BackgroundMode
|
||||
{
|
||||
Default, Custom, Color
|
||||
Default,
|
||||
Custom,
|
||||
Color
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using NebulaAuth.Model.Exceptions;
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NebulaAuth.Model.Exceptions;
|
||||
using NLog;
|
||||
using NLog.Extensions.Logging;
|
||||
using SteamLib.Core;
|
||||
using SteamLib.SteamMobile;
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
@@ -12,15 +13,15 @@ public static class Shell
|
||||
{
|
||||
public static Logger Logger { get; } = LogManager.GetLogger("Logger");
|
||||
public static ILogger ExtensionsLogger { get; private set; } = null!;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
|
||||
var lp = new NLog.Extensions.Logging.NLogLoggerProvider();
|
||||
var lp = new NLogLoggerProvider();
|
||||
var logger = lp.CreateLogger("SteamLib");
|
||||
SteamLibErrorMonitor.MonitorLogger = logger;
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
|
||||
|
||||
var loggerFactory = new NLog.Extensions.Logging.NLogLoggerFactory();
|
||||
var loggerFactory = new NLogLoggerFactory();
|
||||
ExtensionsLogger = loggerFactory.CreateLogger("Logger");
|
||||
|
||||
try
|
||||
@@ -31,11 +32,12 @@ public static class Shell
|
||||
{
|
||||
throw new CantAlignTimeException("", ex);
|
||||
}
|
||||
|
||||
ExtensionsLogger.LogDebug("Application started");
|
||||
}
|
||||
|
||||
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.Fatal((Exception)e.ExceptionObject);
|
||||
Logger.Fatal((Exception) e.ExceptionObject);
|
||||
}
|
||||
}
|
||||
+28
-20
@@ -1,16 +1,14 @@
|
||||
using NebulaAuth.Model.Entities;
|
||||
using SteamLib.Core.Models;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.SteamMobile;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AchiesUtilities.Extensions;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.Model.Exceptions;
|
||||
using NLog.Targets;
|
||||
using SteamLib;
|
||||
using SteamLib.Core.Models;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.SteamMobile;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
@@ -20,12 +18,13 @@ public static class Storage
|
||||
public const string MAFILE_F = "maFiles";
|
||||
public const string REMOVED_F = "maFiles_removed";
|
||||
|
||||
public static readonly int DuplicateFound;
|
||||
|
||||
public static string MafileFolder { get; } = Path.GetFullPath(MAFILE_F);
|
||||
public static string RemovedMafileFolder { get; } = Path.GetFullPath(REMOVED_F);
|
||||
|
||||
public static ObservableCollection<Mafile> MaFiles { get; } = new();
|
||||
|
||||
public static readonly int DuplicateFound;
|
||||
static Storage()
|
||||
{
|
||||
if (Directory.Exists(MafileFolder) == false)
|
||||
@@ -55,6 +54,7 @@ public static class Storage
|
||||
Shell.Logger.Error("Duplicate mafile {file}", Path.GetFileName(file));
|
||||
continue;
|
||||
}
|
||||
|
||||
hashNames.Add(data.AccountName);
|
||||
if (data.SessionData != null) hashIds.Add(data.SteamId);
|
||||
MaFiles.Add(data);
|
||||
@@ -69,7 +69,6 @@ public static class Storage
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="overwrite"></param>
|
||||
@@ -84,11 +83,12 @@ public static class Storage
|
||||
data = ReadMafile(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
when(ex is not MafileNeedReloginException)
|
||||
when (ex is not MafileNeedReloginException)
|
||||
{
|
||||
Shell.Logger.Warn(ex, "Can't load mafile");
|
||||
throw new FormatException("File data is not valid", ex);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(data.AccountName))
|
||||
throw new FormatException("File data is not valid. Missing AccountName");
|
||||
|
||||
@@ -153,6 +153,7 @@ public static class Storage
|
||||
i++;
|
||||
copyPathCompleted = copyPath + $" ({i})";
|
||||
}
|
||||
|
||||
File.Copy(path, copyPathCompleted, false);
|
||||
File.Delete(path);
|
||||
MaFiles.Remove(data);
|
||||
@@ -167,8 +168,15 @@ public static class Storage
|
||||
return Path.Combine(MafileFolder, fileName);
|
||||
}
|
||||
|
||||
private static string CreateFileNameWithAccountName(string accountName) => accountName + ".mafile";
|
||||
private static string CreateFileNameWithSteamId(SteamId steamId) => steamId.Steam64.Id + ".mafile";
|
||||
private static string CreateFileNameWithAccountName(string accountName)
|
||||
{
|
||||
return accountName + ".mafile";
|
||||
}
|
||||
|
||||
private static string CreateFileNameWithSteamId(SteamId steamId)
|
||||
{
|
||||
return steamId.Steam64.Id + ".mafile";
|
||||
}
|
||||
|
||||
public static string? TryFindMafilePath(Mafile data)
|
||||
{
|
||||
@@ -186,10 +194,12 @@ public static class Storage
|
||||
{
|
||||
return Settings.Instance.UseAccountNameAsMafileName ? pathFileName : pathSteamId;
|
||||
}
|
||||
|
||||
if (steamIdExist ^ accountNameExist)
|
||||
{
|
||||
return steamIdExist ? pathSteamId : pathFileName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -197,9 +207,10 @@ public static class Storage
|
||||
//TODO: Refactor
|
||||
internal class MafileNameComparer : IComparer<string>
|
||||
{
|
||||
public bool MafileNameMode { get; }
|
||||
private const string MAF_64_START = "765";
|
||||
private static readonly IComparer<string> DefaultComparer = Comparer<string>.Default;
|
||||
public bool MafileNameMode { get; }
|
||||
|
||||
public MafileNameComparer(bool mafileNameMode)
|
||||
{
|
||||
MafileNameMode = mafileNameMode;
|
||||
@@ -213,8 +224,8 @@ internal class MafileNameComparer : IComparer<string>
|
||||
if (y == null) return 1;
|
||||
|
||||
|
||||
bool xisSteamId = Path.GetFileName(x).StartsWith(MAF_64_START);
|
||||
bool yisSteamId = Path.GetFileName(y).StartsWith(MAF_64_START);
|
||||
var xisSteamId = Path.GetFileName(x).StartsWith(MAF_64_START);
|
||||
var yisSteamId = Path.GetFileName(y).StartsWith(MAF_64_START);
|
||||
|
||||
if (xisSteamId ^ yisSteamId)
|
||||
{
|
||||
@@ -222,13 +233,10 @@ internal class MafileNameComparer : IComparer<string>
|
||||
{
|
||||
return xisSteamId ? 1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return yisSteamId ? 1 : -1;
|
||||
}
|
||||
|
||||
return yisSteamId ? 1 : -1;
|
||||
}
|
||||
|
||||
return DefaultComparer.Compare(x, y);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
throwExceptions="true">
|
||||
|
||||
<targets>
|
||||
<target xsi:type="File" name="File" fileName="log.log"
|
||||
deleteOldFileOnStartup="true"
|
||||
>
|
||||
deleteOldFileOnStartup="true">
|
||||
<layout xsi:type='CompoundLayout'>
|
||||
<layout xsi:type="JsonLayout" includeEventProperties="true" IncludeScopeProperties="true">
|
||||
<attribute name="time" layout="${longdate}" />
|
||||
<attribute name="level" layout="${level:upperCase=true}"/>
|
||||
<attribute name="level" layout="${level:upperCase=true}" />
|
||||
<attribute name="message" layout="${message}" />
|
||||
<attribute name="exception" layout="${exception:format=@}" encode="false"/>
|
||||
<attribute name="exception" layout="${exception:format=@}" encode="false" />
|
||||
</layout>
|
||||
<layout type='SimpleLayout' text="," />
|
||||
</layout>
|
||||
|
||||
|
||||
</target>
|
||||
</targets>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<SatelliteResourceLanguages>en;ru;ua</SatelliteResourceLanguages>
|
||||
<ApplicationIcon>Theme\lock.ico</ApplicationIcon>
|
||||
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
|
||||
<AssemblyVersion>1.5.5</AssemblyVersion>
|
||||
<AssemblyVersion>1.5.6</AssemblyVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -6,51 +6,15 @@ namespace NebulaAuth.Theme;
|
||||
|
||||
public class FontScaleWindow : Window
|
||||
{
|
||||
|
||||
private readonly Func<double, double, double> _diagonal = (h, w) => Math.Sqrt(h * h + w * w);
|
||||
private double _currentDiagonal;
|
||||
private double _defaultDiagonal = 1;
|
||||
private readonly HashSet<FrameworkElement> _cachedObjects = new();
|
||||
private bool _loaded;
|
||||
|
||||
public FontScaleWindow()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var w = (FontScaleWindow)sender;
|
||||
w._defaultDiagonal = _diagonal(MinHeight, MinWidth);
|
||||
w.SizeChanged += OnSizeChanged;
|
||||
w.Loaded -= OnLoaded;
|
||||
_loaded = true;
|
||||
w.Width += 1;
|
||||
w.Width -= 1;
|
||||
}
|
||||
|
||||
|
||||
//<-------------------Window-------------------->
|
||||
public double DefaultFontSize
|
||||
{
|
||||
get => (double)GetValue(DefaultFontSizeProperty);
|
||||
set => SetValue(DefaultFontSizeProperty, value);
|
||||
}
|
||||
|
||||
public double ScaleCoefficient
|
||||
{
|
||||
get => (double)GetValue(ScaleCoefficientProperty);
|
||||
set => SetValue(ScaleCoefficientProperty, value);
|
||||
}
|
||||
|
||||
// Using a DependencyProperty as the backing store for ScaleCoefficient. This enables animation, styling, binding, etc...
|
||||
public static readonly DependencyProperty ScaleCoefficientProperty =
|
||||
DependencyProperty.Register("ScaleCoefficient", typeof(double), typeof(FontScaleWindow), new PropertyMetadata(1d));
|
||||
DependencyProperty.Register("ScaleCoefficient", typeof(double), typeof(FontScaleWindow),
|
||||
new PropertyMetadata(1d));
|
||||
|
||||
|
||||
public static readonly DependencyProperty DefaultFontSizeProperty =
|
||||
DependencyProperty.Register("DefaultFontSize", typeof(double), typeof(FontScaleWindow), new PropertyMetadata(20d));
|
||||
DependencyProperty.Register("DefaultFontSize", typeof(double), typeof(FontScaleWindow),
|
||||
new PropertyMetadata(20d));
|
||||
|
||||
|
||||
private static readonly DependencyPropertyKey ParentScaleWindowKey
|
||||
@@ -63,19 +27,74 @@ public class FontScaleWindow : Window
|
||||
public static readonly DependencyProperty ParentScaleWindowProperty
|
||||
= ParentScaleWindowKey.DependencyProperty;
|
||||
|
||||
private static FontScaleWindow? GetParentScaleWindow(DependencyObject element) =>
|
||||
(FontScaleWindow)element.GetValue(ParentScaleWindowProperty);
|
||||
private static void SetParentScaleWindow(DependencyObject element, FontScaleWindow value) =>
|
||||
element.SetValue(ParentScaleWindowKey, value);
|
||||
|
||||
|
||||
public static readonly DependencyProperty ResizeFontProperty = DependencyProperty.RegisterAttached(
|
||||
"ResizeFont", typeof(bool), typeof(FontScaleWindow), new FrameworkPropertyMetadata(false, ResizeCallBack));
|
||||
//<-------------------Window-------------------->
|
||||
|
||||
|
||||
//<-------------------Scaling-------------------->
|
||||
|
||||
public static readonly DependencyProperty ScaleProperty = DependencyProperty.RegisterAttached(
|
||||
"Scale", typeof(double), typeof(FontScaleWindow),
|
||||
new FrameworkPropertyMetadata(0.9999d, FrameworkPropertyMetadataOptions.Inherits, ScalePropertyCallback));
|
||||
|
||||
|
||||
//<-------------------Window-------------------->
|
||||
public double DefaultFontSize
|
||||
{
|
||||
get => (double) GetValue(DefaultFontSizeProperty);
|
||||
set => SetValue(DefaultFontSizeProperty, value);
|
||||
}
|
||||
|
||||
public double ScaleCoefficient
|
||||
{
|
||||
get => (double) GetValue(ScaleCoefficientProperty);
|
||||
set => SetValue(ScaleCoefficientProperty, value);
|
||||
}
|
||||
|
||||
private readonly HashSet<FrameworkElement> _cachedObjects = new();
|
||||
|
||||
private readonly Func<double, double, double> _diagonal = (h, w) => Math.Sqrt(h * h + w * w);
|
||||
private double _currentDiagonal;
|
||||
private double _defaultDiagonal = 1;
|
||||
private bool _loaded;
|
||||
|
||||
public FontScaleWindow()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var w = (FontScaleWindow) sender;
|
||||
w._defaultDiagonal = _diagonal(MinHeight, MinWidth);
|
||||
w.SizeChanged += OnSizeChanged;
|
||||
w.Loaded -= OnLoaded;
|
||||
_loaded = true;
|
||||
w.Width += 1;
|
||||
w.Width -= 1;
|
||||
}
|
||||
|
||||
private static FontScaleWindow? GetParentScaleWindow(DependencyObject element)
|
||||
{
|
||||
return (FontScaleWindow) element.GetValue(ParentScaleWindowProperty);
|
||||
}
|
||||
|
||||
private static void SetParentScaleWindow(DependencyObject element, FontScaleWindow value)
|
||||
{
|
||||
element.SetValue(ParentScaleWindowKey, value);
|
||||
}
|
||||
|
||||
public static void SetResizeFont(DependencyObject element, bool value)
|
||||
=> element.SetValue(ResizeFontProperty, value);
|
||||
{
|
||||
element.SetValue(ResizeFontProperty, value);
|
||||
}
|
||||
|
||||
public static bool GetResizeFont(DependencyObject element)
|
||||
=> (bool)element.GetValue(ResizeFontProperty);
|
||||
{
|
||||
return (bool) element.GetValue(ResizeFontProperty);
|
||||
}
|
||||
|
||||
private static bool SetWindow(FrameworkElement el)
|
||||
{
|
||||
@@ -91,7 +110,7 @@ public class FontScaleWindow : Window
|
||||
|
||||
private static void ObjOnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var el = (FrameworkElement)sender;
|
||||
var el = (FrameworkElement) sender;
|
||||
if (GetParentScaleWindow(el) == null)
|
||||
{
|
||||
if (!SetWindow(el))
|
||||
@@ -100,6 +119,7 @@ public class FontScaleWindow : Window
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResizeCallBack(sender, new DependencyPropertyChangedEventArgs());
|
||||
}
|
||||
|
||||
@@ -116,17 +136,19 @@ public class FontScaleWindow : Window
|
||||
obj.Loaded += ObjOnLoaded;
|
||||
}
|
||||
|
||||
if (window is not { _loaded: true }) return;
|
||||
if (window is not {_loaded: true}) return;
|
||||
CalculateFontSizeResized(obj);
|
||||
}
|
||||
|
||||
private static void ObjOnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var obj = (FrameworkElement)sender;
|
||||
var obj = (FrameworkElement) sender;
|
||||
var w = GetParentScaleWindow(obj)!;
|
||||
w._cachedObjects.Remove(obj);
|
||||
obj.Unloaded -= ObjOnUnloaded;
|
||||
obj.Loaded -= ObjOnLoaded;
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
_currentDiagonal = _diagonal(e.NewSize.Width, e.NewSize.Height);
|
||||
@@ -135,18 +157,16 @@ public class FontScaleWindow : Window
|
||||
CalculateFontSizeResized(cached);
|
||||
}
|
||||
}
|
||||
//<-------------------Window-------------------->
|
||||
|
||||
|
||||
//<-------------------Scaling-------------------->
|
||||
|
||||
public static readonly DependencyProperty ScaleProperty = DependencyProperty.RegisterAttached(
|
||||
"Scale", typeof(double), typeof(FontScaleWindow), new FrameworkPropertyMetadata(0.9999d ,FrameworkPropertyMetadataOptions.Inherits, ScalePropertyCallback));
|
||||
|
||||
public static void SetScale(DependencyObject element, double value)
|
||||
=> element.SetValue(ScaleProperty, value);
|
||||
{
|
||||
element.SetValue(ScaleProperty, value);
|
||||
}
|
||||
|
||||
public static double GetScale(DependencyObject element)
|
||||
=> (double)element.GetValue(ScaleProperty);
|
||||
{
|
||||
return (double) element.GetValue(ScaleProperty);
|
||||
}
|
||||
|
||||
|
||||
private static void ScalePropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
@@ -160,6 +180,7 @@ public class FontScaleWindow : Window
|
||||
SetParentScaleWindow(d, fsWindow);
|
||||
}
|
||||
}
|
||||
|
||||
ResizeElement(d);
|
||||
}
|
||||
|
||||
@@ -176,9 +197,9 @@ public class FontScaleWindow : Window
|
||||
{
|
||||
CalculateFontSize(d);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void CalculateFontSize(DependencyObject d)
|
||||
{
|
||||
var window = GetParentScaleWindow(d);
|
||||
@@ -196,5 +217,4 @@ public class FontScaleWindow : Window
|
||||
var fontSize = window.DefaultFontSize * elScale * windowsScale;
|
||||
d.SetValue(FontSizeProperty, fontSize);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,33 +2,33 @@
|
||||
x:Class="LolzFucker.Theme.Palette"
|
||||
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"
|
||||
mc:Ignorable="d"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="300">
|
||||
|
||||
<UserControl.Resources>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="FontWeight" Value="DemiBold"/>
|
||||
<Setter Property="Margin" Value="4"/>
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="FontWeight" Value="DemiBold" />
|
||||
<Setter Property="Margin" Value="4" />
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="1*"/>
|
||||
<RowDefinition Height="1*"/>
|
||||
<RowDefinition Height="1*"/>
|
||||
<RowDefinition Height="1*"/>
|
||||
<RowDefinition Height="1*"/>
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="1*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border
|
||||
@@ -36,7 +36,7 @@
|
||||
Grid.ColumnSpan="3">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource PrimaryHueMidForegroundBrush}"
|
||||
Text="Primary - Mid"/>
|
||||
Text="Primary - Mid" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
@@ -46,7 +46,7 @@
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
|
||||
Text="Light"/>
|
||||
Text="Light" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
@@ -55,7 +55,7 @@
|
||||
Grid.Column="1">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource PrimaryHueMidForegroundBrush}"
|
||||
Text="Mid"/>
|
||||
Text="Mid" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
@@ -64,7 +64,7 @@
|
||||
Grid.Column="2">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource PrimaryHueDarkForegroundBrush}"
|
||||
Text="Dark"/>
|
||||
Text="Dark" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
@@ -73,7 +73,7 @@
|
||||
Grid.Column="0">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryHueLightForegroundBrush}"
|
||||
Text="Light"/>
|
||||
Text="Light" />
|
||||
</Border>
|
||||
<Border
|
||||
Background="{DynamicResource SecondaryHueMidBrush}"
|
||||
@@ -81,7 +81,7 @@
|
||||
Grid.Column="1">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryHueMidForegroundBrush}"
|
||||
Text="Mid"/>
|
||||
Text="Mid" />
|
||||
</Border>
|
||||
<Border
|
||||
Background="{DynamicResource SecondaryHueDarkBrush}"
|
||||
@@ -89,7 +89,7 @@
|
||||
Grid.Column="2">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryHueDarkForegroundBrush}"
|
||||
Text="Dark"/>
|
||||
Text="Dark" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
@@ -98,16 +98,16 @@
|
||||
Grid.Column="0">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryHueDarkForegroundBrush}"
|
||||
Text="Background"/>
|
||||
Text="Background" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Background="{DynamicResource MaterialDesignCardBackground}"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryHueMidForegroundBrush}"
|
||||
Text="CardBack"/>
|
||||
Grid.Row="3"
|
||||
Grid.Column="1">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryHueMidForegroundBrush}"
|
||||
Text="CardBack" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
@@ -116,7 +116,7 @@
|
||||
Grid.Column="2">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryHueMidForegroundBrush}"
|
||||
Text="Paper"/>
|
||||
Text="Paper" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
@@ -124,8 +124,8 @@
|
||||
Grid.Row="4"
|
||||
Grid.Column="0">
|
||||
<TextBlock
|
||||
|
||||
Text="Body"/>
|
||||
|
||||
Text="Body" />
|
||||
</Border>
|
||||
<Border
|
||||
Background="{DynamicResource MaterialDesignBodyLight}"
|
||||
@@ -133,14 +133,14 @@
|
||||
Grid.Column="1">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryHueMidForegroundBrush}"
|
||||
Text="BodyLight"/>
|
||||
Text="BodyLight" />
|
||||
</Border>
|
||||
<Border
|
||||
Background="{DynamicResource MaterialDesignToolTipBackground}"
|
||||
Grid.Row="4"
|
||||
Grid.Column="2">
|
||||
<TextBlock
|
||||
Text="ToolTipBack"/>
|
||||
Text="ToolTipBack" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable IdentifierTypo
|
||||
|
||||
@@ -8,18 +9,6 @@ namespace NebulaAuth.Theme.WindowStyle;
|
||||
// ReSharper disable once PartialTypeWithSinglePart //Required
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
public const int WM_NCCALCSIZE = 0x83;
|
||||
public const int WM_NCPAINT = 0x85;
|
||||
|
||||
[DllImport("kernel32", SetLastError = true)]
|
||||
private static extern IntPtr LoadLibrary(string lpFileName);
|
||||
|
||||
[DllImport("dwmapi.dll", PreserveSig = false)]
|
||||
public static extern bool DwmIsCompositionEnabled();
|
||||
|
||||
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
||||
|
||||
public enum DWMWINDOWATTRIBUTE : uint
|
||||
{
|
||||
NCRenderingEnabled = 1,
|
||||
@@ -39,16 +28,17 @@ public static partial class NativeMethods
|
||||
FreezeRepresentation
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MARGINS
|
||||
{
|
||||
public int leftWidth;
|
||||
public int rightWidth;
|
||||
public int topHeight;
|
||||
public int bottomHeight;
|
||||
}
|
||||
public const int WM_NCCALCSIZE = 0x83;
|
||||
public const int WM_NCPAINT = 0x85;
|
||||
|
||||
private delegate int DwmExtendFrameIntoClientAreaDelegate(IntPtr hwnd, ref MARGINS margins);
|
||||
[DllImport("kernel32", SetLastError = true)]
|
||||
private static extern IntPtr LoadLibrary(string lpFileName);
|
||||
|
||||
[DllImport("dwmapi.dll", PreserveSig = false)]
|
||||
public static extern bool DwmIsCompositionEnabled();
|
||||
|
||||
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
||||
|
||||
public static int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins)
|
||||
{
|
||||
@@ -66,7 +56,9 @@ public static partial class NativeMethods
|
||||
return 0;
|
||||
}
|
||||
|
||||
var delegateForFunctionPointer = (DwmExtendFrameIntoClientAreaDelegate)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(DwmExtendFrameIntoClientAreaDelegate));
|
||||
var delegateForFunctionPointer =
|
||||
(DwmExtendFrameIntoClientAreaDelegate) Marshal.GetDelegateForFunctionPointer(procAddress,
|
||||
typeof(DwmExtendFrameIntoClientAreaDelegate));
|
||||
|
||||
return delegateForFunctionPointer(hwnd, ref margins);
|
||||
}
|
||||
@@ -77,9 +69,25 @@ public static partial class NativeMethods
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy,
|
||||
int uFlags);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MARGINS
|
||||
{
|
||||
public int leftWidth;
|
||||
public int rightWidth;
|
||||
public int topHeight;
|
||||
public int bottomHeight;
|
||||
}
|
||||
|
||||
private delegate int DwmExtendFrameIntoClientAreaDelegate(IntPtr hwnd, ref MARGINS margins);
|
||||
|
||||
internal enum WVR
|
||||
{
|
||||
ALIGNTOP = 0x0010,
|
||||
@@ -91,7 +99,4 @@ public static partial class NativeMethods
|
||||
VALIDRECTS = 0x0400,
|
||||
REDRAW = HREDRAW | VREDRAW
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
|
||||
}
|
||||
@@ -9,10 +9,11 @@ namespace NebulaAuth.Theme.WindowStyle;
|
||||
|
||||
public static class WindowChromeHelper
|
||||
{
|
||||
public static Thickness LayoutOffsetThickness => new(0d, 0d, 0d, SystemParameters.WindowResizeBorderThickness.Bottom);
|
||||
public static Thickness LayoutOffsetThickness =>
|
||||
new(0d, 0d, 0d, SystemParameters.WindowResizeBorderThickness.Bottom);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the properly adjusted window resize border thickness from system parameters.
|
||||
/// Gets the properly adjusted window resize border thickness from system parameters.
|
||||
/// </summary>
|
||||
public static Thickness WindowResizeBorderThickness
|
||||
{
|
||||
@@ -52,24 +53,25 @@ public static class WindowChromeHelper
|
||||
float dpi;
|
||||
try
|
||||
{
|
||||
dpi = GetDeviceCaps(dc, (int)index);
|
||||
dpi = GetDeviceCaps(dc, (int) index);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseDC(desktopWnd, dc);
|
||||
}
|
||||
|
||||
return dpi / 96f;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetSystemMetrics(GetSystemMetricsIndex nIndex);
|
||||
|
||||
private enum GetDeviceCapsIndex
|
||||
{
|
||||
LOGPIXELSX = 88,
|
||||
LOGPIXELSY = 90
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetSystemMetrics(GetSystemMetricsIndex nIndex);
|
||||
|
||||
private enum GetSystemMetricsIndex
|
||||
{
|
||||
CXFRAME = 32,
|
||||
|
||||
@@ -60,11 +60,11 @@ public class WindowChromeRenderedBehavior : Behavior<Window>
|
||||
break;
|
||||
case NativeMethods.WM_NCCALCSIZE:
|
||||
handled = true;
|
||||
var rcClientArea = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));
|
||||
rcClientArea.Bottom += (int)(WindowChromeHelper.WindowResizeBorderThickness.Bottom / 2);
|
||||
var rcClientArea = (RECT) Marshal.PtrToStructure(lParam, typeof(RECT));
|
||||
rcClientArea.Bottom += (int) (WindowChromeHelper.WindowResizeBorderThickness.Bottom / 2);
|
||||
Marshal.StructureToPtr(rcClientArea, lParam, false);
|
||||
|
||||
return wParam == new IntPtr(1) ? new IntPtr((int)NativeMethods.WVR.REDRAW) : IntPtr.Zero;
|
||||
return wParam == new IntPtr(1) ? new IntPtr((int) NativeMethods.WVR.REDRAW) : IntPtr.Zero;
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True" />
|
||||
</Style>
|
||||
|
||||
<ControlTemplate x:Key="WindowTemplate" TargetType="{x:Type Window}" >
|
||||
<ControlTemplate x:Key="WindowTemplate" TargetType="{x:Type Window}">
|
||||
<!--<i:Interaction.Behaviors>
|
||||
<local:WindowChromeLoadedBehavior />
|
||||
</i:Interaction.Behaviors>-->
|
||||
@@ -24,43 +24,42 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="TitleBar"
|
||||
Grid.Row="0"
|
||||
Height="28"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
Grid.Row="0"
|
||||
Height="28"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryHueMidBrush}"
|
||||
Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Title}" FontSize="17"/>
|
||||
Foreground="{DynamicResource PrimaryHueMidBrush}"
|
||||
Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Title}"
|
||||
FontSize="17" />
|
||||
<!-- Window Buttons -->
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<StackPanel.Resources>
|
||||
<Style TargetType="materialDesign:PackIcon">
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
</StackPanel.Resources>
|
||||
<Button x:Name="MinimizeButton"
|
||||
Click="OnMinimizeClick"
|
||||
>
|
||||
<materialDesign:PackIcon Kind="WindowMinimize"/>
|
||||
Click="OnMinimizeClick">
|
||||
<materialDesign:PackIcon Kind="WindowMinimize" />
|
||||
</Button>
|
||||
<Button x:Name="MaximizeRestoreButton"
|
||||
Click="OnMaximizeRestoreClick"
|
||||
>
|
||||
<materialDesign:PackIcon Kind="WindowMaximize"/>
|
||||
Click="OnMaximizeRestoreClick">
|
||||
<materialDesign:PackIcon Kind="WindowMaximize" />
|
||||
</Button>
|
||||
<Button x:Name="CloseButton"
|
||||
Click="OnCloseClick">
|
||||
<materialDesign:PackIcon Kind="WindowClose"/>
|
||||
<materialDesign:PackIcon Kind="WindowClose" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Main Window Content -->
|
||||
<Grid x:Name="MainGrid"
|
||||
Grid.Row="1"
|
||||
Background="{DynamicResource MainBackgroundBrush}">
|
||||
Grid.Row="1"
|
||||
Background="{DynamicResource MainBackgroundBrush}">
|
||||
<AdornerDecorator>
|
||||
<ContentPresenter />
|
||||
</AdornerDecorator>
|
||||
@@ -74,11 +73,12 @@
|
||||
|
||||
</Trigger>
|
||||
<Trigger Property="WindowState" Value="Maximized">
|
||||
<Setter TargetName="RootBorder" Property="BorderThickness" Value="{Binding Source={x:Static local:WindowChromeHelper.WindowResizeBorderThickness}}" />
|
||||
<Setter TargetName="RootBorder" Property="BorderThickness"
|
||||
Value="{Binding Source={x:Static local:WindowChromeHelper.WindowResizeBorderThickness}}" />
|
||||
<Setter TargetName="RootBorder" Property="BorderBrush" Value="Transparent" />
|
||||
<Setter TargetName="MaximizeRestoreButton" Property="Content" >
|
||||
<Setter TargetName="MaximizeRestoreButton" Property="Content">
|
||||
<Setter.Value>
|
||||
<materialDesign:PackIcon Kind="WindowRestore"/>
|
||||
<materialDesign:PackIcon Kind="WindowRestore" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace NebulaAuth.Theme.WindowStyle;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для WindowStyle.xaml
|
||||
/// Логика взаимодействия для WindowStyle.xaml
|
||||
/// </summary>
|
||||
partial class WindowStyle
|
||||
{
|
||||
@@ -14,21 +14,21 @@ partial class WindowStyle
|
||||
|
||||
private void OnCloseClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = (Window)((FrameworkElement)sender).TemplatedParent;
|
||||
var window = (Window) ((FrameworkElement) sender).TemplatedParent;
|
||||
|
||||
window.Close();
|
||||
}
|
||||
|
||||
private void OnMaximizeRestoreClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = (Window)((FrameworkElement)sender).TemplatedParent;
|
||||
var window = (Window) ((FrameworkElement) sender).TemplatedParent;
|
||||
|
||||
window.WindowState = window.WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal;
|
||||
}
|
||||
|
||||
private void OnMinimizeClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = (Window)((FrameworkElement)sender).TemplatedParent;
|
||||
var window = (Window) ((FrameworkElement) sender).TemplatedParent;
|
||||
|
||||
window.WindowState = WindowState.Minimized;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
|
||||
namespace NebulaAuth.Utility;
|
||||
|
||||
@@ -25,8 +25,8 @@ public class ClipboardHelper
|
||||
Shell.Logger.Error(ex);
|
||||
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Error", "Error"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ public class ClipboardHelper
|
||||
Shell.Logger.Error(ex);
|
||||
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Error", "Error"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace NebulaAuth.Utility;
|
||||
|
||||
public static class ErrorTranslatorHelper
|
||||
{
|
||||
|
||||
public static string TranslateLoginError(LoginError error)
|
||||
{
|
||||
var result = GetMessage("Login", error.ToString());
|
||||
@@ -20,7 +19,6 @@ public static class ErrorTranslatorHelper
|
||||
{
|
||||
var result = GetMessage("EResult", eResult.ToString());
|
||||
return result ?? eResult.ToString();
|
||||
|
||||
}
|
||||
|
||||
public static string TranslateLinkerError(AuthenticatorLinkerError error)
|
||||
@@ -34,6 +32,5 @@ public static class ErrorTranslatorHelper
|
||||
var message = Loc.Tr(fullPath, "|ABSENT|");
|
||||
if (message == "|ABSENT|") return null;
|
||||
return message;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,93 +1,99 @@
|
||||
using NebulaAuth.Core;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.Exceptions.General;
|
||||
using SteamLib.ProtoCore.Exceptions;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NebulaAuth.Utility;
|
||||
|
||||
public static class ExceptionHandler
|
||||
{
|
||||
private const string EXCEPTION_HANDLER_LOC_PATH = "ExceptionHandler";
|
||||
public static bool Handle(Exception ex, string? prefix = null, string? postfix = null, bool handleAllExceptions = false)
|
||||
|
||||
public static bool Handle(Exception ex, string? prefix = null, string? postfix = null,
|
||||
bool handleAllExceptions = false)
|
||||
{
|
||||
string msg;
|
||||
Shell.Logger.Error(ex);
|
||||
switch (ex)
|
||||
{
|
||||
case SessionPermanentlyExpiredException:
|
||||
{
|
||||
msg = "SessionExpiredException".GetCodeBehindLocalization();
|
||||
break;
|
||||
}
|
||||
{
|
||||
msg = "SessionExpiredException".GetCodeBehindLocalization();
|
||||
break;
|
||||
}
|
||||
case SessionInvalidException:
|
||||
{
|
||||
msg = "SessionExpiredException".GetCodeBehindLocalization();
|
||||
break;
|
||||
}
|
||||
{
|
||||
msg = "SessionExpiredException".GetCodeBehindLocalization();
|
||||
break;
|
||||
}
|
||||
case TaskCanceledException e:
|
||||
{
|
||||
msg = e.InnerException is TimeoutException
|
||||
? "TimeoutException".GetCodeBehindLocalization()
|
||||
: "TaskCanceledException".GetCodeBehindLocalization();
|
||||
break;
|
||||
}
|
||||
{
|
||||
msg = e.InnerException is TimeoutException
|
||||
? "TimeoutException".GetCodeBehindLocalization()
|
||||
: "TaskCanceledException".GetCodeBehindLocalization();
|
||||
break;
|
||||
}
|
||||
case HttpRequestException e:
|
||||
{
|
||||
var str = "RequestError".GetCommonLocalization() + ": ";
|
||||
if (e.StatusCode != null)
|
||||
{
|
||||
var str = "RequestError".GetCommonLocalization() + ": ";
|
||||
if (e.StatusCode != null)
|
||||
{
|
||||
msg = str + e.StatusCode;
|
||||
}
|
||||
else if (e.InnerException != null)
|
||||
{
|
||||
msg = (str + e.InnerException.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = (str + e.Message);
|
||||
}
|
||||
msg = str + e.StatusCode;
|
||||
}
|
||||
else if (e.InnerException != null)
|
||||
{
|
||||
msg = str + e.InnerException.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = str + e.Message;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UnsupportedResponseException:
|
||||
{
|
||||
msg = "UnsupportedResponseException".GetCodeBehindLocalization();
|
||||
break;
|
||||
}
|
||||
{
|
||||
msg = "UnsupportedResponseException".GetCodeBehindLocalization();
|
||||
break;
|
||||
}
|
||||
case CantLoadConfirmationsException e:
|
||||
{
|
||||
msg = LocManager.GetCodeBehindOrDefault(nameof(CantLoadConfirmationsException),
|
||||
EXCEPTION_HANDLER_LOC_PATH, nameof(CantLoadConfirmationsException), "Common");
|
||||
if (e.Error == LoadConfirmationsError.Unknown)
|
||||
{
|
||||
msg = LocManager.GetCodeBehindOrDefault(nameof(CantLoadConfirmationsException), EXCEPTION_HANDLER_LOC_PATH, nameof(CantLoadConfirmationsException), "Common");
|
||||
if (e.Error == LoadConfirmationsError.Unknown)
|
||||
{
|
||||
msg += e.ErrorMessage;
|
||||
msg += ' ';
|
||||
msg += e.ErrorDetails;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += LocManager.GetCodeBehindOrDefault(e.Error.ToString(), EXCEPTION_HANDLER_LOC_PATH, nameof(CantLoadConfirmationsException), e.Error.ToString());
|
||||
}
|
||||
break;
|
||||
msg += e.ErrorMessage;
|
||||
msg += ' ';
|
||||
msg += e.ErrorDetails;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += LocManager.GetCodeBehindOrDefault(e.Error.ToString(), EXCEPTION_HANDLER_LOC_PATH,
|
||||
nameof(CantLoadConfirmationsException), e.Error.ToString());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EResultException e:
|
||||
{
|
||||
msg = "Error".GetCommonLocalization() + ": " + ErrorTranslatorHelper.TranslateEResult(e.Result);
|
||||
break;
|
||||
}
|
||||
{
|
||||
msg = "Error".GetCommonLocalization() + ": " + ErrorTranslatorHelper.TranslateEResult(e.Result);
|
||||
break;
|
||||
}
|
||||
case LoginException e:
|
||||
{
|
||||
msg = "LoginException".GetCodeBehindLocalization() + ": " + ErrorTranslatorHelper.TranslateLoginError(e.Error);
|
||||
break;
|
||||
}
|
||||
{
|
||||
msg = "LoginException".GetCodeBehindLocalization() + ": " +
|
||||
ErrorTranslatorHelper.TranslateLoginError(e.Error);
|
||||
break;
|
||||
}
|
||||
case not null when handleAllExceptions:
|
||||
{
|
||||
msg = "UnknownException".GetCodeBehindLocalization() + ex.Message;
|
||||
break;
|
||||
}
|
||||
{
|
||||
msg = "UnknownException".GetCodeBehindLocalization() + ex.Message;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -95,6 +101,7 @@ public static class ExceptionHandler
|
||||
SnackbarController.SendSnackbar(prefix + msg + postfix);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string GetCommonLocalization(this string key)
|
||||
{
|
||||
return LocManager.GetCommonOrDefault(key, key);
|
||||
|
||||
@@ -9,18 +9,21 @@
|
||||
|
||||
|
||||
<DataTemplate DataType="{x:Type confirmations:Confirmation}">
|
||||
<Grid >
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" DockPanel.Dock="Left" Kind="QuestionMark" Margin="0,0,10,0"/>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" DockPanel.Dock="Left"
|
||||
Kind="QuestionMark" Margin="0,0,10,0" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="1" Text="{Binding TypeName}" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Right" Text="{Binding Time, StringFormat=t}"/>
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3" Style="{StaticResource MaterialDesignIconButton}"
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Right"
|
||||
Text="{Binding Time, StringFormat=t}" />
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||
CommandParameter="{Binding }">
|
||||
<materialDesign:PackIcon Kind="Check" />
|
||||
@@ -33,25 +36,31 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type confirmations:TradeConfirmation}">
|
||||
<Grid >
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<materialDesign:PackIcon VerticalAlignment="Center" Width="20" Height="20" Kind="AccountArrowRight" Margin="0,0,10,0" />
|
||||
<Image Grid.Column="1" VerticalAlignment="Center" DockPanel.Dock="Left" Width="24" Height="24" Source="{Binding UserAvatarUri}" Margin="0,0,10,0" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" TextWrapping="WrapWithOverflow" >
|
||||
<Run Text="{Tr MainWindow.ConfirmationTemplates.TradeWith, IsDynamic=False}"/>
|
||||
<materialDesign:PackIcon VerticalAlignment="Center" Width="20" Height="20" Kind="AccountArrowRight"
|
||||
Margin="0,0,10,0" />
|
||||
<Image Grid.Column="1" VerticalAlignment="Center" DockPanel.Dock="Left" Width="24" Height="24"
|
||||
Source="{Binding UserAvatarUri}" Margin="0,0,10,0" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" TextWrapping="WrapWithOverflow">
|
||||
<Run Text="{Tr MainWindow.ConfirmationTemplates.TradeWith, IsDynamic=False}" />
|
||||
<Run Text="{Binding UserName}" FontWeight="Bold" />
|
||||
</TextBlock>
|
||||
<materialDesign:PackIcon Grid.Column="3" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Kind="Gift" Visibility="{Binding IsReceiveNothing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="4" HorizontalAlignment="Right" Text="{Binding Time, StringFormat=t}"/>
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="5" Style="{StaticResource MaterialDesignIconButton}"
|
||||
<materialDesign:PackIcon Grid.Column="3" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Margin="5,0,5,0" Kind="Gift"
|
||||
Visibility="{Binding IsReceiveNothing, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="4" HorizontalAlignment="Right"
|
||||
Text="{Binding Time, StringFormat=t}" />
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="5"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||
CommandParameter="{Binding }">
|
||||
<materialDesign:PackIcon Kind="Check" />
|
||||
@@ -64,18 +73,21 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type confirmations:AccountRecoveryConfirmation}">
|
||||
<Grid >
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" DockPanel.Dock="Left" Kind="LockOutline" Margin="0,0,10,0"/>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="1" Text="{Tr MainWindow.ConfirmationTemplates.Recovery}"/>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Right" Text="{Binding Time, StringFormat=t}"/>
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3" Style="{StaticResource MaterialDesignIconButton}"
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" DockPanel.Dock="Left"
|
||||
Kind="LockOutline" Margin="0,0,10,0" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="1" Text="{Tr MainWindow.ConfirmationTemplates.Recovery}" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Right"
|
||||
Text="{Binding Time, StringFormat=t}" />
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||
CommandParameter="{Binding }">
|
||||
<materialDesign:PackIcon Kind="Check" />
|
||||
@@ -88,36 +100,43 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type confirmations:MarketConfirmation}">
|
||||
<Grid >
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<materialDesign:PackIcon VerticalAlignment="Center" Width="20" Height="20" Kind="ShoppingCart" Margin="0,0,10,0" />
|
||||
<Border Grid.Column="1" Background="{DynamicResource MaterialDesignPaper}" MaxHeight="36" HorizontalAlignment="Center" BorderBrush="{DynamicResource PrimaryHueMidBrush}" BorderThickness="0.6" Margin="0,0,10,0">
|
||||
<Image HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="32" MaxHeight="32" Source="{Binding ItemImageUri}" />
|
||||
<materialDesign:PackIcon VerticalAlignment="Center" Width="20" Height="20" Kind="ShoppingCart"
|
||||
Margin="0,0,10,0" />
|
||||
<Border Grid.Column="1" Background="{DynamicResource MaterialDesignPaper}" MaxHeight="36"
|
||||
HorizontalAlignment="Center" BorderBrush="{DynamicResource PrimaryHueMidBrush}"
|
||||
BorderThickness="0.6" Margin="0,0,10,0">
|
||||
<Image HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="32" MaxHeight="32"
|
||||
Source="{Binding ItemImageUri}" />
|
||||
</Border>
|
||||
<Grid Grid.Column="2">
|
||||
<TextBlock VerticalAlignment="Center" x:Name="ItemName" theme:FontScaleWindow.ResizeFont="True" TextWrapping="WrapWithOverflow" Text="{Binding ItemName}"/>
|
||||
|
||||
<TextBlock VerticalAlignment="Center" x:Name="ItemName" theme:FontScaleWindow.ResizeFont="True"
|
||||
TextWrapping="WrapWithOverflow" Text="{Binding ItemName}" />
|
||||
|
||||
<TextBlock Foreground="LightGray" Text="{Binding PriceString}">
|
||||
<TextBlock.FontSize>
|
||||
<Binding ElementName="ItemName" Path="FontSize" ConverterParameter="0.7">
|
||||
<Binding.Converter>
|
||||
<converters:CoefficientConverter/>
|
||||
<converters:CoefficientConverter />
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
</TextBlock.FontSize>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="4" HorizontalAlignment="Left" Margin="5,0,0,0" Text="{Binding Time, StringFormat=t}"/>
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="5" Style="{StaticResource MaterialDesignIconButton}"
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="4" HorizontalAlignment="Left" Margin="5,0,0,0"
|
||||
Text="{Binding Time, StringFormat=t}" />
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="5"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||
CommandParameter="{Binding }">
|
||||
<materialDesign:PackIcon Kind="Check" />
|
||||
@@ -130,18 +149,21 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type confirmations:RegisterApiKeyConfirmation}">
|
||||
<Grid >
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" DockPanel.Dock="Left" Kind="KeyLink" Margin="0,0,10,0"/>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" DockPanel.Dock="Left"
|
||||
Kind="KeyLink" Margin="0,0,10,0" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="1" Text="{Tr MainWindow.ConfirmationTemplates.ApiKey}" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Right" Text="{Binding Time, StringFormat=t}"/>
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3" Style="{StaticResource MaterialDesignIconButton}"
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Right"
|
||||
Text="{Binding Time, StringFormat=t}" />
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||
CommandParameter="{Binding }">
|
||||
<materialDesign:PackIcon Kind="Check" />
|
||||
@@ -154,23 +176,26 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type entities:MarketMultiConfirmation}">
|
||||
<Grid >
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<materialDesign:PackIcon VerticalAlignment="Center" Width="20" Height="20" Kind="ShoppingCartPlus" Margin="0,0,10,0" />
|
||||
<materialDesign:PackIcon VerticalAlignment="Center" Width="20" Height="20" Kind="ShoppingCartPlus"
|
||||
Margin="0,0,10,0" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="1">
|
||||
<Run Text="{Tr MainWindow.ConfirmationTemplates.Market, IsDynamic=False}"/>
|
||||
<Run Text="{Binding Confirmations.Count, Mode=OneWay}"/>
|
||||
<Run Text="{Tr Common.Abbreviations.Count.Items, IsDynamic=False}"/>
|
||||
<Run Text="{Tr MainWindow.ConfirmationTemplates.Market, IsDynamic=False}" />
|
||||
<Run Text="{Binding Confirmations.Count, Mode=OneWay}" />
|
||||
<Run Text="{Tr Common.Abbreviations.Count.Items, IsDynamic=False}" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Left" Margin="5,0,0,0" Text="{Binding Time, StringFormat=t}"/>
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3" Style="{StaticResource MaterialDesignIconButton}"
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Left" Margin="5,0,0,0"
|
||||
Text="{Binding Time, StringFormat=t}" />
|
||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||
CommandParameter="{Binding }">
|
||||
<materialDesign:PackIcon Kind="Check" />
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
<UserControl x:Class="NebulaAuth.View.Dialogs.ConfirmCancelDialog"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:theme="clr-namespace:NebulaAuth.Theme"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
Height="Auto" Width="Auto" MaxWidth="500"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock theme:FontScaleWindow.Scale="0.8"
|
||||
theme:FontScaleWindow.ResizeFont="True"
|
||||
HorizontalAlignment="Center" Margin="10,10,10,15"
|
||||
Text="Подтвердите действие"
|
||||
x:Name="ConfirmTextBlock"
|
||||
TextWrapping="WrapWithOverflow"/>
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
<Grid Grid.Row="1" Margin="10,0,10,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button DockPanel.Dock="Left"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
<Button DockPanel.Dock="Left"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource True}"
|
||||
FontSize="{Binding ElementName=ConfirmTextBlock, Path=FontSize,
|
||||
Converter={StaticResource CoefficientConverter},
|
||||
@@ -33,7 +33,7 @@
|
||||
ОК
|
||||
</Button>
|
||||
<Button Grid.Column="1" DockPanel.Dock="Right"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource False}"
|
||||
FontSize="{Binding ElementName=ConfirmTextBlock, Path=FontSize,
|
||||
Converter={StaticResource CoefficientConverter},
|
||||
@@ -42,4 +42,4 @@
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для ConfirmCancelDialog.xaml
|
||||
/// Логика взаимодействия для ConfirmCancelDialog.xaml
|
||||
/// </summary>
|
||||
public partial class ConfirmCancelDialog
|
||||
{
|
||||
|
||||
@@ -1,38 +1,48 @@
|
||||
<UserControl x:Class="NebulaAuth.View.Dialogs.LoginAgainDialog"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:theme="clr-namespace:NebulaAuth.Theme"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
|
||||
xmlns:model="clr-namespace:NebulaAuth.Model"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True"
|
||||
Foreground="WhiteSmoke"
|
||||
Foreground="WhiteSmoke"
|
||||
d:DataContext="{d:DesignInstance other:LoginAgainVM}"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
|
||||
<Grid MinHeight="100" MinWidth="300" Margin="20">
|
||||
<Grid MinHeight="100" MinWidth="300" Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True" Margin="10,0,0,0" HorizontalAlignment="Left">
|
||||
<Run Text="{Tr LoginAgainDialog.LoginFor, IsDynamic=False}"/>
|
||||
<Run FontWeight="Bold" Text="{Binding UserName}"/>
|
||||
<TextBlock theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True" Margin="10,0,0,0"
|
||||
HorizontalAlignment="Left">
|
||||
<Run Text="{Tr LoginAgainDialog.LoginFor, IsDynamic=False}" />
|
||||
<Run FontWeight="Bold" Text="{Binding UserName}" />
|
||||
</TextBlock>
|
||||
<TextBox Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,0" Grid.Row="1" Style="{StaticResource MaterialDesignFloatingHintTextBox}" materialDesign:HintAssist.Hint="{Tr LoginAgainDialog.PasswordBox}" />
|
||||
<CheckBox Grid.Row="2" Margin="10,10,10,0" IsEnabled="{Binding Source={x:Static model:PHandler.IsPasswordSet}}" IsChecked="{Binding SavePassword}" Content="{Tr LoginAgainDialog.SaveEncryptedPassword}"/>
|
||||
<TextBox Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,0" Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
materialDesign:HintAssist.Hint="{Tr LoginAgainDialog.PasswordBox}" />
|
||||
<CheckBox Grid.Row="2" Margin="10,10,10,0" IsEnabled="{Binding Source={x:Static model:PHandler.IsPasswordSet}}"
|
||||
IsChecked="{Binding SavePassword}" Content="{Tr LoginAgainDialog.SaveEncryptedPassword}" />
|
||||
<Grid Grid.Row="3" Margin="10,10,10,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button IsDefault="True" IsEnabled="{Binding IsFormValid}" Margin="0,0,5,5" Style="{StaticResource MaterialDesignOutlinedButton}" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="{StaticResource True}" Content="{Tr LoginAgainDialog.LoginButton}"/>
|
||||
<Button IsCancel="True" Grid.Column="1" Margin="5,0,0,5" Style="{StaticResource MaterialDesignOutlinedButton}" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="{StaticResource False}" Content="{Tr LoginAgainDialog.CancelButton}"/>
|
||||
<Button IsDefault="True" IsEnabled="{Binding IsFormValid}" Margin="0,0,5,5"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource True}" Content="{Tr LoginAgainDialog.LoginButton}" />
|
||||
<Button IsCancel="True" Grid.Column="1" Margin="5,0,0,5"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource False}" Content="{Tr LoginAgainDialog.CancelButton}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для LoginAgainDialog.xaml
|
||||
/// Логика взаимодействия для LoginAgainDialog.xaml
|
||||
/// </summary>
|
||||
public partial class LoginAgainDialog
|
||||
{
|
||||
|
||||
@@ -1,34 +1,39 @@
|
||||
<UserControl x:Class="NebulaAuth.View.Dialogs.LoginAgainOnImportDialog"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:theme="clr-namespace:NebulaAuth.Theme"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
|
||||
xmlns:model="clr-namespace:NebulaAuth.Model"
|
||||
xmlns:entities="clr-namespace:NebulaAuth.Model.Entities"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True"
|
||||
Foreground="WhiteSmoke"
|
||||
d:DataContext="{d:DesignInstance other:LoginAgainOnImportVM}"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
|
||||
<Grid MinHeight="100" MinWidth="300" Margin="20">
|
||||
<Grid MinHeight="100" MinWidth="300" Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True" Margin="10,0,0,0" HorizontalAlignment="Left">
|
||||
<Run Text="{Tr LoginAgainDialog.LoginFor, IsDynamic=False}"/>
|
||||
<Run FontWeight="Bold" Text="{Binding UserName}"/>
|
||||
<TextBlock theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True" Margin="10,0,0,0"
|
||||
HorizontalAlignment="Left">
|
||||
<Run Text="{Tr LoginAgainDialog.LoginFor, IsDynamic=False}" />
|
||||
<Run FontWeight="Bold" Text="{Binding UserName}" />
|
||||
</TextBlock>
|
||||
<TextBox Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,0" Grid.Row="1" Style="{StaticResource MaterialDesignFloatingHintTextBox}" materialDesign:HintAssist.Hint="{Tr LoginAgainDialog.PasswordBox}" />
|
||||
<ComboBox ToolTip="{Tr LoginAgainDialog.ProxyToolTip}" Grid.Row="2" Margin="10,18,10,0" materialDesign:HintAssist.Hint="{Tr Common.Proxy}" ItemsSource="{Binding Proxies}" SelectedItem="{Binding SelectedProxy}" >
|
||||
<TextBox Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,0" Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
materialDesign:HintAssist.Hint="{Tr LoginAgainDialog.PasswordBox}" />
|
||||
<ComboBox ToolTip="{Tr LoginAgainDialog.ProxyToolTip}" Grid.Row="2" Margin="10,18,10,0"
|
||||
materialDesign:HintAssist.Hint="{Tr Common.Proxy}" ItemsSource="{Binding Proxies}"
|
||||
SelectedItem="{Binding SelectedProxy}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type entities:MaProxy}">
|
||||
<TextBlock Text="{Binding Converter={StaticResource ProxyTextConverter}, Mode=OneWay}" />
|
||||
@@ -38,15 +43,23 @@
|
||||
<KeyBinding Key="Delete" Command="{Binding RemoveProxyCommand}" />
|
||||
</UIElement.InputBindings>
|
||||
</ComboBox>
|
||||
<CheckBox Grid.Row="3" Margin="10,10,10,0" IsEnabled="{Binding Source={x:Static model:PHandler.IsPasswordSet}}" IsChecked="{Binding SavePassword}" Content="{Tr LoginAgainDialog.SaveEncryptedPassword}"/>
|
||||
<CheckBox Grid.Row="4" Margin="10,10,10,0" IsEnabled="{Binding MafileHasProxy}" Content="{Tr LoginAgainDialog.UseMafileProxy}" IsChecked="{Binding UseMafileProxy}"/>
|
||||
<CheckBox Grid.Row="3" Margin="10,10,10,0" IsEnabled="{Binding Source={x:Static model:PHandler.IsPasswordSet}}"
|
||||
IsChecked="{Binding SavePassword}" Content="{Tr LoginAgainDialog.SaveEncryptedPassword}" />
|
||||
<CheckBox Grid.Row="4" Margin="10,10,10,0" IsEnabled="{Binding MafileHasProxy}"
|
||||
Content="{Tr LoginAgainDialog.UseMafileProxy}" IsChecked="{Binding UseMafileProxy}" />
|
||||
<Grid Grid.Row="5" Margin="10,10,10,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button IsEnabled="{Binding IsFormValid}" IsDefault="True" Margin="0,5,5,5" Style="{StaticResource MaterialDesignOutlinedButton}" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="{StaticResource True}" Content="{Tr LoginAgainDialog.LoginButton}"/>
|
||||
<Button IsCancel="True" Grid.Column="1" Margin="5,5,0,5" Style="{StaticResource MaterialDesignOutlinedButton}" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="{StaticResource False}" Content="{Tr LoginAgainDialog.CancelButton}"/>
|
||||
<Button IsEnabled="{Binding IsFormValid}" IsDefault="True" Margin="0,5,5,5"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource True}" Content="{Tr LoginAgainDialog.LoginButton}" />
|
||||
<Button IsCancel="True" Grid.Column="1" Margin="5,5,0,5"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource False}" Content="{Tr LoginAgainDialog.CancelButton}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для LoginAgainDialog.xaml
|
||||
/// Логика взаимодействия для LoginAgainDialog.xaml
|
||||
/// </summary>
|
||||
public partial class LoginAgainOnImportDialog
|
||||
{
|
||||
|
||||
@@ -1,32 +1,42 @@
|
||||
<UserControl x:Class="NebulaAuth.View.Dialogs.SetCryptPasswordDialog"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
|
||||
xmlns:theme="clr-namespace:NebulaAuth.Theme"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
Foreground="WhiteSmoke"
|
||||
d:DataContext="{d:DesignInstance other:LoginAgainVM}"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
|
||||
<Grid MinHeight="100" MinWidth="300" MaxWidth="400" Margin="20">
|
||||
<Grid MinHeight="100" MinWidth="300" MaxWidth="400" Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock IsHitTestVisible="False" TextWrapping="Wrap" FontWeight="Normal" Text="{Tr SetEncryptedPasswordDialog.DialogText}" theme:FontScaleWindow.Scale="0.8" theme:FontScaleWindow.ResizeFont="True" Margin="10,0,0,0" HorizontalAlignment="Left"/>
|
||||
<PasswordBox FontSize="16" materialDesign:PasswordBoxAssist.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Margin="10" Grid.Row="1" Style="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}" materialDesign:HintAssist.Hint="{Tr SetEncryptedPasswordDialog.Password}" />
|
||||
<TextBlock IsHitTestVisible="False" TextWrapping="Wrap" FontWeight="Normal"
|
||||
Text="{Tr SetEncryptedPasswordDialog.DialogText}" theme:FontScaleWindow.Scale="0.8"
|
||||
theme:FontScaleWindow.ResizeFont="True" Margin="10,0,0,0" HorizontalAlignment="Left" />
|
||||
<PasswordBox FontSize="16"
|
||||
materialDesign:PasswordBoxAssist.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="10" Grid.Row="1" Style="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}"
|
||||
materialDesign:HintAssist.Hint="{Tr SetEncryptedPasswordDialog.Password}" />
|
||||
<Grid Grid.Row="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button IsDefault="True" Margin="10,5,5,5" Style="{StaticResource MaterialDesignOutlinedButton}" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="{StaticResource True}" Content="{Tr SetEncryptedPasswordDialog.Ok}"/>
|
||||
<Button IsCancel="True" Grid.Column="1" Margin="5,5,10,5" Style="{StaticResource MaterialDesignOutlinedButton}" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="{StaticResource False}" Content="{Tr SetEncryptedPasswordDialog.Cancel}"/>
|
||||
<Button IsDefault="True" Margin="10,5,5,5" Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource True}" Content="{Tr SetEncryptedPasswordDialog.Ok}" />
|
||||
<Button IsCancel="True" Grid.Column="1" Margin="5,5,10,5"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource False}" Content="{Tr SetEncryptedPasswordDialog.Cancel}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для SetCryptPasswordDialog.xaml
|
||||
/// Логика взаимодействия для SetCryptPasswordDialog.xaml
|
||||
/// </summary>
|
||||
public partial class SetCryptPasswordDialog
|
||||
{
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
<UserControl x:Class="NebulaAuth.View.Dialogs.WaitLoginDialog"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:theme="clr-namespace:NebulaAuth.Theme"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
theme:FontScaleWindow.ResizeFont="True" theme:FontScaleWindow.Scale="1"
|
||||
Foreground="WhiteSmoke"
|
||||
Foreground="WhiteSmoke"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ProgressBar Style="{StaticResource MaterialDesignCircularProgressBar}" IsIndeterminate="True" />
|
||||
<ProgressBar Style="{StaticResource MaterialDesignCircularProgressBar}" IsIndeterminate="True" />
|
||||
<TextBlock Margin="10,0,0,0" Grid.Column="1" Text="{Tr WaitLoginDialog.Text}" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<Grid x:Name="CaptchaGrid" Margin="0,25,0,0" Visibility="Collapsed" Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image x:Name="CaptchaImage" Stretch="Uniform" />
|
||||
<TextBox Grid.Row="1" Style="{StaticResource MaterialDesignFloatingHintTextBox}" x:Name="CaptchaTB"/>
|
||||
<TextBox Grid.Row="1" Style="{StaticResource MaterialDesignFloatingHintTextBox}" x:Name="CaptchaTB" />
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="0" Click="SendCaptchaBtn_Click">Отправить</Button>
|
||||
<Button Grid.Column="1" Click="CancelButton_OnClick">Отмена</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -10,11 +10,12 @@ using SteamLib.Exceptions;
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для WaitLoginDialog.xaml
|
||||
/// Логика взаимодействия для WaitLoginDialog.xaml
|
||||
/// </summary>
|
||||
public partial class WaitLoginDialog : ICaptchaResolver
|
||||
{
|
||||
private TaskCompletionSource<string> _tcs = new();
|
||||
|
||||
public WaitLoginDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -22,7 +23,6 @@ public partial class WaitLoginDialog : ICaptchaResolver
|
||||
|
||||
public async Task<string> Resolve(Uri imageUrl, HttpClient client)
|
||||
{
|
||||
|
||||
CaptchaGrid.Visibility = Visibility.Visible;
|
||||
var stream = await client.GetStreamAsync(imageUrl);
|
||||
return await Application.Current.Dispatcher.Invoke(async () =>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<UserControl x:Class="NebulaAuth.View.LinkerView"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
Foreground="WhiteSmoke"
|
||||
FontFamily="{materialDesign:MaterialDesignFont}"
|
||||
MinHeight="640"
|
||||
@@ -20,56 +20,61 @@
|
||||
</d:DesignerProperties.DesignStyle>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
|
||||
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.Resources>
|
||||
<Style TargetType="materialDesign:PackIcon">
|
||||
<Style TargetType="materialDesign:PackIcon">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}" Value="False">
|
||||
<Setter Property="Foreground" Value="IndianRed"/>
|
||||
<Setter Property="Kind" Value="CheckBoxOutlineBlank"/>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}"
|
||||
Value="False">
|
||||
<Setter Property="Foreground" Value="IndianRed" />
|
||||
<Setter Property="Kind" Value="CheckBoxOutlineBlank" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}" Value="True">
|
||||
<Setter Property="Foreground" Value="Green"/>
|
||||
<Setter Property="Kind" Value="CheckBoxOutline"/>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}"
|
||||
Value="True">
|
||||
<Setter Property="Foreground" Value="Green" />
|
||||
<Setter Property="Kind" Value="CheckBoxOutline" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
|
||||
</Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}" Value="True">
|
||||
<Setter Property="Foreground" Value="Gray"/>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}"
|
||||
Value="True">
|
||||
<Setter Property="Foreground" Value="Gray" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
<Grid Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock FontStyle="Normal" Foreground="DarkGray" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="18" Text="{Tr LinkerDialog.Title}"/>
|
||||
<TextBlock Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12" >
|
||||
<Hyperlink Command="{Binding OpenTroubleshootingCommand}">
|
||||
<TextBlock FontStyle="Normal" Foreground="DarkGray" HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" FontSize="18" Text="{Tr LinkerDialog.Title}" />
|
||||
<TextBlock Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12">
|
||||
<Hyperlink Command="{Binding OpenTroubleshootingCommand}">
|
||||
<Run Text="{Tr LinkerDialog.GotErrorHyperlinkText, IsDynamic=False}" />
|
||||
</Hyperlink>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}" HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
|
||||
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
|
||||
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
|
||||
</Button>
|
||||
</Grid>
|
||||
@@ -77,72 +82,85 @@
|
||||
<Grid Grid.Row="2" Margin="10,20,10,10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Padding="10" FontSize="16" Style="{StaticResource MaterialDesignOutlinedComboBox}" SelectedValue="{Binding SelectedProxy}" ItemsSource="{Binding Proxies}" materialDesign:HintAssist.Hint="{Tr LinkerDialog.Proxy}" IsEnabled="{Binding IsPasswordFieldVisible}">
|
||||
<ComboBox Padding="10" FontSize="16" Style="{StaticResource MaterialDesignOutlinedComboBox}"
|
||||
SelectedValue="{Binding SelectedProxy}" ItemsSource="{Binding Proxies}"
|
||||
materialDesign:HintAssist.Hint="{Tr LinkerDialog.Proxy}"
|
||||
IsEnabled="{Binding IsPasswordFieldVisible}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<Run Text="{Binding Key, Mode=OneWay}"/><Run Text=":"/>
|
||||
<Run Text="{Binding Value.Address, Mode=OneWay}"/>
|
||||
<Run Text="{Binding Key, Mode=OneWay}" /><Run Text=":" />
|
||||
<Run Text="{Binding Value.Address, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<Button Grid.Column="1" IsEnabled="{Binding IsPasswordFieldVisible}" Margin="5,0,0,0" Command="{Binding ResetProxyCommand}" Content="{materialDesign:PackIcon Trash}"/>
|
||||
<Button Grid.Column="1" IsEnabled="{Binding IsPasswordFieldVisible}" Margin="5,0,0,0"
|
||||
Command="{Binding ResetProxyCommand}" Content="{materialDesign:PackIcon Trash}" />
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="7" Tag="{Binding IsLogin}">
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.Authorization}"/>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.Authorization}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="4" Orientation="Horizontal" Margin="7" Tag="{Binding IsEmailCode}">
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.EmailCode}"/>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.EmailCode}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="7" Tag="{Binding IsPhoneNumber}">
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.PhoneNumber}"/>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.PhoneNumber}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="6" Orientation="Horizontal" Margin="7" Tag="{Binding IsEmailConfirmation}">
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.EmailLink}"/>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.EmailLink}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="7" Orientation="Horizontal" Margin="7" Tag="{Binding IsLinkCode}">
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.SmsOrCode}"/>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.SmsOrCode}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="8" Orientation="Horizontal" Margin="7" Tag="{Binding IsCompleted}">
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.Completed}"/>
|
||||
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
|
||||
<TextBlock FontSize="16" Text="{Tr LinkerDialog.Completed}" />
|
||||
</StackPanel>
|
||||
<Grid Grid.Row="9" Margin="10,20,10,10">
|
||||
<Grid Grid.Row="9" Margin="10,20,10,10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBox Padding="7" Text="{Binding FieldText}" Visibility="{Binding IsFieldVisible, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource MaterialDesignOutlinedTextBox}" FontSize="16" Margin="0,0,0,10" />
|
||||
<TextBox Grid.Row="1" Padding="7" Text="{Binding PassFieldText}" Visibility="{Binding IsPasswordFieldVisible, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource MaterialDesignOutlinedTextBox}" FontSize="16"/>
|
||||
<GroupBox Grid.Row="2" MaxWidth="400" BorderBrush="{StaticResource PrimaryHueMidBrush}" VerticalAlignment="Bottom" BorderThickness="1" Margin="0,10,0,0" Padding="5">
|
||||
<TextBox Padding="7" Text="{Binding FieldText}"
|
||||
Visibility="{Binding IsFieldVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Style="{StaticResource MaterialDesignOutlinedTextBox}" FontSize="16" Margin="0,0,0,10" />
|
||||
<TextBox Grid.Row="1" Padding="7" Text="{Binding PassFieldText}"
|
||||
Visibility="{Binding IsPasswordFieldVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Style="{StaticResource MaterialDesignOutlinedTextBox}" FontSize="16" />
|
||||
<GroupBox Grid.Row="2" MaxWidth="400" BorderBrush="{StaticResource PrimaryHueMidBrush}"
|
||||
VerticalAlignment="Bottom" BorderThickness="1" Margin="0,10,0,0" Padding="5">
|
||||
<GroupBox.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Tr LinkerDialog.Message}" FontSize="16" Foreground="GhostWhite"/>
|
||||
<TextBlock Text="{Tr LinkerDialog.Message}" FontSize="16" Foreground="GhostWhite" />
|
||||
</DataTemplate>
|
||||
</GroupBox.HeaderTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Foreground="GhostWhite" FontSize="16" Text="{Binding HintText}" HorizontalAlignment="Stretch" TextWrapping="Wrap"/>
|
||||
<Button Command="{Binding CopyCodeCommand}" Visibility="{Binding IsCompleted, Converter={StaticResource BooleanToVisibilityConverter}}" Grid.Row="1">
|
||||
<TextBlock Foreground="GhostWhite" FontSize="16" Text="{Binding HintText}"
|
||||
HorizontalAlignment="Stretch" TextWrapping="Wrap" />
|
||||
<Button Command="{Binding CopyCodeCommand}"
|
||||
Visibility="{Binding IsCompleted, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Grid.Row="1">
|
||||
<materialDesign:PackIcon Kind="ContentCopy" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
<Button Grid.Row="10" Height="35" FontSize="17" Command="{Binding ProceedCommand}" Content="{Tr LinkerDialog.ProceedButton}"/>
|
||||
<Button Grid.Row="10" Height="35" FontSize="17" Command="{Binding ProceedCommand}"
|
||||
Content="{Tr LinkerDialog.ProceedButton}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для LinkerView.xaml
|
||||
/// Логика взаимодействия для LinkerView.xaml
|
||||
/// </summary>
|
||||
public partial class LinkerView
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<UserControl x:Class="NebulaAuth.View.ProxyManagerView"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="500" d:DesignWidth="400"
|
||||
Foreground="WhiteSmoke"
|
||||
FontFamily="{md:MaterialDesignFont}"
|
||||
@@ -16,19 +16,22 @@
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Margin="10" FontStyle="Normal" Foreground="DarkGray" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="18" Text="{Tr ProxyManagerDialog.Title}"/>
|
||||
<Button Margin="0,0,10,0" IsCancel="True" Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}" HorizontalAlignment="Right" Command="{x:Static md:DialogHost.CloseDialogCommand}">
|
||||
<TextBlock Margin="10" FontStyle="Normal" Foreground="DarkGray" HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" FontSize="18" Text="{Tr ProxyManagerDialog.Title}" />
|
||||
<Button Margin="0,0,10,0" IsCancel="True" Grid.Column="1" Width="30" Height="30"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}" HorizontalAlignment="Right"
|
||||
Command="{x:Static md:DialogHost.CloseDialogCommand}">
|
||||
<md:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
|
||||
</Button>
|
||||
|
||||
@@ -36,14 +39,15 @@
|
||||
<Separator Grid.Row="1" />
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock HorizontalAlignment="Stretch"
|
||||
<TextBlock HorizontalAlignment="Stretch"
|
||||
Margin="15" FontSize="16">
|
||||
<Run Text="{Tr ProxyManagerDialog.DefaultProxy, IsDynamic=False}"/>
|
||||
<Run Text="{Binding DefaultProxy.Key, StringFormat='
0:', FallbackValue='-', Mode=OneWay}"/>
|
||||
<Run Text="{Binding DefaultProxy.Value, Converter='{StaticResource ProxyDataTextConverter}', Mode=OneWay, FallbackValue=''}"/>
|
||||
<Run Text="{Tr ProxyManagerDialog.DefaultProxy, IsDynamic=False}" />
|
||||
<Run Text="{Binding DefaultProxy.Key, StringFormat='
0:', FallbackValue='-', Mode=OneWay}" />
|
||||
<Run
|
||||
Text="{Binding DefaultProxy.Value, Converter='{StaticResource ProxyDataTextConverter}', Mode=OneWay, FallbackValue=''}" />
|
||||
</TextBlock>
|
||||
|
||||
<Button Grid.Column="1" Command="{Binding RemoveDefaultCommand}">
|
||||
@@ -51,26 +55,29 @@
|
||||
</Button>
|
||||
</Grid>
|
||||
<md:Card Grid.Row="3" Margin="10">
|
||||
<ListBox VirtualizingStackPanel.VirtualizationMode="Recycling" FontSize="14" SelectedValue="{Binding SelectedProxy}" ItemsSource="{Binding Proxies}">
|
||||
<ListBox VirtualizingStackPanel.VirtualizationMode="Recycling" FontSize="14"
|
||||
SelectedValue="{Binding SelectedProxy}" ItemsSource="{Binding Proxies}">
|
||||
<ListBox.InputBindings>
|
||||
<KeyBinding Key="Delete" Command="{Binding DataContext.RemoveProxyCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" />
|
||||
<KeyBinding Key="Delete"
|
||||
Command="{Binding DataContext.RemoveProxyCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" />
|
||||
</ListBox.InputBindings>
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem" BasedOn="{StaticResource MaterialDesignListBoxItem}">
|
||||
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext}"/>
|
||||
<Setter Property="Tag"
|
||||
Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext}" />
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Proxy.Copy}"
|
||||
Command="{Binding PlacementTarget.Tag.CopyProxyCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||
CommandParameter="{Binding Value}"/>
|
||||
Command="{Binding PlacementTarget.Tag.CopyProxyCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||
CommandParameter="{Binding Value}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Proxy.CopyAddress}"
|
||||
Command="{Binding PlacementTarget.Tag.CopyProxyAddressCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||
CommandParameter="{Binding Value}"/>
|
||||
Command="{Binding PlacementTarget.Tag.CopyProxyAddressCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||
CommandParameter="{Binding Value}" />
|
||||
</ContextMenu>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
|
||||
|
||||
</Style>
|
||||
@@ -78,36 +85,41 @@
|
||||
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid >
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center">
|
||||
<Run Text="{Binding Key, Mode=OneWay}"/><Run Text=": "/>
|
||||
<Run Text="{Binding Value, Mode=OneWay, Converter={StaticResource ProxyDataTextConverter}}"/>
|
||||
<TextBlock VerticalAlignment="Center">
|
||||
<Run Text="{Binding Key, Mode=OneWay}" /><Run Text=": " />
|
||||
<Run
|
||||
Text="{Binding Value, Mode=OneWay, Converter={StaticResource ProxyDataTextConverter}}" />
|
||||
|
||||
</TextBlock>
|
||||
<Button Style="{StaticResource MaterialDesignIconButton}" Padding="0" Width="24" Height="24" md:RippleAssist.IsDisabled="True" Grid.Column="1" HorizontalAlignment="Right"
|
||||
Command="{Binding DataContext.SetDefaultCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||
<Button Style="{StaticResource MaterialDesignIconButton}" Padding="0" Width="24"
|
||||
Height="24" md:RippleAssist.IsDisabled="True" Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{Binding DataContext.SetDefaultCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||
CommandParameter="{Binding}">
|
||||
<md:PackIcon Height="16" Width="16" Kind="Heart" />
|
||||
</Button>
|
||||
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</md:Card>
|
||||
<Grid Grid.Row="4" Margin="5,0,15,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Text="{Binding AddProxyField}" FontSize="12" md:TextFieldAssist.HasClearButton="True" AcceptsReturn="True" MaxHeight="100" Style="{StaticResource MaterialDesignOutlinedTextBox}" Margin="15" md:HintAssist.Hint="IP:PORT:USER:PASS{ID}" />
|
||||
<Button IsDefault="True" Grid.Column="1" Command="{Binding AddProxyCommand}">
|
||||
<TextBox Text="{Binding AddProxyField}" FontSize="12" md:TextFieldAssist.HasClearButton="True"
|
||||
AcceptsReturn="True" MaxHeight="100" Style="{StaticResource MaterialDesignOutlinedTextBox}"
|
||||
Margin="15" md:HintAssist.Hint="IP:PORT:USER:PASS{ID}" />
|
||||
<Button IsDefault="True" Grid.Column="1" Command="{Binding AddProxyCommand}">
|
||||
<md:PackIcon Kind="Add" />
|
||||
</Button>
|
||||
<Button Grid.Column="2" Command="{Binding RemoveProxyCommand}">
|
||||
@@ -117,4 +129,4 @@
|
||||
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для ProxyManagerView.xaml
|
||||
/// Логика взаимодействия для ProxyManagerView.xaml
|
||||
/// </summary>
|
||||
public partial class ProxyManagerView
|
||||
{
|
||||
|
||||
@@ -1,71 +1,90 @@
|
||||
<UserControl x:Class="NebulaAuth.View.SettingsView"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="650"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="650"
|
||||
Foreground="WhiteSmoke"
|
||||
FontFamily="{materialDesign:MaterialDesignFont}"
|
||||
MinHeight="600"
|
||||
MinWidth="400"
|
||||
|
||||
|
||||
MaxWidth="200"
|
||||
d:DataContext="{d:DesignInstance other:SettingsVM}"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
<Grid>
|
||||
<Grid Margin="15,10,15,15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock FontStyle="Normal" Foreground="DarkGray" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="18" Text="{Tr SettingsDialog.Title}"/>
|
||||
<Button IsCancel="True" Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}" HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
|
||||
<TextBlock FontStyle="Normal" Foreground="DarkGray" HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" FontSize="18" Text="{Tr SettingsDialog.Title}" />
|
||||
<Button IsCancel="True" Grid.Column="1" Width="30" Height="30"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}" HorizontalAlignment="Right"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
|
||||
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<Separator Grid.Row="1" Margin="0,10,0,0" />
|
||||
|
||||
<StackPanel Grid.Row="2" Margin="10,30,10,10" Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignOutlinedComboBox}" FontSize="16" ItemsSource="{Binding BackgroundModes}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding BackgroundMode}" materialDesign:HintAssist.Hint="{Tr SettingsDialog.BackgroundHint}" />
|
||||
<ComboBox Style="{StaticResource MaterialDesignFloatingHintComboBox}" Margin="0,20,0,0" FontSize="16" ItemsSource="{Binding Languages}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding Language}" materialDesign:HintAssist.Hint="{Tr LanguageWord}" />
|
||||
<CheckBox Margin="0,10,0,0" FontSize="16" IsChecked="{Binding HideToTray}" Content="{Tr SettingsDialog.MinimizeToTray}"/>
|
||||
<CheckBox Margin="0,10,0,0" FontSize="16" IsChecked="{Binding UseIcon}" Content="{Tr SettingsDialog.UseIndicator}" ToolTip="{Tr SettingsDialog.UseIndicatorHint}"/>
|
||||
<materialDesign:ColorPicker IsEnabled="{Binding UseIcon}" Color="{Binding IconColor, Delay=50}" />
|
||||
<CheckBox Margin="0,10,0,5" FontSize="16" IsChecked="{Binding UseBackground}" Content="{Tr SettingsDialog.UseCustomColor}"/>
|
||||
|
||||
<materialDesign:ColorPicker Height="100" IsEnabled="{Binding UseBackground}" Color="{Binding BackgroundColor, Delay=50}" />
|
||||
<StackPanel Grid.Row="2" Margin="10,30,10,10" Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignOutlinedComboBox}" FontSize="16"
|
||||
ItemsSource="{Binding BackgroundModes}" DisplayMemberPath="Value" SelectedValuePath="Key"
|
||||
SelectedValue="{Binding BackgroundMode}"
|
||||
materialDesign:HintAssist.Hint="{Tr SettingsDialog.BackgroundHint}" />
|
||||
<ComboBox Style="{StaticResource MaterialDesignFloatingHintComboBox}" Margin="0,20,0,0" FontSize="16"
|
||||
ItemsSource="{Binding Languages}" DisplayMemberPath="Value" SelectedValuePath="Key"
|
||||
SelectedValue="{Binding Language}" materialDesign:HintAssist.Hint="{Tr LanguageWord}" />
|
||||
<CheckBox Margin="0,10,0,0" FontSize="16" IsChecked="{Binding HideToTray}"
|
||||
Content="{Tr SettingsDialog.MinimizeToTray}" />
|
||||
<CheckBox Margin="0,10,0,0" FontSize="16" IsChecked="{Binding UseIcon}"
|
||||
Content="{Tr SettingsDialog.UseIndicator}" ToolTip="{Tr SettingsDialog.UseIndicatorHint}" />
|
||||
<materialDesign:ColorPicker IsEnabled="{Binding UseIcon}" Color="{Binding IconColor, Delay=50}" />
|
||||
<CheckBox Margin="0,10,0,5" FontSize="16" IsChecked="{Binding UseBackground}"
|
||||
Content="{Tr SettingsDialog.UseCustomColor}" />
|
||||
|
||||
<materialDesign:ColorPicker Height="100" IsEnabled="{Binding UseBackground}"
|
||||
Color="{Binding BackgroundColor, Delay=50}" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<PasswordBox MinWidth="250" materialDesign:PasswordBoxAssist.Password="{Binding Password}" Height="Auto" VerticalAlignment="Center" FontSize="16" Margin="0,10,0,0"
|
||||
Style="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}"
|
||||
materialDesign:HintAssist.Hint="{Tr SettingsDialog.PasswordBox.CurrentCryptPassword}"
|
||||
materialDesign:HintAssist.HelperText="{Tr SettingsDialog.PasswordBox.Hint}" />
|
||||
<Button Command="{Binding SetPasswordCommand}" Margin="5,0,0,0" VerticalAlignment="Bottom" Grid.Column="1"> <materialDesign:PackIcon Kind="ContentSave"/></Button>
|
||||
<PasswordBox MinWidth="250" materialDesign:PasswordBoxAssist.Password="{Binding Password}"
|
||||
Height="Auto" VerticalAlignment="Center" FontSize="16" Margin="0,10,0,0"
|
||||
Style="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}"
|
||||
materialDesign:HintAssist.Hint="{Tr SettingsDialog.PasswordBox.CurrentCryptPassword}"
|
||||
materialDesign:HintAssist.HelperText="{Tr SettingsDialog.PasswordBox.Hint}" />
|
||||
<Button Command="{Binding SetPasswordCommand}" Margin="5,0,0,0" VerticalAlignment="Bottom"
|
||||
Grid.Column="1">
|
||||
<materialDesign:PackIcon Kind="ContentSave" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<CheckBox Margin="0,20,0,0" FontSize="16" IsChecked="{Binding LegacyMode}" Content="{Tr SettingsDialog.LegacyMafileMode}" ToolTip="{Tr SettingsDialog.LegacyMafileModeHint}"/>
|
||||
<CheckBox Margin="0,20,0,0" FontSize="16" IsChecked="{Binding LegacyMode}"
|
||||
Content="{Tr SettingsDialog.LegacyMafileMode}"
|
||||
ToolTip="{Tr SettingsDialog.LegacyMafileModeHint}" />
|
||||
<!--<CheckBox IsEnabled="False" Margin="0,10,0,0" FontSize="16" IsChecked="{Binding AllowAutoUpdate}" Content="{Tr SettingsDialog.AllowAutoUpdate}"/>-->
|
||||
<CheckBox Margin="0,10,0,0" FontSize="16" IsChecked="{Binding UseAccountNameAsMafileName}" >
|
||||
<CheckBox Margin="0,10,0,0" FontSize="16" IsChecked="{Binding UseAccountNameAsMafileName}">
|
||||
<TextBlock TextWrapping="WrapWithOverflow" Text="{Tr SettingsDialog.UseAccountName}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding IgnorePatchTuesdayErrors}" Margin="0,10,0,0" FontSize="16" ToolTip="{Tr SettingsDialog.IgnorePatchTuesdayErrorsHint}">
|
||||
<CheckBox IsChecked="{Binding IgnorePatchTuesdayErrors}" Margin="0,10,0,0" FontSize="16"
|
||||
ToolTip="{Tr SettingsDialog.IgnorePatchTuesdayErrorsHint}">
|
||||
<TextBlock TextWrapping="WrapWithOverflow" Text="{Tr SettingsDialog.IgnorePatchTuesdayErrors}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для SettingsView.xaml
|
||||
/// Логика взаимодействия для SettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class SettingsView
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<UserControl x:Class="NebulaAuth.View.UpdaterView"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
Foreground="WhiteSmoke"
|
||||
FontFamily="{materialDesign:MaterialDesignFont}"
|
||||
d:DataContext="{d:DesignInstance other:UpdaterVM}"
|
||||
@@ -19,22 +19,22 @@
|
||||
</d:DesignerProperties.DesignStyle>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock FontSize="24" Text="Обновления"/>
|
||||
<TextBlock FontSize="24" Text="Обновления" />
|
||||
<Separator Grid.Row="1" />
|
||||
|
||||
<TextBlock FontSize="16" Margin="5,10,0,10" TextWrapping="WrapWithOverflow" Grid.Row="2" >
|
||||
<Run Text="Доступна новая версия:"/>
|
||||
<Run FontWeight="Bold" Text="{Binding UpdateInfoEventArgs.CurrentVersion}"/>
|
||||
<Run Text="
Вы используете версию"/>
|
||||
<Run FontWeight="Bold" Text="{Binding UpdateInfoEventArgs.InstalledVersion}"/>
|
||||
<Run Text="Хотите обновить программу?"/>
|
||||
<TextBlock FontSize="16" Margin="5,10,0,10" TextWrapping="WrapWithOverflow" Grid.Row="2">
|
||||
<Run Text="Доступна новая версия:" />
|
||||
<Run FontWeight="Bold" Text="{Binding UpdateInfoEventArgs.CurrentVersion}" />
|
||||
<Run Text="
Вы используете версию" />
|
||||
<Run FontWeight="Bold" Text="{Binding UpdateInfoEventArgs.InstalledVersion}" />
|
||||
<Run Text="Хотите обновить программу?" />
|
||||
</TextBlock>
|
||||
<Expander Header="Что изменилось?" Grid.Row="3">
|
||||
<!--<wpf:WebView2 HorizontalAlignment="Stretch" Height="300"
|
||||
@@ -43,12 +43,13 @@
|
||||
</Expander>
|
||||
<Grid Grid.Row="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Content="OK" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" />
|
||||
<Button IsCancel="True" Grid.Column="1" Content="Cancel" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" />
|
||||
<Button Content="OK" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" />
|
||||
<Button IsCancel="True" Grid.Column="1" Content="Cancel"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для UpdaterView.xaml
|
||||
/// Логика взаимодействия для UpdaterView.xaml
|
||||
/// </summary>
|
||||
public partial class UpdaterView
|
||||
{
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using JetBrains.Annotations;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
@@ -9,21 +14,12 @@ using NebulaAuth.Utility;
|
||||
using NebulaAuth.View.Dialogs;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.SteamMobile;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
|
||||
public partial class MainVM : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<Mafile> _maFiles = Storage.MaFiles;
|
||||
|
||||
[UsedImplicitly]
|
||||
public SnackbarMessageQueue MessageQueue => SnackbarController.MessageQueue;
|
||||
[UsedImplicitly] public SnackbarMessageQueue MessageQueue => SnackbarController.MessageQueue;
|
||||
|
||||
|
||||
public Mafile? SelectedMafile
|
||||
@@ -31,10 +27,13 @@ public partial class MainVM : ObservableObject
|
||||
get => _selectedMafile;
|
||||
set => SetMafile(value);
|
||||
}
|
||||
private Mafile? _selectedMafile;
|
||||
|
||||
public bool IsMafileSelected => SelectedMafile != null;
|
||||
|
||||
[ObservableProperty] private ObservableCollection<Mafile> _maFiles = Storage.MaFiles;
|
||||
|
||||
private Mafile? _selectedMafile;
|
||||
|
||||
|
||||
public MainVM()
|
||||
{
|
||||
@@ -63,7 +62,7 @@ public partial class MainVM : ObservableObject
|
||||
OnPropertyChanged(nameof(MarketTimerEnabled));
|
||||
MaClient.SetAccount(mafile);
|
||||
OnPropertyChanged(nameof(ConfirmationsVisible));
|
||||
SetCurrentProxy();
|
||||
SetProxy(mafile?.Proxy, true);
|
||||
OnPropertyChanged(nameof(IsDefaultProxy));
|
||||
if (mafile != null) Code = SteamGuardCodeGenerator.GenerateCode(mafile.SharedSecret);
|
||||
OnPropertyChanged(nameof(IsMafileSelected));
|
||||
@@ -78,7 +77,9 @@ public partial class MainVM : ObservableObject
|
||||
{
|
||||
return;
|
||||
}
|
||||
var loginAgainVm = await DialogsController.ShowLoginAgainDialog(SelectedMafile.AccountName);
|
||||
|
||||
var currentPassword = PHandler.DecryptPassword(SelectedMafile.Password);
|
||||
var loginAgainVm = await DialogsController.ShowLoginAgainDialog(SelectedMafile.AccountName, currentPassword);
|
||||
if (loginAgainVm == null)
|
||||
{
|
||||
return;
|
||||
@@ -94,7 +95,8 @@ public partial class MainVM : ObservableObject
|
||||
}
|
||||
catch (LoginException ex)
|
||||
{
|
||||
SnackbarController.SendSnackbar(ErrorTranslatorHelper.TranslateLoginError(ex.Error), TimeSpan.FromSeconds(1.5));
|
||||
SnackbarController.SendSnackbar(ErrorTranslatorHelper.TranslateLoginError(ex.Error),
|
||||
TimeSpan.FromSeconds(1.5));
|
||||
}
|
||||
catch (Exception ex)
|
||||
when (ExceptionHandler.Handle(ex))
|
||||
@@ -107,6 +109,7 @@ public partial class MainVM : ObservableObject
|
||||
await wait;
|
||||
}
|
||||
}
|
||||
|
||||
private void MaFilesOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
SearchText = string.Empty;
|
||||
@@ -141,16 +144,21 @@ public partial class MainVM : ObservableObject
|
||||
if (selectedMafile == null) return;
|
||||
if (string.IsNullOrWhiteSpace(selectedMafile.RevocationCode))
|
||||
{
|
||||
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Error", "Error") + ": " + GetLocalization("MissingRCode"));
|
||||
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Error", "Error") + ": " +
|
||||
GetLocalization("MissingRCode"));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (await DialogsController.ShowConfirmCancelDialog(GetLocalization("ConfirmRemovingAuthenticator")))
|
||||
{
|
||||
var result = await SessionHandler.Handle(() => MaClient.RemoveAuthenticator(selectedMafile), selectedMafile);
|
||||
var result =
|
||||
await SessionHandler.Handle(() => MaClient.RemoveAuthenticator(selectedMafile), selectedMafile);
|
||||
SnackbarController.SendSnackbar(
|
||||
result.Success ? GetLocalization("AuthenticatorRemoved") : GetLocalization("AuthenticatorNotRemoved"));
|
||||
result.Success
|
||||
? GetLocalization("AuthenticatorRemoved")
|
||||
: GetLocalization("AuthenticatorNotRemoved"));
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
@@ -184,10 +192,11 @@ public partial class MainVM : ObservableObject
|
||||
}
|
||||
else
|
||||
{
|
||||
string msg = res.Error switch
|
||||
var msg = res.Error switch
|
||||
{
|
||||
LoginConfirmationError.NoRequests => GetLocalization("ConfirmLoginFailedNoRequests"),
|
||||
LoginConfirmationError.MoreThanOneRequest => GetLocalization("ConfirmLoginFailedMoreThanOneRequest"), //TODO
|
||||
LoginConfirmationError.MoreThanOneRequest =>
|
||||
GetLocalization("ConfirmLoginFailedMoreThanOneRequest"), //TODO
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
SnackbarController.SendSnackbar(msg);
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using SteamLib.SteamMobile;
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using SteamLib.SteamMobile;
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
|
||||
public partial class MainVM
|
||||
{
|
||||
private Timer _codeTimer;
|
||||
[ObservableProperty] private double _codeProgress;
|
||||
[ObservableProperty] private string _code = "Code";
|
||||
[ObservableProperty] private double _codeProgress;
|
||||
private Timer _codeTimer;
|
||||
|
||||
|
||||
[MemberNotNull(nameof(_codeTimer))]
|
||||
@@ -26,7 +26,6 @@ public partial class MainVM
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void UpdateCode(object? state = null)
|
||||
{
|
||||
var currentTime = TimeAligner.GetSteamTime();
|
||||
@@ -70,6 +69,5 @@ public partial class MainVM
|
||||
if (selectedMafile != null)
|
||||
Code = SteamGuardCodeGenerator.GenerateCode(selectedMafile.SharedSecret);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.Utility;
|
||||
using SteamLib.SteamMobile.Confirmations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
|
||||
public partial class MainVM //Confirmations
|
||||
{
|
||||
public ObservableCollection<Confirmation> Confirmations { get; } = new();
|
||||
private Mafile? _confirmationsLoadedForMafile;
|
||||
public bool ConfirmationsVisible => SelectedMafile == _confirmationsLoadedForMafile;
|
||||
private Mafile? _confirmationsLoadedForMafile;
|
||||
|
||||
[RelayCommand]
|
||||
private async Task GetConfirmations()
|
||||
@@ -69,6 +68,7 @@ public partial class MainVM //Confirmations
|
||||
if (SelectedMafile == null || confirmation == null) return Task.CompletedTask;
|
||||
return SendConfirmation(SelectedMafile, confirmation, true);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private Task Cancel(Confirmation? confirmation)
|
||||
{
|
||||
@@ -81,7 +81,7 @@ public partial class MainVM //Confirmations
|
||||
bool result;
|
||||
try
|
||||
{
|
||||
if (confirmation is MarketMultiConfirmation multi)
|
||||
if (confirmation is MarketMultiConfirmation multi)
|
||||
{
|
||||
result = await MaClient.SendMultipleConfirmation(mafile, multi.Confirmations, confirm);
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public partial class MainVM //Confirmations
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
when(ExceptionHandler.Handle(ex))
|
||||
when (ExceptionHandler.Handle(ex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using Microsoft.Win32;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.View;
|
||||
using NebulaAuth.ViewModel.Other;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@@ -13,17 +6,23 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AchiesUtilities.Extensions;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using Microsoft.Win32;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.Model.Exceptions;
|
||||
using SteamLib.Exceptions;
|
||||
using NebulaAuth.Utility;
|
||||
using NebulaAuth.View;
|
||||
using NebulaAuth.View.Dialogs;
|
||||
using NebulaAuth.ViewModel.Other;
|
||||
using SteamLib.Exceptions;
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
|
||||
public partial class MainVM //File //TODO: Refactor
|
||||
{
|
||||
|
||||
public Settings Settings => Settings.Instance;
|
||||
|
||||
[RelayCommand]
|
||||
@@ -37,6 +36,7 @@ public partial class MainVM //File //TODO: Refactor
|
||||
{
|
||||
mafilePath = Storage.TryFindMafilePath(mafile);
|
||||
}
|
||||
|
||||
if (mafilePath != null)
|
||||
{
|
||||
path = $"/select, \"{mafilePath}\"";
|
||||
@@ -60,15 +60,14 @@ public partial class MainVM //File //TODO: Refactor
|
||||
var openFileDialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "Mafile|*.mafile;*.maFile",
|
||||
Multiselect = false,
|
||||
|
||||
Multiselect = false
|
||||
};
|
||||
var fs = openFileDialog.ShowDialog();
|
||||
if (fs != true) return Task.CompletedTask;
|
||||
var path = openFileDialog.FileName;
|
||||
return AddMafile([path]);
|
||||
|
||||
}
|
||||
|
||||
public async Task AddMafile(string[] path)
|
||||
{
|
||||
bool? confirmOverwrite = null;
|
||||
@@ -88,7 +87,8 @@ public partial class MainVM //File //TODO: Refactor
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
confirmOverwrite ??= await DialogsController.ShowConfirmCancelDialog(GetLocalization("ConfirmMafileOverwrite"));
|
||||
confirmOverwrite ??=
|
||||
await DialogsController.ShowConfirmCancelDialog(GetLocalization("ConfirmMafileOverwrite"));
|
||||
|
||||
if (confirmOverwrite == true)
|
||||
{
|
||||
@@ -116,7 +116,9 @@ public partial class MainVM //File //TODO: Refactor
|
||||
}
|
||||
else
|
||||
{
|
||||
SnackbarController.SendSnackbar($"{GetLocalization("MafileImportError")} {Path.GetFileName(str)}{GetLocalization("MissingSessionInMafile")}", TimeSpan.FromSeconds(4));
|
||||
SnackbarController.SendSnackbar(
|
||||
$"{GetLocalization("MafileImportError")} {Path.GetFileName(str)}{GetLocalization("MissingSessionInMafile")}",
|
||||
TimeSpan.FromSeconds(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,6 +128,7 @@ public partial class MainVM //File //TODO: Refactor
|
||||
{
|
||||
msg += $" {GetLocalization("ImportAdded")} {added}.";
|
||||
}
|
||||
|
||||
if (notAdded > 0)
|
||||
{
|
||||
msg += $" {GetLocalization("ImportSkipped")} {notAdded}.";
|
||||
@@ -135,6 +138,7 @@ public partial class MainVM //File //TODO: Refactor
|
||||
{
|
||||
msg += $" {GetLocalization("ImportErrors")} {errors}.";
|
||||
}
|
||||
|
||||
SnackbarController.SendSnackbar(msg, TimeSpan.FromSeconds(2));
|
||||
}
|
||||
|
||||
@@ -151,6 +155,7 @@ public partial class MainVM //File //TODO: Refactor
|
||||
{
|
||||
data.Proxy = loginAgainVm.SelectedProxy;
|
||||
}
|
||||
|
||||
var waitDialog = new WaitLoginDialog();
|
||||
var wait = DialogHost.Show(waitDialog);
|
||||
try
|
||||
@@ -160,7 +165,8 @@ public partial class MainVM //File //TODO: Refactor
|
||||
}
|
||||
catch (LoginException ex)
|
||||
{
|
||||
SnackbarController.SendSnackbar(ErrorTranslatorHelper.TranslateLoginError(ex.Error), TimeSpan.FromSeconds(1.5));
|
||||
SnackbarController.SendSnackbar(ErrorTranslatorHelper.TranslateLoginError(ex.Error),
|
||||
TimeSpan.FromSeconds(1.5));
|
||||
}
|
||||
catch (Exception ex)
|
||||
when (ExceptionHandler.Handle(ex))
|
||||
@@ -172,6 +178,7 @@ public partial class MainVM //File //TODO: Refactor
|
||||
DialogsController.CloseDialog();
|
||||
await wait;
|
||||
}
|
||||
|
||||
var result = data.SessionData != null;
|
||||
if (!result) return result;
|
||||
Storage.SaveMafile(data);
|
||||
@@ -181,7 +188,6 @@ public partial class MainVM //File //TODO: Refactor
|
||||
SelectedMafile = data;
|
||||
} //As this operation used only for 1 mafile at time, we can safely assume that we can select it for convenience
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -211,7 +217,7 @@ public partial class MainVM //File //TODO: Refactor
|
||||
private async Task OpenSettingsDialog()
|
||||
{
|
||||
var vm = new SettingsVM();
|
||||
var view = new SettingsView()
|
||||
var view = new SettingsView
|
||||
{
|
||||
DataContext = vm
|
||||
};
|
||||
@@ -236,15 +242,14 @@ public partial class MainVM //File //TODO: Refactor
|
||||
if (arr.All(p => p.ContainsIgnoreCase("mafile") == false)) return;
|
||||
|
||||
|
||||
|
||||
await AddMafile(arr);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CopyLogin(object? mafile)
|
||||
{
|
||||
if(mafile is not Mafile maf) return;
|
||||
if(ClipboardHelper.Set(maf.AccountName))
|
||||
if (mafile is not Mafile maf) return;
|
||||
if (ClipboardHelper.Set(maf.AccountName))
|
||||
SnackbarController.SendSnackbar(GetLocalization("LoginCopied"));
|
||||
}
|
||||
|
||||
@@ -253,7 +258,7 @@ public partial class MainVM //File //TODO: Refactor
|
||||
{
|
||||
if (mafile is not Mafile maf) return;
|
||||
|
||||
if(ClipboardHelper.Set(maf.SteamId.ToString()))
|
||||
if (ClipboardHelper.Set(maf.SteamId.ToString()))
|
||||
SnackbarController.SendSnackbar(GetLocalization("SteamIdCopied"));
|
||||
}
|
||||
|
||||
@@ -262,7 +267,30 @@ public partial class MainVM //File //TODO: Refactor
|
||||
{
|
||||
if (mafile is not Mafile maf) return;
|
||||
var path = Storage.TryFindMafilePath(maf);
|
||||
if(ClipboardHelper.SetFiles([path]))
|
||||
if (ClipboardHelper.SetFiles([path]))
|
||||
SnackbarController.SendSnackbar(GetLocalization("MafileCopied"));
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanCopyPassword))]
|
||||
private void CopyPassword(object? mafile)
|
||||
{
|
||||
if (mafile is not Mafile maf) return;
|
||||
if (maf.Password == null) return;
|
||||
try
|
||||
{
|
||||
var pass = PHandler.Decrypt(maf.Password);
|
||||
if (ClipboardHelper.Set(pass))
|
||||
SnackbarController.SendSnackbar(GetLocalization("PasswordCopied"));
|
||||
}
|
||||
catch
|
||||
{
|
||||
SnackbarController.SendSnackbar(GetLocalization("CantDecryptPassword"));
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanCopyPassword(object? mafile)
|
||||
{
|
||||
if (mafile is not Mafile maf) return false;
|
||||
return maf.Password != null && PHandler.IsPasswordSet;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,15 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using AchiesUtilities.Extensions;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using AchiesUtilities.Extensions;
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
|
||||
public partial class MainVM //Groups
|
||||
{
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<string> _groups = [];
|
||||
|
||||
|
||||
public string? SelectedGroup
|
||||
{
|
||||
get => _selectedGroup;
|
||||
@@ -24,21 +20,21 @@ public partial class MainVM //Groups
|
||||
}
|
||||
}
|
||||
|
||||
private string? _selectedGroup;
|
||||
|
||||
public string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set
|
||||
{
|
||||
if(SetProperty(ref _searchText, value))
|
||||
if (SetProperty(ref _searchText, value))
|
||||
PerformQuery();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[ObservableProperty] private ObservableCollection<string> _groups = [];
|
||||
|
||||
private string _searchText = string.Empty;
|
||||
|
||||
|
||||
private string? _selectedGroup;
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
@@ -53,7 +49,6 @@ public partial class MainVM //Groups
|
||||
QueryGroups();
|
||||
SelectedGroup = value;
|
||||
OnPropertyChanged(nameof(SelectedMafile)); //For bindings
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -62,8 +57,8 @@ public partial class MainVM //Groups
|
||||
if (value == null) return;
|
||||
if (value.Length < 2) return;
|
||||
|
||||
var group = (string?)value[0];
|
||||
var mafile = (Mafile?)value[1];
|
||||
var group = (string?) value[0];
|
||||
var mafile = (Mafile?) value[1];
|
||||
|
||||
if (group == null || mafile == null) return;
|
||||
mafile.Group = group;
|
||||
@@ -86,6 +81,7 @@ public partial class MainVM //Groups
|
||||
{
|
||||
SelectedGroup = null;
|
||||
}
|
||||
|
||||
PerformQuery();
|
||||
}
|
||||
|
||||
@@ -103,7 +99,6 @@ public partial class MainVM //Groups
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void PerformQuery()
|
||||
{
|
||||
MaacDisplay = false;
|
||||
@@ -112,20 +107,24 @@ public partial class MainVM //Groups
|
||||
MaFiles = Storage.MaFiles;
|
||||
return;
|
||||
}
|
||||
|
||||
long? searchSteamId = null;
|
||||
if (long.TryParse(SearchText, out var steamId))
|
||||
{
|
||||
searchSteamId = steamId;
|
||||
}
|
||||
|
||||
var query = Storage.MaFiles.AsEnumerable();
|
||||
if (!string.IsNullOrWhiteSpace(SearchText))
|
||||
{
|
||||
query = query.Where(SearchPredicate);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(SelectedGroup))
|
||||
{
|
||||
query = query.Where(m => m.Group != null && m.Group.Equals(SelectedGroup));
|
||||
}
|
||||
|
||||
var perform = query.ToList();
|
||||
MaFiles = new ObservableCollection<Mafile>(perform);
|
||||
SelectedMafile = MaFiles.FirstOrDefault();
|
||||
@@ -136,7 +135,7 @@ public partial class MainVM //Groups
|
||||
return mafile.AccountName.ContainsIgnoreCase(SearchText) || mafile.SteamId.Steam64.Id.Equals(searchSteamId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ResetQuery()
|
||||
{
|
||||
_selectedGroup = null;
|
||||
|
||||
@@ -21,13 +21,13 @@ public partial class MainVM //MAAC
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _maacDisplay;
|
||||
|
||||
public bool MarketTimerEnabled
|
||||
{
|
||||
get => SelectedMafile?.LinkedClient?.AutoConfirmMarket ?? false;
|
||||
set => SetMarketTimer(value);
|
||||
}
|
||||
|
||||
public bool TradeTimerEnabled
|
||||
{
|
||||
get => SelectedMafile?.LinkedClient?.AutoConfirmTrades ?? false;
|
||||
@@ -40,6 +40,8 @@ public partial class MainVM //MAAC
|
||||
set => SetTimer(value);
|
||||
}
|
||||
|
||||
private bool _maacDisplay;
|
||||
|
||||
private void SetMarketTimer(bool value)
|
||||
{
|
||||
var selectedMafile = SelectedMafile;
|
||||
@@ -48,7 +50,8 @@ public partial class MainVM //MAAC
|
||||
{
|
||||
MultiAccountAutoConfirmer.TryAddToConfirm(selectedMafile);
|
||||
}
|
||||
if (!value && selectedMafile is { LinkedClient.AutoConfirmTrades: false })
|
||||
|
||||
if (!value && selectedMafile is {LinkedClient.AutoConfirmTrades: false})
|
||||
{
|
||||
MultiAccountAutoConfirmer.RemoveFromConfirm(selectedMafile);
|
||||
}
|
||||
@@ -68,7 +71,7 @@ public partial class MainVM //MAAC
|
||||
MultiAccountAutoConfirmer.TryAddToConfirm(selectedMafile);
|
||||
}
|
||||
|
||||
if (!value && selectedMafile is { LinkedClient.AutoConfirmMarket: false })
|
||||
if (!value && selectedMafile is {LinkedClient.AutoConfirmMarket: false})
|
||||
{
|
||||
MultiAccountAutoConfirmer.RemoveFromConfirm(selectedMafile);
|
||||
}
|
||||
@@ -87,11 +90,13 @@ public partial class MainVM //MAAC
|
||||
{
|
||||
timerCheckSeconds = 10; //Guard
|
||||
}
|
||||
|
||||
if (value < 10)
|
||||
{
|
||||
value = timerCheckSeconds;
|
||||
SnackbarController.SendSnackbar(GetLocalization("TimerTooFast"));
|
||||
}
|
||||
|
||||
Settings.TimerSeconds = value;
|
||||
OnPropertyChanged(nameof(TimerCheckSeconds));
|
||||
if (timerCheckSeconds != TimerCheckSeconds)
|
||||
@@ -110,7 +115,7 @@ public partial class MainVM //MAAC
|
||||
private void SwitchMAACOnGroup(bool market)
|
||||
{
|
||||
var group = SelectedGroup;
|
||||
if(group == null) return;
|
||||
if (group == null) return;
|
||||
var mafilesInGroup = MaFiles.Where(m => group.Equals(m.Group));
|
||||
SwitchMAACOn(mafilesInGroup, market);
|
||||
}
|
||||
@@ -133,22 +138,29 @@ public partial class MainVM //MAAC
|
||||
MultiAccountAutoConfirmer.TryAddToConfirm(mafile);
|
||||
SetCurrentMode(mafile.LinkedClient, turnOn);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var mafile in mafiles)
|
||||
{
|
||||
SetCurrentMode(mafile.LinkedClient, turnOn);
|
||||
if(PretendsToRemove(mafile))
|
||||
if (PretendsToRemove(mafile))
|
||||
MultiAccountAutoConfirmer.RemoveFromConfirm(mafile);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
bool PretendsToRemove(Mafile mafile) => mafile.LinkedClient is {AutoConfirmMarket: false, AutoConfirmTrades: false};
|
||||
bool GetCurrentMode(PortableMaClient linkedClient) => market ? linkedClient.AutoConfirmMarket : linkedClient.AutoConfirmTrades;
|
||||
bool PretendsToRemove(Mafile mafile)
|
||||
{
|
||||
return mafile.LinkedClient is {AutoConfirmMarket: false, AutoConfirmTrades: false};
|
||||
}
|
||||
|
||||
bool GetCurrentMode(PortableMaClient linkedClient)
|
||||
{
|
||||
return market ? linkedClient.AutoConfirmMarket : linkedClient.AutoConfirmTrades;
|
||||
}
|
||||
|
||||
void SetCurrentMode(PortableMaClient? linkedClient, bool value)
|
||||
{
|
||||
if (linkedClient == null) return;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
|
||||
@@ -17,84 +15,56 @@ public partial class MainVM
|
||||
public MaProxy? SelectedProxy
|
||||
{
|
||||
get => _selectedProxy;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedProxy, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(IsDefaultProxy));
|
||||
OnProxyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
set => SetProxy(value, false);
|
||||
}
|
||||
private MaProxy? _selectedProxy;
|
||||
|
||||
public bool IsDefaultProxy => SelectedProxy == null && MaClient.DefaultProxy != null && SelectedMafile != null;
|
||||
private bool _internalProxyRefreshInProgress;
|
||||
|
||||
[ObservableProperty] private bool _proxyExist = true;
|
||||
public bool IsDefaultProxy => SelectedProxy == null && MaClient.DefaultProxy != null && SelectedMafile != null;
|
||||
private bool _handleProxyChange;
|
||||
private MaProxy? _selectedProxy;
|
||||
|
||||
//TODO: Refactor this method
|
||||
private void SetCurrentProxy()
|
||||
private void SetProxy(MaProxy? proxy, bool system)
|
||||
{
|
||||
if (ReferenceEquals(_selectedProxy, SelectedMafile?.Proxy) == false && _selectedProxy?.Equals(SelectedMafile?.Proxy) == false)
|
||||
_handleProxyChange = true;
|
||||
|
||||
if (SelectedMafile == null)
|
||||
if (_internalProxyRefreshInProgress)
|
||||
{
|
||||
SelectedProxy = null;
|
||||
ProxyExist = true;
|
||||
return;
|
||||
}
|
||||
if (SelectedMafile.Proxy == null)
|
||||
|
||||
if (!SetProperty(ref _selectedProxy, proxy, nameof(SelectedProxy)))
|
||||
{
|
||||
SelectedProxy = SelectedMafile.Proxy;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (!system && SelectedMafile != null)
|
||||
{
|
||||
var existed = Proxies.FirstOrDefault(p => p.Id == SelectedMafile.Proxy.Id);
|
||||
|
||||
|
||||
if (existed == null || existed.Data.Equals(SelectedMafile.Proxy.Data) == false)
|
||||
{
|
||||
SelectedProxy = SelectedMafile.Proxy;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedProxy = existed;
|
||||
}
|
||||
SelectedMafile.Proxy = SelectedProxy;
|
||||
Storage.UpdateMafile(SelectedMafile);
|
||||
}
|
||||
|
||||
CheckProxyExist();
|
||||
|
||||
}
|
||||
|
||||
private void CheckProxyExist()
|
||||
{
|
||||
if (SelectedProxy == null)
|
||||
{
|
||||
ProxyExist = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedId = SelectedProxy.Id;
|
||||
ProxyExist = ProxyStorage.Proxies.TryGetValue(selectedId, out var existedProxy)
|
||||
&& SelectedProxy.Data.Equals(existedProxy); //Id is not important in 'Equals()' as we extract it from the dictionary
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OpenProxyManager()
|
||||
{
|
||||
await DialogsController.ShowProxyManager(SelectedProxy);
|
||||
var oldSelection = SelectedProxy;
|
||||
Proxies.Clear();
|
||||
foreach (var kvp in ProxyStorage.Proxies)
|
||||
try
|
||||
{
|
||||
Proxies.Add(new MaProxy(kvp.Key, kvp.Value));
|
||||
var anyChanges = await DialogsController.ShowProxyManager();
|
||||
if (!anyChanges) return;
|
||||
_internalProxyRefreshInProgress = true;
|
||||
Proxies.Clear();
|
||||
foreach (var kvp in ProxyStorage.Proxies)
|
||||
{
|
||||
Proxies.Add(new MaProxy(kvp.Key, kvp.Value));
|
||||
}
|
||||
}
|
||||
SelectedProxy = oldSelection;
|
||||
SetCurrentProxy();
|
||||
OnPropertyChanged(nameof(IsDefaultProxy));
|
||||
_handleProxyChange = false;
|
||||
finally
|
||||
{
|
||||
_internalProxyRefreshInProgress = false;
|
||||
}
|
||||
|
||||
CheckProxyExist();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -103,23 +73,25 @@ public partial class MainVM
|
||||
if (SelectedProxy == null) return;
|
||||
if (SelectedMafile == null) return;
|
||||
SelectedMafile.Proxy = null;
|
||||
SelectedProxy = null;
|
||||
Storage.UpdateMafile(SelectedMafile);
|
||||
ProxyExist = true;
|
||||
SetProxy(null, false); //Not system, triggered by user
|
||||
}
|
||||
|
||||
private void OnProxyChanged()
|
||||
private void CheckProxyExist()
|
||||
{
|
||||
if (_handleProxyChange)
|
||||
if (SelectedProxy == null)
|
||||
{
|
||||
_handleProxyChange = false;
|
||||
return;
|
||||
ProxyExist = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedId = SelectedProxy.Id;
|
||||
ProxyExist = ProxyStorage.Proxies.TryGetValue(selectedId, out var existedProxy)
|
||||
&& SelectedProxy.Data
|
||||
.Equals(
|
||||
existedProxy); //Id is not important in 'Equals()' as we extract it from the dictionary
|
||||
}
|
||||
|
||||
if (SelectedMafile == null) return;
|
||||
ProxyExist = true;
|
||||
SelectedMafile.Proxy = SelectedProxy;
|
||||
Storage.UpdateMafile(SelectedMafile);
|
||||
OnPropertyChanged(nameof(ProxyExist));
|
||||
OnPropertyChanged(nameof(IsDefaultProxy));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
using AchiesUtilities.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AchiesUtilities.Collections;
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -18,14 +26,7 @@ using SteamLib.Exceptions.Mobile;
|
||||
using SteamLib.ProtoCore.Exceptions;
|
||||
using SteamLib.SteamMobile.AuthenticatorLinker;
|
||||
using SteamLib.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace NebulaAuth.ViewModel.Other;
|
||||
|
||||
@@ -33,69 +34,7 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
{
|
||||
private const string LOCALIZATION_KEY = "LinkVM";
|
||||
private static Logger Logger => Shell.Logger;
|
||||
private static Microsoft.Extensions.Logging.ILogger Logger2 => Shell.ExtensionsLogger;
|
||||
#region Properties
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsPasswordFieldVisible))]
|
||||
private bool _isLogin;
|
||||
|
||||
|
||||
[ObservableProperty] private bool _isEmailCode;
|
||||
[ObservableProperty] private bool _isPhoneNumber;
|
||||
[ObservableProperty] private bool _isEmailConfirmation;
|
||||
[ObservableProperty] private bool _isLinkCode;
|
||||
[ObservableProperty] private bool _isCompleted;
|
||||
|
||||
[ObservableProperty] private bool _isFieldVisible = true;
|
||||
|
||||
|
||||
|
||||
[ObservableProperty] private string _fieldText;
|
||||
[ObservableProperty] private string _passFieldText;
|
||||
[ObservableProperty] private string _hintText = GetLocalizationOrDefault("EnterLoginAndPassword");
|
||||
|
||||
private TaskCompletionSource<string> _emailCodeTcs = new();
|
||||
private TaskCompletionSource<long?> _phoneNumberTcs = new();
|
||||
private TaskCompletionSource _emailConfTcs = new();
|
||||
private TaskCompletionSource<string> _linkCodeTcs = new();
|
||||
|
||||
private bool _isLinkStarted;
|
||||
private string _rCode = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(ProceedCommand))]
|
||||
private bool _canProceed = true;
|
||||
public bool IsPasswordFieldVisible => !IsLogin;
|
||||
|
||||
private LoginV2ExecutorOptions _loginV2ExecutorOptions;
|
||||
private SteamAuthenticatorLinker _linker;
|
||||
private MobileSessionData _sessionData;
|
||||
|
||||
#endregion
|
||||
|
||||
#region HttpClient
|
||||
|
||||
private static HttpClient Client { get; }
|
||||
private static HttpClientHandler Handler { get; }
|
||||
private static DynamicProxy Proxy { get; }
|
||||
public ObservableDictionary<int, ProxyData> Proxies => ProxyStorage.Proxies;
|
||||
|
||||
public KeyValuePair<int, ProxyData>? SelectedProxy
|
||||
{
|
||||
get => _selectedProxy;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _selectedProxy, value);
|
||||
SetProxy();
|
||||
}
|
||||
}
|
||||
private KeyValuePair<int, ProxyData>? _selectedProxy;
|
||||
|
||||
|
||||
#endregion
|
||||
private static ILogger Logger2 => Shell.ExtensionsLogger;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
public LinkAccountVM()
|
||||
@@ -142,7 +81,8 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
FieldText = string.Empty;
|
||||
IsFieldVisible = false;
|
||||
HintText = string.Empty;
|
||||
_sessionData = (MobileSessionData)await LoginV2Executor.DoLogin(_loginV2ExecutorOptions, userName, pass);
|
||||
_sessionData =
|
||||
(MobileSessionData) await LoginV2Executor.DoLogin(_loginV2ExecutorOptions, userName, pass);
|
||||
Handler.CookieContainer.SetSteamMobileCookiesWithMobileToken(_sessionData);
|
||||
IsEmailCode = true;
|
||||
}
|
||||
@@ -167,7 +107,6 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
InvokeOnDispatcher(ResetState);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (IsEmailCode == false)
|
||||
@@ -189,7 +128,7 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
{
|
||||
_isLinkStarted = true;
|
||||
var linkOptions = new LinkOptions(Client, LoginV2Executor.NullConsumer, this,
|
||||
null, this, this, backupHandler: Backup, Logger2);
|
||||
null, this, this, Backup, Logger2);
|
||||
_linker = new SteamAuthenticatorLinker(linkOptions);
|
||||
var result = await _linker.LinkAccount(_sessionData);
|
||||
IsLinkCode = true;
|
||||
@@ -224,7 +163,6 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
HintText = $"{GetLocalizationCommon("Error")}: {ErrorTranslatorHelper.TranslateLinkerError(ex.Error)}";
|
||||
InvokeOnDispatcher(ResetState);
|
||||
return;
|
||||
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
@@ -249,7 +187,7 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
}
|
||||
|
||||
|
||||
linkStarted:
|
||||
linkStarted:
|
||||
if (IsPhoneNumber == false)
|
||||
{
|
||||
var phoneText = FieldText;
|
||||
@@ -260,31 +198,30 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
HintText = string.Empty;
|
||||
IsFieldVisible = false;
|
||||
_phoneNumberTcs.SetResult(null);
|
||||
_phoneNumberTcs = new();
|
||||
_phoneNumberTcs = new TaskCompletionSource<long?>();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(phoneText) && phoneText.Length >= 4 && long.TryParse(phoneText, out var phone))
|
||||
if (!string.IsNullOrWhiteSpace(phoneText) && phoneText.Length >= 4 &&
|
||||
long.TryParse(phoneText, out var phone))
|
||||
{
|
||||
HintText = string.Empty;
|
||||
IsFieldVisible = false;
|
||||
_phoneNumberTcs.SetResult(phone);
|
||||
_phoneNumberTcs = new();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
HintText = GetLocalizationOrDefault("PleaseEnterCorrectPhone");
|
||||
CanProceed = true;
|
||||
_phoneNumberTcs = new TaskCompletionSource<long?>();
|
||||
return;
|
||||
}
|
||||
|
||||
HintText = GetLocalizationOrDefault("PleaseEnterCorrectPhone");
|
||||
CanProceed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsEmailConfirmation == false)
|
||||
{
|
||||
HintText = string.Empty;
|
||||
_emailConfTcs.SetResult();
|
||||
_emailConfTcs = new();
|
||||
_emailConfTcs = new TaskCompletionSource();
|
||||
CanProceed = false;
|
||||
return;
|
||||
}
|
||||
@@ -298,17 +235,14 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
HintText = string.Empty;
|
||||
IsFieldVisible = false;
|
||||
_linkCodeTcs.SetResult(linkCode);
|
||||
_linkCodeTcs = new();
|
||||
return;
|
||||
_linkCodeTcs = new TaskCompletionSource<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
HintText = GetLocalizationOrDefault("PleaseEnterCorrectCode");
|
||||
CanProceed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -344,13 +278,113 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
{
|
||||
Directory.CreateDirectory("mafiles_backup");
|
||||
}
|
||||
|
||||
var json = NebulaSerializer.SerializeMafile(data, null);
|
||||
File.WriteAllText(Path.Combine("mafiles_backup", data.AccountName + ".mafile"), json); //TODO: Move logic to Storage
|
||||
File.WriteAllText(Path.Combine("mafiles_backup", data.AccountName + ".mafile"),
|
||||
json); //TODO: Move logic to Storage
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenTroubleshooting()
|
||||
{
|
||||
const string troubleshootingURI =
|
||||
"https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/docs/{0}/LinkingTroubleshooting";
|
||||
|
||||
var localized = string.Format(troubleshootingURI, LocManager.GetCurrentLanguageCode());
|
||||
Process.Start(new ProcessStartInfo(new Uri(localized).ToString())
|
||||
{
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CopyCode()
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetText(_rCode);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Shell.Logger.Error(ex, "Error whily copying RCode");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static string GetLocalizationOrDefault(string key)
|
||||
{
|
||||
return LocManager.GetCodeBehindOrDefault(key, LOCALIZATION_KEY, key);
|
||||
}
|
||||
|
||||
private static string GetLocalizationCommon(string key)
|
||||
{
|
||||
return LocManager.GetCommonOrDefault(key, key);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
[ObservableProperty] [NotifyPropertyChangedFor(nameof(IsPasswordFieldVisible))]
|
||||
private bool _isLogin;
|
||||
|
||||
|
||||
[ObservableProperty] private bool _isEmailCode;
|
||||
[ObservableProperty] private bool _isPhoneNumber;
|
||||
[ObservableProperty] private bool _isEmailConfirmation;
|
||||
[ObservableProperty] private bool _isLinkCode;
|
||||
[ObservableProperty] private bool _isCompleted;
|
||||
|
||||
[ObservableProperty] private bool _isFieldVisible = true;
|
||||
|
||||
|
||||
[ObservableProperty] private string _fieldText;
|
||||
[ObservableProperty] private string _passFieldText;
|
||||
[ObservableProperty] private string _hintText = GetLocalizationOrDefault("EnterLoginAndPassword");
|
||||
|
||||
private TaskCompletionSource<string> _emailCodeTcs = new();
|
||||
private TaskCompletionSource<long?> _phoneNumberTcs = new();
|
||||
private TaskCompletionSource _emailConfTcs = new();
|
||||
private TaskCompletionSource<string> _linkCodeTcs = new();
|
||||
|
||||
private bool _isLinkStarted;
|
||||
private string _rCode = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
|
||||
[ObservableProperty] [NotifyCanExecuteChangedFor(nameof(ProceedCommand))]
|
||||
private bool _canProceed = true;
|
||||
|
||||
public bool IsPasswordFieldVisible => !IsLogin;
|
||||
|
||||
private LoginV2ExecutorOptions _loginV2ExecutorOptions;
|
||||
private SteamAuthenticatorLinker _linker;
|
||||
private MobileSessionData _sessionData;
|
||||
|
||||
#endregion
|
||||
|
||||
#region HttpClient
|
||||
|
||||
private static HttpClient Client { get; }
|
||||
private static HttpClientHandler Handler { get; }
|
||||
private static DynamicProxy Proxy { get; }
|
||||
public ObservableDictionary<int, ProxyData> Proxies => ProxyStorage.Proxies;
|
||||
|
||||
public KeyValuePair<int, ProxyData>? SelectedProxy
|
||||
{
|
||||
get => _selectedProxy;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _selectedProxy, value);
|
||||
SetProxy();
|
||||
}
|
||||
}
|
||||
|
||||
private KeyValuePair<int, ProxyData>? _selectedProxy;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Providers
|
||||
|
||||
public int MaxRetryCount { get; }
|
||||
|
||||
public Task<string> GetEmailAuthCode(ILoginConsumer caller)
|
||||
{
|
||||
CanProceed = true;
|
||||
@@ -384,6 +418,7 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
IsFieldVisible = true;
|
||||
return _phoneNumberTcs.Task;
|
||||
}
|
||||
|
||||
public async Task<int> GetSmsCode(ILoginConsumer caller, long? phoneNumber, string? hint)
|
||||
{
|
||||
IsPhoneNumber = true;
|
||||
@@ -402,7 +437,7 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
|
||||
static LinkAccountVM()
|
||||
{
|
||||
Proxy = new DynamicProxy(null);
|
||||
Proxy = new DynamicProxy();
|
||||
var clientPair = ClientBuilder.BuildMobileClient(Proxy, null);
|
||||
Client = clientPair.Client;
|
||||
Handler = clientPair.Handler;
|
||||
@@ -419,44 +454,4 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenTroubleshooting()
|
||||
{
|
||||
const string troubleshootingURI =
|
||||
"https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/docs/{0}/LinkingTroubleshooting";
|
||||
|
||||
var localized = string.Format(troubleshootingURI, LocManager.GetCurrentLanguageCode());
|
||||
Process.Start(new ProcessStartInfo(new Uri(localized).ToString())
|
||||
{
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CopyCode()
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetText(_rCode);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Shell.Logger.Error(ex, "Error whily copying RCode");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static string GetLocalizationOrDefault(string key)
|
||||
{
|
||||
return LocManager.GetCodeBehindOrDefault(key, LOCALIZATION_KEY, key);
|
||||
}
|
||||
|
||||
private static string GetLocalizationCommon(string key)
|
||||
{
|
||||
return LocManager.GetCommonOrDefault(key, key);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,20 +1,14 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Model.Entities;
|
||||
|
||||
namespace NebulaAuth.ViewModel.Other;
|
||||
|
||||
public partial class LoginAgainOnImportVM : ObservableObject
|
||||
{
|
||||
public ObservableCollection<MaProxy> Proxies { get; } = new();
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsFormValid))]
|
||||
private string _password = null!;
|
||||
[ObservableProperty] private bool _savePassword;
|
||||
[ObservableProperty] private string _userName = null!;
|
||||
[ObservableProperty] private bool _mafileHasProxy;
|
||||
|
||||
public MaProxy? SelectedProxy
|
||||
{
|
||||
@@ -27,6 +21,7 @@ public partial class LoginAgainOnImportVM : ObservableObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseMafileProxy
|
||||
{
|
||||
get => _useMafileProxy;
|
||||
@@ -40,20 +35,29 @@ public partial class LoginAgainOnImportVM : ObservableObject
|
||||
}
|
||||
|
||||
public bool IsFormValid => !string.IsNullOrWhiteSpace(Password);
|
||||
[ObservableProperty] private bool _mafileHasProxy;
|
||||
|
||||
[ObservableProperty] [NotifyPropertyChangedFor(nameof(IsFormValid))]
|
||||
private string _password = null!;
|
||||
|
||||
[ObservableProperty] private bool _savePassword;
|
||||
|
||||
|
||||
private MaProxy? _selectedProxy;
|
||||
private bool _useMafileProxy;
|
||||
[ObservableProperty] private string _userName = null!;
|
||||
|
||||
public LoginAgainOnImportVM(Mafile mafile, IEnumerable<MaProxy> proxies)
|
||||
{
|
||||
UserName = mafile.AccountName;
|
||||
MafileHasProxy = mafile.Proxy != null;
|
||||
UseMafileProxy = MafileHasProxy;
|
||||
Proxies = new(proxies);
|
||||
Proxies = new ObservableCollection<MaProxy>(proxies);
|
||||
}
|
||||
|
||||
public LoginAgainOnImportVM()
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void RemoveProxy()
|
||||
|
||||
@@ -4,16 +4,12 @@ namespace NebulaAuth.ViewModel.Other;
|
||||
|
||||
public partial class LoginAgainVM : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsFormValid))]
|
||||
public bool IsFormValid => !string.IsNullOrWhiteSpace(Password);
|
||||
|
||||
[ObservableProperty] [NotifyPropertyChangedFor(nameof(IsFormValid))]
|
||||
private string _password = null!;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _savePassword;
|
||||
[ObservableProperty] private bool _savePassword;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _userName = null!;
|
||||
|
||||
|
||||
public bool IsFormValid => !string.IsNullOrWhiteSpace(Password);
|
||||
[ObservableProperty] private string _userName = null!;
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
using AchiesUtilities.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using AchiesUtilities.Collections;
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
|
||||
namespace NebulaAuth.ViewModel.Other;
|
||||
|
||||
@@ -16,13 +16,14 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
{
|
||||
private const string LOCALIZATION_KEY = "ProxyManagerVM";
|
||||
|
||||
[ObservableProperty] private KeyValuePair<int, ProxyData>? _selectedProxy;
|
||||
[ObservableProperty] private string _addProxyField = string.Empty;
|
||||
[ObservableProperty] private KeyValuePair<int, ProxyData>? _defaultProxy;
|
||||
private static readonly Regex IdRegex = new(@"\{(\d+)\}$", RegexOptions.Compiled);
|
||||
public ObservableDictionary<int, ProxyData> Proxies => ProxyStorage.Proxies;
|
||||
|
||||
private static readonly Regex IdRegex = new(@"\{(\d+)\}$", RegexOptions.Compiled);
|
||||
public bool AnyChanges { get; private set; }
|
||||
[ObservableProperty] private string _addProxyField = string.Empty;
|
||||
[ObservableProperty] private KeyValuePair<int, ProxyData>? _defaultProxy;
|
||||
|
||||
[ObservableProperty] private KeyValuePair<int, ProxyData>? _selectedProxy;
|
||||
|
||||
public ProxyManagerVM()
|
||||
{
|
||||
@@ -33,6 +34,7 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
[RelayCommand]
|
||||
private void AddProxy()
|
||||
{
|
||||
AnyChanges = true;
|
||||
var input = AddProxyField;
|
||||
if (string.IsNullOrEmpty(input)) return;
|
||||
|
||||
@@ -69,7 +71,6 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (ProxyStorage.DefaultScheme.TryParse(str, out var proxy))
|
||||
{
|
||||
if (id != null && proxies.Any(kvp => kvp.Key == id))
|
||||
@@ -77,6 +78,7 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
SnackbarController.SendSnackbar(string.Format(GetLocalizationOrDefault("DuplicateId"), id));
|
||||
return;
|
||||
}
|
||||
|
||||
proxies.Add(KeyValuePair.Create(id, proxy));
|
||||
}
|
||||
else
|
||||
@@ -86,6 +88,7 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
SnackbarController.SendSnackbar(GetLocalizationOrDefault("WrongFormat"));
|
||||
return;
|
||||
}
|
||||
|
||||
SnackbarController.SendSnackbar(string.Format(GetLocalizationOrDefault("WrongFormatOnLine"), i));
|
||||
return;
|
||||
}
|
||||
@@ -108,6 +111,7 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
[RelayCommand]
|
||||
private void RemoveProxy()
|
||||
{
|
||||
AnyChanges = true;
|
||||
var selected = SelectedProxy;
|
||||
if (selected == null) return;
|
||||
var s = selected.Value;
|
||||
@@ -137,6 +141,7 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
private void SetDefault(object? arg)
|
||||
{
|
||||
if (arg is not KeyValuePair<int, ProxyData> proxy) return;
|
||||
AnyChanges = true;
|
||||
DefaultProxy = proxy;
|
||||
MaClient.DefaultProxy = proxy.Value;
|
||||
ProxyStorage.Save();
|
||||
@@ -145,6 +150,7 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
[RelayCommand]
|
||||
private void RemoveDefault()
|
||||
{
|
||||
AnyChanges = true;
|
||||
DefaultProxy = null;
|
||||
MaClient.DefaultProxy = null;
|
||||
ProxyStorage.Save();
|
||||
@@ -162,7 +168,6 @@ public partial class ProxyManagerVM : ObservableObject
|
||||
{
|
||||
Shell.Logger.Error(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Model;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
|
||||
namespace NebulaAuth.ViewModel.Other;
|
||||
|
||||
@@ -27,7 +27,7 @@ public partial class SettingsVM : ObservableObject
|
||||
{
|
||||
{BackgroundMode.Default, LocManager.GetOrDefault("Default", "SettingsDialog", "BackgroundMode", "Default")},
|
||||
{BackgroundMode.Custom, LocManager.GetOrDefault("Default", "SettingsDialog", "BackgroundMode", "Custom")},
|
||||
{BackgroundMode.Color, LocManager.GetOrDefault("Default", "SettingsDialog", "BackgroundMode", "NoBackground")},
|
||||
{BackgroundMode.Color, LocManager.GetOrDefault("Default", "SettingsDialog", "BackgroundMode", "NoBackground")}
|
||||
};
|
||||
|
||||
public Dictionary<LocalizationLanguage, string> Languages { get; } = new()
|
||||
@@ -47,7 +47,6 @@ public partial class SettingsVM : ObservableObject
|
||||
{
|
||||
get => Settings.IconColor;
|
||||
set => Settings.IconColor = value;
|
||||
|
||||
}
|
||||
|
||||
public bool UseIcon
|
||||
@@ -81,7 +80,6 @@ public partial class SettingsVM : ObservableObject
|
||||
}
|
||||
|
||||
|
||||
|
||||
public LocalizationLanguage Language
|
||||
{
|
||||
get => Settings.Language;
|
||||
@@ -124,5 +122,4 @@ public partial class SettingsVM : ObservableObject
|
||||
{
|
||||
Settings.IsPasswordSet = PHandler.SetPassword(Password);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,8 +5,8 @@ namespace NebulaAuth.ViewModel.Other;
|
||||
|
||||
public class UpdaterVM : ObservableObject
|
||||
{
|
||||
|
||||
public UpdateInfoEventArgs UpdateInfoEventArgs { get; }
|
||||
|
||||
public UpdaterVM(UpdateInfoEventArgs args)
|
||||
{
|
||||
UpdateInfoEventArgs = args;
|
||||
|
||||
@@ -70,9 +70,12 @@
|
||||
"ua": "Завантаження..."
|
||||
},
|
||||
"StartTip": {
|
||||
"ru": "Чтобы начать пользоваться программой вы можете привязать аккаунт через меню \"Аккаунт\", либо импортировать существующие мафайлы одним из способов:\n1. Скопировать их в папку mafiles и перезапустить приложение\n2. Перетянуть файлы прямо в окно программы\n3. Скопировать файлы и нажать CTRL+V в окне программы\n4. Через меню \"Файл\" - \"Импорт\"",
|
||||
"en": "To start using the program, you can link an account through the \"Account\" menu, or import existing mafiles in one of the following ways:\n1. Copy them to the mafiles folder and restart the application\n2. Drag files directly into the program window\n3. Copy files and press CTRL+V in the program window\n4. Through the \"File\" - \"Import\" menu",
|
||||
"ua": "Щоб почати користуватися програмою, ви можете прив'язати акаунт через меню \"Акаунт\", або імпортувати існуючі мафайли одним із способів:\n1. Скопіювати їх у папку mafiles та перезапустити програму\n2. Перетягнути файли безпосередньо в вікно програми\n3. Скопіювати файли та натиснути CTRL+V в вікні програми\n4. Через меню \"Файл\" - \"Імпорт\""
|
||||
"ru":
|
||||
"Чтобы начать пользоваться программой вы можете привязать аккаунт через меню \"Аккаунт\", либо импортировать существующие мафайлы одним из способов:\n1. Скопировать их в папку mafiles и перезапустить приложение\n2. Перетянуть файлы прямо в окно программы\n3. Скопировать файлы и нажать CTRL+V в окне программы\n4. Через меню \"Файл\" - \"Импорт\"",
|
||||
"en":
|
||||
"To start using the program, you can link an account through the \"Account\" menu, or import existing mafiles in one of the following ways:\n1. Copy them to the mafiles folder and restart the application\n2. Drag files directly into the program window\n3. Copy files and press CTRL+V in the program window\n4. Through the \"File\" - \"Import\" menu",
|
||||
"ua":
|
||||
"Щоб почати користуватися програмою, ви можете прив'язати акаунт через меню \"Акаунт\", або імпортувати існуючі мафайли одним із способів:\n1. Скопіювати їх у папку mafiles та перезапустити програму\n2. Перетягнути файли безпосередньо в вікно програми\n3. Скопіювати файли та натиснути CTRL+V в вікні програми\n4. Через меню \"Файл\" - \"Імпорт\""
|
||||
}
|
||||
},
|
||||
"Menu": {
|
||||
@@ -283,6 +286,11 @@
|
||||
"en": "Remove from group",
|
||||
"ru": "Удалить из группы",
|
||||
"ua": "Видалити з групи"
|
||||
},
|
||||
"CopyPassword": {
|
||||
"en": "Copy password",
|
||||
"ru": "Скопировать пароль",
|
||||
"ua": "Скопіювати пароль"
|
||||
}
|
||||
},
|
||||
"Proxy": {
|
||||
@@ -385,9 +393,12 @@
|
||||
"ua": "Ігнорувати помилки Patch Tuesday у таймері (експ.)"
|
||||
},
|
||||
"IgnorePatchTuesdayErrorsHint": {
|
||||
"en": "Ignore errors that occur every Tuesday (Wednesday night in GMT) when Steam doing technical works and auto-confirm timers turns off unnecessarily",
|
||||
"ru": "Игнорировать ошибки, которые возникают каждый вторник (ночь среды по GMT) когда Steam проводит технические работы и автоподтверждения отключаются без надобности",
|
||||
"ua": "Ігнорувати помилки, які виникають щосереди (ніч середи за GMT) коли Steam проводить технічні роботи і автопідтвердження вимикаються без потреби"
|
||||
"en":
|
||||
"Ignore errors that occur every Tuesday (Wednesday night in GMT) when Steam doing technical works and auto-confirm timers turns off unnecessarily",
|
||||
"ru":
|
||||
"Игнорировать ошибки, которые возникают каждый вторник (ночь среды по GMT) когда Steam проводит технические работы и автоподтверждения отключаются без надобности",
|
||||
"ua":
|
||||
"Ігнорувати помилки, які виникають щосереди (ніч середи за GMT) коли Steam проводить технічні роботи і автопідтвердження вимикаються без потреби"
|
||||
}
|
||||
|
||||
|
||||
@@ -517,9 +528,12 @@
|
||||
},
|
||||
"SetEncryptedPasswordDialog": {
|
||||
"DialogText": {
|
||||
"en": "You have previously set an encryption password for passwords in mafiles, specify it so that the application can automatically log in in case of problems with the session. (Optional)",
|
||||
"ru": "Ранее вы устанавливали пароль шифрования для паролей в мафайлах, укажите его, чтобы приложение могло автоматически авторизоваться в случае проблем с сессией. (Не обязательно)",
|
||||
"ua": "Раніше ви встановлювали пароль шифрування для паролів в мафайлах, вкажіть його, щоб додаток міг автоматично авторизуватися у випадку проблем з сесією. (Не обов'язково)"
|
||||
"en":
|
||||
"You have previously set an encryption password for passwords in mafiles, specify it so that the application can automatically log in in case of problems with the session. (Optional)",
|
||||
"ru":
|
||||
"Ранее вы устанавливали пароль шифрования для паролей в мафайлах, укажите его, чтобы приложение могло автоматически авторизоваться в случае проблем с сессией. (Не обязательно)",
|
||||
"ua":
|
||||
"Раніше ви встановлювали пароль шифрування для паролів в мафайлах, вкажіть його, щоб додаток міг автоматично авторизуватися у випадку проблем з сесією. (Не обов'язково)"
|
||||
},
|
||||
"Ok": {
|
||||
"en": "Ok",
|
||||
@@ -625,9 +639,12 @@
|
||||
"ua": "помилки:"
|
||||
},
|
||||
"RemoveMafileConfirmation": {
|
||||
"ru": "Удалить мафайл? Мафайл будет перемещен в папку 'mafiles_removed' (Это действие не удаляет Steam Guard с аккаунта)",
|
||||
"en": "Remove mafile? Mafile will be moved to 'mafiles_removed' directory (This action does not remove the Steam Guard from the account)",
|
||||
"ua": "Видалити мафайл? Мафайл буде переміщено у каталог 'mafiles_removed' (Ця дія не видаляє автентифікатор з облікового запису)"
|
||||
"ru":
|
||||
"Удалить мафайл? Мафайл будет перемещен в папку 'mafiles_removed' (Это действие не удаляет Steam Guard с аккаунта)",
|
||||
"en":
|
||||
"Remove mafile? Mafile will be moved to 'mafiles_removed' directory (This action does not remove the Steam Guard from the account)",
|
||||
"ua":
|
||||
"Видалити мафайл? Мафайл буде переміщено у каталог 'mafiles_removed' (Ця дія не видаляє автентифікатор з облікового запису)"
|
||||
},
|
||||
"CantRemoveAlreadyExist": {
|
||||
"ru": "Не удалось переместить мафайл, возможно в папке уже существует мафайл с таким именем.",
|
||||
@@ -678,6 +695,17 @@
|
||||
"en": "Mafile not copied",
|
||||
"ru": "Мафайл не скопирован",
|
||||
"ua": "Мафайл не скопійовано"
|
||||
},
|
||||
"PasswordCopied": {
|
||||
"en": "Password copied",
|
||||
"ru": "Пароль скопирован",
|
||||
"ua": "Пароль скопійовано"
|
||||
},
|
||||
"CantDecryptPassword": {
|
||||
"en": "Can't decrypt password. Maybe password from this mafile was encrypted with another encryption password",
|
||||
"ru":
|
||||
"Не удалось расшифровать пароль. Возможно пароль из этого мафайла был зашифрован другим паролем шифрования",
|
||||
"ua": "Не вдалося розшифрувати пароль. Можливо пароль з цього мафайла був зашифрований іншим паролем шифрування"
|
||||
}
|
||||
},
|
||||
"ErrorTranslator": {
|
||||
@@ -927,8 +955,10 @@
|
||||
},
|
||||
"InvalidStateWithStatus2": {
|
||||
"ru": "Неверное состояние (InvalidState) со статусом 2. Откройте ссылку на руководство сверху этого окна.",
|
||||
"en": "Invalid state (InvalidState) with status 2. Open link to the troubleshooting guide at the top of this window.",
|
||||
"ua": "Невірний стан (InvalidState) зі статусом 2. Відкрийте посилання на посібник з усунення несправностей у верхній частині цього вікна."
|
||||
"en":
|
||||
"Invalid state (InvalidState) with status 2. Open link to the troubleshooting guide at the top of this window.",
|
||||
"ua":
|
||||
"Невірний стан (InvalidState) зі статусом 2. Відкрийте посилання на посібник з усунення несправностей у верхній частині цього вікна."
|
||||
},
|
||||
"GeneralFailure": {
|
||||
"ru": "General Failure",
|
||||
@@ -1098,12 +1128,20 @@
|
||||
"ru": "Авто ",
|
||||
"en": "Auto ",
|
||||
"ua": "Авто "
|
||||
},
|
||||
"TimerPreventedOverlap": {
|
||||
"ru": "Авто: таймер подтверждений пытался начать новый цикл, когда предыдущий еще не завершился. Советуем увеличить задержку",
|
||||
"en": "Auto: confirmation timer tried to start new cycle when previous one was not finished yet. We recommend to increase delay",
|
||||
"ua": "Авто: таймер підтверджень намагався запустити новий цикл, коли попередній ще не закінчився. Рекомендуємо збільшити затримку"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CantAlignTimeError": {
|
||||
"ru": "Не удается синхронизировать время со Steam. Возможно отсутствует подключение к интернету или Steam недоступен. Подробная ошибка сохранена в log.log",
|
||||
"en": "Can't align time with Steam. Maybe no internet connection or Steam is unavailable. Detailed error saved in log.log",
|
||||
"ua": "Не вдається синхронізувати час з Steam. Можливо відсутнє підключення до інтернету або Steam недоступний. Детальна помилка збережена в log.log"
|
||||
"ru":
|
||||
"Не удается синхронизировать время со Steam. Возможно отсутствует подключение к интернету или Steam недоступен. Подробная ошибка сохранена в log.log",
|
||||
"en":
|
||||
"Can't align time with Steam. Maybe no internet connection or Steam is unavailable. Detailed error saved in log.log",
|
||||
"ua":
|
||||
"Не вдається синхронізувати час з Steam. Можливо відсутнє підключення до інтернету або Steam недоступний. Детальна помилка збережена в log.log"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<item>
|
||||
<version>1.5.5.0</version>
|
||||
<url>https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.5.5/NebulaAuth.1.5.5.zip</url>
|
||||
<changelog>https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.5.5.html</changelog>
|
||||
<version>1.5.6.0</version>
|
||||
<url>https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.5.6/NebulaAuth.1.5.6.zip</url>
|
||||
<changelog>https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.5.6.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
</item>
|
||||
@@ -1,7 +1,7 @@
|
||||
using SteamLib.Core.Enums;
|
||||
using SteamLib.Core.Interfaces;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
using SteamLib.Core.Enums;
|
||||
using SteamLib.Core.Interfaces;
|
||||
using SteamLib.Core.Models;
|
||||
|
||||
namespace SteamLib.Account;
|
||||
@@ -12,21 +12,6 @@ public sealed class MobileSessionData : SessionData, IMobileSessionData
|
||||
{
|
||||
public SteamAuthToken? MobileToken { get; private set; }
|
||||
|
||||
public SteamAuthToken? GetMobileToken() => MobileToken;
|
||||
|
||||
|
||||
[MemberNotNull(nameof(MobileToken))]
|
||||
public void SetMobileToken(SteamAuthToken token)
|
||||
{
|
||||
if (token.Type != SteamAccessTokenType.Mobile)
|
||||
throw new ArgumentException("Token must be of type MobileAccess", nameof(token))
|
||||
{
|
||||
Data = { { "ActualType", token.Type } }
|
||||
};
|
||||
|
||||
MobileToken = token;
|
||||
}
|
||||
|
||||
[JsonConstructor]
|
||||
public MobileSessionData(string sessionId, SteamId steamId, SteamAuthToken refreshToken,
|
||||
SteamAuthToken? mobileToken, IDictionary<SteamDomain, SteamAuthToken>? tokens)
|
||||
@@ -42,7 +27,29 @@ public sealed class MobileSessionData : SessionData, IMobileSessionData
|
||||
MobileToken = mobileToken;
|
||||
}
|
||||
|
||||
public override MobileSessionData Clone() => (MobileSessionData)((ISessionData)this).Clone();
|
||||
public SteamAuthToken? GetMobileToken()
|
||||
{
|
||||
return MobileToken;
|
||||
}
|
||||
|
||||
|
||||
[MemberNotNull(nameof(MobileToken))]
|
||||
public void SetMobileToken(SteamAuthToken token)
|
||||
{
|
||||
if (token.Type != SteamAccessTokenType.Mobile)
|
||||
throw new ArgumentException("Token must be of type MobileAccess", nameof(token))
|
||||
{
|
||||
Data = {{"ActualType", token.Type}}
|
||||
};
|
||||
|
||||
MobileToken = token;
|
||||
}
|
||||
|
||||
public override MobileSessionData Clone()
|
||||
{
|
||||
return (MobileSessionData) ((ISessionData) this).Clone();
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return new MobileSessionData(SessionId, SteamId, RefreshToken, MobileToken, Tokens);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Concurrent;
|
||||
using Newtonsoft.Json;
|
||||
using SteamLib.Core.Enums;
|
||||
using SteamLib.Core.Interfaces;
|
||||
using SteamLib.Core.Models;
|
||||
using SteamLib.Web.Converters;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace SteamLib.Account;
|
||||
|
||||
@@ -16,25 +16,30 @@ public class SessionData : ISessionData
|
||||
|
||||
[JsonConverter(typeof(SteamIdToSteam64Converter))]
|
||||
public SteamId SteamId { get; }
|
||||
|
||||
public SteamAuthToken RefreshToken { get; }
|
||||
public ConcurrentDictionary<SteamDomain, SteamAuthToken> Tokens { get; }
|
||||
|
||||
[JsonConstructor]
|
||||
public SessionData(string sessionId, SteamId steamId, SteamAuthToken refreshToken, IDictionary<SteamDomain, SteamAuthToken>? tokens)
|
||||
public SessionData(string sessionId, SteamId steamId, SteamAuthToken refreshToken,
|
||||
IDictionary<SteamDomain, SteamAuthToken>? tokens)
|
||||
{
|
||||
SessionId = sessionId;
|
||||
SteamId = steamId;
|
||||
RefreshToken = refreshToken;
|
||||
Tokens = new ConcurrentDictionary<SteamDomain, SteamAuthToken>(tokens ?? new Dictionary<SteamDomain, SteamAuthToken>());
|
||||
Tokens = new ConcurrentDictionary<SteamDomain, SteamAuthToken>(tokens ??
|
||||
new Dictionary<SteamDomain, SteamAuthToken>());
|
||||
}
|
||||
|
||||
public SessionData(string sessionId, SteamId steamId, SteamAuthToken refreshToken, IEnumerable<SteamAuthToken>? tokensCollection)
|
||||
public SessionData(string sessionId, SteamId steamId, SteamAuthToken refreshToken,
|
||||
IEnumerable<SteamAuthToken>? tokensCollection)
|
||||
{
|
||||
SessionId = sessionId;
|
||||
SteamId = steamId;
|
||||
RefreshToken = refreshToken;
|
||||
tokensCollection ??= Array.Empty<SteamAuthToken>();
|
||||
Tokens = new ConcurrentDictionary<SteamDomain, SteamAuthToken>(tokensCollection.ToDictionary(t => t.Domain, t => t));
|
||||
Tokens = new ConcurrentDictionary<SteamDomain, SteamAuthToken>(
|
||||
tokensCollection.ToDictionary(t => t.Domain, t => t));
|
||||
}
|
||||
|
||||
public virtual SteamAuthToken? GetToken(SteamDomain domain)
|
||||
@@ -44,7 +49,6 @@ public class SessionData : ISessionData
|
||||
if (Tokens.TryGetValue(domain, out var token))
|
||||
return token;
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public void SetToken(SteamDomain domain, SteamAuthToken token)
|
||||
@@ -53,12 +57,13 @@ public class SessionData : ISessionData
|
||||
}
|
||||
|
||||
|
||||
public virtual SessionData Clone()
|
||||
{
|
||||
return (SessionData) ((ICloneable) this).Clone();
|
||||
}
|
||||
|
||||
public virtual SessionData Clone() => (SessionData)((ICloneable)this).Clone();
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return new SessionData(SessionId, SteamId, RefreshToken, Tokens);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -11,22 +11,22 @@ namespace SteamLib.Account;
|
||||
public readonly struct SteamAuthToken
|
||||
{
|
||||
public string Token { get; }
|
||||
|
||||
|
||||
[JsonConverter(typeof(SteamIdToSteam64Converter))]
|
||||
public SteamId SteamId { get; }
|
||||
|
||||
[JsonConverter(typeof(UnixTimeStampConverter))]
|
||||
public UnixTimeStamp Expires { get; }
|
||||
|
||||
public SteamDomain Domain { get; init; }
|
||||
public SteamAccessTokenType Type { get; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsExpired => Expires.Time < DateTime.UtcNow;
|
||||
[JsonIgnore] public bool IsExpired => Expires.Time < DateTime.UtcNow;
|
||||
|
||||
[JsonIgnore]
|
||||
public string SignedToken { get; }
|
||||
[JsonIgnore] public string SignedToken { get; }
|
||||
|
||||
public SteamAuthToken(string token, long steamId, UnixTimeStamp expires, SteamDomain domain, SteamAccessTokenType type)
|
||||
public SteamAuthToken(string token, long steamId, UnixTimeStamp expires, SteamDomain domain,
|
||||
SteamAccessTokenType type)
|
||||
{
|
||||
Token = token;
|
||||
Expires = expires;
|
||||
@@ -37,7 +37,8 @@ public readonly struct SteamAuthToken
|
||||
}
|
||||
|
||||
[JsonConstructor]
|
||||
public SteamAuthToken(string token, SteamId steamId, UnixTimeStamp expires, SteamDomain domain, SteamAccessTokenType type)
|
||||
public SteamAuthToken(string token, SteamId steamId, UnixTimeStamp expires, SteamDomain domain,
|
||||
SteamAccessTokenType type)
|
||||
{
|
||||
Token = token;
|
||||
SteamId = steamId;
|
||||
@@ -46,5 +47,4 @@ public readonly struct SteamAuthToken
|
||||
Type = type;
|
||||
SignedToken = SteamTokenHelper.CombineJwtWithSteamId(SteamId.Steam64.Id, Token);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,12 @@ public static class SteamAuthenticatorLinkerApi
|
||||
{
|
||||
private const string PHONE_REQ = SteamConstants.STEAM_COMMUNITY + "steamguard/phoneajax";
|
||||
|
||||
private static string GetAccessToken(this ISessionData s)
|
||||
{
|
||||
return s.GetToken(SteamDomain.Community)?.Token ??
|
||||
throw new SessionInvalidException("Access token was null. MobileEndpoints requires valid AccessToken");
|
||||
}
|
||||
|
||||
#region Global
|
||||
|
||||
public static Task<AccountPhoneStatus_Response> HasPhone(HttpClient client, string accessToken)
|
||||
@@ -28,11 +34,10 @@ public static class SteamAuthenticatorLinkerApi
|
||||
const string uri = SteamConstants.STEAM_API + "IPhoneService/AccountPhoneStatus/v1";
|
||||
|
||||
var reqUri = AddAccessToken(uri, accessToken);
|
||||
return client.PostProto<AccountPhoneStatus_Response>(reqUri, request: null);
|
||||
return client.PostProto<AccountPhoneStatus_Response>(reqUri, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="data"></param>
|
||||
@@ -40,21 +45,22 @@ public static class SteamAuthenticatorLinkerApi
|
||||
/// <returns></returns>
|
||||
/// <exception cref="AuthenticatorLinkerException"></exception>
|
||||
/// <exception cref="EResultException"></exception>
|
||||
public static async Task<AddAuthenticator_Response> LinkRequest(HttpClient client, ISessionData data, string deviceId)
|
||||
public static async Task<AddAuthenticator_Response> LinkRequest(HttpClient client, ISessionData data,
|
||||
string deviceId)
|
||||
{
|
||||
const string uri = SteamConstants.STEAM_API + "ITwoFactorService/AddAuthenticator/v1";
|
||||
data.EnsureValidated();
|
||||
var reqUri = AddAccessToken(uri, data.GetAccessToken());
|
||||
var req = new AddAuthenticator_Request
|
||||
{
|
||||
SteamId = (ulong)data.SteamId.Steam64.Id,
|
||||
SteamId = (ulong) data.SteamId.Steam64.Id,
|
||||
AuthenticatorType = 1,
|
||||
DeviceIdentifier = deviceId,
|
||||
Version = 2
|
||||
};
|
||||
|
||||
var resp = await client.PostProtoMsg<AddAuthenticator_Response>(reqUri, req);
|
||||
if (resp is { Result: EResult.InvalidState, ResponseMsg.Status: 2 })
|
||||
if (resp is {Result: EResult.InvalidState, ResponseMsg.Status: 2})
|
||||
{
|
||||
throw new AuthenticatorLinkerException(AuthenticatorLinkerError.InvalidStateWithStatus2);
|
||||
}
|
||||
@@ -74,7 +80,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
var validateSmsReq = new FinalizeAddAuthenticator_Request
|
||||
{
|
||||
SteamId = data.SteamId.Steam64.ToUlong(),
|
||||
AuthenticatorTime = (ulong)time,
|
||||
AuthenticatorTime = (ulong) time,
|
||||
ConfirmationCode = confirmationCode,
|
||||
ValidateConfirmationCode = true
|
||||
};
|
||||
@@ -101,8 +107,8 @@ public static class SteamAuthenticatorLinkerApi
|
||||
{
|
||||
SteamId = data.SteamId.Steam64.ToUlong(),
|
||||
AuthenticatorCode = code,
|
||||
AuthenticatorTime = (ulong)time,
|
||||
ConfirmationCode = confirmationCode,
|
||||
AuthenticatorTime = (ulong) time,
|
||||
ConfirmationCode = confirmationCode
|
||||
};
|
||||
|
||||
|
||||
@@ -124,6 +130,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
return new LinkResult(LinkError.UnableToGenerateCorrectCodes);
|
||||
}
|
||||
}
|
||||
|
||||
return new LinkResult(LinkError.GeneralFailure);
|
||||
}
|
||||
|
||||
@@ -131,6 +138,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
{
|
||||
return await CheckPhoneNumber(client, phoneNumber, sessionId) == CheckPhoneResult.Valid;
|
||||
}
|
||||
|
||||
public static async Task<CheckPhoneResult> CheckPhoneNumber(HttpClient client, long phoneNumber, string sessionId)
|
||||
{
|
||||
var phone = '+' + phoneNumber.ToString();
|
||||
@@ -172,6 +180,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
|
||||
return resp.Result == EResult.Pending;
|
||||
}
|
||||
|
||||
public static async Task<bool> CheckEmailConfirmation(HttpClient client, string accessToken)
|
||||
{
|
||||
const string uri = SteamConstants.STEAM_API + "IPhoneService/IsAccountWaitingForEmailConfirmation/v1";
|
||||
@@ -182,7 +191,8 @@ public static class SteamAuthenticatorLinkerApi
|
||||
while (i < 5)
|
||||
{
|
||||
i++;
|
||||
var resp = await client.PostProto<IsAccountWaitingForEmailConfirmation_Response>(reqUri, new EmptyMessage());
|
||||
var resp = await client.PostProto<IsAccountWaitingForEmailConfirmation_Response>(reqUri,
|
||||
new EmptyMessage());
|
||||
|
||||
if (resp.IsWaiting == false) return true;
|
||||
|
||||
@@ -191,6 +201,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Task<EResult> SendSmsCode(HttpClient client, string accessToken)
|
||||
{
|
||||
const string uri = SteamConstants.STEAM_API + "IPhoneService/SendPhoneVerificationCode/v1";
|
||||
@@ -198,6 +209,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
var reqUri = AddAccessToken(uri, accessToken);
|
||||
return client.PostProto(reqUri, new SendPhoneVerificationCode_Request());
|
||||
}
|
||||
|
||||
public static async Task<bool> CheckSmsCode(HttpClient client, int smsCode, string sessionId)
|
||||
{
|
||||
var data = new Dictionary<string, string>
|
||||
@@ -220,19 +232,27 @@ public static class SteamAuthenticatorLinkerApi
|
||||
var hasPhone = await HasPhone(client, sessionId);
|
||||
return hasPhone.HasPhone;
|
||||
}
|
||||
|
||||
private static FormUrlEncodedContent CreateAjaxContent(string op, string arg, string sessionId)
|
||||
{
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{"op",op},
|
||||
{"op", op},
|
||||
{"arg", arg},
|
||||
{"sessionid", sessionId}
|
||||
};
|
||||
return new FormUrlEncodedContent(data);
|
||||
}
|
||||
|
||||
public static string GenerateDeviceId() => "android:" + Guid.NewGuid();
|
||||
private static string AddAccessToken(string uri, string accessToken) => uri + "?access_token=" + accessToken;
|
||||
public static string GenerateDeviceId()
|
||||
{
|
||||
return "android:" + Guid.NewGuid();
|
||||
}
|
||||
|
||||
private static string AddAccessToken(string uri, string accessToken)
|
||||
{
|
||||
return uri + "?access_token=" + accessToken;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -261,6 +281,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
|
||||
return HasPhone(linker.Client, linker.SessionData.GetAccessToken());
|
||||
}
|
||||
|
||||
internal static Task<bool> AttachPhone(this SteamAuthenticatorLinker linker, long phoneNumber)
|
||||
{
|
||||
if (linker.SessionData == null)
|
||||
@@ -268,6 +289,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
|
||||
return AttachPhone(linker.Client, phoneNumber, linker.SessionData.GetAccessToken());
|
||||
}
|
||||
|
||||
internal static Task<bool> CheckEmailConfirmation(this SteamAuthenticatorLinker linker)
|
||||
{
|
||||
if (linker.SessionData == null)
|
||||
@@ -283,6 +305,7 @@ public static class SteamAuthenticatorLinkerApi
|
||||
|
||||
return SendSmsCode(linker.Client, linker.SessionData.GetAccessToken());
|
||||
}
|
||||
|
||||
internal static Task<bool> CheckSmsCode(this SteamAuthenticatorLinker linker, int smsCode)
|
||||
{
|
||||
if (linker.SessionData == null)
|
||||
@@ -291,7 +314,8 @@ public static class SteamAuthenticatorLinkerApi
|
||||
return CheckSmsCode(linker.Client, smsCode, linker.SessionData.SessionId);
|
||||
}
|
||||
|
||||
internal static Task<LinkResult> FinalizeLink(this SteamAuthenticatorLinker linker, string confirmationCode, byte[] sharedSecret, bool validateSmsCode)
|
||||
internal static Task<LinkResult> FinalizeLink(this SteamAuthenticatorLinker linker, string confirmationCode,
|
||||
byte[] sharedSecret, bool validateSmsCode)
|
||||
{
|
||||
if (linker.SessionData == null)
|
||||
throw new InvalidOperationException("SessionData is null");
|
||||
@@ -299,7 +323,5 @@ public static class SteamAuthenticatorLinkerApi
|
||||
return FinalizeLink(linker.Client, confirmationCode, linker.SessionData, sharedSecret, validateSmsCode);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
private static string GetAccessToken(this ISessionData s) => s.GetToken(SteamDomain.Community)?.Token ?? throw new SessionInvalidException("Access token was null. MobileEndpoints requires valid AccessToken");
|
||||
}
|
||||
@@ -1,27 +1,24 @@
|
||||
using JetBrains.Annotations;
|
||||
using System.Net;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SteamLib.Core;
|
||||
using SteamLib.Core.Models;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.ProtoCore;
|
||||
using SteamLib.ProtoCore.Enums;
|
||||
using SteamLib.ProtoCore.Exceptions;
|
||||
using SteamLib.ProtoCore.Services;
|
||||
using System.Net;
|
||||
using SteamLib.Core.Models;
|
||||
|
||||
namespace SteamLib.Api.Mobile;
|
||||
|
||||
|
||||
[PublicAPI]
|
||||
public static class SteamMobileApi
|
||||
{
|
||||
|
||||
private const string GENERATE_ACCESS_TOKEN =
|
||||
SteamConstants.STEAM_API + "IAuthenticationService/GenerateAccessTokenForApp/v1";
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="refreshToken"></param>
|
||||
@@ -29,7 +26,8 @@ public static class SteamMobileApi
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>Refreshed AccessToken</returns>
|
||||
/// <exception cref="SessionInvalidException"></exception>
|
||||
public static async Task<string> RefreshJwt(HttpClient client, string refreshToken, SteamId steamId, CancellationToken cancellationToken = default)
|
||||
public static async Task<string> RefreshJwt(HttpClient client, string refreshToken, SteamId steamId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var req = new GenerateAccessTokenForApp_Request
|
||||
{
|
||||
@@ -40,27 +38,30 @@ public static class SteamMobileApi
|
||||
|
||||
try
|
||||
{
|
||||
var resp = await client.PostProto<GenerateAccessTokenForApp_Response>(GENERATE_ACCESS_TOKEN, req, cancellationToken: cancellationToken);
|
||||
var resp = await client.PostProto<GenerateAccessTokenForApp_Response>(GENERATE_ACCESS_TOKEN, req,
|
||||
cancellationToken);
|
||||
return resp.AccessToken;
|
||||
}
|
||||
catch (EResultException ex)
|
||||
when (ex.Result == EResult.AccessDenied)
|
||||
{
|
||||
throw new SessionPermanentlyExpiredException("RefreshToken is not accepted by Steam. You must login again and use new token");
|
||||
throw new SessionPermanentlyExpiredException(
|
||||
"RefreshToken is not accepted by Steam. You must login again and use new token");
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> HasPhoneAttached(HttpClient client, string sessionId, CancellationToken cancellationToken = default)
|
||||
public static async Task<bool> HasPhoneAttached(HttpClient client, string sessionId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{"op", "has_phone"},
|
||||
{"arg", "null"},
|
||||
{"sessionid", sessionId}
|
||||
|
||||
};
|
||||
var content = new FormUrlEncodedContent(data);
|
||||
var resp = await client.PostAsync(SteamConstants.STEAM_COMMUNITY + "steamguard/phoneajax", content, cancellationToken);
|
||||
var resp = await client.PostAsync(SteamConstants.STEAM_COMMUNITY + "steamguard/phoneajax", content,
|
||||
cancellationToken);
|
||||
var respContent = await resp.EnsureSuccessStatusCode().Content.ReadAsStringAsync(cancellationToken);
|
||||
|
||||
return SteamLibErrorMonitor.HandleResponse(respContent, () =>
|
||||
@@ -71,7 +72,8 @@ public static class SteamMobileApi
|
||||
}
|
||||
|
||||
|
||||
public static async Task<RemoveAuthenticator_Response> RemoveAuthenticator(HttpClient client, string accessToken, string rCode, CancellationToken cancellationToken = default)
|
||||
public static async Task<RemoveAuthenticator_Response> RemoveAuthenticator(HttpClient client, string accessToken,
|
||||
string rCode, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var req = SteamConstants.STEAM_API + "ITwoFactorService/RemoveAuthenticator/v1?access_token=" + accessToken;
|
||||
var reqData = new RemoveAuthenticator_Request
|
||||
@@ -82,7 +84,7 @@ public static class SteamMobileApi
|
||||
};
|
||||
try
|
||||
{
|
||||
return await client.PostProto<RemoveAuthenticator_Response>(req, reqData, cancellationToken: cancellationToken);
|
||||
return await client.PostProto<RemoveAuthenticator_Response>(req, reqData, cancellationToken);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
when (ex.StatusCode is HttpStatusCode.Unauthorized)
|
||||
@@ -90,7 +92,4 @@ public static class SteamMobileApi
|
||||
throw new SessionInvalidException(SessionInvalidException.GOT_401_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,20 +1,23 @@
|
||||
using SteamLib.Core;
|
||||
using System.Net;
|
||||
using SteamLib.Core;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.ProtoCore;
|
||||
using SteamLib.ProtoCore.Services;
|
||||
using System.Net;
|
||||
|
||||
namespace SteamLib.Api.Mobile;
|
||||
|
||||
public static class SteamMobileAuthenticatorApi
|
||||
{
|
||||
public static async Task<GetAuthSessionsForAccount_Response> GetAuthSessionsForAccount(HttpClient client, string accessToken, CancellationToken cancellationToken = default)
|
||||
public static async Task<GetAuthSessionsForAccount_Response> GetAuthSessionsForAccount(HttpClient client,
|
||||
string accessToken, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var req = SteamConstants.STEAM_API + "IAuthenticationService/GetAuthSessionsForAccount/v1?access_token=" + accessToken;
|
||||
var req = SteamConstants.STEAM_API + "IAuthenticationService/GetAuthSessionsForAccount/v1?access_token=" +
|
||||
accessToken;
|
||||
|
||||
try
|
||||
{
|
||||
return await client.GetProto<GetAuthSessionsForAccount_Response>(req, new EmptyMessage(), cancellationToken: cancellationToken);
|
||||
return await client.GetProto<GetAuthSessionsForAccount_Response>(req, new EmptyMessage(),
|
||||
cancellationToken);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
when (ex.StatusCode == HttpStatusCode.Unauthorized)
|
||||
@@ -23,7 +26,8 @@ public static class SteamMobileAuthenticatorApi
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<GetAuthSessionInfo_Response> GetAuthSessionInfo(HttpClient client, string accessToken, ulong clientId, CancellationToken cancellationToken = default)
|
||||
public static async Task<GetAuthSessionInfo_Response> GetAuthSessionInfo(HttpClient client, string accessToken,
|
||||
ulong clientId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var req = SteamConstants.STEAM_API + "IAuthenticationService/GetAuthSessionInfo/v1?access_token=" + accessToken;
|
||||
var reqData = new GetAuthSessionInfo_Request
|
||||
@@ -32,7 +36,7 @@ public static class SteamMobileAuthenticatorApi
|
||||
};
|
||||
try
|
||||
{
|
||||
return await client.PostProto<GetAuthSessionInfo_Response>(req, reqData, cancellationToken: cancellationToken);
|
||||
return await client.PostProto<GetAuthSessionInfo_Response>(req, reqData, cancellationToken);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
when (ex.StatusCode == HttpStatusCode.Unauthorized)
|
||||
@@ -45,11 +49,11 @@ public static class SteamMobileAuthenticatorApi
|
||||
public static async Task<bool> UpdateAuthSessionStatus(HttpClient client, string accessToken, string sharedSecret,
|
||||
UpdateAuthSessionWithMobileConfirmation_Request request)
|
||||
{
|
||||
var req = SteamConstants.STEAM_API + "IAuthenticationService/UpdateAuthSessionWithMobileConfirmation/v1?access_token=" + accessToken;
|
||||
var req = SteamConstants.STEAM_API +
|
||||
"IAuthenticationService/UpdateAuthSessionWithMobileConfirmation/v1?access_token=" + accessToken;
|
||||
|
||||
request.ComputeSignature(sharedSecret);
|
||||
await client.PostProtoEnsureSuccess(req, request);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
using AchiesUtilities.Web.Extensions;
|
||||
using System.Net;
|
||||
using AchiesUtilities.Web.Extensions;
|
||||
using JetBrains.Annotations;
|
||||
using SteamLib.Core;
|
||||
using SteamLib.Core.Models;
|
||||
using SteamLib.Core.StatusCodes;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.SteamMobile;
|
||||
using SteamLib.SteamMobile.Confirmations;
|
||||
using SteamLib.Utility;
|
||||
using SteamLib.Web.Scrappers.JSON;
|
||||
using System.Net;
|
||||
using SteamLib.Core.Models;
|
||||
|
||||
namespace SteamLib.Api.Mobile;
|
||||
|
||||
@@ -19,7 +19,8 @@ public static class SteamMobileConfirmationsApi
|
||||
private const string CONF_OP = SteamConstants.STEAM_COMMUNITY + "mobileconf/ajaxop";
|
||||
private const string MULTI_CONF_OP = SteamConstants.STEAM_COMMUNITY + "mobileconf/multiajaxop";
|
||||
|
||||
public static async Task<IEnumerable<Confirmation>> GetConfirmations(HttpClient client, MobileData data, SteamId steamId, CancellationToken cancellationToken = default)
|
||||
public static async Task<IEnumerable<Confirmation>> GetConfirmations(HttpClient client, MobileData data,
|
||||
SteamId steamId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var nvc = GetConfirmationKvp(steamId, data.DeviceId, data.IdentitySecret, "list");
|
||||
|
||||
@@ -27,11 +28,12 @@ public static class SteamMobileConfirmationsApi
|
||||
var reqMsg = new HttpRequestMessage(HttpMethod.Get, req);
|
||||
var resp = await client.SendAsync(reqMsg, cancellationToken);
|
||||
|
||||
|
||||
|
||||
if (resp.StatusCode == HttpStatusCode.Redirect)
|
||||
{
|
||||
throw new SessionInvalidException("Mobile session expired");
|
||||
}
|
||||
|
||||
var respStr = await resp.Content.ReadAsStringAsync(cancellationToken);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
|
||||
@@ -44,11 +46,11 @@ public static class SteamMobileConfirmationsApi
|
||||
{
|
||||
SteamLibErrorMonitor.LogErrorResponse(respStr, ex);
|
||||
throw;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> SendConfirmation(HttpClient client, Confirmation confirmation, SteamId steamId, MobileData data, bool confirm, CancellationToken cancellationToken = default)
|
||||
public static async Task<bool> SendConfirmation(HttpClient client, Confirmation confirmation, SteamId steamId,
|
||||
MobileData data, bool confirm, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var op = confirm ? "allow" : "cancel";
|
||||
|
||||
@@ -63,10 +65,10 @@ public static class SteamMobileConfirmationsApi
|
||||
|
||||
var req = CONF_OP + query.ToQueryString();
|
||||
var resp = await client.GetStringAsync(req, cancellationToken);
|
||||
var successCode = SteamLibErrorMonitor.HandleResponse(resp, () => SteamStatusCode.Translate<SteamStatusCode>(resp, out _));
|
||||
var successCode =
|
||||
SteamLibErrorMonitor.HandleResponse(resp, () => SteamStatusCode.Translate<SteamStatusCode>(resp, out _));
|
||||
|
||||
return successCode.Equals(SteamStatusCode.Ok);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -86,12 +88,13 @@ public static class SteamMobileConfirmationsApi
|
||||
var content = new FormUrlEncodedContent(query);
|
||||
var resp = await client.PostAsync(MULTI_CONF_OP, content, cancellationToken);
|
||||
var respStr = await resp.Content.ReadAsStringAsync(cancellationToken);
|
||||
var successCode = SteamLibErrorMonitor.HandleResponse(respStr, () => SteamStatusCode.Translate<SteamStatusCode>(respStr, out _));
|
||||
var successCode = SteamLibErrorMonitor.HandleResponse(respStr,
|
||||
() => SteamStatusCode.Translate<SteamStatusCode>(respStr, out _));
|
||||
return successCode.Equals(SteamStatusCode.Ok);
|
||||
|
||||
}
|
||||
|
||||
internal static IEnumerable<KeyValuePair<string, string>> GetConfirmationKvp(SteamId steamId, string deviceId, string identitySecret, string tag = "conf")
|
||||
internal static IEnumerable<KeyValuePair<string, string>> GetConfirmationKvp(SteamId steamId, string deviceId,
|
||||
string identitySecret, string tag = "conf")
|
||||
{
|
||||
var time = TimeAligner.GetSteamTime();
|
||||
var hash = EncryptionHelper.GenerateConfirmationHash(time, identitySecret, tag);
|
||||
|
||||
@@ -13,14 +13,15 @@ namespace SteamLib.Api;
|
||||
public static class SteamGlobalApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Raw AccessToken value
|
||||
/// Raw AccessToken value
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="domain"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SessionInvalidException"></exception>
|
||||
public static async Task<string> RefreshJwt(HttpClient client, SteamDomain domain, CancellationToken cancellationToken = default)
|
||||
public static async Task<string> RefreshJwt(HttpClient client, SteamDomain domain,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var domainUri = SteamDomains.GetDomain(domain);
|
||||
var data = new Dictionary<string, string>
|
||||
@@ -50,16 +51,18 @@ public static class SteamGlobalApi
|
||||
var translated = SteamStatusCode.Translate<SteamStatusCode>(errorCode, out _);
|
||||
inner = new SteamStatusCodeException(translated, respStr);
|
||||
}
|
||||
throw new SessionInvalidException("AjaxRefresh was not successful. 'steamRefresh_steam' cookie is missing or refresh token is invalid", inner);
|
||||
|
||||
throw new SessionInvalidException(
|
||||
"AjaxRefresh was not successful. 'steamRefresh_steam' cookie is missing or refresh token is invalid",
|
||||
inner);
|
||||
}
|
||||
|
||||
data = new Dictionary<string, string>()
|
||||
data = new Dictionary<string, string>
|
||||
{
|
||||
{"steamID", jwtRefresh.SteamId.ToString()},
|
||||
{"nonce", jwtRefresh.Nonce},
|
||||
{"redir", jwtRefresh.Redir},
|
||||
{"auth", jwtRefresh.Auth},
|
||||
{"auth", jwtRefresh.Auth}
|
||||
};
|
||||
|
||||
cont = new FormUrlEncodedContent(data);
|
||||
@@ -81,7 +84,7 @@ public static class SteamGlobalApi
|
||||
throw new SessionInvalidException(
|
||||
"AjaxRefresh (set-token) response was not successful. Response string stored in Exception.Data")
|
||||
{
|
||||
Data = { { "Response", updateResp } }
|
||||
Data = {{"Response", updateResp}}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,12 +105,10 @@ public static class SteamGlobalApi
|
||||
|
||||
private class JwtUpdateJson
|
||||
{
|
||||
[JsonProperty("result")]
|
||||
public int Result { get; set; }
|
||||
[JsonProperty("result")] public int Result { get; set; }
|
||||
|
||||
[JsonProperty("rtExpiry")]
|
||||
[JsonConverter(typeof(UnixTimeStampConverter))]
|
||||
public UnixTimeStamp RtExpiry { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using AchiesUtilities.Extensions;
|
||||
using System.Net;
|
||||
using AchiesUtilities.Extensions;
|
||||
using JetBrains.Annotations;
|
||||
using SteamLib.Account;
|
||||
using SteamLib.Core;
|
||||
using SteamLib.Core.Enums;
|
||||
using SteamLib.Core.Interfaces;
|
||||
using System.Net;
|
||||
|
||||
namespace SteamLib.Authentication;
|
||||
|
||||
@@ -19,9 +19,10 @@ public static class AdmissionHelper
|
||||
#region Main
|
||||
|
||||
/// <summary>
|
||||
/// Clear and set new session
|
||||
/// Clear and set new session
|
||||
/// </summary>
|
||||
public static void SetSteamCookies(this CookieContainer container, ISessionData sessionData, string setLanguage = "english")
|
||||
public static void SetSteamCookies(this CookieContainer container, ISessionData sessionData,
|
||||
string setLanguage = "english")
|
||||
{
|
||||
container.ClearSteamCookies(setLanguage);
|
||||
|
||||
@@ -43,7 +44,8 @@ public static class AdmissionHelper
|
||||
public static void SetDomainCookie(this CookieContainer container, SteamDomain domain, SteamAuthToken token)
|
||||
{
|
||||
var uri = SteamDomains.GetDomainUri(domain);
|
||||
foreach (var cookie in container.GetCookies(uri).Where(c => c.Expired == false && c.Name.EqualsIgnoreCase(ACCESS_COOKIE_NAME)))
|
||||
foreach (var cookie in container.GetCookies(uri)
|
||||
.Where(c => c.Expired == false && c.Name.EqualsIgnoreCase(ACCESS_COOKIE_NAME)))
|
||||
{
|
||||
cookie.Expired = true;
|
||||
}
|
||||
@@ -53,12 +55,11 @@ public static class AdmissionHelper
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Clear and set new session
|
||||
/// Clear and set new session
|
||||
/// </summary>
|
||||
public static void SetSteamMobileCookies(this CookieContainer container, IMobileSessionData mobileSession,
|
||||
public static void SetSteamMobileCookies(this CookieContainer container, IMobileSessionData mobileSession,
|
||||
string setLanguage = "english")
|
||||
{
|
||||
|
||||
container.ClearSteamCookies(setLanguage);
|
||||
container.AddMinimalMobileCookies();
|
||||
|
||||
@@ -79,12 +80,14 @@ public static class AdmissionHelper
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear and set new session. Not recommended. Uses <see cref="IMobileSessionData.GetMobileToken()"/> for domain <see cref="SteamDomain.Community"/> instead of its own cookie. It's okay to use it only for confirmations. But Market, Trading and other pages won't be authorized
|
||||
/// Clear and set new session. Not recommended. Uses <see cref="IMobileSessionData.GetMobileToken()" /> for domain
|
||||
/// <see cref="SteamDomain.Community" /> instead of its own cookie. It's okay to use it only for confirmations. But
|
||||
/// Market, Trading and other pages won't be authorized
|
||||
/// </summary>
|
||||
public static void SetSteamMobileCookiesWithMobileToken(this CookieContainer container, IMobileSessionData mobileSession,
|
||||
public static void SetSteamMobileCookiesWithMobileToken(this CookieContainer container,
|
||||
IMobileSessionData mobileSession,
|
||||
string setLanguage = "english")
|
||||
{
|
||||
|
||||
container.ClearSteamCookies(setLanguage);
|
||||
container.AddMinimalMobileCookies();
|
||||
|
||||
@@ -99,10 +102,9 @@ public static class AdmissionHelper
|
||||
var domainCookieSet = false;
|
||||
foreach (var domain in SteamDomains.AllDomains)
|
||||
{
|
||||
|
||||
var token = mobileSession.GetToken(domain);
|
||||
if (token == null || token.Value.IsExpired) continue;
|
||||
if(domain == SteamDomain.Community )
|
||||
if (domain == SteamDomain.Community)
|
||||
domainCookieSet = true;
|
||||
AddTokenCookie(container, token.Value);
|
||||
}
|
||||
@@ -123,6 +125,7 @@ public static class AdmissionHelper
|
||||
#endregion
|
||||
|
||||
#region Clear
|
||||
|
||||
public static void ClearSteamCookies(this CookieContainer container, string setLanguage = "english")
|
||||
{
|
||||
var cookies = container.GetAllCookies().Where(IsSteamCookie).ToList();
|
||||
@@ -130,6 +133,7 @@ public static class AdmissionHelper
|
||||
{
|
||||
cookie.Expired = true;
|
||||
}
|
||||
|
||||
container.Add(SteamDomains.GetDomainUri(SteamDomain.Community), new Cookie(LANGUAGE_COOKIE_NAME, setLanguage));
|
||||
TransferCommunityCookies(container);
|
||||
}
|
||||
@@ -141,10 +145,10 @@ public static class AdmissionHelper
|
||||
TransferCommunityCookies(container);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
public static void TransferCommunityCookies(CookieContainer container)
|
||||
{
|
||||
var cookies = container.GetAllCookies();
|
||||
@@ -152,7 +156,8 @@ public static class AdmissionHelper
|
||||
|
||||
foreach (Cookie cookie in cookies)
|
||||
{
|
||||
if (cookie.Domain.Contains("steamcommunity.com") == false || cookie.Expired || cookie.Name.EqualsIgnoreCase(ACCESS_COOKIE_NAME)) continue;
|
||||
if (cookie.Domain.Contains("steamcommunity.com") == false || cookie.Expired ||
|
||||
cookie.Name.EqualsIgnoreCase(ACCESS_COOKIE_NAME)) continue;
|
||||
|
||||
|
||||
container.Add(SteamDomains.GetDomainUri(SteamDomain.Store), CloneCookie(cookie));
|
||||
@@ -162,14 +167,19 @@ public static class AdmissionHelper
|
||||
|
||||
return;
|
||||
|
||||
static Cookie CloneCookie(Cookie cookie)
|
||||
=> new(cookie.Name, cookie.Value, cookie.Path) { Expires = cookie.Expires, Secure = cookie.Secure, HttpOnly = cookie.HttpOnly };
|
||||
static Cookie CloneCookie(Cookie cookie)
|
||||
{
|
||||
return new Cookie(cookie.Name, cookie.Value, cookie.Path)
|
||||
{Expires = cookie.Expires, Secure = cookie.Secure, HttpOnly = cookie.HttpOnly};
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddRefreshToken(CookieContainer container, SteamAuthToken token)
|
||||
{
|
||||
if (token.Type is not (SteamAccessTokenType.Refresh or SteamAccessTokenType.MobileRefresh))
|
||||
throw new ArgumentException($"Token must be of type Refresh or MobileRefresh. Provided token has type: {token.Type}", nameof(token));
|
||||
throw new ArgumentException(
|
||||
$"Token must be of type Refresh or MobileRefresh. Provided token has type: {token.Type}",
|
||||
nameof(token));
|
||||
var refreshToken = token.SignedToken;
|
||||
container.Add(SteamDomains.LoginSteamDomain, new Cookie(REFRESH_COOKIE_NAME, refreshToken)
|
||||
{
|
||||
@@ -180,10 +190,12 @@ public static class AdmissionHelper
|
||||
public static void AddTokenCookie(CookieContainer container, SteamAuthToken token)
|
||||
{
|
||||
if (token.Type is not SteamAccessTokenType.AccessToken)
|
||||
throw new ArgumentException($"Token must be of type AccessToken. Provided token has type: {token.Type}", nameof(token));
|
||||
throw new ArgumentException($"Token must be of type AccessToken. Provided token has type: {token.Type}",
|
||||
nameof(token));
|
||||
|
||||
AddTokenCookie(container, token.Domain, token);
|
||||
}
|
||||
|
||||
private static void AddTokenCookie(CookieContainer container, SteamDomain domain, SteamAuthToken token)
|
||||
{
|
||||
var domainUri = SteamDomains.GetDomainUri(domain);
|
||||
@@ -196,8 +208,11 @@ public static class AdmissionHelper
|
||||
}
|
||||
|
||||
|
||||
public static bool IsSteamCookie(Cookie cookie) =>
|
||||
cookie.Domain.Contains("steamcommunity.com") || cookie.Domain.Contains("steampowered.com") || cookie.Domain.Contains("steam.tv");
|
||||
public static bool IsSteamCookie(Cookie cookie)
|
||||
{
|
||||
return cookie.Domain.Contains("steamcommunity.com") || cookie.Domain.Contains("steampowered.com") ||
|
||||
cookie.Domain.Contains("steam.tv");
|
||||
}
|
||||
|
||||
|
||||
public static string? GetSessionId(this CookieContainer container, string domain = "steamcommunity.com")
|
||||
@@ -218,7 +233,5 @@ public static class AdmissionHelper
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -27,19 +27,21 @@ public class LoginExecutor
|
||||
}
|
||||
|
||||
|
||||
public static async Task<TransferParameters> DoLogin(LoginExecutorOptions options, string username, string password, CancellationToken cancellationToken = default) //TODO: logs
|
||||
public static async Task<TransferParameters> DoLogin(LoginExecutorOptions options, string username, string password,
|
||||
CancellationToken cancellationToken = default) //TODO: logs
|
||||
{
|
||||
var executor = new LoginExecutor(options);
|
||||
var client = executor.HttpClient;
|
||||
|
||||
LoginStage loginStage = new GetRsaStage(username);
|
||||
loginStage = await ((GetRsaStage)loginStage).Proceed(client, cancellationToken);
|
||||
loginStage = await ((GetRsaStage) loginStage).Proceed(client, cancellationToken);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (loginStage is ReadyToLoginStage rdyToLoginStage)
|
||||
{
|
||||
loginStage = await rdyToLoginStage.Proceed(client, password, string.Empty, cancellationToken);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var twoFactorTry = 0;
|
||||
var emailTry = 0;
|
||||
@@ -49,39 +51,42 @@ public class LoginExecutor
|
||||
switch (loginStage)
|
||||
{
|
||||
case CaptchaNeededStage captchaNeededStage:
|
||||
{
|
||||
if (executor.CaptchaResolver == null) throw new LoginException(LoginError.CaptchaRequired);
|
||||
var captchaText = await executor.CaptchaResolver.Resolve(captchaNeededStage.CaptchaImage, client);
|
||||
loginStage = await captchaNeededStage.Proceed(client, captchaText, cancellationToken);
|
||||
break;
|
||||
}
|
||||
{
|
||||
if (executor.CaptchaResolver == null) throw new LoginException(LoginError.CaptchaRequired);
|
||||
var captchaText = await executor.CaptchaResolver.Resolve(captchaNeededStage.CaptchaImage, client);
|
||||
loginStage = await captchaNeededStage.Proceed(client, captchaText, cancellationToken);
|
||||
break;
|
||||
}
|
||||
case EmailAuthStage emailAuthStage:
|
||||
{
|
||||
if (executor.EmailAuthProvider == null) throw new LoginException(LoginError.EmailAuthRequired);
|
||||
if (emailTry > executor.EmailAuthProvider.MaxRetryCount) throw new LoginException(LoginError.InvalidEmailAuthCode);
|
||||
var code = await executor.EmailAuthProvider.GetEmailAuthCode(executor.Caller);
|
||||
loginStage = await emailAuthStage.Proceed(client, code, cancellationToken);
|
||||
emailTry++;
|
||||
break;
|
||||
}
|
||||
{
|
||||
if (executor.EmailAuthProvider == null) throw new LoginException(LoginError.EmailAuthRequired);
|
||||
if (emailTry > executor.EmailAuthProvider.MaxRetryCount)
|
||||
throw new LoginException(LoginError.InvalidEmailAuthCode);
|
||||
var code = await executor.EmailAuthProvider.GetEmailAuthCode(executor.Caller);
|
||||
loginStage = await emailAuthStage.Proceed(client, code, cancellationToken);
|
||||
emailTry++;
|
||||
break;
|
||||
}
|
||||
case TwoFactorStage twoFactorStage:
|
||||
{
|
||||
if (executor.SteamGuardProvider == null) throw new LoginException(LoginError.SteamGuardRequired);
|
||||
if (twoFactorTry > executor.SteamGuardProvider.MaxRetryCount) throw new LoginException(LoginError.InvalidTwoFactorCode);
|
||||
var twoFactor = await executor.SteamGuardProvider.GetSteamGuardCode(executor.Caller);
|
||||
loginStage = await twoFactorStage.Proceed(client, twoFactor, cancellationToken);
|
||||
twoFactorTry++;
|
||||
break;
|
||||
}
|
||||
{
|
||||
if (executor.SteamGuardProvider == null) throw new LoginException(LoginError.SteamGuardRequired);
|
||||
if (twoFactorTry > executor.SteamGuardProvider.MaxRetryCount)
|
||||
throw new LoginException(LoginError.InvalidTwoFactorCode);
|
||||
var twoFactor = await executor.SteamGuardProvider.GetSteamGuardCode(executor.Caller);
|
||||
loginStage = await twoFactorStage.Proceed(client, twoFactor, cancellationToken);
|
||||
twoFactorTry++;
|
||||
break;
|
||||
}
|
||||
case ReadyToLoginStage readyToLoginStage: //When captcha proceeded, stage goes there
|
||||
{
|
||||
loginStage = await readyToLoginStage.Proceed(client, password, string.Empty, cancellationToken);
|
||||
break;
|
||||
}
|
||||
{
|
||||
loginStage = await readyToLoginStage.Proceed(client, password, string.Empty, cancellationToken);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(loginStage));
|
||||
}
|
||||
}
|
||||
|
||||
if (loginStage is LoginErrorStage error)
|
||||
{
|
||||
var content = await error.Response.Content.ReadAsStringAsync(cancellationToken);
|
||||
@@ -89,6 +94,7 @@ public class LoginExecutor
|
||||
{
|
||||
throw new LoginException(LoginError.InvalidCredentials);
|
||||
}
|
||||
|
||||
throw new LoginException(content);
|
||||
}
|
||||
|
||||
@@ -97,12 +103,10 @@ public class LoginExecutor
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected login stage at this point. " + loginStage.GetType())
|
||||
{
|
||||
Data = { { "stage", loginStage } }
|
||||
Data = {{"stage", loginStage}}
|
||||
};
|
||||
}
|
||||
|
||||
return successStage.TransferParameters;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using SteamLib.Core.Interfaces;
|
||||
|
||||
namespace SteamLib.Login.Default;
|
||||
|
||||
|
||||
public class LoginExecutorOptions
|
||||
{
|
||||
public ILoginConsumer Consumer { get; }
|
||||
@@ -12,10 +11,10 @@ public class LoginExecutorOptions
|
||||
public IEmailProvider? EmailAuthProvider { get; init; }
|
||||
public ICaptchaResolver? CaptchaResolver { get; init; }
|
||||
public ISteamGuardProvider? SteamGuardProvider { get; init; }
|
||||
|
||||
public LoginExecutorOptions(ILoginConsumer consumer, HttpClient httpClient)
|
||||
{
|
||||
Consumer = consumer;
|
||||
HttpClient = httpClient;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ namespace SteamLib.Login.Default;
|
||||
|
||||
#pragma warning disable CS8618
|
||||
/// <summary>
|
||||
/// Class to Deserialize the json response strings after the login/>
|
||||
/// Class to Deserialize the json response strings after the login/>
|
||||
/// </summary>
|
||||
internal class LoginResultJson
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace SteamLib.Login.Default;
|
||||
internal abstract class LoginStage
|
||||
{
|
||||
protected Dictionary<string, string> Data { get; }
|
||||
|
||||
protected LoginStage()
|
||||
{
|
||||
Data = new Dictionary<string, string>();
|
||||
@@ -23,12 +24,14 @@ internal abstract class LoginStage
|
||||
{
|
||||
return !json.CaptchaNeeded && !json.EmailAuthNeeded && !json.RequiresTwoFactor && json.Success;
|
||||
}
|
||||
|
||||
protected async Task<LoginResultJson> ConvertJson(HttpResponseMessage response)
|
||||
{
|
||||
response.EnsureSuccessStatusCode();
|
||||
var webResponseString = await response.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<LoginResultJson>(webResponseString)!;
|
||||
}
|
||||
|
||||
protected async Task<LoginSuccessStage> CompleteLogin(HttpResponseMessage response)
|
||||
{
|
||||
var jsonStr = await response.Content.ReadAsStringAsync();
|
||||
@@ -36,17 +39,22 @@ internal abstract class LoginStage
|
||||
var transferParameters = json["transfer_parameters"]!.ToObject<TransferParameters>()!;
|
||||
return new LoginSuccessStage(transferParameters);
|
||||
}
|
||||
protected async Task<HttpResponseMessage> LoginRequest(HttpClient client, CancellationToken cancellationToken = default)
|
||||
|
||||
protected async Task<HttpResponseMessage> LoginRequest(HttpClient client,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await client.PostAsync("https://steamcommunity.com/login/dologin", new FormUrlEncodedContent(Data), cancellationToken);
|
||||
return await client.PostAsync("https://steamcommunity.com/login/dologin", new FormUrlEncodedContent(Data),
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
internal class LoginErrorStage : LoginStage
|
||||
{
|
||||
public readonly string ErrorString;
|
||||
public readonly int ErrorCode;
|
||||
public readonly LoginStage Stage;
|
||||
public readonly string ErrorString;
|
||||
public readonly HttpResponseMessage Response;
|
||||
public readonly LoginStage Stage;
|
||||
|
||||
public LoginErrorStage(string errorString, HttpResponseMessage responseMessage, LoginStage stage)
|
||||
{
|
||||
ErrorString = errorString;
|
||||
@@ -55,9 +63,13 @@ internal class LoginErrorStage : LoginStage
|
||||
Response = responseMessage;
|
||||
}
|
||||
}
|
||||
|
||||
internal class GetRsaStage : LoginStage
|
||||
{
|
||||
public GetRsaStage(Dictionary<string, string> data) : base(data) { }
|
||||
public GetRsaStage(Dictionary<string, string> data) : base(data)
|
||||
{
|
||||
}
|
||||
|
||||
public GetRsaStage(string userName)
|
||||
{
|
||||
Data["username"] = userName;
|
||||
@@ -65,11 +77,10 @@ internal class GetRsaStage : LoginStage
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns> <see cref="ReadyToLoginStage"/> if success</returns>
|
||||
/// <returns> <see cref="ReadyToLoginStage" /> if success</returns>
|
||||
public async Task<LoginStage> Proceed(HttpClient client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var content = new FormUrlEncodedContent(Data);
|
||||
@@ -82,26 +93,30 @@ internal class GetRsaStage : LoginStage
|
||||
Data["rsatimestamp"] = rsaJson.timestamp;
|
||||
return new ReadyToLoginStage(Data, rsaJson.publickey_exp, rsaJson.publickey_mod);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class ReadyToLoginStage : LoginStage
|
||||
{
|
||||
private readonly string _publicKeyExp;
|
||||
private readonly string _publicKeyMod;
|
||||
|
||||
internal ReadyToLoginStage(Dictionary<string, string> data, string publicKeyExp, string publicKeyMod) : base(data)
|
||||
{
|
||||
_publicKeyExp = publicKeyExp;
|
||||
_publicKeyMod = publicKeyMod;
|
||||
}
|
||||
|
||||
public async Task<LoginStage> Proceed(HttpClient client, string password, string loginFriendlyName, CancellationToken cancellationToken = default)
|
||||
public async Task<LoginStage> Proceed(HttpClient client, string password, string loginFriendlyName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var encryptedBase64Password = EncryptionHelper.ToBase64EncryptedPassword(_publicKeyExp, _publicKeyMod, password);
|
||||
var unixTimestamp = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
var encryptedBase64Password =
|
||||
EncryptionHelper.ToBase64EncryptedPassword(_publicKeyExp, _publicKeyMod, password);
|
||||
var unixTimestamp = (int) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
Data["password"] = encryptedBase64Password;
|
||||
Data["remember_login"] = "true";
|
||||
Data["loginfriendlyname"] = loginFriendlyName;
|
||||
Data["donotcache"] = unixTimestamp + "000"; // Added three "0"'s because Steam has a weird unix timestamp interpretation.
|
||||
Data["donotcache"] =
|
||||
unixTimestamp + "000"; // Added three "0"'s because Steam has a weird unix timestamp interpretation.
|
||||
|
||||
_ = await client.GetAsync("https://steamcommunity.com/", cancellationToken);
|
||||
var webResponse = await LoginRequest(client, cancellationToken);
|
||||
@@ -130,10 +145,15 @@ internal class ReadyToLoginStage : LoginStage
|
||||
return new LoginErrorStage("Can't proceed login.", webResponse, this);
|
||||
}
|
||||
}
|
||||
|
||||
internal class TwoFactorStage : LoginStage
|
||||
{
|
||||
internal TwoFactorStage(Dictionary<string, string> data) : base(data) { }
|
||||
public async Task<LoginStage> Proceed(HttpClient client, string twoFactorCode, CancellationToken cancellationToken = default)
|
||||
internal TwoFactorStage(Dictionary<string, string> data) : base(data)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<LoginStage> Proceed(HttpClient client, string twoFactorCode,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Data["twofactorcode"] = twoFactorCode;
|
||||
var webResponse = await LoginRequest(client, cancellationToken);
|
||||
@@ -156,13 +176,16 @@ internal class TwoFactorStage : LoginStage
|
||||
|
||||
return new LoginErrorStage("Can't proceed login. Bad TwoFactor code", webResponse, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class EmailAuthStage : LoginStage
|
||||
{
|
||||
internal EmailAuthStage(Dictionary<string, string> data) : base(data) { }
|
||||
internal EmailAuthStage(Dictionary<string, string> data) : base(data)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<LoginStage> Proceed(HttpClient client, string emailCode, CancellationToken cancellationToken = default)
|
||||
public async Task<LoginStage> Proceed(HttpClient client, string emailCode,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Data["emailauth"] = emailCode;
|
||||
var webResponse = await LoginRequest(client, cancellationToken);
|
||||
@@ -187,9 +210,11 @@ internal class EmailAuthStage : LoginStage
|
||||
return new LoginErrorStage("Can't proceed login. Bad Email code", webResponse, this);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CaptchaNeededStage : LoginStage
|
||||
{
|
||||
internal Uri CaptchaImage { get; }
|
||||
|
||||
public CaptchaNeededStage(Dictionary<string, string> data, string captchaGid) : base(data)
|
||||
{
|
||||
captchaGid = Uri.EscapeDataString(captchaGid);
|
||||
@@ -198,13 +223,13 @@ internal class CaptchaNeededStage : LoginStage
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="captchaText"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns><see cref="ReadyToLoginStage"/> or <see cref="LoginErrorStage"/></returns>
|
||||
public async Task<LoginStage> Proceed(HttpClient client, string captchaText, CancellationToken cancellationToken = default)
|
||||
/// <returns><see cref="ReadyToLoginStage" /> or <see cref="LoginErrorStage" /></returns>
|
||||
public async Task<LoginStage> Proceed(HttpClient client, string captchaText,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Data["captcha_text"] = captchaText;
|
||||
//When captcha required we need to do login from start
|
||||
@@ -215,13 +240,13 @@ internal class CaptchaNeededStage : LoginStage
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
internal class LoginSuccessStage : LoginStage
|
||||
{
|
||||
public TransferParameters TransferParameters { get; }
|
||||
|
||||
public LoginSuccessStage(TransferParameters transferParameters)
|
||||
{
|
||||
TransferParameters = transferParameters;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable IdentifierTypo
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace SteamLib.Login.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Class to Deserialize the json response strings of the getResKey request/>
|
||||
/// Class to Deserialize the json response strings of the getResKey request/>
|
||||
/// </summary>
|
||||
internal class RsaKeyJson
|
||||
{
|
||||
|
||||
@@ -4,27 +4,21 @@ namespace SteamLib.Authentication.LoginV2;
|
||||
|
||||
public class FinalizeLoginJson
|
||||
{
|
||||
[JsonProperty("steamID")]
|
||||
public ulong SteamId { get; set; }
|
||||
[JsonProperty("steamID")] public ulong SteamId { get; set; }
|
||||
|
||||
[JsonProperty("transfer_info")]
|
||||
public List<TransferInfo> TransferInfo { get; set; } = [];
|
||||
[JsonProperty("transfer_info")] public List<TransferInfo> TransferInfo { get; set; } = [];
|
||||
}
|
||||
|
||||
public class TransferInfo
|
||||
{
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; } = null!;
|
||||
[JsonProperty("url")] public string Url { get; set; } = null!;
|
||||
|
||||
[JsonProperty("params")]
|
||||
public TransferInfoParams TransferInfoParams { get; set; } = null!;
|
||||
[JsonProperty("params")] public TransferInfoParams TransferInfoParams { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class TransferInfoParams
|
||||
{
|
||||
[JsonProperty("nonce")]
|
||||
public string Nonce { get; set; } = null!;
|
||||
[JsonProperty("nonce")] public string Nonce { get; set; } = null!;
|
||||
|
||||
[JsonProperty("auth")]
|
||||
public string Auth { get; set; } = null!;
|
||||
[JsonProperty("auth")] public string Auth { get; set; } = null!;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user