More updates on wino acc.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="{x:Bind domain:Translator.WinoAccount_RegisterDialog_Title}"
|
||||
FullSizeDesired="True"
|
||||
PrimaryButtonClick="RegisterClicked"
|
||||
PrimaryButtonStyle="{ThemeResource AccentButtonStyle}"
|
||||
PrimaryButtonText="{x:Bind domain:Translator.WinoAccount_RegisterDialog_PrimaryButton}"
|
||||
@@ -16,15 +17,14 @@
|
||||
<ContentDialog.Resources>
|
||||
<x:Double x:Key="ContentDialogMinWidth">560</x:Double>
|
||||
<x:Double x:Key="ContentDialogMaxWidth">560</x:Double>
|
||||
<x:Double x:Key="ContentDialogMaxHeight">900</x:Double>
|
||||
</ContentDialog.Resources>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="20">
|
||||
|
||||
<!-- Hero illustration area -->
|
||||
<Border
|
||||
Height="140"
|
||||
CornerRadius="12">
|
||||
<Border Height="140" CornerRadius="12">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
|
||||
<GradientStop Offset="0" Color="#1A818CF8" />
|
||||
@@ -99,9 +99,7 @@
|
||||
Height="28"
|
||||
Fill="White" />
|
||||
<!-- Person body -->
|
||||
<Path
|
||||
Data="M28 68 A20 16 0 0 1 68 68"
|
||||
Fill="White" />
|
||||
<Path Data="M28 68 A20 16 0 0 1 68 68" Fill="White" />
|
||||
|
||||
<!-- Plus badge -->
|
||||
<Ellipse
|
||||
@@ -239,6 +237,12 @@
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
x:Name="ErrorTextBlock"
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
TextWrapping="WrapWholeWords"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<!-- Input fields -->
|
||||
<StackPanel Spacing="12">
|
||||
<TextBox
|
||||
@@ -266,9 +270,7 @@
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
CornerRadius="12">
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.WinoAccount_RegisterDialog_PrivacyTitle}" />
|
||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind domain:Translator.WinoAccount_RegisterDialog_PrivacyTitle}" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
@@ -281,8 +283,8 @@
|
||||
<CheckBox
|
||||
x:Name="PrivacyPolicyCheckBox"
|
||||
Checked="InputChanged"
|
||||
Unchecked="InputChanged"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_RegisterDialog_PrivacyCheckbox}" />
|
||||
Content="{x:Bind domain:Translator.WinoAccount_RegisterDialog_PrivacyCheckbox}"
|
||||
Unchecked="InputChanged" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -293,12 +295,6 @@
|
||||
HorizontalAlignment="Left"
|
||||
IsActive="False"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<TextBlock
|
||||
x:Name="ErrorTextBlock"
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
TextWrapping="WrapWholeWords"
|
||||
Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</ContentDialog>
|
||||
|
||||
@@ -3,6 +3,10 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
@@ -44,6 +48,59 @@ public class PreferencesService(IConfigurationService configurationService) : Ob
|
||||
RenderPlaintextLinks = RenderPlaintextLinks
|
||||
};
|
||||
|
||||
public string ExportPreferences()
|
||||
{
|
||||
var settings = new Dictionary<string, object?>(StringComparer.Ordinal);
|
||||
|
||||
foreach (var property in GetSyncablePreferenceProperties())
|
||||
{
|
||||
settings[property.Name] = property.GetValue(this);
|
||||
}
|
||||
|
||||
using var stream = new MemoryStream();
|
||||
using (var writer = new Utf8JsonWriter(stream))
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
WritePreferenceValue(writer, setting.Key, setting.Value);
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(stream.ToArray());
|
||||
}
|
||||
|
||||
public (int appliedCount, int failedCount) ImportPreferences(string settingsJson)
|
||||
{
|
||||
using var document = JsonDocument.Parse(settingsJson);
|
||||
var rootElement = document.RootElement;
|
||||
var appliedCount = 0;
|
||||
var failedCount = 0;
|
||||
|
||||
foreach (var property in GetSyncablePreferenceProperties())
|
||||
{
|
||||
if (!rootElement.TryGetProperty(property.Name, out var value) || value.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
property.SetValue(this, ReadPreferenceValue(property.PropertyType, value));
|
||||
appliedCount++;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return (appliedCount, failedCount);
|
||||
}
|
||||
|
||||
public MailListDisplayMode MailItemDisplayMode
|
||||
{
|
||||
get => _configurationService.Get(nameof(MailItemDisplayMode), MailListDisplayMode.Spacious);
|
||||
@@ -368,6 +425,122 @@ public class PreferencesService(IConfigurationService configurationService) : Ob
|
||||
|
||||
return daysOfWeek;
|
||||
}
|
||||
|
||||
private static void WritePreferenceValue(Utf8JsonWriter writer, string propertyName, object? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull(propertyName);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case string stringValue:
|
||||
writer.WriteString(propertyName, stringValue);
|
||||
return;
|
||||
case bool boolValue:
|
||||
writer.WriteBoolean(propertyName, boolValue);
|
||||
return;
|
||||
case int intValue:
|
||||
writer.WriteNumber(propertyName, intValue);
|
||||
return;
|
||||
case long longValue:
|
||||
writer.WriteNumber(propertyName, longValue);
|
||||
return;
|
||||
case double doubleValue:
|
||||
writer.WriteNumber(propertyName, doubleValue);
|
||||
return;
|
||||
case float floatValue:
|
||||
writer.WriteNumber(propertyName, floatValue);
|
||||
return;
|
||||
case Guid guidValue:
|
||||
writer.WriteString(propertyName, guidValue);
|
||||
return;
|
||||
case TimeSpan timeSpanValue:
|
||||
writer.WriteString(propertyName, timeSpanValue.ToString("c", CultureInfo.InvariantCulture));
|
||||
return;
|
||||
}
|
||||
|
||||
var valueType = Nullable.GetUnderlyingType(value.GetType()) ?? value.GetType();
|
||||
if (valueType.IsEnum)
|
||||
{
|
||||
writer.WriteString(propertyName, value.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteString(propertyName, Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
private static object? ReadPreferenceValue(Type propertyType, JsonElement value)
|
||||
{
|
||||
var targetType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
||||
|
||||
if (value.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (targetType == typeof(string))
|
||||
{
|
||||
return value.GetString() ?? string.Empty;
|
||||
}
|
||||
|
||||
if (targetType == typeof(bool))
|
||||
{
|
||||
return value.GetBoolean();
|
||||
}
|
||||
|
||||
if (targetType == typeof(int))
|
||||
{
|
||||
return value.GetInt32();
|
||||
}
|
||||
|
||||
if (targetType == typeof(long))
|
||||
{
|
||||
return value.GetInt64();
|
||||
}
|
||||
|
||||
if (targetType == typeof(double))
|
||||
{
|
||||
return value.GetDouble();
|
||||
}
|
||||
|
||||
if (targetType == typeof(float))
|
||||
{
|
||||
return value.GetSingle();
|
||||
}
|
||||
|
||||
if (targetType == typeof(Guid))
|
||||
{
|
||||
return Guid.Parse(value.GetString() ?? string.Empty);
|
||||
}
|
||||
|
||||
if (targetType == typeof(TimeSpan))
|
||||
{
|
||||
return TimeSpan.Parse(value.GetString() ?? string.Empty, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (targetType.IsEnum)
|
||||
{
|
||||
return Enum.Parse(targetType, value.GetString() ?? string.Empty, true);
|
||||
}
|
||||
|
||||
return Convert.ChangeType(value.GetString(), targetType, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private static IEnumerable<PropertyInfo> GetSyncablePreferenceProperties()
|
||||
{
|
||||
foreach (var property in typeof(IPreferencesService).GetProperties(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
if (!property.CanRead || !property.CanWrite || property.GetIndexParameters().Length > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ public static class WinoAccountAuthErrorTranslator
|
||||
ApiErrorCodes.AccountLocked => Translator.WinoAccount_Error_AccountLocked,
|
||||
ApiErrorCodes.AccountBanned => Translator.WinoAccount_Error_AccountBanned,
|
||||
ApiErrorCodes.AccountSuspended => Translator.WinoAccount_Error_AccountSuspended,
|
||||
ApiErrorCodes.EmailNotConfirmed => Translator.WinoAccount_Error_EmailNotConfirmed,
|
||||
ApiErrorCodes.RefreshTokenInvalid => Translator.WinoAccount_Error_RefreshTokenInvalid,
|
||||
ApiErrorCodes.EmailAlreadyRegistered => Translator.WinoAccount_Error_EmailAlreadyRegistered,
|
||||
ApiErrorCodes.ExternalLoginEmailRequired => Translator.WinoAccount_Error_ExternalLoginEmailRequired,
|
||||
|
||||
@@ -74,6 +74,99 @@
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<Button Command="{x:Bind ViewModel.RegisterCommand}" Content="{x:Bind domain:Translator.Buttons_CreateAccount}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Margin="0,16,0,4"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackSectionHeader}" />
|
||||
|
||||
<controls:SettingsCard
|
||||
MaxWidth="520"
|
||||
Description="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoDescription}">
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
<controls:SettingsCard.Header>
|
||||
<StackPanel Spacing="8">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock FontWeight="SemiBold" Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoTitle}" />
|
||||
<Border
|
||||
Padding="8,2"
|
||||
Background="{ThemeResource SystemAccentColor}"
|
||||
CornerRadius="4">
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
FontWeight="Bold"
|
||||
Foreground="White"
|
||||
Text="PRO" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
MaxWidth="400"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoDescription}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureTranslate}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureRewrite}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Border
|
||||
Padding="12,8"
|
||||
Background="{ThemeResource SystemFillColorCautionBackgroundBrush}"
|
||||
CornerRadius="8">
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource SystemFillColorCautionBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.WinoAccount_Management_PurchaseRequiresSignIn}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
</Border>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.BuyAiPackCommand}"
|
||||
Content="{x:Bind domain:Translator.Buttons_Purchase}"
|
||||
IsEnabled="{x:Bind ViewModel.CanBuyAiPack, Mode=OneWay}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<ProgressRing
|
||||
Width="18"
|
||||
Height="18"
|
||||
IsActive="True"
|
||||
Visibility="{x:Bind ViewModel.IsAiPackCheckoutInProgress, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Run Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoPrice}" />
|
||||
<Run Text=" · " />
|
||||
<Run Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoRequests}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</controls:SettingsCard.Header>
|
||||
</controls:SettingsCard>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
@@ -181,9 +274,15 @@
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.OpenBuyPageCommand}"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackGetButton}"
|
||||
Command="{x:Bind ViewModel.BuyAiPackCommand}"
|
||||
Content="{x:Bind domain:Translator.Buttons_Purchase}"
|
||||
IsEnabled="{x:Bind ViewModel.CanBuyAiPack, Mode=OneWay}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<ProgressRing
|
||||
Width="18"
|
||||
Height="18"
|
||||
IsActive="True"
|
||||
Visibility="{x:Bind ViewModel.IsAiPackCheckoutInProgress, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
|
||||
Reference in New Issue
Block a user