Get rid of system.drawing and uwp notifications pacakge. Remove the AOT/trim stuff for now.
This commit is contained in:
+14
-15
@@ -16,25 +16,24 @@
|
|||||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.251219" />
|
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.251219" />
|
||||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.250926-build.2293" />
|
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.250926-build.2293" />
|
||||||
<PackageVersion Include="CommunityToolkit.WinUI.Lottie" Version="8.2.250604" />
|
<PackageVersion Include="CommunityToolkit.WinUI.Lottie" Version="8.2.250604" />
|
||||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
|
||||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.DependencyPropertyGenerator" Version="0.1.250926-build.2293" />
|
<PackageVersion Include="CommunityToolkit.Labs.WinUI.DependencyPropertyGenerator" Version="0.1.250926-build.2293" />
|
||||||
<PackageVersion Include="EmailValidation" Version="1.3.0" />
|
<PackageVersion Include="EmailValidation" Version="1.3.0" />
|
||||||
<PackageVersion Include="gravatar-dotnet" Version="0.1.3" />
|
<PackageVersion Include="gravatar-dotnet" Version="0.1.3" />
|
||||||
<PackageVersion Include="HtmlAgilityPack" Version="1.12.4" />
|
<PackageVersion Include="HtmlAgilityPack" Version="1.12.4" />
|
||||||
<PackageVersion Include="Ical.Net" Version="4.3.1" />
|
<PackageVersion Include="Ical.Net" Version="5.2.1" />
|
||||||
<PackageVersion Include="IsExternalInit" Version="1.0.3" />
|
<PackageVersion Include="IsExternalInit" Version="1.0.3" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.3" />
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
|
||||||
<PackageVersion Include="Microsoft.Graph" Version="5.103.0" />
|
<PackageVersion Include="Microsoft.Graph" Version="5.103.0" />
|
||||||
<PackageVersion Include="Microsoft.Graphics.Win2D" Version="1.4.0" />
|
<PackageVersion Include="Microsoft.Graphics.Win2D" Version="1.4.0" />
|
||||||
<PackageVersion Include="Microsoft.Identity.Client" Version="4.83.3" />
|
<PackageVersion Include="Microsoft.Identity.Client" Version="4.83.3" />
|
||||||
<PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.82.1" />
|
<PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.83.3" />
|
||||||
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.82.1" />
|
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.83.3" />
|
||||||
<PackageVersion Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" />
|
<PackageVersion Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" />
|
||||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.1" />
|
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.1" />
|
||||||
<PackageVersion Include="Wino.Mail.Contracts" Version="1.0.17" />
|
<PackageVersion Include="Wino.Mail.Contracts" Version="1.0.18" />
|
||||||
<PackageVersion Include="MimeKit" Version="4.15.1" />
|
<PackageVersion Include="MimeKit" Version="4.15.1" />
|
||||||
<PackageVersion Include="morelinq" Version="4.4.0" />
|
<PackageVersion Include="morelinq" Version="4.4.0" />
|
||||||
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
|
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
|
||||||
@@ -52,21 +51,21 @@
|
|||||||
<PackageVersion Include="System.Drawing.Common" Version="10.0.5" />
|
<PackageVersion Include="System.Drawing.Common" Version="10.0.5" />
|
||||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
|
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
|
||||||
<PackageVersion Include="System.Text.Json" Version="10.0.3" />
|
<PackageVersion Include="System.Text.Json" Version="10.0.5" />
|
||||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
|
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
|
||||||
<PackageVersion Include="H.NotifyIcon.WinUI" Version="2.4.1" />
|
<PackageVersion Include="H.NotifyIcon.WinUI" Version="2.4.1" />
|
||||||
<PackageVersion Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
<PackageVersion Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||||
<PackageVersion Include="Google.Apis.Auth" Version="1.73.0" />
|
<PackageVersion Include="Google.Apis.Auth" Version="1.73.0" />
|
||||||
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.73.0.4063" />
|
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.73.0.4073" />
|
||||||
<PackageVersion Include="Google.Apis.Drive.v3" Version="1.73.0.4068" />
|
<PackageVersion Include="Google.Apis.Drive.v3" Version="1.73.0.4098" />
|
||||||
<PackageVersion Include="Google.Apis.Gmail.v1" Version="1.73.0.4029" />
|
<PackageVersion Include="Google.Apis.Gmail.v1" Version="1.73.0.4029" />
|
||||||
<PackageVersion Include="Google.Apis.PeopleService.v1" Version="1.72.0.3973" />
|
<PackageVersion Include="Google.Apis.PeopleService.v1" Version="1.72.0.3973" />
|
||||||
<PackageVersion Include="HtmlKit" Version="1.2.0" />
|
<PackageVersion Include="HtmlKit" Version="1.2.0" />
|
||||||
<PackageVersion Include="MailKit" Version="4.15.1" />
|
<PackageVersion Include="MailKit" Version="4.15.1" />
|
||||||
<PackageVersion Include="TimePeriodLibrary.NET" Version="2.1.6" />
|
<PackageVersion Include="TimePeriodLibrary.NET" Version="2.1.6" />
|
||||||
<PackageVersion Include="System.Reactive" Version="6.1.0" />
|
<PackageVersion Include="System.Reactive" Version="6.1.0" />
|
||||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.3" />
|
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.5" />
|
||||||
<PackageVersion Include="System.Text.Encodings.Web" Version="10.0.3" />
|
<PackageVersion Include="System.Text.Encodings.Web" Version="10.0.5" />
|
||||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1721" />
|
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1721" />
|
||||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.250930001-experimental1" />
|
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.250930001-experimental1" />
|
||||||
<PackageVersion Include="WinUIEx" Version="2.9.0" />
|
<PackageVersion Include="WinUIEx" Version="2.9.0" />
|
||||||
@@ -74,7 +73,7 @@
|
|||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||||
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
|
<PackageVersion Include="FluentAssertions" Version="8.9.0" />
|
||||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ public class CalendarPageViewModelTests
|
|||||||
|
|
||||||
public IEnumerable<AccountCalendarViewModel> ActiveCalendars => _activeCalendars;
|
public IEnumerable<AccountCalendarViewModel> ActiveCalendars => _activeCalendars;
|
||||||
public IEnumerable<AccountCalendarViewModel> AllCalendars => _calendars;
|
public IEnumerable<AccountCalendarViewModel> AllCalendars => _calendars;
|
||||||
|
public bool IsAnySynchronizationInProgress => false;
|
||||||
public ReadOnlyObservableGroupedCollection<MailAccount, AccountCalendarViewModel> GroupedCalendars { get; set; } = null!;
|
public ReadOnlyObservableGroupedCollection<MailAccount, AccountCalendarViewModel> GroupedCalendars { get; set; } = null!;
|
||||||
|
|
||||||
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar) => _groupedCalendars.Add(groupedAccountCalendar);
|
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar) => _groupedCalendars.Add(groupedAccountCalendar);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Wino.Core.Domain.Misc;
|
using Wino.Core.Domain.Misc;
|
||||||
@@ -46,25 +45,21 @@ public static class ColorHelpers
|
|||||||
return "#FFFFFF";
|
return "#FFFFFF";
|
||||||
}
|
}
|
||||||
|
|
||||||
var color = ColorTranslator.FromHtml(normalizedColor);
|
var (red, green, blue) = ParseRgb(normalizedColor);
|
||||||
var luminance = ((0.299 * color.R) + (0.587 * color.G) + (0.114 * color.B)) / 255d;
|
var luminance = ((0.299 * red) + (0.587 * green) + (0.114 * blue)) / 255d;
|
||||||
return luminance > 0.6 ? "#111111" : "#FFFFFF";
|
return luminance > 0.6 ? "#111111" : "#FFFFFF";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ToHexString(this Color c) => $"#{c.R:X2}{c.G:X2}{c.B:X2}";
|
|
||||||
|
|
||||||
public static string ToRgbString(this Color c) => $"RGB({c.R}, {c.G}, {c.B})";
|
|
||||||
private static string AdjustColor(string hexColor, int cycle)
|
private static string AdjustColor(string hexColor, int cycle)
|
||||||
{
|
{
|
||||||
var color = ColorTranslator.FromHtml(hexColor);
|
var (red, green, blue) = ParseRgb(hexColor);
|
||||||
var factor = Math.Max(0.55, 1.0 - (cycle * 0.08));
|
var factor = Math.Max(0.55, 1.0 - (cycle * 0.08));
|
||||||
|
|
||||||
var adjusted = Color.FromArgb(
|
var adjustedRed = (int)Math.Clamp(red * factor, 0, 255);
|
||||||
(int)Math.Clamp(color.R * factor, 0, 255),
|
var adjustedGreen = (int)Math.Clamp(green * factor, 0, 255);
|
||||||
(int)Math.Clamp(color.G * factor, 0, 255),
|
var adjustedBlue = (int)Math.Clamp(blue * factor, 0, 255);
|
||||||
(int)Math.Clamp(color.B * factor, 0, 255));
|
|
||||||
|
|
||||||
return adjusted.ToHexString();
|
return $"#{adjustedRed:X2}{adjustedGreen:X2}{adjustedBlue:X2}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryNormalizeHexColor(string value, out string normalized)
|
private static bool TryNormalizeHexColor(string value, out string normalized)
|
||||||
@@ -97,4 +92,18 @@ public static class ColorHelpers
|
|||||||
|
|
||||||
private static string NormalizeHexColor(string value)
|
private static string NormalizeHexColor(string value)
|
||||||
=> TryNormalizeHexColor(value, out var normalized) ? normalized : string.Empty;
|
=> TryNormalizeHexColor(value, out var normalized) ? normalized : string.Empty;
|
||||||
|
|
||||||
|
private static (int Red, int Green, int Blue) ParseRgb(string hexColor)
|
||||||
|
{
|
||||||
|
var normalized = NormalizeHexColor(hexColor);
|
||||||
|
if (string.IsNullOrWhiteSpace(normalized))
|
||||||
|
{
|
||||||
|
return (255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
Convert.ToInt32(normalized.Substring(1, 2), 16),
|
||||||
|
Convert.ToInt32(normalized.Substring(3, 2), 16),
|
||||||
|
Convert.ToInt32(normalized.Substring(5, 2), 16));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<ResourceDictionary Source="/Styles/WinoInfoBar.xaml" />
|
<ResourceDictionary Source="/Styles/WinoInfoBar.xaml" />
|
||||||
<ResourceDictionary Source="/Styles/SharedStyles.xaml" />
|
<ResourceDictionary Source="/Styles/SharedStyles.xaml" />
|
||||||
<ResourceDictionary Source="/Styles/IconTemplates.xaml" />
|
<ResourceDictionary Source="/Styles/IconTemplates.xaml" />
|
||||||
<ResourceDictionary Source="/Styles/OperationCommandBar.xaml" />
|
<coreStyles:OperationCommandBarResources />
|
||||||
|
|
||||||
|
|
||||||
<coreStyles:CustomMessageDialogStyles />
|
<coreStyles:CustomMessageDialogStyles />
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Toolkit.Uwp.Notifications;
|
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Media.Animation;
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
@@ -492,7 +491,7 @@ public partial class App : WinoApplication,
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task HandleToastActivationAsync(AppNotificationActivatedEventArgs toastArgs)
|
private async Task HandleToastActivationAsync(AppNotificationActivatedEventArgs toastArgs)
|
||||||
{
|
{
|
||||||
var toastArguments = ToastArguments.Parse(toastArgs.Argument);
|
var toastArguments = NotificationArguments.Parse(toastArgs.Argument);
|
||||||
|
|
||||||
if (toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string storeUpdateAction) &&
|
if (toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string storeUpdateAction) &&
|
||||||
storeUpdateAction == Constants.ToastStoreUpdateActionInstall)
|
storeUpdateAction == Constants.ToastStoreUpdateActionInstall)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||||
Text="{x:Bind domain:Translator.AiActions_CheckingStatus, Mode=OneWay}" />
|
Text="{x:Bind domain:Translator.AiActions_CheckingStatus}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -77,12 +77,12 @@
|
|||||||
<StackPanel Grid.Column="1" Spacing="6">
|
<StackPanel Grid.Column="1" Spacing="6">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||||
Text="{x:Bind domain:Translator.AiActions_SignedOutTitle, Mode=OneWay}"
|
Text="{x:Bind domain:Translator.AiActions_SignedOutTitle}"
|
||||||
TextWrapping="WrapWholeWords" />
|
TextWrapping="WrapWholeWords" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{x:Bind domain:Translator.AiActions_SignedOutDescription, Mode=OneWay}"
|
Text="{x:Bind domain:Translator.AiActions_SignedOutDescription}"
|
||||||
TextWrapping="WrapWholeWords" />
|
TextWrapping="WrapWholeWords" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -95,12 +95,12 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Button
|
<Button
|
||||||
Click="SignInButton_Click"
|
Click="SignInButton_Click"
|
||||||
Content="{x:Bind domain:Translator.Buttons_SignIn, Mode=OneWay}"
|
Content="{x:Bind domain:Translator.Buttons_SignIn}"
|
||||||
Style="{StaticResource AccentButtonStyle}" />
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
<Button
|
<Button
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Click="CreateAccountButton_Click"
|
Click="CreateAccountButton_Click"
|
||||||
Content="{x:Bind domain:Translator.Buttons_CreateAccount, Mode=OneWay}" />
|
Content="{x:Bind domain:Translator.Buttons_CreateAccount}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -113,24 +113,24 @@
|
|||||||
Background="{ThemeResource CardBackgroundFillColorTertiaryBrush}"
|
Background="{ThemeResource CardBackgroundFillColorTertiaryBrush}"
|
||||||
CornerRadius="12">
|
CornerRadius="12">
|
||||||
<StackPanel Spacing="8">
|
<StackPanel Spacing="8">
|
||||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind domain:Translator.AiActions_NoPackTitle, Mode=OneWay}" />
|
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind domain:Translator.AiActions_NoPackTitle}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
Text="{x:Bind domain:Translator.AiActions_NoPackDescription, Mode=OneWay}"
|
Text="{x:Bind domain:Translator.AiActions_NoPackDescription}"
|
||||||
TextWrapping="WrapWholeWords" />
|
TextWrapping="WrapWholeWords" />
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
<Border
|
<Border
|
||||||
Padding="8,2"
|
Padding="8,2"
|
||||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||||
CornerRadius="8">
|
CornerRadius="8">
|
||||||
<TextBlock Foreground="White" Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoPrice, Mode=OneWay}" />
|
<TextBlock Foreground="White" Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoPrice}" />
|
||||||
</Border>
|
</Border>
|
||||||
<Border
|
<Border
|
||||||
Padding="8,2"
|
Padding="8,2"
|
||||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||||
CornerRadius="8">
|
CornerRadius="8">
|
||||||
<TextBlock Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoRequests, Mode=OneWay}" />
|
<TextBlock Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoRequests}" />
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
<Button
|
<Button
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Click="PurchaseButton_Click"
|
Click="PurchaseButton_Click"
|
||||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackGetButton, Mode=OneWay}"
|
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackGetButton}"
|
||||||
Style="{StaticResource AccentButtonStyle}" />
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
<controls:SegmentedItem
|
<controls:SegmentedItem
|
||||||
x:Name="TranslateSegment"
|
x:Name="TranslateSegment"
|
||||||
Padding="12,6"
|
Padding="12,6"
|
||||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureTranslate, Mode=OneWay}">
|
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureTranslate}">
|
||||||
<controls:SegmentedItem.Icon>
|
<controls:SegmentedItem.Icon>
|
||||||
<SymbolIcon Symbol="Switch" />
|
<SymbolIcon Symbol="Switch" />
|
||||||
</controls:SegmentedItem.Icon>
|
</controls:SegmentedItem.Icon>
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
<controls:SegmentedItem
|
<controls:SegmentedItem
|
||||||
x:Name="RewriteSegment"
|
x:Name="RewriteSegment"
|
||||||
Padding="12,6"
|
Padding="12,6"
|
||||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureRewrite, Mode=OneWay}">
|
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureRewrite}">
|
||||||
<controls:SegmentedItem.Icon>
|
<controls:SegmentedItem.Icon>
|
||||||
<SymbolIcon Symbol="Edit" />
|
<SymbolIcon Symbol="Edit" />
|
||||||
</controls:SegmentedItem.Icon>
|
</controls:SegmentedItem.Icon>
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
<controls:SegmentedItem
|
<controls:SegmentedItem
|
||||||
x:Name="SummarizeSegment"
|
x:Name="SummarizeSegment"
|
||||||
Padding="12,6"
|
Padding="12,6"
|
||||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize, Mode=OneWay}">
|
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize}">
|
||||||
<controls:SegmentedItem.Icon>
|
<controls:SegmentedItem.Icon>
|
||||||
<SymbolIcon Symbol="Bullets" />
|
<SymbolIcon Symbol="Bullets" />
|
||||||
</controls:SegmentedItem.Icon>
|
</controls:SegmentedItem.Icon>
|
||||||
@@ -202,7 +202,7 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage, Mode=OneWay}" />
|
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="TranslateLanguageComboBox"
|
x:Name="TranslateLanguageComboBox"
|
||||||
MinWidth="120"
|
MinWidth="120"
|
||||||
@@ -216,7 +216,7 @@
|
|||||||
<Button
|
<Button
|
||||||
x:Name="RunTranslateButton"
|
x:Name="RunTranslateButton"
|
||||||
Click="RunTranslateButton_Click"
|
Click="RunTranslateButton_Click"
|
||||||
Content="{x:Bind domain:Translator.Composer_AiTranslateApply, Mode=OneWay}"
|
Content="{x:Bind domain:Translator.Composer_AiTranslateApply}"
|
||||||
Style="{StaticResource AccentButtonStyle}" />
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{x:Bind domain:Translator.Composer_AiRewriteMode, Mode=OneWay}" />
|
Text="{x:Bind domain:Translator.Composer_AiRewriteMode}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="RewriteModeComboBox"
|
x:Name="RewriteModeComboBox"
|
||||||
MinWidth="140"
|
MinWidth="140"
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
<Button
|
<Button
|
||||||
x:Name="RunRewriteButton"
|
x:Name="RunRewriteButton"
|
||||||
Click="RunRewriteButton_Click"
|
Click="RunRewriteButton_Click"
|
||||||
Content="{x:Bind domain:Translator.Composer_AiRewriteApply, Mode=OneWay}"
|
Content="{x:Bind domain:Translator.Composer_AiRewriteApply}"
|
||||||
Style="{StaticResource AccentButtonStyle}" />
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -254,7 +254,7 @@
|
|||||||
TextWrapping="WrapWholeWords" />
|
TextWrapping="WrapWholeWords" />
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="CustomRewriteTextBox"
|
x:Name="CustomRewriteTextBox"
|
||||||
PlaceholderText="{x:Bind domain:Translator.Composer_AiRewriteCustomPlaceholder, Mode=OneWay}"
|
PlaceholderText="{x:Bind domain:Translator.Composer_AiRewriteCustomPlaceholder}"
|
||||||
Visibility="Collapsed" />
|
Visibility="Collapsed" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage, Mode=OneWay}" />
|
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="SummarizeLanguageComboBox"
|
x:Name="SummarizeLanguageComboBox"
|
||||||
MinWidth="120"
|
MinWidth="120"
|
||||||
@@ -285,7 +285,7 @@
|
|||||||
x:Name="RunSummarizeButton"
|
x:Name="RunSummarizeButton"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Click="RunSummarizeButton_Click"
|
Click="RunSummarizeButton_Click"
|
||||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize, Mode=OneWay}"
|
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize}"
|
||||||
Style="{StaticResource AccentButtonStyle}" />
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
<FontIcon
|
<FontIcon
|
||||||
x:Name="SummarizeCachedIndicator"
|
x:Name="SummarizeCachedIndicator"
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
FontSize="16"
|
FontSize="16"
|
||||||
Foreground="#2AA84A"
|
Foreground="#2AA84A"
|
||||||
Glyph=""
|
Glyph=""
|
||||||
ToolTipService.ToolTip="{x:Bind domain:Translator.Composer_AiSummarize, Mode=OneWay}"
|
ToolTipService.ToolTip="{x:Bind domain:Translator.Composer_AiSummarize}"
|
||||||
Visibility="Collapsed" />
|
Visibility="Collapsed" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
|||||||
{
|
{
|
||||||
var button = LoadCommandBarElementTemplate(
|
var button = LoadCommandBarElementTemplate(
|
||||||
MailOperationTemplateKey,
|
MailOperationTemplateKey,
|
||||||
new MenuOperationCommandBarItemViewModel(
|
new OperationCommandBarMenuOperationItemViewModel(
|
||||||
mailOperationItem,
|
mailOperationItem,
|
||||||
XamlHelpers.GetOperationString(mailOperationItem.Operation),
|
XamlHelpers.GetOperationString(mailOperationItem.Operation),
|
||||||
XamlHelpers.GetWinoIconGlyph(mailOperationItem.Operation),
|
XamlHelpers.GetWinoIconGlyph(mailOperationItem.Operation),
|
||||||
@@ -307,7 +307,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
|||||||
var label = XamlHelpers.GetOperationString(folderOperationItem.Operation);
|
var label = XamlHelpers.GetOperationString(folderOperationItem.Operation);
|
||||||
var button = LoadCommandBarElementTemplate(
|
var button = LoadCommandBarElementTemplate(
|
||||||
FolderOperationTemplateKey,
|
FolderOperationTemplateKey,
|
||||||
new MenuOperationCommandBarItemViewModel(
|
new OperationCommandBarMenuOperationItemViewModel(
|
||||||
folderOperationItem,
|
folderOperationItem,
|
||||||
label,
|
label,
|
||||||
XamlHelpers.GetPathGeometry(folderOperationItem.Operation),
|
XamlHelpers.GetPathGeometry(folderOperationItem.Operation),
|
||||||
@@ -331,7 +331,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
|||||||
{
|
{
|
||||||
var button = (AppBarToggleButton)LoadCommandBarElementTemplate(
|
var button = (AppBarToggleButton)LoadCommandBarElementTemplate(
|
||||||
AIActionsTemplateKey,
|
AIActionsTemplateKey,
|
||||||
new AIActionsCommandBarItemViewModel(Translator.Composer_AiActions, "\uE945"));
|
new OperationCommandBarAIActionsItemViewModel(Translator.Composer_AiActions, "\uE945"));
|
||||||
|
|
||||||
button.SetBinding(AppBarToggleButton.IsCheckedProperty, new Binding
|
button.SetBinding(AppBarToggleButton.IsCheckedProperty, new Binding
|
||||||
{
|
{
|
||||||
@@ -350,7 +350,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
|||||||
|
|
||||||
var button = (AppBarButton)LoadCommandBarElementTemplate(
|
var button = (AppBarButton)LoadCommandBarElementTemplate(
|
||||||
ThemeToggleTemplateKey,
|
ThemeToggleTemplateKey,
|
||||||
new ThemeCommandBarItemViewModel(label, icon));
|
new OperationCommandBarThemeItemViewModel(label, icon));
|
||||||
|
|
||||||
button.Click += ThemeButton_Click;
|
button.Click += ThemeButton_Click;
|
||||||
return button;
|
return button;
|
||||||
@@ -430,48 +430,48 @@ public sealed partial class OperationCommandBar : CommandBar
|
|||||||
: CommandBarOverflowButtonVisibility.Auto;
|
: CommandBarOverflowButtonVisibility.Auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class MenuOperationCommandBarItemViewModel
|
|
||||||
{
|
|
||||||
public MenuOperationCommandBarItemViewModel(IMenuOperation operation, string label, WinoIconGlyph icon, CommandBarLabelPosition labelPosition)
|
|
||||||
{
|
|
||||||
Operation = operation;
|
|
||||||
Label = label;
|
|
||||||
Icon = icon;
|
|
||||||
ToolTip = label;
|
|
||||||
LabelPosition = labelPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMenuOperation Operation { get; }
|
|
||||||
public string Label { get; }
|
|
||||||
public WinoIconGlyph Icon { get; }
|
|
||||||
public string ToolTip { get; }
|
|
||||||
public bool IsEnabled => Operation.IsEnabled;
|
|
||||||
public CommandBarLabelPosition LabelPosition { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class AIActionsCommandBarItemViewModel
|
|
||||||
{
|
|
||||||
public AIActionsCommandBarItemViewModel(string toolTip, string glyph)
|
|
||||||
{
|
|
||||||
ToolTip = toolTip;
|
|
||||||
Glyph = glyph;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToolTip { get; }
|
|
||||||
public string Glyph { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class ThemeCommandBarItemViewModel
|
|
||||||
{
|
|
||||||
public ThemeCommandBarItemViewModel(string toolTip, WinoIconGlyph icon)
|
|
||||||
{
|
|
||||||
ToolTip = toolTip;
|
|
||||||
Icon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToolTip { get; }
|
|
||||||
public WinoIconGlyph Icon { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class SeparatorCommandBarItemViewModel;
|
private sealed class SeparatorCommandBarItemViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class OperationCommandBarMenuOperationItemViewModel
|
||||||
|
{
|
||||||
|
public OperationCommandBarMenuOperationItemViewModel(IMenuOperation operation, string label, WinoIconGlyph icon, CommandBarLabelPosition labelPosition)
|
||||||
|
{
|
||||||
|
Operation = operation;
|
||||||
|
Label = label;
|
||||||
|
Icon = icon;
|
||||||
|
ToolTip = label;
|
||||||
|
LabelPosition = labelPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMenuOperation Operation { get; }
|
||||||
|
public string Label { get; }
|
||||||
|
public WinoIconGlyph Icon { get; }
|
||||||
|
public string ToolTip { get; }
|
||||||
|
public bool IsEnabled => Operation.IsEnabled;
|
||||||
|
public CommandBarLabelPosition LabelPosition { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class OperationCommandBarAIActionsItemViewModel
|
||||||
|
{
|
||||||
|
public OperationCommandBarAIActionsItemViewModel(string toolTip, string glyph)
|
||||||
|
{
|
||||||
|
ToolTip = toolTip;
|
||||||
|
Glyph = glyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToolTip { get; }
|
||||||
|
public string Glyph { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class OperationCommandBarThemeItemViewModel
|
||||||
|
{
|
||||||
|
public OperationCommandBarThemeItemViewModel(string toolTip, WinoIconGlyph icon)
|
||||||
|
{
|
||||||
|
ToolTip = toolTip;
|
||||||
|
Icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToolTip { get; }
|
||||||
|
public WinoIconGlyph Icon { get; }
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing.Printing;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Models.Printing;
|
using Wino.Core.Domain.Models.Printing;
|
||||||
|
using Wino.Mail.WinUI.Helpers;
|
||||||
|
|
||||||
namespace Wino.Mail.WinUI.Dialogs;
|
namespace Wino.Mail.WinUI.Dialogs;
|
||||||
|
|
||||||
@@ -87,15 +87,7 @@ public sealed partial class PrintDialog : ContentDialog
|
|||||||
{
|
{
|
||||||
var printers = await Task.Run(() =>
|
var printers = await Task.Run(() =>
|
||||||
{
|
{
|
||||||
var printerList = new List<string>();
|
return InstalledPrinterHelper.GetInstalledPrinters().AsEnumerable();
|
||||||
|
|
||||||
// Get all installed printers using System.Drawing.Printing
|
|
||||||
foreach (string printerName in PrinterSettings.InstalledPrinters)
|
|
||||||
{
|
|
||||||
printerList.Add(printerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return printerList.AsEnumerable();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
SetAvailablePrinters(printers);
|
SetAvailablePrinters(printers);
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ public static class CalendarXamlHelpers
|
|||||||
interval: recurrenceRule.Interval <= 0 ? 1 : recurrenceRule.Interval,
|
interval: recurrenceRule.Interval <= 0 ? 1 : recurrenceRule.Interval,
|
||||||
frequency: frequency.Value,
|
frequency: frequency.Value,
|
||||||
daysOfWeek: recurrenceRule.ByDay?.Select(day => day.DayOfWeek).ToList() ?? [],
|
daysOfWeek: recurrenceRule.ByDay?.Select(day => day.DayOfWeek).ToList() ?? [],
|
||||||
recurrenceEndDate: recurrenceRule.Until == default ? null : new DateTimeOffset(recurrenceRule.Until));
|
recurrenceEndDate: recurrenceRule.Until == null
|
||||||
|
? null
|
||||||
|
: new DateTimeOffset(DateTime.SpecifyKind(recurrenceRule.Until.AsUtc, DateTimeKind.Utc)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetDetailsPopupDurationString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
|
public static string GetDetailsPopupDurationString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wino.Mail.WinUI.Helpers;
|
||||||
|
|
||||||
|
internal static partial class InstalledPrinterHelper
|
||||||
|
{
|
||||||
|
private const int PrinterEnumLocal = 0x00000002;
|
||||||
|
private const int PrinterEnumConnections = 0x00000004;
|
||||||
|
|
||||||
|
public static IReadOnlyList<string> GetInstalledPrinters()
|
||||||
|
{
|
||||||
|
var flags = PrinterEnumLocal | PrinterEnumConnections;
|
||||||
|
|
||||||
|
if (!EnumPrinters(flags, null, 4, IntPtr.Zero, 0, out var bytesNeeded, out _))
|
||||||
|
{
|
||||||
|
var error = Marshal.GetLastWin32Error();
|
||||||
|
if (error != 122 || bytesNeeded <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"EnumPrinters failed to query buffer size. Win32 error: {error}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var printerBuffer = Marshal.AllocHGlobal(bytesNeeded);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!EnumPrinters(flags, null, 4, printerBuffer, bytesNeeded, out _, out var printerCount))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"EnumPrinters failed to enumerate printers. Win32 error: {Marshal.GetLastWin32Error()}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var printers = new List<string>(printerCount);
|
||||||
|
var structSize = Marshal.SizeOf<PrinterInfo4>();
|
||||||
|
|
||||||
|
for (var i = 0; i < printerCount; i++)
|
||||||
|
{
|
||||||
|
var current = IntPtr.Add(printerBuffer, i * structSize);
|
||||||
|
var info = Marshal.PtrToStructure<PrinterInfo4>(current);
|
||||||
|
if (!string.IsNullOrWhiteSpace(info.PrinterName))
|
||||||
|
{
|
||||||
|
printers.Add(info.PrinterName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return printers;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(printerBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[LibraryImport("winspool.drv", EntryPoint = "EnumPrintersW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static partial bool EnumPrinters(
|
||||||
|
int flags,
|
||||||
|
string? name,
|
||||||
|
int level,
|
||||||
|
IntPtr pPrinterEnum,
|
||||||
|
int cbBuf,
|
||||||
|
out int pcbNeeded,
|
||||||
|
out int pcReturned);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct PrinterInfo4
|
||||||
|
{
|
||||||
|
public nint PrinterNamePointer;
|
||||||
|
public nint ServerNamePointer;
|
||||||
|
public int Attributes;
|
||||||
|
|
||||||
|
public string? PrinterName => Marshal.PtrToStringUni(PrinterNamePointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Wino.Mail.WinUI;
|
||||||
|
|
||||||
|
internal sealed class NotificationArguments
|
||||||
|
{
|
||||||
|
private static readonly NotificationArguments Empty = new(new Dictionary<string, string>(StringComparer.Ordinal));
|
||||||
|
|
||||||
|
private readonly IReadOnlyDictionary<string, string> _values;
|
||||||
|
|
||||||
|
private NotificationArguments(IReadOnlyDictionary<string, string> values)
|
||||||
|
{
|
||||||
|
_values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string this[string key] => _values[key];
|
||||||
|
|
||||||
|
public static NotificationArguments Parse(string? encodedArguments)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(encodedArguments))
|
||||||
|
return Empty;
|
||||||
|
|
||||||
|
var values = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var pair in encodedArguments.Split('&', StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
var separatorIndex = pair.IndexOf('=');
|
||||||
|
if (separatorIndex < 0)
|
||||||
|
{
|
||||||
|
values[WebUtility.UrlDecode(pair)] = string.Empty;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = WebUtility.UrlDecode(pair[..separatorIndex]);
|
||||||
|
var value = WebUtility.UrlDecode(pair[(separatorIndex + 1)..]);
|
||||||
|
|
||||||
|
values[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NotificationArguments(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(string key, out string value)
|
||||||
|
=> _values.TryGetValue(key, out value!);
|
||||||
|
|
||||||
|
public bool TryGetValue<TEnum>(string key, out TEnum value) where TEnum : struct, Enum
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
|
||||||
|
return _values.TryGetValue(key, out var rawValue) &&
|
||||||
|
Enum.TryParse(rawValue, ignoreCase: true, out value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="58272BurakKSE.WinoMailPreview"
|
Name="58272BurakKSE.WinoMailPreview"
|
||||||
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
|
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
|
||||||
Version="2.0.15.0" />
|
Version="2.0.0.0" />
|
||||||
|
|
||||||
<mp:PhoneIdentity PhoneProductId="3879fcfb-a561-4599-9103-e0c9b35a271f" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
<mp:PhoneIdentity PhoneProductId="3879fcfb-a561-4599-9103-e0c9b35a271f" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Diagnostics;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Toolkit.Uwp.Notifications;
|
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.Windows.AppNotifications;
|
using Microsoft.Windows.AppNotifications;
|
||||||
@@ -206,7 +205,7 @@ public class Program
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var toastArguments = ToastArguments.Parse(toastArgs.Argument);
|
var toastArguments = NotificationArguments.Parse(toastArgs.Argument);
|
||||||
|
|
||||||
if (toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string storeUpdateAction) &&
|
if (toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string storeUpdateAction) &&
|
||||||
storeUpdateAction == Constants.ToastStoreUpdateActionInstall)
|
storeUpdateAction == Constants.ToastStoreUpdateActionInstall)
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.Toolkit.Uwp.Notifications;
|
using Microsoft.Windows.AppNotifications;
|
||||||
|
using Microsoft.Windows.AppNotifications.Builder;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Windows.Data.Xml.Dom;
|
using Windows.Data.Xml.Dom;
|
||||||
using Windows.UI.Notifications;
|
using Windows.UI.Notifications;
|
||||||
@@ -85,23 +86,17 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
if (mailCount == 0)
|
if (mailCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If there are more than 3 mails, just display 1 general toast.
|
// If there are more than 3 mails, just display 1 general notification.
|
||||||
if (mailCount > 3)
|
if (mailCount > 3)
|
||||||
{
|
{
|
||||||
var builder = new ToastContentBuilder();
|
var builder = CreateBuilder();
|
||||||
builder.SetToastScenario(ToastScenario.Default);
|
|
||||||
|
|
||||||
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
|
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
|
||||||
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
|
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
|
||||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
|
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
|
||||||
|
|
||||||
builder.AddButton(GetDismissButton());
|
ShowNotification(builder);
|
||||||
builder.AddAudio(new ToastAudio()
|
|
||||||
{
|
|
||||||
Src = new Uri("ms-winsoundevent:Notification.Mail")
|
|
||||||
});
|
|
||||||
|
|
||||||
ShowToast(builder);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -121,8 +116,7 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
|
|
||||||
private async Task CreateSingleNotificationAsync(MailCopy mailItem)
|
private async Task CreateSingleNotificationAsync(MailCopy mailItem)
|
||||||
{
|
{
|
||||||
var builder = new ToastContentBuilder();
|
var builder = CreateBuilder();
|
||||||
builder.SetToastScenario(ToastScenario.Default);
|
|
||||||
|
|
||||||
var avatarThumbnail = await _thumbnailService.GetThumbnailAsync(mailItem.FromAddress, awaitLoad: true);
|
var avatarThumbnail = await _thumbnailService.GetThumbnailAsync(mailItem.FromAddress, awaitLoad: true);
|
||||||
if (!string.IsNullOrEmpty(avatarThumbnail))
|
if (!string.IsNullOrEmpty(avatarThumbnail))
|
||||||
@@ -133,63 +127,47 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
var bytes = Convert.FromBase64String(avatarThumbnail);
|
var bytes = Convert.FromBase64String(avatarThumbnail);
|
||||||
await stream.WriteAsync(bytes);
|
await stream.WriteAsync(bytes);
|
||||||
}
|
}
|
||||||
builder.AddAppLogoOverride(new Uri($"ms-appdata:///temp/{tempFile.Name}"), hintCrop: ToastGenericAppLogoCrop.Default);
|
|
||||||
|
builder.SetAppLogoOverride(new Uri($"ms-appdata:///temp/{tempFile.Name}"), AppNotificationImageCrop.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override system notification timestamp with received date of the mail.
|
builder.SetTimeStamp(mailItem.CreationDate.ToLocalTime());
|
||||||
builder.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime());
|
|
||||||
|
|
||||||
builder.AddText(mailItem.FromName);
|
builder.AddText(mailItem.FromName);
|
||||||
builder.AddText(mailItem.Subject);
|
builder.AddText(mailItem.Subject);
|
||||||
builder.AddText(mailItem.PreviewText);
|
builder.AddText(mailItem.PreviewText);
|
||||||
|
|
||||||
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
|
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
|
||||||
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate);
|
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate.ToString());
|
||||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
|
|
||||||
builder.AddButton(GetMarkAsReadButton(mailItem.UniqueId));
|
builder.AddButton(GetMarkAsReadButton(mailItem.UniqueId));
|
||||||
builder.AddButton(GetDeleteButton(mailItem.UniqueId));
|
builder.AddButton(GetDeleteButton(mailItem.UniqueId));
|
||||||
builder.AddButton(GetArchiveButton(mailItem.UniqueId));
|
builder.AddButton(GetArchiveButton(mailItem.UniqueId));
|
||||||
builder.AddAudio(new ToastAudio()
|
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
|
||||||
{
|
|
||||||
Src = new Uri("ms-winsoundevent:Notification.Mail")
|
|
||||||
});
|
|
||||||
|
|
||||||
// Use UniqueId as tag to allow removal
|
ShowNotification(builder, mailItem.UniqueId.ToString());
|
||||||
ShowToast(builder, mailItem.UniqueId.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ToastButton GetDismissButton()
|
private AppNotificationButton GetArchiveButton(Guid mailUniqueId)
|
||||||
=> new ToastButton()
|
=> new AppNotificationButton(Translator.MailOperation_Archive)
|
||||||
.SetDismissActivation()
|
.SetIcon(GetNotificationIconUri("mail-archive"))
|
||||||
.SetImageUri(GetNotificationIconUri("dismiss"));
|
|
||||||
|
|
||||||
private ToastButton GetArchiveButton(Guid mailUniqueId)
|
|
||||||
=> new ToastButton()
|
|
||||||
.SetContent(Translator.MailOperation_Archive)
|
|
||||||
.SetImageUri(GetNotificationIconUri("mail-archive"))
|
|
||||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||||
.AddArgument(Constants.ToastActionKey, MailOperation.Archive)
|
.AddArgument(Constants.ToastActionKey, MailOperation.Archive.ToString())
|
||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail)
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
.SetBackgroundActivation();
|
|
||||||
|
|
||||||
private ToastButton GetDeleteButton(Guid mailUniqueId)
|
private AppNotificationButton GetDeleteButton(Guid mailUniqueId)
|
||||||
=> new ToastButton()
|
=> new AppNotificationButton(Translator.MailOperation_Delete)
|
||||||
.SetContent(Translator.MailOperation_Delete)
|
.SetIcon(GetNotificationIconUri("mail-delete"))
|
||||||
.SetImageUri(GetNotificationIconUri("mail-delete"))
|
|
||||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||||
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete)
|
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete.ToString())
|
||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail)
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
.SetBackgroundActivation();
|
|
||||||
|
|
||||||
private ToastButton GetMarkAsReadButton(Guid mailUniqueId)
|
private AppNotificationButton GetMarkAsReadButton(Guid mailUniqueId)
|
||||||
=> new ToastButton()
|
=> new AppNotificationButton(Translator.MailOperation_MarkAsRead)
|
||||||
.SetContent(Translator.MailOperation_MarkAsRead)
|
.SetIcon(GetNotificationIconUri("mail-markread"))
|
||||||
.SetImageUri(GetNotificationIconUri("mail-markread"))
|
|
||||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||||
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead)
|
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead.ToString())
|
||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail)
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
.SetBackgroundActivation();
|
|
||||||
|
|
||||||
public async Task UpdateTaskbarIconBadgeAsync()
|
public async Task UpdateTaskbarIconBadgeAsync()
|
||||||
{
|
{
|
||||||
@@ -216,26 +194,24 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
|
|
||||||
if (totalUnreadCount > 0)
|
if (totalUnreadCount > 0)
|
||||||
{
|
{
|
||||||
// Get the blank badge XML payload for a badge number
|
|
||||||
XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
|
XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
|
||||||
|
|
||||||
// Set the value of the badge in the XML to our number
|
|
||||||
XmlElement? badgeElement = badgeXml.SelectSingleNode("/badge") as XmlElement;
|
XmlElement? badgeElement = badgeXml.SelectSingleNode("/badge") as XmlElement;
|
||||||
if (badgeElement == null)
|
if (badgeElement == null)
|
||||||
{
|
{
|
||||||
badgeUpdater.Clear();
|
badgeUpdater.Clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
badgeElement.SetAttribute("value", totalUnreadCount.ToString());
|
badgeElement.SetAttribute("value", totalUnreadCount.ToString());
|
||||||
|
|
||||||
// Create the badge notification
|
BadgeNotification badge = new(badgeXml);
|
||||||
BadgeNotification badge = new BadgeNotification(badgeXml);
|
|
||||||
|
|
||||||
// And update the badge
|
|
||||||
badgeUpdater.Update(badge);
|
badgeUpdater.Update(badge);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
badgeUpdater.Clear();
|
badgeUpdater.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -247,7 +223,7 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ToastNotificationManager.History.Remove(mailUniqueId.ToString(), null);
|
AppNotificationManager.Default.RemoveByTagAsync(mailUniqueId.ToString()).AsTask().GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
catch (ArgumentException)
|
catch (ArgumentException)
|
||||||
{
|
{
|
||||||
@@ -261,44 +237,39 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
|
|
||||||
public void CreateAttentionRequiredNotification(MailAccount account)
|
public void CreateAttentionRequiredNotification(MailAccount account)
|
||||||
{
|
{
|
||||||
var builder = new ToastContentBuilder();
|
var builder = CreateBuilder();
|
||||||
builder.SetToastScenario(ToastScenario.Default);
|
|
||||||
|
|
||||||
builder.AddText(Translator.Exception_AccountNeedsAttention_Title);
|
builder.AddText(Translator.Exception_AccountNeedsAttention_Title);
|
||||||
builder.AddText(string.Format(Translator.Exception_AccountNeedsAttention_Message, account.Name));
|
builder.AddText(string.Format(Translator.Exception_AccountNeedsAttention_Message, account.Name));
|
||||||
|
|
||||||
builder.AddButton(GetDismissButton());
|
|
||||||
|
|
||||||
builder.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString());
|
builder.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString());
|
||||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
builder.AddButton(new ToastButton().SetContent(Translator.Buttons_FixAccount));
|
builder.AddButton(new AppNotificationButton(Translator.Buttons_FixAccount)
|
||||||
ShowToast(builder);
|
.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString())
|
||||||
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail));
|
||||||
|
|
||||||
|
ShowNotification(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateWebView2RuntimeMissingNotification()
|
public void CreateWebView2RuntimeMissingNotification()
|
||||||
{
|
{
|
||||||
var builder = new ToastContentBuilder();
|
var builder = CreateBuilder();
|
||||||
builder.SetToastScenario(ToastScenario.Default);
|
|
||||||
|
|
||||||
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Title);
|
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Title);
|
||||||
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message);
|
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message);
|
||||||
|
|
||||||
builder.AddButton(GetDismissButton());
|
|
||||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
ShowToast(builder);
|
|
||||||
|
ShowNotification(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateStoreUpdateNotification()
|
public void CreateStoreUpdateNotification()
|
||||||
{
|
{
|
||||||
var builder = new ToastContentBuilder();
|
var builder = CreateBuilder();
|
||||||
builder.SetToastScenario(ToastScenario.Default);
|
|
||||||
|
|
||||||
builder.AddText(Translator.Notifications_StoreUpdateAvailableTitle);
|
builder.AddText(Translator.Notifications_StoreUpdateAvailableTitle);
|
||||||
builder.AddText(Translator.Notifications_StoreUpdateAvailableMessage);
|
builder.AddText(Translator.Notifications_StoreUpdateAvailableMessage);
|
||||||
builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall);
|
builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall);
|
||||||
builder.AddButton(GetDismissButton());
|
|
||||||
|
|
||||||
ShowToast(builder, "store-update-available");
|
ShowNotification(builder, "store-update-available");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds)
|
public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds)
|
||||||
@@ -306,8 +277,7 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
if (calendarItem == null)
|
if (calendarItem == null)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
var builder = new ToastContentBuilder();
|
var builder = CreateBuilder(AppNotificationScenario.Reminder);
|
||||||
builder.SetToastScenario(ToastScenario.Reminder);
|
|
||||||
|
|
||||||
var localStart = calendarItem.GetLocalStartDate();
|
var localStart = calendarItem.GetLocalStartDate();
|
||||||
var nowLocal = DateTime.Now;
|
var nowLocal = DateTime.Now;
|
||||||
@@ -334,49 +304,38 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
? preferredSnoozeMinutes
|
? preferredSnoozeMinutes
|
||||||
: allowedSnoozeMinutes[0];
|
: allowedSnoozeMinutes[0];
|
||||||
|
|
||||||
var selectionBox = new ToastSelectionBox(Constants.ToastCalendarSnoozeDurationInputId)
|
var selectionBox = new AppNotificationComboBox(Constants.ToastCalendarSnoozeDurationInputId)
|
||||||
{
|
.SetSelectedItem(defaultSnoozeMinutes.ToString());
|
||||||
DefaultSelectionBoxItemId = defaultSnoozeMinutes.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var snoozeMinutes in allowedSnoozeMinutes)
|
foreach (var snoozeMinutes in allowedSnoozeMinutes)
|
||||||
{
|
{
|
||||||
selectionBox.Items.Add(new ToastSelectionBoxItem(
|
selectionBox.AddItem(
|
||||||
snoozeMinutes.ToString(),
|
snoozeMinutes.ToString(),
|
||||||
string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes)));
|
string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes));
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AddToastInput(selectionBox);
|
builder.AddComboBox(selectionBox);
|
||||||
var snoozeButton = new ToastButton()
|
builder.AddButton(new AppNotificationButton(Translator.CalendarReminder_SnoozeAction)
|
||||||
.SetContent(Translator.CalendarReminder_SnoozeAction)
|
|
||||||
.SetImageUri(GetNotificationIconUri("calendar-snooze"))
|
|
||||||
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarSnoozeAction)
|
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarSnoozeAction)
|
||||||
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
|
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
|
||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar)
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
|
||||||
.SetBackgroundActivation();
|
|
||||||
|
|
||||||
builder.AddButton(snoozeButton);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AddButton(new ToastButton()
|
builder.AddButton(new AppNotificationButton(Translator.Buttons_Open)
|
||||||
.SetDismissActivation()
|
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarNavigateAction)
|
||||||
.SetImageUri(GetNotificationIconUri("dismiss")));
|
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
|
||||||
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
|
||||||
|
|
||||||
if (Uri.TryCreate(calendarItem.HtmlLink, UriKind.Absolute, out var joinUri))
|
if (Uri.TryCreate(calendarItem.HtmlLink, UriKind.Absolute, out var joinUri))
|
||||||
{
|
{
|
||||||
builder.AddButton(new ToastButton()
|
builder.AddButton(new AppNotificationButton(Translator.CalendarEventDetails_JoinOnline)
|
||||||
.SetContent(Translator.CalendarEventDetails_JoinOnline)
|
.SetInvokeUri(joinUri));
|
||||||
.SetImageUri(GetNotificationIconUri("calendar-join"))
|
|
||||||
.SetProtocolActivation(joinUri));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AddAudio(new ToastAudio()
|
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Reminder"));
|
||||||
{
|
|
||||||
Src = new Uri("ms-winsoundevent:Notification.Reminder")
|
|
||||||
});
|
|
||||||
|
|
||||||
var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}";
|
var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}";
|
||||||
ShowToast(builder, tag);
|
ShowNotification(builder, tag);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -411,17 +370,17 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo);
|
return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ShowToast(ToastContentBuilder builder, string? tag = null)
|
private static AppNotificationBuilder CreateBuilder(AppNotificationScenario scenario = AppNotificationScenario.Default)
|
||||||
|
=> new AppNotificationBuilder().SetScenario(scenario);
|
||||||
|
|
||||||
|
private static void ShowNotification(AppNotificationBuilder builder, string? tag = null)
|
||||||
{
|
{
|
||||||
var toastNotification = new ToastNotification(builder.GetToastContent().GetXml());
|
var notification = builder.BuildNotification();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(tag))
|
if (!string.IsNullOrWhiteSpace(tag))
|
||||||
{
|
notification.Tag = tag;
|
||||||
toastNotification.Tag = tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
var notifier = ToastNotificationManager.CreateToastNotifier();
|
AppNotificationManager.Default.Show(notification);
|
||||||
notifier.Show(toastNotification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Uri GetNotificationIconUri(string iconName)
|
private Uri GetNotificationIconUri(string iconName)
|
||||||
@@ -430,4 +389,3 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
return new($"{NotificationIconRootUri}{iconName}.png");
|
return new($"{NotificationIconRootUri}{iconName}.png");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<ResourceDictionary
|
<ResourceDictionary
|
||||||
|
x:Class="Wino.Mail.WinUI.Styles.OperationCommandBarResources"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="using:Wino.Mail.WinUI.Controls">
|
xmlns:controls="using:Wino.Mail.WinUI.Controls">
|
||||||
@@ -10,50 +11,50 @@
|
|||||||
<Setter Property="OverflowButtonVisibility" Value="Auto" />
|
<Setter Property="OverflowButtonVisibility" Value="Auto" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<DataTemplate x:Key="OperationCommandBarMailOperationTemplate">
|
<DataTemplate x:Key="OperationCommandBarMailOperationTemplate" x:DataType="controls:OperationCommandBarMenuOperationItemViewModel">
|
||||||
<AppBarButton
|
<AppBarButton
|
||||||
MinWidth="40"
|
MinWidth="40"
|
||||||
IsEnabled="{Binding IsEnabled}"
|
IsEnabled="{x:Bind IsEnabled}"
|
||||||
Label="{Binding Label}"
|
Label="{x:Bind Label}"
|
||||||
LabelPosition="{Binding LabelPosition}"
|
LabelPosition="{x:Bind LabelPosition}"
|
||||||
ToolTipService.ToolTip="{Binding ToolTip}">
|
ToolTipService.ToolTip="{x:Bind ToolTip}">
|
||||||
<AppBarButton.Icon>
|
<AppBarButton.Icon>
|
||||||
<controls:WinoFontIcon Icon="{Binding Icon}" />
|
<controls:WinoFontIcon Icon="{x:Bind Icon}" />
|
||||||
</AppBarButton.Icon>
|
</AppBarButton.Icon>
|
||||||
</AppBarButton>
|
</AppBarButton>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="OperationCommandBarFolderOperationTemplate">
|
<DataTemplate x:Key="OperationCommandBarFolderOperationTemplate" x:DataType="controls:OperationCommandBarMenuOperationItemViewModel">
|
||||||
<AppBarButton
|
<AppBarButton
|
||||||
MinWidth="40"
|
MinWidth="40"
|
||||||
IsEnabled="{Binding IsEnabled}"
|
IsEnabled="{x:Bind IsEnabled}"
|
||||||
Label="{Binding Label}"
|
Label="{x:Bind Label}"
|
||||||
LabelPosition="{Binding LabelPosition}"
|
LabelPosition="{x:Bind LabelPosition}"
|
||||||
ToolTipService.ToolTip="{Binding ToolTip}">
|
ToolTipService.ToolTip="{x:Bind ToolTip}">
|
||||||
<AppBarButton.Icon>
|
<AppBarButton.Icon>
|
||||||
<controls:WinoFontIcon Icon="{Binding Icon}" />
|
<controls:WinoFontIcon Icon="{x:Bind Icon}" />
|
||||||
</AppBarButton.Icon>
|
</AppBarButton.Icon>
|
||||||
</AppBarButton>
|
</AppBarButton>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="OperationCommandBarAIActionsTemplate">
|
<DataTemplate x:Key="OperationCommandBarAIActionsTemplate" x:DataType="controls:OperationCommandBarAIActionsItemViewModel">
|
||||||
<AppBarToggleButton
|
<AppBarToggleButton
|
||||||
MinWidth="40"
|
MinWidth="40"
|
||||||
LabelPosition="Collapsed"
|
LabelPosition="Collapsed"
|
||||||
ToolTipService.ToolTip="{Binding ToolTip}">
|
ToolTipService.ToolTip="{x:Bind ToolTip}">
|
||||||
<AppBarToggleButton.Icon>
|
<AppBarToggleButton.Icon>
|
||||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{Binding Glyph}" />
|
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{x:Bind Glyph}" />
|
||||||
</AppBarToggleButton.Icon>
|
</AppBarToggleButton.Icon>
|
||||||
</AppBarToggleButton>
|
</AppBarToggleButton>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="OperationCommandBarThemeToggleTemplate">
|
<DataTemplate x:Key="OperationCommandBarThemeToggleTemplate" x:DataType="controls:OperationCommandBarThemeItemViewModel">
|
||||||
<AppBarButton
|
<AppBarButton
|
||||||
MinWidth="40"
|
MinWidth="40"
|
||||||
LabelPosition="Collapsed"
|
LabelPosition="Collapsed"
|
||||||
ToolTipService.ToolTip="{Binding ToolTip}">
|
ToolTipService.ToolTip="{x:Bind ToolTip}">
|
||||||
<AppBarButton.Icon>
|
<AppBarButton.Icon>
|
||||||
<controls:WinoFontIcon Icon="{Binding Icon}" />
|
<controls:WinoFontIcon Icon="{x:Bind Icon}" />
|
||||||
</AppBarButton.Icon>
|
</AppBarButton.Icon>
|
||||||
</AppBarButton>
|
</AppBarButton>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
namespace Wino.Mail.WinUI.Styles;
|
||||||
|
|
||||||
|
public partial class OperationCommandBarResources : ResourceDictionary
|
||||||
|
{
|
||||||
|
public OperationCommandBarResources()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,11 +101,11 @@
|
|||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
Click="AttentionIconClicked"
|
Click="AttentionIconClicked"
|
||||||
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
|
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
|
||||||
ToolTipService.ToolTip="{x:Bind domain:Translator.Info_AccountAttentionRequiredClickableMessage, Mode=OneWay}"
|
ToolTipService.ToolTip="{x:Bind domain:Translator.Info_AccountAttentionRequiredClickableMessage}"
|
||||||
Visibility="{x:Bind IsAttentionRequired, Mode=OneWay}">
|
Visibility="{x:Bind IsAttentionRequired, Mode=OneWay}">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
<PathIcon x:Name="AttentionIcon" Data="F1 M 2.021484 18.769531 C 1.767578 18.769531 1.52832 18.720703 1.303711 18.623047 C 1.079102 18.525391 0.880534 18.391928 0.708008 18.222656 C 0.535482 18.053385 0.398763 17.856445 0.297852 17.631836 C 0.19694 17.407227 0.146484 17.167969 0.146484 16.914062 C 0.146484 16.614584 0.211589 16.328125 0.341797 16.054688 L 7.695312 1.347656 C 7.851562 1.035156 8.082682 0.784506 8.388672 0.595703 C 8.694661 0.406902 9.023438 0.3125 9.375 0.3125 C 9.726562 0.3125 10.055338 0.406902 10.361328 0.595703 C 10.667317 0.784506 10.898438 1.035156 11.054688 1.347656 L 18.408203 16.054688 C 18.53841 16.328125 18.603516 16.614584 18.603516 16.914062 C 18.603516 17.167969 18.553059 17.407227 18.452148 17.631836 C 18.351236 17.856445 18.216145 18.053385 18.046875 18.222656 C 17.877604 18.391928 17.679035 18.525391 17.451172 18.623047 C 17.223307 18.720703 16.982422 18.769531 16.728516 18.769531 Z M 16.728516 17.519531 C 16.884766 17.519531 17.027994 17.460938 17.158203 17.34375 C 17.28841 17.226562 17.353516 17.086588 17.353516 16.923828 C 17.353516 16.806641 17.330729 16.702475 17.285156 16.611328 L 9.931641 1.904297 C 9.879557 1.793621 9.80306 1.708984 9.702148 1.650391 C 9.601236 1.591797 9.492188 1.5625 9.375 1.5625 C 9.257812 1.5625 9.148763 1.593426 9.047852 1.655273 C 8.946939 1.717123 8.870442 1.800131 8.818359 1.904297 L 1.464844 16.611328 C 1.419271 16.702475 1.396484 16.803387 1.396484 16.914062 C 1.396484 17.083334 1.459961 17.226562 1.586914 17.34375 C 1.713867 17.460938 1.858724 17.519531 2.021484 17.519531 Z M 8.75 11.875 L 8.75 6.875 C 8.75 6.705729 8.811849 6.559245 8.935547 6.435547 C 9.059244 6.31185 9.205729 6.25 9.375 6.25 C 9.544271 6.25 9.690755 6.31185 9.814453 6.435547 C 9.93815 6.559245 10 6.705729 10 6.875 L 10 11.875 C 10 12.044271 9.93815 12.190756 9.814453 12.314453 C 9.690755 12.438151 9.544271 12.5 9.375 12.5 C 9.205729 12.5 9.059244 12.438151 8.935547 12.314453 C 8.811849 12.190756 8.75 12.044271 8.75 11.875 Z M 8.4375 14.375 C 8.4375 14.114584 8.528646 13.893229 8.710938 13.710938 C 8.893229 13.528646 9.114583 13.4375 9.375 13.4375 C 9.635416 13.4375 9.856771 13.528646 10.039062 13.710938 C 10.221354 13.893229 10.3125 14.114584 10.3125 14.375 C 10.3125 14.635417 10.221354 14.856771 10.039062 15.039062 C 9.856771 15.221354 9.635416 15.3125 9.375 15.3125 C 9.114583 15.3125 8.893229 15.221354 8.710938 15.039062 C 8.528646 14.856771 8.4375 14.635417 8.4375 14.375 Z " />
|
<PathIcon x:Name="AttentionIcon" Data="F1 M 2.021484 18.769531 C 1.767578 18.769531 1.52832 18.720703 1.303711 18.623047 C 1.079102 18.525391 0.880534 18.391928 0.708008 18.222656 C 0.535482 18.053385 0.398763 17.856445 0.297852 17.631836 C 0.19694 17.407227 0.146484 17.167969 0.146484 16.914062 C 0.146484 16.614584 0.211589 16.328125 0.341797 16.054688 L 7.695312 1.347656 C 7.851562 1.035156 8.082682 0.784506 8.388672 0.595703 C 8.694661 0.406902 9.023438 0.3125 9.375 0.3125 C 9.726562 0.3125 10.055338 0.406902 10.361328 0.595703 C 10.667317 0.784506 10.898438 1.035156 11.054688 1.347656 L 18.408203 16.054688 C 18.53841 16.328125 18.603516 16.614584 18.603516 16.914062 C 18.603516 17.167969 18.553059 17.407227 18.452148 17.631836 C 18.351236 17.856445 18.216145 18.053385 18.046875 18.222656 C 17.877604 18.391928 17.679035 18.525391 17.451172 18.623047 C 17.223307 18.720703 16.982422 18.769531 16.728516 18.769531 Z M 16.728516 17.519531 C 16.884766 17.519531 17.027994 17.460938 17.158203 17.34375 C 17.28841 17.226562 17.353516 17.086588 17.353516 16.923828 C 17.353516 16.806641 17.330729 16.702475 17.285156 16.611328 L 9.931641 1.904297 C 9.879557 1.793621 9.80306 1.708984 9.702148 1.650391 C 9.601236 1.591797 9.492188 1.5625 9.375 1.5625 C 9.257812 1.5625 9.148763 1.593426 9.047852 1.655273 C 8.946939 1.717123 8.870442 1.800131 8.818359 1.904297 L 1.464844 16.611328 C 1.419271 16.702475 1.396484 16.803387 1.396484 16.914062 C 1.396484 17.083334 1.459961 17.226562 1.586914 17.34375 C 1.713867 17.460938 1.858724 17.519531 2.021484 17.519531 Z M 8.75 11.875 L 8.75 6.875 C 8.75 6.705729 8.811849 6.559245 8.935547 6.435547 C 9.059244 6.31185 9.205729 6.25 9.375 6.25 C 9.544271 6.25 9.690755 6.31185 9.814453 6.435547 C 9.93815 6.559245 10 6.705729 10 6.875 L 10 11.875 C 10 12.044271 9.93815 12.190756 9.814453 12.314453 C 9.690755 12.438151 9.544271 12.5 9.375 12.5 C 9.205729 12.5 9.059244 12.438151 8.935547 12.314453 C 8.811849 12.190756 8.75 12.044271 8.75 11.875 Z M 8.4375 14.375 C 8.4375 14.114584 8.528646 13.893229 8.710938 13.710938 C 8.893229 13.528646 9.114583 13.4375 9.375 13.4375 C 9.635416 13.4375 9.856771 13.528646 10.039062 13.710938 C 10.221354 13.893229 10.3125 14.114584 10.3125 14.375 C 10.3125 14.635417 10.221354 14.856771 10.039062 15.039062 C 9.856771 15.221354 9.635416 15.3125 9.375 15.3125 C 9.114583 15.3125 8.893229 15.221354 8.710938 15.039062 C 8.528646 14.856771 8.4375 14.635417 8.4375 14.375 Z " />
|
||||||
<TextBlock Text="{x:Bind domain:Translator.Info_AccountAttentionRequiredAction, Mode=OneWay}" />
|
<TextBlock Text="{x:Bind domain:Translator.Info_AccountAttentionRequiredAction}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -567,8 +567,8 @@
|
|||||||
<ProgressBar
|
<ProgressBar
|
||||||
Height="4"
|
Height="4"
|
||||||
IsIndeterminate="True"
|
IsIndeterminate="True"
|
||||||
ShowPaused="False"
|
|
||||||
ShowError="False"
|
ShowError="False"
|
||||||
|
ShowPaused="False"
|
||||||
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}" />
|
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -12,30 +12,11 @@
|
|||||||
<UseWinUI>true</UseWinUI>
|
<UseWinUI>true</UseWinUI>
|
||||||
<EnableMsixTooling>true</EnableMsixTooling>
|
<EnableMsixTooling>true</EnableMsixTooling>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
<!-- AOT / Trimming -->
|
|
||||||
<PublishAot Condition="'$(Configuration)' == 'Debug'">False</PublishAot>
|
|
||||||
<PublishAot Condition="'$(Configuration)' != 'Debug'">True</PublishAot>
|
|
||||||
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
|
||||||
<NoWarn>$(NoWarn);CS8305</NoWarn>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
|
||||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
|
||||||
|
|
||||||
<!-- Trimming -->
|
|
||||||
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
|
|
||||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Single instancing -->
|
<!-- Single instancing -->
|
||||||
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
|
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<TrimmerRootAssembly Include="Google.Apis.Auth" />
|
|
||||||
<TrimmerRootAssembly Include="Google.Apis.Drive.v3" />
|
|
||||||
<TrimmerRootAssembly Include="Google.Apis.Gmail.v1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Remove="Assets\BadgeLogo.scale-100.png" />
|
<Content Remove="Assets\BadgeLogo.scale-100.png" />
|
||||||
@@ -203,7 +184,6 @@
|
|||||||
<PackageReference Include="CommunityToolkit.WinUI.Lottie" />
|
<PackageReference Include="CommunityToolkit.WinUI.Lottie" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Graphics.Win2D" />
|
<PackageReference Include="Microsoft.Graphics.Win2D" />
|
||||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
|
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
|
||||||
@@ -215,7 +195,6 @@
|
|||||||
<PackageReference Include="Sentry.Serilog" />
|
<PackageReference Include="Sentry.Serilog" />
|
||||||
<PackageReference Include="sqlite-net-pcl" />
|
<PackageReference Include="sqlite-net-pcl" />
|
||||||
<PackageReference Include="EmailValidation" />
|
<PackageReference Include="EmailValidation" />
|
||||||
<PackageReference Include="System.Drawing.Common" />
|
|
||||||
<PackageReference Include="SkiaSharp.Views.WinUI" />
|
<PackageReference Include="SkiaSharp.Views.WinUI" />
|
||||||
<PackageReference Include="WinUIEx" />
|
<PackageReference Include="WinUIEx" />
|
||||||
<PackageReference Include="Wino.Mail.Contracts" />
|
<PackageReference Include="Wino.Mail.Contracts" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Diagnostics;
|
using CommunityToolkit.Diagnostics;
|
||||||
@@ -662,14 +662,40 @@ public class AccountService : BaseDatabaseService, IAccountService
|
|||||||
|
|
||||||
private static string GetReadableTextColorHex(string backgroundColorHex)
|
private static string GetReadableTextColorHex(string backgroundColorHex)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(backgroundColorHex))
|
if (!TryParseHexColor(backgroundColorHex, out var red, out var green, out var blue))
|
||||||
return "#FFFFFF";
|
return "#FFFFFF";
|
||||||
|
|
||||||
var color = ColorTranslator.FromHtml(backgroundColorHex);
|
var luminance = ((0.299 * red) + (0.587 * green) + (0.114 * blue)) / 255d;
|
||||||
var luminance = ((0.299 * color.R) + (0.587 * color.G) + (0.114 * color.B)) / 255d;
|
|
||||||
return luminance > 0.6 ? "#111111" : "#FFFFFF";
|
return luminance > 0.6 ? "#111111" : "#FFFFFF";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseHexColor(string value, out int red, out int green, out int blue)
|
||||||
|
{
|
||||||
|
red = 255;
|
||||||
|
green = 255;
|
||||||
|
blue = 255;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var color = value.Trim();
|
||||||
|
if (color.StartsWith('#'))
|
||||||
|
{
|
||||||
|
color = color[1..];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color.Length != 6 ||
|
||||||
|
!int.TryParse(color, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
red = Convert.ToInt32(color.Substring(0, 2), 16);
|
||||||
|
green = Convert.ToInt32(color.Substring(2, 2), 16);
|
||||||
|
blue = Convert.ToInt32(color.Substring(4, 2), 16);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task UpdateAccountOrdersAsync(Dictionary<Guid, int> accountIdOrderPair)
|
public async Task UpdateAccountOrdersAsync(Dictionary<Guid, int> accountIdOrderPair)
|
||||||
{
|
{
|
||||||
foreach (var pair in accountIdOrderPair)
|
foreach (var pair in accountIdOrderPair)
|
||||||
|
|||||||
@@ -421,14 +421,14 @@ public sealed class CalDavClient : ICalDavClient
|
|||||||
var result = new List<CalDavCalendarEvent>();
|
var result = new List<CalDavCalendarEvent>();
|
||||||
|
|
||||||
var masters = allEvents
|
var masters = allEvents
|
||||||
.Where(e => e != null && !string.IsNullOrWhiteSpace(e.Uid) && e.RecurrenceId == null)
|
.Where(e => e != null && !string.IsNullOrWhiteSpace(e.Uid) && GetRecurrenceId(e) == null)
|
||||||
.GroupBy(e => e.Uid, StringComparer.OrdinalIgnoreCase)
|
.GroupBy(e => e.Uid, StringComparer.OrdinalIgnoreCase)
|
||||||
.Select(g => g.First())
|
.Select(g => g.First())
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var exceptionMap = allEvents
|
var exceptionMap = allEvents
|
||||||
.Where(e => e != null && !string.IsNullOrWhiteSpace(e.Uid) && e.RecurrenceId != null)
|
.Where(e => e != null && !string.IsNullOrWhiteSpace(e.Uid) && GetRecurrenceId(e) != null)
|
||||||
.GroupBy(e => $"{e.Uid}|{GetOccurrenceKey(e.RecurrenceId)}", StringComparer.OrdinalIgnoreCase)
|
.GroupBy(e => $"{e.Uid}|{GetOccurrenceKey(GetRecurrenceId(e))}", StringComparer.OrdinalIgnoreCase)
|
||||||
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
|
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var consumedExceptions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var consumedExceptions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -453,7 +453,15 @@ public sealed class CalDavClient : ICalDavClient
|
|||||||
seriesMasterRemoteEventId: string.Empty,
|
seriesMasterRemoteEventId: string.Empty,
|
||||||
recurrence: BuildRecurrenceString(master)));
|
recurrence: BuildRecurrenceString(master)));
|
||||||
|
|
||||||
var occurrences = master.GetOccurrences(windowStartUtc.UtcDateTime, windowEndUtc.UtcDateTime);
|
var occurrences = master
|
||||||
|
.GetOccurrences(
|
||||||
|
new CalDateTime(windowStartUtc.UtcDateTime, true),
|
||||||
|
new Ical.Net.Evaluation.EvaluationOptions())
|
||||||
|
.Where(o => Overlaps(
|
||||||
|
ToDateTimeOffset(o.Period.StartTime),
|
||||||
|
ToDateTimeOffset(o.Period.EndTime),
|
||||||
|
windowStartUtc,
|
||||||
|
windowEndUtc));
|
||||||
|
|
||||||
foreach (var occurrence in occurrences)
|
foreach (var occurrence in occurrences)
|
||||||
{
|
{
|
||||||
@@ -507,9 +515,10 @@ public sealed class CalDavClient : ICalDavClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var exceptionEvent in allEvents.Where(e => e != null && e.RecurrenceId != null && !string.IsNullOrWhiteSpace(e.Uid)))
|
foreach (var exceptionEvent in allEvents.Where(e => e != null && GetRecurrenceId(e) != null && !string.IsNullOrWhiteSpace(e.Uid)))
|
||||||
{
|
{
|
||||||
var key = $"{exceptionEvent.Uid}|{GetOccurrenceKey(exceptionEvent.RecurrenceId)}";
|
var recurrenceId = GetRecurrenceId(exceptionEvent);
|
||||||
|
var key = $"{exceptionEvent.Uid}|{GetOccurrenceKey(recurrenceId)}";
|
||||||
if (consumedExceptions.Contains(key))
|
if (consumedExceptions.Contains(key))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -525,7 +534,7 @@ public sealed class CalDavClient : ICalDavClient
|
|||||||
sourceEvent: exceptionEvent,
|
sourceEvent: exceptionEvent,
|
||||||
start: start,
|
start: start,
|
||||||
end: end,
|
end: end,
|
||||||
remoteEventId: BuildRemoteEventId(exceptionEvent.Uid, GetOccurrenceKey(exceptionEvent.RecurrenceId)),
|
remoteEventId: BuildRemoteEventId(exceptionEvent.Uid, GetOccurrenceKey(recurrenceId)),
|
||||||
resourceHref: resourceHref,
|
resourceHref: resourceHref,
|
||||||
eTag: eTag,
|
eTag: eTag,
|
||||||
icsContent: icsContent,
|
icsContent: icsContent,
|
||||||
@@ -546,16 +555,30 @@ public sealed class CalDavClient : ICalDavClient
|
|||||||
|
|
||||||
private static bool HasRecurrence(CalendarEvent calendarEvent)
|
private static bool HasRecurrence(CalendarEvent calendarEvent)
|
||||||
=> (calendarEvent.RecurrenceRules?.Any() ?? false)
|
=> (calendarEvent.RecurrenceRules?.Any() ?? false)
|
||||||
|| (calendarEvent.RecurrenceDates?.Any() ?? false);
|
|| (calendarEvent.RecurrenceDates?.GetAllPeriods().Any() ?? false);
|
||||||
|
|
||||||
private static string BuildRemoteEventId(string uid, string occurrenceKey)
|
private static string BuildRemoteEventId(string uid, string occurrenceKey)
|
||||||
=> string.IsNullOrWhiteSpace(occurrenceKey) ? uid : $"{uid}::{occurrenceKey}";
|
=> string.IsNullOrWhiteSpace(occurrenceKey) ? uid : $"{uid}::{occurrenceKey}";
|
||||||
|
|
||||||
private static string GetOccurrenceKey(IDateTime dateTime)
|
private static CalDateTime GetRecurrenceId(CalendarEvent calendarEvent)
|
||||||
|
=> calendarEvent?.RecurrenceIdentifier?.StartTime;
|
||||||
|
|
||||||
|
private static string GetOccurrenceKey(CalDateTime dateTime)
|
||||||
=> dateTime.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'");
|
=> dateTime.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'");
|
||||||
|
|
||||||
private static DateTimeOffset ToDateTimeOffset(IDateTime dateTime)
|
private static DateTimeOffset ToDateTimeOffset(CalDateTime dateTime)
|
||||||
=> dateTime?.AsDateTimeOffset ?? default;
|
{
|
||||||
|
if (dateTime == null)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
if (dateTime.IsFloating)
|
||||||
|
{
|
||||||
|
var floatingValue = DateTime.SpecifyKind(dateTime.Value, DateTimeKind.Unspecified);
|
||||||
|
return new DateTimeOffset(floatingValue, TimeZoneInfo.Local.GetUtcOffset(floatingValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DateTimeOffset(DateTime.SpecifyKind(dateTime.AsUtc, DateTimeKind.Utc));
|
||||||
|
}
|
||||||
|
|
||||||
private static bool Overlaps(DateTimeOffset start, DateTimeOffset end, DateTimeOffset windowStart, DateTimeOffset windowEnd)
|
private static bool Overlaps(DateTimeOffset start, DateTimeOffset end, DateTimeOffset windowStart, DateTimeOffset windowEnd)
|
||||||
{
|
{
|
||||||
@@ -601,7 +624,7 @@ public sealed class CalDavClient : ICalDavClient
|
|||||||
.Where(a => a?.Trigger != null && a.Trigger.IsRelative && a.Trigger.Duration.HasValue)
|
.Where(a => a?.Trigger != null && a.Trigger.IsRelative && a.Trigger.Duration.HasValue)
|
||||||
.Select(a => new CalDavEventReminder
|
.Select(a => new CalDavEventReminder
|
||||||
{
|
{
|
||||||
DurationInSeconds = (int)Math.Abs(a.Trigger.Duration.Value.TotalSeconds),
|
DurationInSeconds = (int)Math.Abs(a.Trigger.Duration.Value.ToTimeSpanUnspecified().TotalSeconds),
|
||||||
ReminderType = string.Equals(a.Action, "EMAIL", StringComparison.OrdinalIgnoreCase)
|
ReminderType = string.Equals(a.Action, "EMAIL", StringComparison.OrdinalIgnoreCase)
|
||||||
? CalendarItemReminderType.Email
|
? CalendarItemReminderType.Email
|
||||||
: CalendarItemReminderType.Popup
|
: CalendarItemReminderType.Popup
|
||||||
@@ -638,7 +661,7 @@ public sealed class CalDavClient : ICalDavClient
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ResolveTimeZoneId(IDateTime sourceDateTime, DateTimeOffset parsedDateTime)
|
private static string ResolveTimeZoneId(CalDateTime sourceDateTime, DateTimeOffset parsedDateTime)
|
||||||
{
|
{
|
||||||
var explicitTimeZoneId = sourceDateTime?.TzId;
|
var explicitTimeZoneId = sourceDateTime?.TzId;
|
||||||
if (!string.IsNullOrWhiteSpace(explicitTimeZoneId))
|
if (!string.IsNullOrWhiteSpace(explicitTimeZoneId))
|
||||||
@@ -664,34 +687,26 @@ public sealed class CalDavClient : ICalDavClient
|
|||||||
|
|
||||||
if (sourceEvent.ExceptionDates != null)
|
if (sourceEvent.ExceptionDates != null)
|
||||||
{
|
{
|
||||||
foreach (var periodList in sourceEvent.ExceptionDates)
|
var dates = sourceEvent.ExceptionDates
|
||||||
{
|
.GetAllDates()
|
||||||
var dates = periodList
|
.Where(d => d != null)
|
||||||
.Where(p => p.StartTime != null)
|
.Select(d => d.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'"))
|
||||||
.Select(p => p.StartTime.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'"))
|
.ToList();
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (dates.Count > 0)
|
if (dates.Count > 0)
|
||||||
{
|
recurrenceLines.Add($"EXDATE:{string.Join(",", dates)}");
|
||||||
recurrenceLines.Add($"EXDATE:{string.Join(",", dates)}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceEvent.RecurrenceDates != null)
|
if (sourceEvent.RecurrenceDates != null)
|
||||||
{
|
{
|
||||||
foreach (var periodList in sourceEvent.RecurrenceDates)
|
var dates = sourceEvent.RecurrenceDates
|
||||||
{
|
.GetAllPeriods()
|
||||||
var dates = periodList
|
.Where(p => p.StartTime != null)
|
||||||
.Where(p => p.StartTime != null)
|
.Select(p => p.StartTime.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'"))
|
||||||
.Select(p => p.StartTime.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'"))
|
.ToList();
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (dates.Count > 0)
|
if (dates.Count > 0)
|
||||||
{
|
recurrenceLines.Add($"RDATE:{string.Join(",", dates)}");
|
||||||
recurrenceLines.Add($"RDATE:{string.Join(",", dates)}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return recurrenceLines.Count == 0
|
return recurrenceLines.Count == 0
|
||||||
|
|||||||
Reference in New Issue
Block a user