Get rid of system.drawing and uwp notifications pacakge. Remove the AOT/trim stuff for now.
This commit is contained in:
+13
-14
@@ -16,25 +16,24 @@
|
||||
<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.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="EmailValidation" Version="1.3.0" />
|
||||
<PackageVersion Include="gravatar-dotnet" Version="0.1.3" />
|
||||
<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="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" />
|
||||
<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.Graphics.Win2D" Version="1.4.0" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client" Version="4.83.3" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.82.1" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.82.1" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.83.3" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.83.3" />
|
||||
<PackageVersion Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" />
|
||||
<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="morelinq" Version="4.4.0" />
|
||||
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
@@ -52,21 +51,21 @@
|
||||
<PackageVersion Include="System.Drawing.Common" Version="10.0.5" />
|
||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||
<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.WinUI" Version="2.4.1" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Google.Apis.Auth" Version="1.73.0" />
|
||||
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.73.0.4063" />
|
||||
<PackageVersion Include="Google.Apis.Drive.v3" Version="1.73.0.4068" />
|
||||
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.73.0.4073" />
|
||||
<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.PeopleService.v1" Version="1.72.0.3973" />
|
||||
<PackageVersion Include="HtmlKit" Version="1.2.0" />
|
||||
<PackageVersion Include="MailKit" Version="4.15.1" />
|
||||
<PackageVersion Include="TimePeriodLibrary.NET" Version="2.1.6" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.1.0" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.3" />
|
||||
<PackageVersion Include="System.Text.Encodings.Web" Version="10.0.3" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.5" />
|
||||
<PackageVersion Include="System.Text.Encodings.Web" Version="10.0.5" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1721" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.250930001-experimental1" />
|
||||
<PackageVersion Include="WinUIEx" Version="2.9.0" />
|
||||
@@ -74,7 +73,7 @@
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -282,6 +282,7 @@ public class CalendarPageViewModelTests
|
||||
|
||||
public IEnumerable<AccountCalendarViewModel> ActiveCalendars => _activeCalendars;
|
||||
public IEnumerable<AccountCalendarViewModel> AllCalendars => _calendars;
|
||||
public bool IsAnySynchronizationInProgress => false;
|
||||
public ReadOnlyObservableGroupedCollection<MailAccount, AccountCalendarViewModel> GroupedCalendars { get; set; } = null!;
|
||||
|
||||
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar) => _groupedCalendars.Add(groupedAccountCalendar);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Wino.Core.Domain.Misc;
|
||||
@@ -46,25 +45,21 @@ public static class ColorHelpers
|
||||
return "#FFFFFF";
|
||||
}
|
||||
|
||||
var color = ColorTranslator.FromHtml(normalizedColor);
|
||||
var luminance = ((0.299 * color.R) + (0.587 * color.G) + (0.114 * color.B)) / 255d;
|
||||
var (red, green, blue) = ParseRgb(normalizedColor);
|
||||
var luminance = ((0.299 * red) + (0.587 * green) + (0.114 * blue)) / 255d;
|
||||
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)
|
||||
{
|
||||
var color = ColorTranslator.FromHtml(hexColor);
|
||||
var (red, green, blue) = ParseRgb(hexColor);
|
||||
var factor = Math.Max(0.55, 1.0 - (cycle * 0.08));
|
||||
|
||||
var adjusted = Color.FromArgb(
|
||||
(int)Math.Clamp(color.R * factor, 0, 255),
|
||||
(int)Math.Clamp(color.G * factor, 0, 255),
|
||||
(int)Math.Clamp(color.B * factor, 0, 255));
|
||||
var adjustedRed = (int)Math.Clamp(red * factor, 0, 255);
|
||||
var adjustedGreen = (int)Math.Clamp(green * factor, 0, 255);
|
||||
var adjustedBlue = (int)Math.Clamp(blue * factor, 0, 255);
|
||||
|
||||
return adjusted.ToHexString();
|
||||
return $"#{adjustedRed:X2}{adjustedGreen:X2}{adjustedBlue:X2}";
|
||||
}
|
||||
|
||||
private static bool TryNormalizeHexColor(string value, out string normalized)
|
||||
@@ -97,4 +92,18 @@ public static class ColorHelpers
|
||||
|
||||
private static string NormalizeHexColor(string value)
|
||||
=> 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/SharedStyles.xaml" />
|
||||
<ResourceDictionary Source="/Styles/IconTemplates.xaml" />
|
||||
<ResourceDictionary Source="/Styles/OperationCommandBar.xaml" />
|
||||
<coreStyles:OperationCommandBarResources />
|
||||
|
||||
|
||||
<coreStyles:CustomMessageDialogStyles />
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
@@ -492,7 +491,7 @@ public partial class App : WinoApplication,
|
||||
/// </summary>
|
||||
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) &&
|
||||
storeUpdateAction == Constants.ToastStoreUpdateActionInstall)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.AiActions_CheckingStatus, Mode=OneWay}" />
|
||||
Text="{x:Bind domain:Translator.AiActions_CheckingStatus}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
@@ -77,12 +77,12 @@
|
||||
<StackPanel Grid.Column="1" Spacing="6">
|
||||
<TextBlock
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.AiActions_SignedOutTitle, Mode=OneWay}"
|
||||
Text="{x:Bind domain:Translator.AiActions_SignedOutTitle}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.AiActions_SignedOutDescription, Mode=OneWay}"
|
||||
Text="{x:Bind domain:Translator.AiActions_SignedOutDescription}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@@ -95,12 +95,12 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Click="SignInButton_Click"
|
||||
Content="{x:Bind domain:Translator.Buttons_SignIn, Mode=OneWay}"
|
||||
Content="{x:Bind domain:Translator.Buttons_SignIn}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Click="CreateAccountButton_Click"
|
||||
Content="{x:Bind domain:Translator.Buttons_CreateAccount, Mode=OneWay}" />
|
||||
Content="{x:Bind domain:Translator.Buttons_CreateAccount}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
@@ -113,24 +113,24 @@
|
||||
Background="{ThemeResource CardBackgroundFillColorTertiaryBrush}"
|
||||
CornerRadius="12">
|
||||
<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
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.AiActions_NoPackDescription, Mode=OneWay}"
|
||||
Text="{x:Bind domain:Translator.AiActions_NoPackDescription}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Border
|
||||
Padding="8,2"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
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
|
||||
Padding="8,2"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
CornerRadius="8">
|
||||
<TextBlock Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoRequests, Mode=OneWay}" />
|
||||
<TextBlock Text="{x:Bind domain:Translator.WinoAccount_Management_AiPackPromoRequests}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
@@ -139,7 +139,7 @@
|
||||
<Button
|
||||
HorizontalAlignment="Left"
|
||||
Click="PurchaseButton_Click"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackGetButton, Mode=OneWay}"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackGetButton}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<controls:SegmentedItem
|
||||
x:Name="TranslateSegment"
|
||||
Padding="12,6"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureTranslate, Mode=OneWay}">
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureTranslate}">
|
||||
<controls:SegmentedItem.Icon>
|
||||
<SymbolIcon Symbol="Switch" />
|
||||
</controls:SegmentedItem.Icon>
|
||||
@@ -170,7 +170,7 @@
|
||||
<controls:SegmentedItem
|
||||
x:Name="RewriteSegment"
|
||||
Padding="12,6"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureRewrite, Mode=OneWay}">
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureRewrite}">
|
||||
<controls:SegmentedItem.Icon>
|
||||
<SymbolIcon Symbol="Edit" />
|
||||
</controls:SegmentedItem.Icon>
|
||||
@@ -178,7 +178,7 @@
|
||||
<controls:SegmentedItem
|
||||
x:Name="SummarizeSegment"
|
||||
Padding="12,6"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize, Mode=OneWay}">
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize}">
|
||||
<controls:SegmentedItem.Icon>
|
||||
<SymbolIcon Symbol="Bullets" />
|
||||
</controls:SegmentedItem.Icon>
|
||||
@@ -202,7 +202,7 @@
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage, Mode=OneWay}" />
|
||||
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage}" />
|
||||
<ComboBox
|
||||
x:Name="TranslateLanguageComboBox"
|
||||
MinWidth="120"
|
||||
@@ -216,7 +216,7 @@
|
||||
<Button
|
||||
x:Name="RunTranslateButton"
|
||||
Click="RunTranslateButton_Click"
|
||||
Content="{x:Bind domain:Translator.Composer_AiTranslateApply, Mode=OneWay}"
|
||||
Content="{x:Bind domain:Translator.Composer_AiTranslateApply}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
@@ -230,7 +230,7 @@
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.Composer_AiRewriteMode, Mode=OneWay}" />
|
||||
Text="{x:Bind domain:Translator.Composer_AiRewriteMode}" />
|
||||
<ComboBox
|
||||
x:Name="RewriteModeComboBox"
|
||||
MinWidth="140"
|
||||
@@ -244,7 +244,7 @@
|
||||
<Button
|
||||
x:Name="RunRewriteButton"
|
||||
Click="RunRewriteButton_Click"
|
||||
Content="{x:Bind domain:Translator.Composer_AiRewriteApply, Mode=OneWay}"
|
||||
Content="{x:Bind domain:Translator.Composer_AiRewriteApply}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
@@ -254,7 +254,7 @@
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<TextBox
|
||||
x:Name="CustomRewriteTextBox"
|
||||
PlaceholderText="{x:Bind domain:Translator.Composer_AiRewriteCustomPlaceholder, Mode=OneWay}"
|
||||
PlaceholderText="{x:Bind domain:Translator.Composer_AiRewriteCustomPlaceholder}"
|
||||
Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
|
||||
@@ -269,7 +269,7 @@
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage, Mode=OneWay}" />
|
||||
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage}" />
|
||||
<ComboBox
|
||||
x:Name="SummarizeLanguageComboBox"
|
||||
MinWidth="120"
|
||||
@@ -285,7 +285,7 @@
|
||||
x:Name="RunSummarizeButton"
|
||||
HorizontalAlignment="Left"
|
||||
Click="RunSummarizeButton_Click"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize, Mode=OneWay}"
|
||||
Content="{x:Bind domain:Translator.WinoAccount_Management_AiPackFeatureSummarize}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<FontIcon
|
||||
x:Name="SummarizeCachedIndicator"
|
||||
@@ -295,7 +295,7 @@
|
||||
FontSize="16"
|
||||
Foreground="#2AA84A"
|
||||
Glyph=""
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.Composer_AiSummarize, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.Composer_AiSummarize}"
|
||||
Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
{
|
||||
var button = LoadCommandBarElementTemplate(
|
||||
MailOperationTemplateKey,
|
||||
new MenuOperationCommandBarItemViewModel(
|
||||
new OperationCommandBarMenuOperationItemViewModel(
|
||||
mailOperationItem,
|
||||
XamlHelpers.GetOperationString(mailOperationItem.Operation),
|
||||
XamlHelpers.GetWinoIconGlyph(mailOperationItem.Operation),
|
||||
@@ -307,7 +307,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
var label = XamlHelpers.GetOperationString(folderOperationItem.Operation);
|
||||
var button = LoadCommandBarElementTemplate(
|
||||
FolderOperationTemplateKey,
|
||||
new MenuOperationCommandBarItemViewModel(
|
||||
new OperationCommandBarMenuOperationItemViewModel(
|
||||
folderOperationItem,
|
||||
label,
|
||||
XamlHelpers.GetPathGeometry(folderOperationItem.Operation),
|
||||
@@ -331,7 +331,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
{
|
||||
var button = (AppBarToggleButton)LoadCommandBarElementTemplate(
|
||||
AIActionsTemplateKey,
|
||||
new AIActionsCommandBarItemViewModel(Translator.Composer_AiActions, "\uE945"));
|
||||
new OperationCommandBarAIActionsItemViewModel(Translator.Composer_AiActions, "\uE945"));
|
||||
|
||||
button.SetBinding(AppBarToggleButton.IsCheckedProperty, new Binding
|
||||
{
|
||||
@@ -350,7 +350,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
|
||||
var button = (AppBarButton)LoadCommandBarElementTemplate(
|
||||
ThemeToggleTemplateKey,
|
||||
new ThemeCommandBarItemViewModel(label, icon));
|
||||
new OperationCommandBarThemeItemViewModel(label, icon));
|
||||
|
||||
button.Click += ThemeButton_Click;
|
||||
return button;
|
||||
@@ -430,9 +430,12 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
: CommandBarOverflowButtonVisibility.Auto;
|
||||
}
|
||||
|
||||
private sealed class MenuOperationCommandBarItemViewModel
|
||||
private sealed class SeparatorCommandBarItemViewModel;
|
||||
}
|
||||
|
||||
public sealed class OperationCommandBarMenuOperationItemViewModel
|
||||
{
|
||||
public MenuOperationCommandBarItemViewModel(IMenuOperation operation, string label, WinoIconGlyph icon, CommandBarLabelPosition labelPosition)
|
||||
public OperationCommandBarMenuOperationItemViewModel(IMenuOperation operation, string label, WinoIconGlyph icon, CommandBarLabelPosition labelPosition)
|
||||
{
|
||||
Operation = operation;
|
||||
Label = label;
|
||||
@@ -449,9 +452,9 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
public CommandBarLabelPosition LabelPosition { get; }
|
||||
}
|
||||
|
||||
private sealed class AIActionsCommandBarItemViewModel
|
||||
public sealed class OperationCommandBarAIActionsItemViewModel
|
||||
{
|
||||
public AIActionsCommandBarItemViewModel(string toolTip, string glyph)
|
||||
public OperationCommandBarAIActionsItemViewModel(string toolTip, string glyph)
|
||||
{
|
||||
ToolTip = toolTip;
|
||||
Glyph = glyph;
|
||||
@@ -461,9 +464,9 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
public string Glyph { get; }
|
||||
}
|
||||
|
||||
private sealed class ThemeCommandBarItemViewModel
|
||||
public sealed class OperationCommandBarThemeItemViewModel
|
||||
{
|
||||
public ThemeCommandBarItemViewModel(string toolTip, WinoIconGlyph icon)
|
||||
public OperationCommandBarThemeItemViewModel(string toolTip, WinoIconGlyph icon)
|
||||
{
|
||||
ToolTip = toolTip;
|
||||
Icon = icon;
|
||||
@@ -472,6 +475,3 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
public string ToolTip { get; }
|
||||
public WinoIconGlyph Icon { get; }
|
||||
}
|
||||
|
||||
private sealed class SeparatorCommandBarItemViewModel;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Printing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Serilog;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Printing;
|
||||
using Wino.Mail.WinUI.Helpers;
|
||||
|
||||
namespace Wino.Mail.WinUI.Dialogs;
|
||||
|
||||
@@ -87,15 +87,7 @@ public sealed partial class PrintDialog : ContentDialog
|
||||
{
|
||||
var printers = await Task.Run(() =>
|
||||
{
|
||||
var printerList = new List<string>();
|
||||
|
||||
// Get all installed printers using System.Drawing.Printing
|
||||
foreach (string printerName in PrinterSettings.InstalledPrinters)
|
||||
{
|
||||
printerList.Add(printerName);
|
||||
}
|
||||
|
||||
return printerList.AsEnumerable();
|
||||
return InstalledPrinterHelper.GetInstalledPrinters().AsEnumerable();
|
||||
});
|
||||
|
||||
SetAvailablePrinters(printers);
|
||||
|
||||
@@ -76,7 +76,9 @@ public static class CalendarXamlHelpers
|
||||
interval: recurrenceRule.Interval <= 0 ? 1 : recurrenceRule.Interval,
|
||||
frequency: frequency.Value,
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
Name="58272BurakKSE.WinoMailPreview"
|
||||
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"/>
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
@@ -206,7 +205,7 @@ public class Program
|
||||
return true;
|
||||
}
|
||||
|
||||
var toastArguments = ToastArguments.Parse(toastArgs.Argument);
|
||||
var toastArguments = NotificationArguments.Parse(toastArgs.Argument);
|
||||
|
||||
if (toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string storeUpdateAction) &&
|
||||
storeUpdateAction == Constants.ToastStoreUpdateActionInstall)
|
||||
|
||||
@@ -4,7 +4,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
using Microsoft.Windows.AppNotifications.Builder;
|
||||
using Serilog;
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.UI.Notifications;
|
||||
@@ -85,23 +86,17 @@ public class NotificationBuilder : INotificationBuilder
|
||||
if (mailCount == 0)
|
||||
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)
|
||||
{
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
var builder = CreateBuilder();
|
||||
|
||||
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
|
||||
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
|
||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
|
||||
|
||||
builder.AddButton(GetDismissButton());
|
||||
builder.AddAudio(new ToastAudio()
|
||||
{
|
||||
Src = new Uri("ms-winsoundevent:Notification.Mail")
|
||||
});
|
||||
|
||||
ShowToast(builder);
|
||||
ShowNotification(builder);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -121,8 +116,7 @@ public class NotificationBuilder : INotificationBuilder
|
||||
|
||||
private async Task CreateSingleNotificationAsync(MailCopy mailItem)
|
||||
{
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
var builder = CreateBuilder();
|
||||
|
||||
var avatarThumbnail = await _thumbnailService.GetThumbnailAsync(mailItem.FromAddress, awaitLoad: true);
|
||||
if (!string.IsNullOrEmpty(avatarThumbnail))
|
||||
@@ -133,63 +127,47 @@ public class NotificationBuilder : INotificationBuilder
|
||||
var bytes = Convert.FromBase64String(avatarThumbnail);
|
||||
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.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime());
|
||||
|
||||
builder.SetTimeStamp(mailItem.CreationDate.ToLocalTime());
|
||||
builder.AddText(mailItem.FromName);
|
||||
builder.AddText(mailItem.Subject);
|
||||
builder.AddText(mailItem.PreviewText);
|
||||
|
||||
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.AddButton(GetMarkAsReadButton(mailItem.UniqueId));
|
||||
builder.AddButton(GetDeleteButton(mailItem.UniqueId));
|
||||
builder.AddButton(GetArchiveButton(mailItem.UniqueId));
|
||||
builder.AddAudio(new ToastAudio()
|
||||
{
|
||||
Src = new Uri("ms-winsoundevent:Notification.Mail")
|
||||
});
|
||||
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
|
||||
|
||||
// Use UniqueId as tag to allow removal
|
||||
ShowToast(builder, mailItem.UniqueId.ToString());
|
||||
ShowNotification(builder, mailItem.UniqueId.ToString());
|
||||
}
|
||||
|
||||
private ToastButton GetDismissButton()
|
||||
=> new ToastButton()
|
||||
.SetDismissActivation()
|
||||
.SetImageUri(GetNotificationIconUri("dismiss"));
|
||||
|
||||
private ToastButton GetArchiveButton(Guid mailUniqueId)
|
||||
=> new ToastButton()
|
||||
.SetContent(Translator.MailOperation_Archive)
|
||||
.SetImageUri(GetNotificationIconUri("mail-archive"))
|
||||
private AppNotificationButton GetArchiveButton(Guid mailUniqueId)
|
||||
=> new AppNotificationButton(Translator.MailOperation_Archive)
|
||||
.SetIcon(GetNotificationIconUri("mail-archive"))
|
||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.Archive)
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail)
|
||||
.SetBackgroundActivation();
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.Archive.ToString())
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||
|
||||
private ToastButton GetDeleteButton(Guid mailUniqueId)
|
||||
=> new ToastButton()
|
||||
.SetContent(Translator.MailOperation_Delete)
|
||||
.SetImageUri(GetNotificationIconUri("mail-delete"))
|
||||
private AppNotificationButton GetDeleteButton(Guid mailUniqueId)
|
||||
=> new AppNotificationButton(Translator.MailOperation_Delete)
|
||||
.SetIcon(GetNotificationIconUri("mail-delete"))
|
||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete)
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail)
|
||||
.SetBackgroundActivation();
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete.ToString())
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||
|
||||
private ToastButton GetMarkAsReadButton(Guid mailUniqueId)
|
||||
=> new ToastButton()
|
||||
.SetContent(Translator.MailOperation_MarkAsRead)
|
||||
.SetImageUri(GetNotificationIconUri("mail-markread"))
|
||||
private AppNotificationButton GetMarkAsReadButton(Guid mailUniqueId)
|
||||
=> new AppNotificationButton(Translator.MailOperation_MarkAsRead)
|
||||
.SetIcon(GetNotificationIconUri("mail-markread"))
|
||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead)
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail)
|
||||
.SetBackgroundActivation();
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead.ToString())
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||
|
||||
public async Task UpdateTaskbarIconBadgeAsync()
|
||||
{
|
||||
@@ -216,27 +194,25 @@ public class NotificationBuilder : INotificationBuilder
|
||||
|
||||
if (totalUnreadCount > 0)
|
||||
{
|
||||
// Get the blank badge XML payload for a badge number
|
||||
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;
|
||||
if (badgeElement == null)
|
||||
{
|
||||
badgeUpdater.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
badgeElement.SetAttribute("value", totalUnreadCount.ToString());
|
||||
|
||||
// Create the badge notification
|
||||
BadgeNotification badge = new BadgeNotification(badgeXml);
|
||||
|
||||
// And update the badge
|
||||
BadgeNotification badge = new(badgeXml);
|
||||
badgeUpdater.Update(badge);
|
||||
}
|
||||
else
|
||||
{
|
||||
badgeUpdater.Clear();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error while updating taskbar badge.");
|
||||
@@ -247,7 +223,7 @@ public class NotificationBuilder : INotificationBuilder
|
||||
{
|
||||
try
|
||||
{
|
||||
ToastNotificationManager.History.Remove(mailUniqueId.ToString(), null);
|
||||
AppNotificationManager.Default.RemoveByTagAsync(mailUniqueId.ToString()).AsTask().GetAwaiter().GetResult();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
@@ -261,44 +237,39 @@ public class NotificationBuilder : INotificationBuilder
|
||||
|
||||
public void CreateAttentionRequiredNotification(MailAccount account)
|
||||
{
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
var builder = CreateBuilder();
|
||||
|
||||
builder.AddText(Translator.Exception_AccountNeedsAttention_Title);
|
||||
builder.AddText(string.Format(Translator.Exception_AccountNeedsAttention_Message, account.Name));
|
||||
|
||||
builder.AddButton(GetDismissButton());
|
||||
|
||||
builder.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString());
|
||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||
builder.AddButton(new ToastButton().SetContent(Translator.Buttons_FixAccount));
|
||||
ShowToast(builder);
|
||||
builder.AddButton(new AppNotificationButton(Translator.Buttons_FixAccount)
|
||||
.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString())
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail));
|
||||
|
||||
ShowNotification(builder);
|
||||
}
|
||||
|
||||
public void CreateWebView2RuntimeMissingNotification()
|
||||
{
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
var builder = CreateBuilder();
|
||||
|
||||
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Title);
|
||||
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message);
|
||||
|
||||
builder.AddButton(GetDismissButton());
|
||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||
ShowToast(builder);
|
||||
|
||||
ShowNotification(builder);
|
||||
}
|
||||
|
||||
public void CreateStoreUpdateNotification()
|
||||
{
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
var builder = CreateBuilder();
|
||||
|
||||
builder.AddText(Translator.Notifications_StoreUpdateAvailableTitle);
|
||||
builder.AddText(Translator.Notifications_StoreUpdateAvailableMessage);
|
||||
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)
|
||||
@@ -306,8 +277,7 @@ public class NotificationBuilder : INotificationBuilder
|
||||
if (calendarItem == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Reminder);
|
||||
var builder = CreateBuilder(AppNotificationScenario.Reminder);
|
||||
|
||||
var localStart = calendarItem.GetLocalStartDate();
|
||||
var nowLocal = DateTime.Now;
|
||||
@@ -334,49 +304,38 @@ public class NotificationBuilder : INotificationBuilder
|
||||
? preferredSnoozeMinutes
|
||||
: allowedSnoozeMinutes[0];
|
||||
|
||||
var selectionBox = new ToastSelectionBox(Constants.ToastCalendarSnoozeDurationInputId)
|
||||
{
|
||||
DefaultSelectionBoxItemId = defaultSnoozeMinutes.ToString()
|
||||
};
|
||||
var selectionBox = new AppNotificationComboBox(Constants.ToastCalendarSnoozeDurationInputId)
|
||||
.SetSelectedItem(defaultSnoozeMinutes.ToString());
|
||||
|
||||
foreach (var snoozeMinutes in allowedSnoozeMinutes)
|
||||
{
|
||||
selectionBox.Items.Add(new ToastSelectionBoxItem(
|
||||
selectionBox.AddItem(
|
||||
snoozeMinutes.ToString(),
|
||||
string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes)));
|
||||
string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes));
|
||||
}
|
||||
|
||||
builder.AddToastInput(selectionBox);
|
||||
var snoozeButton = new ToastButton()
|
||||
.SetContent(Translator.CalendarReminder_SnoozeAction)
|
||||
.SetImageUri(GetNotificationIconUri("calendar-snooze"))
|
||||
builder.AddComboBox(selectionBox);
|
||||
builder.AddButton(new AppNotificationButton(Translator.CalendarReminder_SnoozeAction)
|
||||
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarSnoozeAction)
|
||||
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar)
|
||||
.SetBackgroundActivation();
|
||||
|
||||
builder.AddButton(snoozeButton);
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
|
||||
}
|
||||
|
||||
builder.AddButton(new ToastButton()
|
||||
.SetDismissActivation()
|
||||
.SetImageUri(GetNotificationIconUri("dismiss")));
|
||||
builder.AddButton(new AppNotificationButton(Translator.Buttons_Open)
|
||||
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarNavigateAction)
|
||||
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
|
||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
|
||||
|
||||
if (Uri.TryCreate(calendarItem.HtmlLink, UriKind.Absolute, out var joinUri))
|
||||
{
|
||||
builder.AddButton(new ToastButton()
|
||||
.SetContent(Translator.CalendarEventDetails_JoinOnline)
|
||||
.SetImageUri(GetNotificationIconUri("calendar-join"))
|
||||
.SetProtocolActivation(joinUri));
|
||||
builder.AddButton(new AppNotificationButton(Translator.CalendarEventDetails_JoinOnline)
|
||||
.SetInvokeUri(joinUri));
|
||||
}
|
||||
|
||||
builder.AddAudio(new ToastAudio()
|
||||
{
|
||||
Src = new Uri("ms-winsoundevent:Notification.Reminder")
|
||||
});
|
||||
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Reminder"));
|
||||
|
||||
var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}";
|
||||
ShowToast(builder, tag);
|
||||
ShowNotification(builder, tag);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -411,17 +370,17 @@ public class NotificationBuilder : INotificationBuilder
|
||||
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))
|
||||
{
|
||||
toastNotification.Tag = tag;
|
||||
}
|
||||
notification.Tag = tag;
|
||||
|
||||
var notifier = ToastNotificationManager.CreateToastNotifier();
|
||||
notifier.Show(toastNotification);
|
||||
AppNotificationManager.Default.Show(notification);
|
||||
}
|
||||
|
||||
private Uri GetNotificationIconUri(string iconName)
|
||||
@@ -430,4 +389,3 @@ public class NotificationBuilder : INotificationBuilder
|
||||
return new($"{NotificationIconRootUri}{iconName}.png");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<ResourceDictionary
|
||||
x:Class="Wino.Mail.WinUI.Styles.OperationCommandBarResources"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Wino.Mail.WinUI.Controls">
|
||||
@@ -10,50 +11,50 @@
|
||||
<Setter Property="OverflowButtonVisibility" Value="Auto" />
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="OperationCommandBarMailOperationTemplate">
|
||||
<DataTemplate x:Key="OperationCommandBarMailOperationTemplate" x:DataType="controls:OperationCommandBarMenuOperationItemViewModel">
|
||||
<AppBarButton
|
||||
MinWidth="40"
|
||||
IsEnabled="{Binding IsEnabled}"
|
||||
Label="{Binding Label}"
|
||||
LabelPosition="{Binding LabelPosition}"
|
||||
ToolTipService.ToolTip="{Binding ToolTip}">
|
||||
IsEnabled="{x:Bind IsEnabled}"
|
||||
Label="{x:Bind Label}"
|
||||
LabelPosition="{x:Bind LabelPosition}"
|
||||
ToolTipService.ToolTip="{x:Bind ToolTip}">
|
||||
<AppBarButton.Icon>
|
||||
<controls:WinoFontIcon Icon="{Binding Icon}" />
|
||||
<controls:WinoFontIcon Icon="{x:Bind Icon}" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="OperationCommandBarFolderOperationTemplate">
|
||||
<DataTemplate x:Key="OperationCommandBarFolderOperationTemplate" x:DataType="controls:OperationCommandBarMenuOperationItemViewModel">
|
||||
<AppBarButton
|
||||
MinWidth="40"
|
||||
IsEnabled="{Binding IsEnabled}"
|
||||
Label="{Binding Label}"
|
||||
LabelPosition="{Binding LabelPosition}"
|
||||
ToolTipService.ToolTip="{Binding ToolTip}">
|
||||
IsEnabled="{x:Bind IsEnabled}"
|
||||
Label="{x:Bind Label}"
|
||||
LabelPosition="{x:Bind LabelPosition}"
|
||||
ToolTipService.ToolTip="{x:Bind ToolTip}">
|
||||
<AppBarButton.Icon>
|
||||
<controls:WinoFontIcon Icon="{Binding Icon}" />
|
||||
<controls:WinoFontIcon Icon="{x:Bind Icon}" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="OperationCommandBarAIActionsTemplate">
|
||||
<DataTemplate x:Key="OperationCommandBarAIActionsTemplate" x:DataType="controls:OperationCommandBarAIActionsItemViewModel">
|
||||
<AppBarToggleButton
|
||||
MinWidth="40"
|
||||
LabelPosition="Collapsed"
|
||||
ToolTipService.ToolTip="{Binding ToolTip}">
|
||||
ToolTipService.ToolTip="{x:Bind ToolTip}">
|
||||
<AppBarToggleButton.Icon>
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{Binding Glyph}" />
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{x:Bind Glyph}" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="OperationCommandBarThemeToggleTemplate">
|
||||
<DataTemplate x:Key="OperationCommandBarThemeToggleTemplate" x:DataType="controls:OperationCommandBarThemeItemViewModel">
|
||||
<AppBarButton
|
||||
MinWidth="40"
|
||||
LabelPosition="Collapsed"
|
||||
ToolTipService.ToolTip="{Binding ToolTip}">
|
||||
ToolTipService.ToolTip="{x:Bind ToolTip}">
|
||||
<AppBarButton.Icon>
|
||||
<controls:WinoFontIcon Icon="{Binding Icon}" />
|
||||
<controls:WinoFontIcon Icon="{x:Bind Icon}" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
</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"
|
||||
Click="AttentionIconClicked"
|
||||
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}">
|
||||
<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 " />
|
||||
<TextBlock Text="{x:Bind domain:Translator.Info_AccountAttentionRequiredAction, Mode=OneWay}" />
|
||||
<TextBlock Text="{x:Bind domain:Translator.Info_AccountAttentionRequiredAction}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
@@ -567,8 +567,8 @@
|
||||
<ProgressBar
|
||||
Height="4"
|
||||
IsIndeterminate="True"
|
||||
ShowPaused="False"
|
||||
ShowError="False"
|
||||
ShowPaused="False"
|
||||
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -13,29 +13,10 @@
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
<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 -->
|
||||
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="Google.Apis.Auth" />
|
||||
<TrimmerRootAssembly Include="Google.Apis.Drive.v3" />
|
||||
<TrimmerRootAssembly Include="Google.Apis.Gmail.v1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="Assets\BadgeLogo.scale-100.png" />
|
||||
@@ -203,7 +184,6 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Lottie" />
|
||||
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
|
||||
@@ -215,7 +195,6 @@
|
||||
<PackageReference Include="Sentry.Serilog" />
|
||||
<PackageReference Include="sqlite-net-pcl" />
|
||||
<PackageReference Include="EmailValidation" />
|
||||
<PackageReference Include="System.Drawing.Common" />
|
||||
<PackageReference Include="SkiaSharp.Views.WinUI" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
<PackageReference Include="Wino.Mail.Contracts" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Diagnostics;
|
||||
@@ -662,14 +662,40 @@ public class AccountService : BaseDatabaseService, IAccountService
|
||||
|
||||
private static string GetReadableTextColorHex(string backgroundColorHex)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(backgroundColorHex))
|
||||
if (!TryParseHexColor(backgroundColorHex, out var red, out var green, out var blue))
|
||||
return "#FFFFFF";
|
||||
|
||||
var color = ColorTranslator.FromHtml(backgroundColorHex);
|
||||
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";
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
foreach (var pair in accountIdOrderPair)
|
||||
|
||||
@@ -421,14 +421,14 @@ public sealed class CalDavClient : ICalDavClient
|
||||
var result = new List<CalDavCalendarEvent>();
|
||||
|
||||
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)
|
||||
.Select(g => g.First())
|
||||
.ToList();
|
||||
|
||||
var exceptionMap = allEvents
|
||||
.Where(e => e != null && !string.IsNullOrWhiteSpace(e.Uid) && e.RecurrenceId != null)
|
||||
.GroupBy(e => $"{e.Uid}|{GetOccurrenceKey(e.RecurrenceId)}", StringComparer.OrdinalIgnoreCase)
|
||||
.Where(e => e != null && !string.IsNullOrWhiteSpace(e.Uid) && GetRecurrenceId(e) != null)
|
||||
.GroupBy(e => $"{e.Uid}|{GetOccurrenceKey(GetRecurrenceId(e))}", StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var consumedExceptions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
@@ -453,7 +453,15 @@ public sealed class CalDavClient : ICalDavClient
|
||||
seriesMasterRemoteEventId: string.Empty,
|
||||
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)
|
||||
{
|
||||
@@ -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))
|
||||
continue;
|
||||
|
||||
@@ -525,7 +534,7 @@ public sealed class CalDavClient : ICalDavClient
|
||||
sourceEvent: exceptionEvent,
|
||||
start: start,
|
||||
end: end,
|
||||
remoteEventId: BuildRemoteEventId(exceptionEvent.Uid, GetOccurrenceKey(exceptionEvent.RecurrenceId)),
|
||||
remoteEventId: BuildRemoteEventId(exceptionEvent.Uid, GetOccurrenceKey(recurrenceId)),
|
||||
resourceHref: resourceHref,
|
||||
eTag: eTag,
|
||||
icsContent: icsContent,
|
||||
@@ -546,16 +555,30 @@ public sealed class CalDavClient : ICalDavClient
|
||||
|
||||
private static bool HasRecurrence(CalendarEvent calendarEvent)
|
||||
=> (calendarEvent.RecurrenceRules?.Any() ?? false)
|
||||
|| (calendarEvent.RecurrenceDates?.Any() ?? false);
|
||||
|| (calendarEvent.RecurrenceDates?.GetAllPeriods().Any() ?? false);
|
||||
|
||||
private static string BuildRemoteEventId(string uid, string 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'");
|
||||
|
||||
private static DateTimeOffset ToDateTimeOffset(IDateTime dateTime)
|
||||
=> dateTime?.AsDateTimeOffset ?? default;
|
||||
private static DateTimeOffset ToDateTimeOffset(CalDateTime dateTime)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -601,7 +624,7 @@ public sealed class CalDavClient : ICalDavClient
|
||||
.Where(a => a?.Trigger != null && a.Trigger.IsRelative && a.Trigger.Duration.HasValue)
|
||||
.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)
|
||||
? CalendarItemReminderType.Email
|
||||
: 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;
|
||||
if (!string.IsNullOrWhiteSpace(explicitTimeZoneId))
|
||||
@@ -664,35 +687,27 @@ public sealed class CalDavClient : ICalDavClient
|
||||
|
||||
if (sourceEvent.ExceptionDates != null)
|
||||
{
|
||||
foreach (var periodList in sourceEvent.ExceptionDates)
|
||||
{
|
||||
var dates = periodList
|
||||
.Where(p => p.StartTime != null)
|
||||
.Select(p => p.StartTime.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'"))
|
||||
var dates = sourceEvent.ExceptionDates
|
||||
.GetAllDates()
|
||||
.Where(d => d != null)
|
||||
.Select(d => d.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'"))
|
||||
.ToList();
|
||||
|
||||
if (dates.Count > 0)
|
||||
{
|
||||
recurrenceLines.Add($"EXDATE:{string.Join(",", dates)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceEvent.RecurrenceDates != null)
|
||||
{
|
||||
foreach (var periodList in sourceEvent.RecurrenceDates)
|
||||
{
|
||||
var dates = periodList
|
||||
var dates = sourceEvent.RecurrenceDates
|
||||
.GetAllPeriods()
|
||||
.Where(p => p.StartTime != null)
|
||||
.Select(p => p.StartTime.AsUtc.ToString("yyyyMMdd'T'HHmmss'Z'"))
|
||||
.ToList();
|
||||
|
||||
if (dates.Count > 0)
|
||||
{
|
||||
recurrenceLines.Add($"RDATE:{string.Join(",", dates)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return recurrenceLines.Count == 0
|
||||
? string.Empty
|
||||
|
||||
Reference in New Issue
Block a user